diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ebad1c9701..2cc9f00b2d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -413,6 +413,7 @@ set(PARALLEL_QUERY_DIR ${CMAKE_SOURCE_DIR}/src/query/parallel) set(PARALLEL_HASH_JOIN_DIR ${CMAKE_SOURCE_DIR}/src/query/parallel/px_hash_join) set(PARALLEL_HEAP_SCAN_DIR ${CMAKE_SOURCE_DIR}/src/query/parallel/px_heap_scan) set(PARALLEL_QUERY_EXECUTE_DIR ${CMAKE_SOURCE_DIR}/src/query/parallel/px_query_execute) +set(HISTOGRAM_DIR ${CMAKE_SOURCE_DIR}/src/histogram) include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories( @@ -444,6 +445,7 @@ include_directories( src/query/parallel/px_heap_scan src/query/parallel src/query/parallel/px_query_execute + src/histogram ) if(WITH_CCI) include_directories( diff --git a/cs/CMakeLists.txt b/cs/CMakeLists.txt index 42eeaed7b5e..0cfe5bb311e 100644 --- a/cs/CMakeLists.txt +++ b/cs/CMakeLists.txt @@ -427,6 +427,18 @@ set(PARALLEL_QUERY_EXECUTE_HEADERS ${PARALLEL_QUERY_EXECUTE_DIR}/px_query_checker.hpp ) +set (HISTOGRAM_SOURCES + ${HISTOGRAM_DIR}/histogram_cl.cpp + ${HISTOGRAM_DIR}/histogram_builder.cpp + ${HISTOGRAM_DIR}/histogram_reader.cpp + ) + +set (HISTOGRAM_HEADERS + ${HISTOGRAM_DIR}/histogram_cl.hpp + ${HISTOGRAM_DIR}/histogram_builder.hpp + ${HISTOGRAM_DIR}/histogram_reader.hpp + ) + list(APPEND CONNECTION_SOURCES ${CONNECTION_DIR}/heartbeat.c) if(UNIX) list(APPEND EXECUTABLE_SOURCES ${EXECUTABLES_DIR}/checksumdb.c) @@ -512,6 +524,7 @@ SET_SOURCE_FILES_PROPERTIES( ${API_SOURCES} ${PARALLEL_HEAP_SCAN_SOURCES} ${PARALLEL_QUERY_EXECUTE_SOURCES} + ${HISTOGRAM_SOURCES} PROPERTIES LANGUAGE CXX ) SET_SOURCE_FILES_PROPERTIES( @@ -558,6 +571,8 @@ add_library(cubridcs SHARED ${PARALLEL_HEAP_SCAN_HEADERS} ${PARALLEL_QUERY_EXECUTE_SOURCES} ${PARALLEL_QUERY_EXECUTE_HEADERS} + ${HISTOGRAM_SOURCES} + ${HISTOGRAM_HEADERS} ) set_target_properties(cubridcs PROPERTIES SOVERSION "${CUBRID_MAJOR_VERSION}.${CUBRID_MINOR_VERSION}") diff --git a/sa/CMakeLists.txt b/sa/CMakeLists.txt index ceea59a3a02..ce7423456dc 100644 --- a/sa/CMakeLists.txt +++ b/sa/CMakeLists.txt @@ -544,6 +544,17 @@ set(XASL_HEADERS ${XASL_DIR}/xasl_iteration.hpp ) +set(HISTOGRAM_SOURCES + ${HISTOGRAM_DIR}/histogram_cl.cpp + ${HISTOGRAM_DIR}/histogram_reader.cpp + ${HISTOGRAM_DIR}/histogram_builder.cpp +) +set(HISTOGRAM_HEADERS + ${HISTOGRAM_DIR}/histogram_cl.hpp + ${HISTOGRAM_DIR}/histogram_reader.hpp + ${HISTOGRAM_DIR}/histogram_builder.hpp +) + set(LOADDB_SOURCES ${BISON_loader_grammar_OUTPUT_SOURCE} ${FLEX_loader_lexer_OUTPUTS} @@ -646,6 +657,7 @@ SET_SOURCE_FILES_PROPERTIES( ${PROBES_OBJECT} ${XASL_SOURCES} ${LOADDB_SOURCES} + ${HISTOGRAM_SOURCES} PROPERTIES LANGUAGE CXX ) @@ -699,6 +711,8 @@ add_library(cubridsa SHARED ${XASL_SOURCES} ${LOADDB_SOURCES} ${LOADDB_HEADERS} + ${HISTOGRAM_SOURCES} + ${HISTOGRAM_HEADERS} ) set_target_properties(cubridsa PROPERTIES SOVERSION "${CUBRID_MAJOR_VERSION}.${CUBRID_MINOR_VERSION}") diff --git a/src/base/ddl_log.c b/src/base/ddl_log.c index bdb77288425..0f7f928dd03 100644 --- a/src/base/ddl_log.c +++ b/src/base/ddl_log.c @@ -1433,6 +1433,8 @@ logddl_is_ddl_type (int node_type, PT_NODE * node) case PT_CREATE_ENTITY: case PT_CREATE_INDEX: case PT_CREATE_SERIAL: + case PT_UPDATE_HISTOGRAM: + case PT_DROP_HISTOGRAM: case PT_CREATE_STORED_PROCEDURE: case PT_CREATE_SYNONYM: case PT_CREATE_TRIGGER: diff --git a/src/compat/db.h b/src/compat/db.h index 5b0fa6eed4c..8d40a7598e5 100644 --- a/src/compat/db.h +++ b/src/compat/db.h @@ -253,6 +253,7 @@ extern DB_OBJECT *db_create_internal (DB_OBJECT * obj); extern DB_OBJECT *db_create_by_name_internal (const char *name); extern int db_put_internal (DB_OBJECT * obj, const char *name, DB_VALUE * value); extern DB_OTMPL *dbt_create_object_internal (DB_OBJECT * classobj); +extern DB_OTMPL *dbt_create_object_internal_for_read_only (DB_OBJECT * classobj); extern int dbt_put_internal (DB_OTMPL * def, const char *name, DB_VALUE * value); extern int db_dput_internal (DB_OBJECT * obj, DB_ATTDESC * attribute, DB_VALUE * value); extern int dbt_dput_internal (DB_OTMPL * def, DB_ATTDESC * attribute, DB_VALUE * value); diff --git a/src/compat/db_obj.c b/src/compat/db_obj.c index aeec1a4d546..4aab9e04361 100644 --- a/src/compat/db_obj.c +++ b/src/compat/db_obj.c @@ -512,6 +512,20 @@ dbt_create_object_internal (MOP classobj) return def; } +DB_OTMPL * +dbt_create_object_internal_for_read_only (MOP classobj) +{ + DB_OTMPL *def = NULL; + + CHECK_CONNECT_NULL (); + CHECK_1ARG_NULL (classobj); + CHECK_MODIFICATION_NULL (); + + def = obt_def_object_for_read_only (classobj); + + return def; +} + /* * dbt_edit_object() - This function creates an object template for an existing * object. The template is initially empty. The template is populated with @@ -1184,6 +1198,7 @@ db_find_multi_unique (MOP classmop, int size, char *attr_names[], DB_VALUE * val obj_find_multi_attr (classmop, size, (const char **) attr_names, (const DB_VALUE **) values, purpose == DB_FETCH_WRITE ? AU_FETCH_UPDATE : AU_FETCH_READ); + er_clear (); return retval; } diff --git a/src/compat/dbi_compat.h b/src/compat/dbi_compat.h index babbea9497b..64c9eb67951 100644 --- a/src/compat/dbi_compat.h +++ b/src/compat/dbi_compat.h @@ -63,6 +63,8 @@ extern "C" #define SQLX_CMD_REGISTER_DATABASE CUBRID_STMT_REGISTER_DATABASE #define SQLX_CMD_CREATE_CLASS CUBRID_STMT_CREATE_CLASS #define SQLX_CMD_CREATE_INDEX CUBRID_STMT_CREATE_INDEX +#define SQLX_CMD_UPDATE_HISTOGRAM CUBRID_STMT_UPDATE_HISTOGRAM +#define SQLX_CMD_DROP_HISTOGRAM CUBRID_STMT_DROP_HISTOGRAM #define SQLX_CMD_CREATE_TRIGGER CUBRID_STMT_CREATE_TRIGGER #define SQLX_CMD_CREATE_SERIAL CUBRID_STMT_CREATE_SERIAL #define SQLX_CMD_DROP_DATABASE CUBRID_STMT_DROP_DATABASE diff --git a/src/compat/dbtype_def.h b/src/compat/dbtype_def.h index e96af124b76..0c694cfeb03 100644 --- a/src/compat/dbtype_def.h +++ b/src/compat/dbtype_def.h @@ -119,7 +119,8 @@ extern "C" CUBRID_STMT_ALTER_USER, CUBRID_STMT_SET_SYS_PARAMS, CUBRID_STMT_ALTER_INDEX, - + CUBRID_STMT_UPDATE_HISTOGRAM, + CUBRID_STMT_DROP_HISTOGRAM, CUBRID_STMT_CREATE_STORED_PROCEDURE, CUBRID_STMT_DROP_STORED_PROCEDURE, CUBRID_STMT_PREPARE_STATEMENT, diff --git a/src/executables/csql_result.c b/src/executables/csql_result.c index 5f4cf1e032d..aeeb4b05759 100644 --- a/src/executables/csql_result.c +++ b/src/executables/csql_result.c @@ -108,6 +108,8 @@ static CSQL_CMD_STRING_TABLE csql_Cmd_string_table[] = { {CUBRID_STMT_ROLLBACK_WORK, "ROLLBACK"}, {CUBRID_STMT_GRANT, "GRANT"}, {CUBRID_STMT_REVOKE, "REVOKE"}, + {CUBRID_STMT_UPDATE_HISTOGRAM, "ANALYZE UPDATE HISTOGRAM"}, + {CUBRID_STMT_DROP_HISTOGRAM, "ANALYZE DROP HISTOGRAM"}, {CUBRID_STMT_CREATE_USER, "CREATE USER"}, {CUBRID_STMT_DROP_USER, "DROP USER"}, {CUBRID_STMT_ALTER_USER, "ALTER USER"}, diff --git a/src/executables/unload_object.c b/src/executables/unload_object.c index 7afbd772bcf..25ec750e296 100644 --- a/src/executables/unload_object.c +++ b/src/executables/unload_object.c @@ -139,6 +139,7 @@ static const char *prohibited_classes[] = { CT_DUAL_NAME, CT_SERVER_NAME, CT_SYNONYM_NAME, + CT_DB_HISTOGRAM_NAME, /* catalog vclasses */ CTV_CLASS_NAME, CTV_SUPER_CLASS_NAME, @@ -162,6 +163,7 @@ static const char *prohibited_classes[] = { CTV_CHARSET_NAME, CTV_SERVER_NAME, CTV_SYNONYM_NAME, + CTV_DB_HISTOGRAM_NAME, NULL }; diff --git a/src/histogram/histogram_builder.cpp b/src/histogram/histogram_builder.cpp new file mode 100644 index 00000000000..2f2b5667510 --- /dev/null +++ b/src/histogram/histogram_builder.cpp @@ -0,0 +1,265 @@ +/* + * Copyright 2008 Search Solution Corporation + * Copyright 2016 CUBRID Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * histogram_builder.cpp - Histogram builder implementation + */ + +#include "histogram_builder.hpp" +#include "histogram_reader.hpp" +#include +#include "object_domain.h" + +namespace hist +{ + // ---------- endian writers for buffer (explicit specializations before use) ---------- + template<> + void HistogramBuilder::write (char *&dest, std::int32_t v) + { + OR_PUT_INT (dest, v); + dest += OR_INT64_SIZE; // keep 8-byte slot for data_hi (4B value + 4B padding) + } + + template<> + void HistogramBuilder::write (char *&dest, std::int64_t v) + { + OR_PUT_INT64 (dest, &v); + dest += OR_INT64_SIZE; + } + + template<> + void HistogramBuilder::write (char *&dest, std::uint64_t v) + { + OR_PUT_INT64 (dest, reinterpret_cast (&v)); + dest += OR_INT64_SIZE; + } + + template<> + void HistogramBuilder::write (char *&dest, double v) + { + OR_PUT_DOUBLE (dest, v); + dest += OR_DOUBLE_SIZE; + } + + template<> + void HistogramBuilder::write (char *&dest, std::string v) + { + // write length and offset or inline data + OR_PUT_INT (dest, v.length()); + dest += OR_INT_SIZE; + if (v.length() <= 4) + { + // inline data + memcpy (dest, v.data(), v.length()); + } + else + { + // str blob data (length + pointer) + OR_PUT_INT (dest, cur_str_off_); + cur_str_off_ += v.length(); + } + dest += OR_INT_SIZE; + } + + void HistogramBuilder::add (HistogramTypes data_hi, std::int64_t cumulative, std::int64_t approx_ndv) + { + assert (cumulative >= 0); + buckets_.push_back (Bucket{data_hi, cumulative, approx_ndv}); + } + + char *HistogramBuilder::build (THREAD_ENTRY *thread_p, DB_TYPE type, int *histogram_total_length) + { + /* ---- precompute record sizes ---- */ + const std::uint32_t bucket_area_size = hist::BUCKET_RECORD_SIZE * buckets_.size(); + + /* ---- header ---- */ + HeaderV1 H{}; + std::memcpy (H.magic, "HST1", 4); + H.version = htonl (1); + H.nbuckets = htonl (static_cast (buckets_.size())); + H.type = htonl (static_cast (type)); + H.str_size = 0; + H.total_size = 0; + + /* ---- records ---- */ + char *buffer = static_cast (db_private_alloc (thread_p, sizeof (HeaderV1) + bucket_area_size)); + if (buffer == NULL) + { + return NULL; + } + std::memset (buffer, 0, sizeof (HeaderV1) + bucket_area_size); // must be initialized to zero + char *end_buffer = buffer + sizeof (HeaderV1) + bucket_area_size; + char *buffer_ptr = buffer + sizeof (HeaderV1); + char *str_blob_ptr; + + /* ---- index-based loop for safer access ---- */ + for (size_t i = 0; i < buckets_.size(); ++i) + { + const Bucket b = buckets_[i]; + switch (type) + { + /* ---- int64_t value ---- */ + case DB_TYPE_INTEGER: + case DB_TYPE_SHORT: + case DB_TYPE_BIGINT: + { + if (std::holds_alternative (b.data_hi)) + { + // ---- int64_t value to int32_t value ---- + std::int64_t val = std::get (b.data_hi); + write (buffer_ptr, static_cast (val)); + } + else + { + db_private_free (thread_p, buffer); + assert (false); + return NULL; + } + } + break; + /* ---- double value ---- */ + case DB_TYPE_DOUBLE: + case DB_TYPE_FLOAT: + case DB_TYPE_NUMERIC: + { + if (std::holds_alternative (b.data_hi)) + { + write (buffer_ptr, std::get (b.data_hi)); + } + else + { + db_private_free (thread_p, buffer); + assert (false); + return NULL; + } + } + break; + /* ---- string value ---- */ + case DB_TYPE_STRING: + case DB_TYPE_BIT: + case DB_TYPE_VARBIT: + case DB_TYPE_CHAR: + { + if (std::holds_alternative (b.data_hi)) + { + write (buffer_ptr, std::get (b.data_hi)); + } + else if (std::holds_alternative (b.data_hi)) + { + std::string_view sv = std::get (b.data_hi); + write (buffer_ptr, std::string (sv)); + } + else + { + db_private_free (thread_p, buffer); + assert (false); + return NULL; + } + } + break; + /* ---- uint64_t value ---- */ + case DB_TYPE_TIME: + case DB_TYPE_TIMESTAMP: + case DB_TYPE_TIMESTAMPLTZ: + case DB_TYPE_DATE: + case DB_TYPE_MONETARY: + case DB_TYPE_TIMESTAMPTZ: + { + if (std::holds_alternative (b.data_hi)) + { + write (buffer_ptr, std::get (b.data_hi)); + } + else + { + db_private_free (thread_p, buffer); + assert (false); + return NULL; + } + } + break; + default: + /* never reach here */ + db_private_free (thread_p, buffer); + assert (false); + return NULL; + } + write (buffer_ptr, b.cumulative); + write (buffer_ptr, b.approx_ndv); + } + + assert (buffer_ptr == end_buffer); + + /* ---- build string blob ---- */ + if (cur_str_off_ > 0) + { + str_blob_ptr = static_cast (db_private_alloc (thread_p, cur_str_off_)); + if (str_blob_ptr == NULL) + { + db_private_free (thread_p, buffer); + return NULL; + } + char *cur_str_blob_ptr = str_blob_ptr; + std::memset (str_blob_ptr, 0, cur_str_off_); // must be initialized to zero + char *str_blob_ptr_end = str_blob_ptr + cur_str_off_; + for (const auto &b : buckets_) + { + std::string str_val; + if (std::holds_alternative (b.data_hi)) + { + str_val = std::get (b.data_hi); + } + else if (std::holds_alternative (b.data_hi)) + { + str_val = std::string (std::get (b.data_hi)); + } + else + { + db_private_free (thread_p, buffer); + db_private_free (thread_p, str_blob_ptr); + assert (false); + return NULL; + } + + if (str_val.length() > 4) + { + memcpy (cur_str_blob_ptr, str_val.data(), str_val.length()); + cur_str_blob_ptr += str_val.length(); + } + } + /* ---- write string ---- */ + assert (cur_str_blob_ptr == str_blob_ptr_end); + buffer = static_cast (db_private_realloc (thread_p, buffer, + sizeof (HeaderV1) + bucket_area_size + cur_str_off_)); + if (buffer == NULL) + { + db_private_free (thread_p, str_blob_ptr); + return NULL; + } + memcpy (buffer + sizeof (HeaderV1) + bucket_area_size, str_blob_ptr, cur_str_off_); + db_private_free (thread_p, str_blob_ptr); + } + + /* ---- write header ---- */ + H.str_size = htonl (static_cast (cur_str_off_)); + H.total_size = htonl (static_cast (sizeof (HeaderV1) + bucket_area_size + cur_str_off_)); + memcpy (buffer, &H, sizeof (HeaderV1)); + *histogram_total_length = sizeof (HeaderV1) + bucket_area_size + cur_str_off_; + + return buffer; + } +} // namespace hist diff --git a/src/histogram/histogram_builder.hpp b/src/histogram/histogram_builder.hpp new file mode 100644 index 00000000000..d97d29cdf80 --- /dev/null +++ b/src/histogram/histogram_builder.hpp @@ -0,0 +1,63 @@ +/* + * Copyright 2008 Search Solution Corporation + * Copyright 2016 CUBRID Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * histogram_builder.hpp - Histogram builder declaration + */ + +#ifndef _HISTOGRAM_BUILDER_HPP_ +#define _HISTOGRAM_BUILDER_HPP_ + +#include +#include +#include +#include +#include +#include "histogram_reader.hpp" +#include "object_representation.h" + +namespace hist +{ + using HistogramTypes = std::variant; + struct Bucket + { + HistogramTypes data_hi; /* std::variant: int64_t, uint64_t, double, string_view, string */ + std::int64_t cumulative; + std::int64_t approx_ndv; + }; + + class HistogramBuilder + { + public: + void add (HistogramTypes data_hi, std::int64_t cumulative, + std::int64_t approx_ndv = std::numeric_limits::quiet_NaN()); + char *build (THREAD_ENTRY *thread_p, DB_TYPE type, int *histogram_total_length); + + private: + HeaderV1 header_; + std::vector buckets_; + std::int32_t cur_str_off_ = 0; + + // endian writers + template + void write (char *&dest, T v); + }; + +} // namespace histo + +#endif // _HISTOGRAM_BUILDER_HPP_ \ No newline at end of file diff --git a/src/histogram/histogram_cl.cpp b/src/histogram/histogram_cl.cpp new file mode 100644 index 00000000000..6cb9cee1022 --- /dev/null +++ b/src/histogram/histogram_cl.cpp @@ -0,0 +1,1454 @@ +/* + * Copyright 2008 Search Solution Corporation + * Copyright 2016 CUBRID Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * histogram_cl.cpp - Histogram Client Library implementation + */ + + +#include "dbtype_def.h" +#include "histogram_cl.hpp" +#include "db.h" +#include "histogram_builder.hpp" +#include "thread_compat.hpp" +#include "db_query.h" +#include "locator_cl.h" +#include "schema_manager.h" +#include "schema_system_catalog_constants.h" +#include +#include +#include +#include "parser.h" +#include "class_object.h" +#include "object_accessor.h" +#include "authenticate.h" +#include "query_planner.h" + +static bool histogram_extract_key (const DB_VALUE *db_val, hist::histogram_key &key); + +/* + * analyze_classes () + * + * return: NO_ERROR if successful, otherwise an error code + * thread_p(in): thread pointer + * tbl_name(in): table name + * attr_name(in): attribute name + * max_number_of_buckets(in): maximum number of buckets + * with_fullscan(in): true iff WITH FULLSCAN + * classop(in): class object pointer + */ +int +analyze_classes (THREAD_ENTRY *thread_p, const char *tbl_name, const char *attr_name, int max_number_of_buckets, + bool with_fullscan, MOP classop) +{ + int error = NO_ERROR; + char *histogram_blob = NULL; + int histogram_total_length = 0; + + // ---- get null frequency ---- + error = get_null_frequency (thread_p, tbl_name, attr_name, with_fullscan, classop); + if (error != NO_ERROR) + { + return error; + } + + // ---- get histogram ---- + error = + get_histogram (thread_p, tbl_name, attr_name, max_number_of_buckets, with_fullscan, &histogram_blob, + &histogram_total_length); + if (error != NO_ERROR) + { + if (histogram_blob != NULL) + { + db_private_free (thread_p, histogram_blob); + } + return error; + } + + // ---- set histogram ---- + error = set_histogram (thread_p, tbl_name, attr_name, histogram_blob, histogram_total_length, classop); + if (error != NO_ERROR) + { + if (histogram_blob != NULL) + { + db_private_free (thread_p, histogram_blob); + } + return error; + } + + // ---- free histogram blob ---- + if (histogram_blob != NULL) + { + db_private_free (thread_p, histogram_blob); + } + + return NO_ERROR; +} + +/* + * get_null_frequency () + * + * return: NO_ERROR if successful, otherwise an error code + * classop(in): class object pointer + * attr_name(in): attribute name + * null_frequency(out): null frequency + */ +int +get_null_frequency (THREAD_ENTRY *thread_p, const char *tbl_name, const char *attr_name, bool with_fullscan, + MOP classop) +{ + int error = NO_ERROR; + DB_OBJECT *histogram_obj, *edit_histogram_object = NULL; + DB_OTMPL *obj_tmpl = NULL; + DB_VALUE null_frequency_value; + DB_QUERY_RESULT *query_result; + DB_QUERY_ERROR query_error; + + /* (query_length + table_name_length + attr_name_length) */ + char query_buf[512+222+254]; + + if (!with_fullscan) + { + snprintf (query_buf, sizeof (query_buf), NULL_FREQUENCY_WITH_SAMPLING_SCAN_QUERY_TEMPLATE, attr_name, tbl_name); + } + else + { + snprintf (query_buf, sizeof (query_buf), NULL_FREQUENCY_QUERY_TEMPLATE, attr_name, tbl_name); + } + + error = db_compile_and_execute_local (query_buf, &query_result, &query_error); + + if (error < 1) + { + db_query_end (query_result); + return error; + } + + error = db_query_first_tuple (query_result); + + if (error != DB_CURSOR_SUCCESS) + { + if (error == DB_CURSOR_END) + { + error = NO_ERROR; + } + else + { + ASSERT_ERROR (); + } + db_query_end (query_result); + return error; + } + + error = db_query_get_tuple_value_by_name (query_result, const_cast < char *> ("null_frequency"), &null_frequency_value); + if (error != NO_ERROR) + { + error = ER_FAILED; + goto end; + } + + error = db_get_histogram (classop, attr_name, &histogram_obj); + if (error != NO_ERROR) + { + error = ER_FAILED; + goto end; + } + + obj_tmpl = dbt_edit_object (histogram_obj); + if (obj_tmpl == NULL) + { + error = ER_FAILED; + dbt_abort_object (obj_tmpl); + goto end; + } + + error = dbt_put (obj_tmpl, "null_frequency", &null_frequency_value); + if (error != NO_ERROR) + { + error = ER_FAILED; + dbt_abort_object (obj_tmpl); + goto end; + } + + edit_histogram_object = dbt_finish_object (obj_tmpl); + if (edit_histogram_object == NULL) + { + assert (er_errid () != NO_ERROR); + error = er_errid (); + goto end; + } + + assert (edit_histogram_object == histogram_obj); + obj_tmpl = NULL; + + error = locator_flush_instance (edit_histogram_object); + if (error != NO_ERROR) + { + error = ER_FAILED; + goto end; + } + +end: + db_query_end (query_result); + db_value_clear (&null_frequency_value); + return error; +} + +/* + * get_histogram () + * + * return: NO_ERROR if successful, otherwise an error code + * thread_p(in): thread pointer + * tbl_name(in): table name + * attr_name(in): attribute name + * max_number_of_buckets(in): maximum number of buckets + * with_fullscan(in): true iff WITH FULLSCAN + * histogram_blob(out): histogram blob + * histogram_total_length(out): histogram total length + */ +int +get_histogram (THREAD_ENTRY *thread_p, const char *tbl_name, const char *attr_name, int max_number_of_buckets, + bool with_fullscan, char **histogram_blob, int *histogram_total_length) +{ + int error = NO_ERROR; + DB_QUERY_RESULT *query_result; + DB_QUERY_ERROR query_error; + hist::HistogramBuilder histogram_builder; + DB_TYPE type = DB_TYPE_UNKNOWN; + // ---- number of MCV ---- + int number_of_mcv = std::min (100, max_number_of_buckets / 2); + + // ---- query buffer ---- (query_length + table_name_length + attr_name_length) + char query_buf[1024+222+254]; + + if (!with_fullscan) + { + snprintf (query_buf, sizeof (query_buf), HISTOGRAM_WITH_SAMPLING_SCAN_QUERY_TEMPLATE, attr_name, tbl_name, + attr_name, number_of_mcv, max_number_of_buckets, max_number_of_buckets); + } + else + { + snprintf (query_buf, sizeof (query_buf), HISTOGRAM_QUERY_TEMPLATE, attr_name, tbl_name, attr_name, + number_of_mcv, max_number_of_buckets, max_number_of_buckets); + } + + error = db_compile_and_execute_local (query_buf, &query_result, &query_error); + + if (error < 0) + { + db_query_end (query_result); + return error; + } + + if (error == 0) /* empty histogram */ + { + goto build_histogram; + } + + error = db_query_first_tuple (query_result); + + if (error != DB_CURSOR_SUCCESS) + { + if (error == DB_CURSOR_END) + { + error = NO_ERROR; + } + else + { + ASSERT_ERROR (); + } + error = ER_FAILED; + goto error_end; + } + + + do + { + DB_VALUE value[4]; + hist::HistogramTypes hi{}; + error = db_query_get_tuple_value_by_name (query_result, const_cast < char *> ("endpoint"), &value[0]); + if (error != NO_ERROR) + { + error = ER_FAILED; + goto error_end; + } + error = db_query_get_tuple_value_by_name (query_result, const_cast < char *> ("rows_in_bucket"), &value[1]); + if (error != NO_ERROR) + { + error = ER_FAILED; + goto error_end; + } + error = db_query_get_tuple_value_by_name (query_result, const_cast < char *> ("cumulative"), &value[2]); + if (error != NO_ERROR) + { + error = ER_FAILED; + goto error_end; + } + error = db_query_get_tuple_value_by_name (query_result, const_cast < char *> ("approx_ndv"), &value[3]); + if (error != NO_ERROR) + { + error = ER_FAILED; + goto error_end; + } + + /* ---- extract key from DB_VALUE ---- */ + hist::histogram_key key; + if (!histogram_extract_key (&value[0], key)) + { + assert (false); + error = ER_FAILED; + goto error_end; + } + + type = static_cast (value[0].domain.general_info.type); + + switch (key.kind) + { + case hist::histogram_key_kind::i64: + { + histogram_builder.add (static_cast (key.i64), db_get_bigint (&value[2]), db_get_bigint (&value[3])); + break; + } + case hist::histogram_key_kind::dbl: + { + histogram_builder.add (static_cast (key.dbl), db_get_bigint (&value[2]), db_get_bigint (&value[3])); + break; + } + case hist::histogram_key_kind::str: + { + histogram_builder.add (key.str, db_get_bigint (&value[2]), db_get_bigint (&value[3])); + break; + } + case hist::histogram_key_kind::u64: + { + histogram_builder.add (key.u64, db_get_bigint (&value[2]), db_get_bigint (&value[3])); + break; + } + default: + { + /* never reach here */ + assert (false); + db_value_clear (&value[0]); + db_value_clear (&value[1]); + db_value_clear (&value[2]); + db_value_clear (&value[3]); + error = ER_FAILED; + goto error_end; + } + } + db_value_clear (&value[0]); + db_value_clear (&value[1]); + db_value_clear (&value[2]); + db_value_clear (&value[3]); + } + while (db_query_next_tuple (query_result) == DB_CURSOR_SUCCESS); + +build_histogram: + + db_query_end (query_result); + *histogram_blob = histogram_builder.build (thread_p, type, histogram_total_length); + if (*histogram_blob == NULL) + { + return ER_FAILED; + } + + return NO_ERROR; + +error_end: + db_query_end (query_result); + return error; +} + +int +set_histogram (THREAD_ENTRY *thread_p, const char *tbl_name, const char *attr_name, char *histogram_blob, + int histogram_total_length, MOP classop) +{ + int error = NO_ERROR; + DB_OBJECT *histogram_obj, *edit_histogram_object = NULL; + DB_OTMPL *obj_tmpl = NULL; + DB_VALUE histogram_value; + error = db_get_histogram (classop, attr_name, &histogram_obj); + if (error != NO_ERROR) + { + return error; + } + + obj_tmpl = dbt_edit_object (histogram_obj); + if (obj_tmpl == NULL) + { + assert (er_errid () != NO_ERROR); + error = er_errid (); + goto end; + } + + /* SM_MAX_STRING_LENGTH = 1073741823 */ + db_make_varbit (&histogram_value, 1073741823, histogram_blob, histogram_total_length * 8); + error = dbt_put (obj_tmpl, "histogram_values", &histogram_value); + if (error != NO_ERROR) + { + goto end; + } + + edit_histogram_object = dbt_finish_object (obj_tmpl); + if (edit_histogram_object == NULL) + { + assert (er_errid () != NO_ERROR); + error = er_errid (); + goto end; + } + + assert (edit_histogram_object == histogram_obj); + obj_tmpl = NULL; + + error = locator_flush_instance (edit_histogram_object); + if (error != NO_ERROR) + { + goto end; + } + +end: + db_value_clear (&histogram_value); + assert (error == NO_ERROR); + return error; +} + +static bool +histogram_init_reader_from_lhs (PT_NODE *lhs, hist::HistogramReader &reader) +{ + if (lhs == NULL || lhs->node_type != PT_NAME) + { + return false; + } + + DB_VALUE *histogram_value = lhs->info.name.histogram; + if (histogram_value == NULL) + { + return false; + } + + int histogram_total_length = 0; + const char *histogram_blob_ptr = db_get_bit (histogram_value, &histogram_total_length); + if (histogram_blob_ptr == NULL || histogram_total_length <= 0) + { + return false; + } + + std::string_view histogram_blob (histogram_blob_ptr, + static_cast (histogram_total_length / 8)); + + int error = reader.reset (histogram_blob); + if (error != NO_ERROR) + { + return false; + } + + return true; +} + +static bool +histogram_extract_key (const DB_VALUE *db_val, hist::histogram_key &key) +{ + const DB_TYPE type = static_cast (db_val->domain.general_info.type); + + switch (type) + { + case DB_TYPE_INTEGER: + key.kind = hist::histogram_key_kind::i64; + key.i64 = db_get_int (db_val); + return true; + + case DB_TYPE_SHORT: + key.kind = hist::histogram_key_kind::i64; + key.i64 = static_cast (db_get_short (db_val)); + return true; + + case DB_TYPE_BIGINT: + key.kind = hist::histogram_key_kind::i64; + key.i64 = db_get_bigint (db_val); + return true; + + case DB_TYPE_FLOAT: + key.kind = hist::histogram_key_kind::dbl; + key.dbl = static_cast (db_get_float (db_val)); + return true; + + case DB_TYPE_DOUBLE: + key.kind = hist::histogram_key_kind::dbl; + key.dbl = db_get_double (db_val); + return true; + + case DB_TYPE_NUMERIC: + key.kind = hist::histogram_key_kind::dbl; + numeric_coerce_num_to_double (db_get_numeric (db_val), db_value_scale (db_val), &key.dbl); + return true; + + case DB_TYPE_BIT: + case DB_TYPE_VARBIT: + { + int length = 0; + const char *str = db_get_bit (db_val, &length); + if (str == NULL) + { + return false; + } + key.kind = hist::histogram_key_kind::str; + key.str.assign (str, static_cast ((length + 7) / 8)); + return true; + } + + case DB_TYPE_CHAR: /* later consider for null trailing exists */ + case DB_TYPE_STRING: + { + const char *str = db_get_string (db_val); + if (str == NULL) + { + return false; + } + key.kind = hist::histogram_key_kind::str; + key.str.assign (str); + return true; + } + + case DB_TYPE_TIME: + { + DB_TIME *timep = db_get_time (db_val); + key.kind = hist::histogram_key_kind::u64; + key.u64 = static_cast (*timep); + return true; + } + + case DB_TYPE_TIMESTAMP: + case DB_TYPE_TIMESTAMPLTZ: + { + DB_TIMESTAMP *tsp = db_get_timestamp (db_val); + key.kind = hist::histogram_key_kind::u64; + key.u64 = static_cast (*tsp); + return true; + } + + case DB_TYPE_DATE: + { + DB_DATE *datep = db_get_date (db_val); + key.kind = hist::histogram_key_kind::u64; + key.u64 = static_cast (*datep); + return true; + } + + case DB_TYPE_MONETARY: + { + DB_MONETARY *monetary = db_get_monetary (db_val); + key.kind = hist::histogram_key_kind::u64; + key.u64 = static_cast (monetary->amount); + return true; + } + + case DB_TYPE_TIMESTAMPTZ: + { + DB_TIMESTAMPTZ *timestamptz = db_get_timestamptz (db_val); + key.kind = hist::histogram_key_kind::u64; + key.u64 = static_cast (timestamptz->timestamp); + return true; + } + + case DB_TYPE_DATETIMETZ: + case DB_TYPE_DATETIMELTZ: + { + DB_DATETIMETZ *datetimetz = db_get_datetimetz (db_val); + key.kind = hist::histogram_key_kind::u64; + key.u64 = (static_cast (datetimetz->datetime.date) << 32) + | static_cast (datetimetz->datetime.time); + return true; + } + + default: + assert (false); /* impossible to reach here - blocked at parser layer first */ + return false; + } +} + +/* numeric domain fraction less than function for int64_t and uint64_t and double and string */ + +static double +numeric_domain_frac_i64_lt (std::int64_t lo, std::int64_t hi, std::int64_t v) +{ + if (lo >= v) + { + return 0.0; + } + if (hi <= v) + { + return 1.0; + } + return (static_cast (v) - static_cast (lo)) / (static_cast (hi) - static_cast (lo)); +} + +double numeric_domain_frac_u64_lt (std::uint64_t lo, std::uint64_t hi, std::uint64_t v) +{ + if (hi <= v) + { + return 1.0; + } + const long double dlo = static_cast (lo); + const long double dhi = static_cast (hi); + const long double dv = static_cast (v); + const long double den = dhi - dlo; + + long double t = (dv - dlo) / den; + return static_cast (t); +} + +double numeric_domain_frac_dbl_lt (double lo, double hi, double v) +{ + if (hi <= v) + { + return 1.0; + } + const long double dlo = static_cast (lo); + const long double dhi = static_cast (hi); + const long double dv = static_cast (v); + const long double den = dhi - dlo; + + long double t = (dv - dlo) / den; + return static_cast (t); +} + +static double +clamp01 (double x) +{ + if (x < 0.0) + { + return 0.0; + } + if (x > 1.0) + { + return 1.0; + } + return x; +} + +static double +string_pos (const unsigned char *s, std::size_t len, std::size_t max_len = 16) +{ + const long double base = 257.0L; + + long double acc = 0.0L; + long double factor = 1.0L; + + const std::size_t use_len = (len < max_len) ? len : max_len; + + for (std::size_t i = 0; i < use_len; ++i) + { + factor /= base; + const unsigned char ch = s[i]; + acc += static_cast (ch) * factor; + } + + return static_cast (acc); +} + +static double +string_domain_frac_lt (const std::string &lo, const std::string &hi, const std::string &v) +{ + if (hi <= v) + { + return 1.0; + } + + auto to_bytes = [] (const std::string &s) -> const unsigned char * + { + return reinterpret_cast (s.data ()); + }; + + const double plo = string_pos (to_bytes (lo), lo.size ()); + const double phi = string_pos (to_bytes (hi), hi.size ()); + const double pv = string_pos (to_bytes (v), v.size ()); + + const double den = phi - plo; + double t = (pv - plo) / den; + return clamp01 (t); +} + +/* histogram get selectivity functions */ + +void +histogram_get_equal_selectivity (PT_NODE *lhs, PT_NODE *rhs, double *selectivity, bool *success) +{ + assert (selectivity != NULL); + + PRED_CLASS pc_rhs = qo_classify (rhs); + if (pc_rhs != PC_CONST) + { + *success = false; + return; + } + hist::HistogramReader histogram_reader; + if (!histogram_init_reader_from_lhs (lhs, histogram_reader)) + { + *success = false; + return; + } + + hist::histogram_key key; + if (!histogram_extract_key (&rhs->info.value.db_value, key)) + { + *success = false; + return; + } + + int bucket_index = -1; + bool found = false; + + switch (key.kind) + { + case hist::histogram_key_kind::i64: + found = histogram_reader.find_bucket_and_check (key.i64, bucket_index); + break; + + case hist::histogram_key_kind::dbl: + found = histogram_reader.find_bucket_and_check (key.dbl, bucket_index); + break; + + case hist::histogram_key_kind::str: + found = histogram_reader.find_bucket_and_check (key.str, bucket_index); + break; + + case hist::histogram_key_kind::u64: + found = histogram_reader.find_bucket_and_check (key.u64, bucket_index); + break; + + case hist::histogram_key_kind::invalid: + default: + assert (false); + break; + } + + if (!found || bucket_index < 0) + { + /* not found in histogram */ + *success = true; + *selectivity = 0.0; + return; + } + + const double bucket_rows = static_cast (histogram_reader.bucket_rows (bucket_index)); + const double total_rows = static_cast (histogram_reader.total_rows ()); + const double approx_ndv = static_cast (histogram_reader.bucket_approx_ndv (bucket_index)); + const double null_frequency = lhs->info.name.null_frequency; + + if (total_rows <= 0.0 || approx_ndv <= 0.0) + { + /* safe default */ + *success = false; + return; + } + + *selectivity = (bucket_rows / total_rows) / approx_ndv; + *selectivity *= (1.0 - null_frequency); + *success = true; + return; +} + +void +histogram_get_comp_selectivity (PT_NODE *lhs, PT_NODE *rhs, bool is_ge, bool include_equal, double *selectivity, + bool *success) +{ + assert (selectivity != NULL); + + PRED_CLASS pc_rhs = qo_classify (rhs); + if (pc_rhs != PC_CONST) + { + *success = false; + return; + } + + hist::HistogramReader histogram_reader; + + if (!histogram_init_reader_from_lhs (lhs, histogram_reader)) + { + *success = false; + return; + } + + hist::histogram_key key; + if (!histogram_extract_key (&rhs->info.value.db_value, key)) + { + *success = false; + return; + } + + int bucket_index = -1; + const double total_rows = histogram_reader.total_rows (); + if (total_rows <= 0.0) + { + *success = true; + *selectivity = 0.0; + return; + } + + double bucket_rows = 0.0; + + /* caculate bucket_rows for column <= rhs or column < rhs */ + switch (key.kind) + { + case hist::histogram_key_kind::i64: + bucket_index = histogram_reader.find_bucket (key.i64); + + if (bucket_index < 0) + { + *success = true; + *selectivity = 0.0; + return; + } + + if (histogram_reader.bucket_approx_ndv (bucket_index) == 1) + { + if (histogram_reader.check_value_included (bucket_index, key.i64)) + { + if (is_ge == include_equal) + { + bucket_rows = histogram_reader.bucket_cumulative (bucket_index - 1); + } + else + { + bucket_rows = histogram_reader.bucket_cumulative (bucket_index); + } + } + else + { + if (bucket_index == static_cast (histogram_reader.bucket_count()) - 1) + { + bucket_rows = histogram_reader.bucket_cumulative (bucket_index); + } + else + { + bucket_rows = histogram_reader.bucket_cumulative (bucket_index - 1); + } + } + } + else + { + /* linear interpolation */ + const double frac = numeric_domain_frac_i64_lt (histogram_reader.bucket_hi (bucket_index - 1), + histogram_reader.bucket_hi (bucket_index), key.i64); + bucket_rows = histogram_reader.bucket_cumulative (bucket_index - 1) + histogram_reader.bucket_rows ( + bucket_index) * frac; + } + break; + + case hist::histogram_key_kind::dbl: + bucket_index = histogram_reader.find_bucket (key.dbl); + + if (bucket_index < 0) + { + *success = true; + *selectivity = 0.0; + return; + } + + if (histogram_reader.bucket_approx_ndv (bucket_index) == 1) + { + if (histogram_reader.check_value_included (bucket_index, key.dbl)) + { + if (!is_ge && include_equal) + { + bucket_rows = histogram_reader.bucket_cumulative (bucket_index); + } + else + { + bucket_rows = histogram_reader.bucket_cumulative (bucket_index - 1); + } + } + else + { + if (bucket_index == static_cast (histogram_reader.bucket_count()) - 1) + { + bucket_rows = histogram_reader.bucket_cumulative (bucket_index); + } + else + { + bucket_rows = histogram_reader.bucket_cumulative (bucket_index - 1); + } + } + } + else + { + /* linear interpolation */ + const double frac = numeric_domain_frac_dbl_lt (histogram_reader.bucket_hi (bucket_index - 1), + histogram_reader.bucket_hi (bucket_index), key.dbl); + bucket_rows = histogram_reader.bucket_cumulative (bucket_index - 1) + histogram_reader.bucket_rows ( + bucket_index) * frac; + } + break; + + case hist::histogram_key_kind::str: + bucket_index = histogram_reader.find_bucket (key.str); + if (bucket_index < 0) + { + *success = true; + *selectivity = 0.0; + return; + } + + if (histogram_reader.bucket_approx_ndv (bucket_index) == 1) + { + if (histogram_reader.check_value_included (bucket_index, key.str)) + { + if (!is_ge && include_equal) + { + bucket_rows = histogram_reader.bucket_cumulative (bucket_index); + } + else + { + bucket_rows = histogram_reader.bucket_cumulative (bucket_index - 1); + } + } + else + { + if (bucket_index == static_cast (histogram_reader.bucket_count()) - 1) + { + bucket_rows = histogram_reader.bucket_cumulative (bucket_index); + } + else + { + bucket_rows = histogram_reader.bucket_cumulative (bucket_index - 1); + } + } + } + else + { + /* linear interpolation */ + const double frac = string_domain_frac_lt (histogram_reader.bucket_hi (bucket_index - 1), + histogram_reader.bucket_hi (bucket_index), key.str); + bucket_rows = histogram_reader.bucket_cumulative (bucket_index - 1) + histogram_reader.bucket_rows ( + bucket_index) * frac; + } + break; + + case hist::histogram_key_kind::u64: + bucket_index = histogram_reader.find_bucket (key.u64); + + if (bucket_index < 0) + { + *success = true; + *selectivity = 0.0; + return; + } + + if (histogram_reader.bucket_approx_ndv (bucket_index) == 1) + { + if (histogram_reader.check_value_included (bucket_index, key.u64)) + { + if (!is_ge && include_equal) + { + bucket_rows = histogram_reader.bucket_cumulative (bucket_index); + } + else + { + bucket_rows = histogram_reader.bucket_cumulative (bucket_index - 1); + } + } + else + { + if (bucket_index == static_cast (histogram_reader.bucket_count()) - 1) + { + bucket_rows = histogram_reader.bucket_cumulative (bucket_index); + } + else + { + bucket_rows = histogram_reader.bucket_cumulative (bucket_index - 1); + } + } + } + else + { + /* linear interpolation */ + const double frac = numeric_domain_frac_u64_lt (histogram_reader.bucket_hi (bucket_index - 1), + histogram_reader.bucket_hi (bucket_index), key.u64); + bucket_rows = histogram_reader.bucket_cumulative (bucket_index - 1) + histogram_reader.bucket_rows ( + bucket_index) * frac; + } + break; + + case hist::histogram_key_kind::invalid: + default: + /* never reach here */ + assert (false); + break; + } + + if (bucket_index < 0) + { + /* not found in histogram */ + *success = true; + *selectivity = 0.0; + return; + } + + /* selectivity = bucket_rows / total_rows */ + *selectivity = bucket_rows / total_rows; + + if (is_ge) + { + *selectivity = 1.0 - *selectivity; + } + + *selectivity *= (1.0 - lhs->info.name.null_frequency); + *success = true; + return; +} + +int +db_get_histogram (MOP classop, const char *attr_name, DB_OBJECT **histogram_obj) +{ + int error = NO_ERROR; + DB_OBJECT *histogram_class; + DB_OTMPL *obj_tmpl = NULL; + DB_VALUE value[2]; + DB_VALUE *value_ptrs[2] = { &value[0], &value[1] }; + const char *search_attrs[2] = { "class_of", "key_attr" }; + + histogram_class = sm_find_class (CT_DB_HISTOGRAM_NAME); + if (histogram_class == NULL) + { + error = ER_BO_MISSING_OR_INVALID_CATALOG; + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0); + return error; + } + + db_make_object (&value[0], classop); + db_make_string (&value[1], attr_name); + + *histogram_obj = db_find_multi_unique (histogram_class, 2, (char **) search_attrs, value_ptrs, DB_FETCH_READ); + + db_value_clear (value_ptrs[0]); + db_value_clear (value_ptrs[1]); + + return NO_ERROR; +} + +int +stats_get_histogram (MOP classop, HIST_STATS **histogram) +{ + int error = NO_ERROR; + DB_OBJECT *histogram_obj = NULL; + SM_ATTRIBUTE *att; + SM_CLASS *class_ = NULL; + int attr_count = 0; + + error = au_fetch_class (classop, &class_, AU_FETCH_READ, AU_SELECT); + if (error != NO_ERROR) + { + return error; + } + + attr_count = class_->att_count; + *histogram = (HIST_STATS *) db_ws_alloc (sizeof (HIST_STATS)); + if (*histogram == NULL) + { + return ER_OUT_OF_VIRTUAL_MEMORY; + } + memset (*histogram, 0, sizeof (HIST_STATS)); + + (*histogram)->n_attrs = attr_count; + if (attr_count == 0) + { + (*histogram)->histogram = NULL; + (*histogram)->null_frequency = NULL; + return NO_ERROR; + } + + (*histogram)->histogram = (DB_VALUE **) db_ws_alloc (sizeof (DB_VALUE *) * attr_count); + if ((*histogram)->histogram == NULL) + { + db_ws_free (*histogram); + *histogram = NULL; + return ER_OUT_OF_VIRTUAL_MEMORY; + } + memset ((*histogram)->histogram, 0, sizeof (DB_VALUE *) * attr_count); + + (*histogram)->null_frequency = (double *) db_ws_alloc (sizeof (double) * attr_count); + if ((*histogram)->null_frequency == NULL) + { + db_ws_free ((*histogram)->histogram); + db_ws_free (*histogram); + *histogram = NULL; + return ER_OUT_OF_VIRTUAL_MEMORY; + } + memset ((*histogram)->null_frequency, 0, sizeof (double) * attr_count); + + + int i = 0; + + if (*histogram == NULL || (*histogram)->histogram == NULL || (*histogram)->null_frequency == NULL + || class_->attributes == NULL) + { + goto error_end; + } + + for (att = class_->attributes; att != NULL && class_->attributes != NULL; att = (SM_ATTRIBUTE *) att->header.next) + { + const char *attname = (char *) att->header.name; + DB_VALUE *histogram_value = NULL; + DB_VALUE null_frequency_value; + error = db_get_histogram (classop, attname, &histogram_obj); + + if (*histogram == NULL || (*histogram)->histogram == NULL || (*histogram)->null_frequency == NULL) + { + goto error_end; + } + + (*histogram)->histogram[i] = NULL; + (*histogram)->null_frequency[i] = 0.0; + + if (error != NO_ERROR) + { + goto error_end; + } + + if (histogram_obj == NULL) + { + i++; + continue; + } + + histogram_value = (DB_VALUE *) db_ws_alloc (sizeof (DB_VALUE)); + if (histogram_value == NULL) + { + error = ER_OUT_OF_VIRTUAL_MEMORY; + goto error_end; + } + error = db_get (histogram_obj, "histogram_values", histogram_value); + if (error != NO_ERROR) + { + db_ws_free (histogram_value); + goto error_end; + } + error = db_get (histogram_obj, "null_frequency", &null_frequency_value); + if (error != NO_ERROR) + { + db_value_clear (histogram_value); + db_ws_free (histogram_value); + goto error_end; + } + + (*histogram)->histogram[i] = histogram_value; /* should clear histogram_value */ + if (db_value_is_null (&null_frequency_value)) + { + (*histogram)->null_frequency[i] = 0.0; + } + else + { + (*histogram)->null_frequency[i] = db_get_double (&null_frequency_value); + } + i++; + } + return NO_ERROR; + +error_end: + /* Free all allocated memory */ + if (*histogram != NULL) + { + if ((*histogram)->histogram != NULL) + { + for (int j = 0; j < i; j++) + { + if ((*histogram)->histogram[j] != NULL) + { + db_value_clear ((*histogram)->histogram[j]); + db_ws_free ((*histogram)->histogram[j]); + (*histogram)->histogram[j] = NULL; + } + } + db_ws_free ((*histogram)->histogram); + (*histogram)->histogram = NULL; + } + if ((*histogram)->null_frequency != NULL) + { + db_ws_free ((*histogram)->null_frequency); + (*histogram)->null_frequency = NULL; + } + db_ws_free (*histogram); + *histogram = NULL; + } + return error; +} + +int stats_free_histogram_and_init (HIST_STATS *histogram) +{ + if (histogram == NULL) + { + return NO_ERROR; + } + if (histogram->histogram != NULL && histogram->n_attrs > 0) + { + for (int i = 0; i < histogram->n_attrs; i++) + { + if (histogram->histogram[i] == NULL) + { + continue; + } + db_value_clear (histogram->histogram[i]); + db_ws_free (histogram->histogram[i]); + } + db_ws_free (histogram->histogram); + } + + if (histogram->null_frequency != NULL) + { + db_ws_free (histogram->null_frequency); + } + + db_ws_free (histogram); + return NO_ERROR; +} + +bool +is_histogrammable_type (DB_TYPE type) +{ + switch (type) + { + /* numeric */ + case DB_TYPE_INTEGER: + case DB_TYPE_SHORT: + case DB_TYPE_FLOAT: + case DB_TYPE_DOUBLE: + case DB_TYPE_NUMERIC: + case DB_TYPE_MONETARY: + case DB_TYPE_BIGINT: + return true; + + /* bit string */ + case DB_TYPE_BIT: + case DB_TYPE_VARBIT: + return true; + + /* character string */ + case DB_TYPE_CHAR: + case DB_TYPE_STRING: + return true; + + /* date / time */ + case DB_TYPE_TIME: + case DB_TYPE_DATE: + case DB_TYPE_TIMESTAMP: + case DB_TYPE_TIMESTAMPLTZ: + case DB_TYPE_TIMESTAMPTZ: + case DB_TYPE_DATETIMELTZ: + case DB_TYPE_DATETIMETZ: + return true; + + default: + return false; + } +} + +/*===========================================================================*/ +/* dump_histogram */ + +/* ++------------------ HISTOGRAM ------------------+ +| column : age (int) | +| rows : 100000 sample : 10000 (10.0%) | +| pages : 120 / 500 | +| buckets: 16 nulls : 123 | ++------------------------------------------------+ +#00 [-inf, 10] rows= 1234(0.012) ndv=10 cum=0.012 + +*/ + +/*===========================================================================*/ +#define HIST_DUMP_WIDTH 47 /* inner width of the histogram */ + +int +dump_histogram (MOP classop, const char *attr_name, DB_TYPE attr_type, bool with_fullscan, int error, FILE *f) +{ + char line[HIST_DUMP_WIDTH + 1]; + SM_CLASS *class_ = NULL; + const char *col_name = attr_name; + const char *type_name = db_get_type_name (attr_type); + int rows_scanned = 0; + DB_VALUE histogram_value, null_frequency_value; + DB_OBJECT *histogram_obj = NULL; + int histogram_total_length = 0; + + double null_frequency = 0.0; + if (error != NO_ERROR) + { + snprintf (line, sizeof (line), "ERROR: Failed to dump histogram column: %s", attr_name); + fprintf (f, "| %-47s|\n", line); + fprintf (f, "+------------------------------------------------+\n"); + return NO_ERROR; + } + + class_ = sm_get_class_with_statistics (classop); + if (class_ == NULL) + { + return ER_FAILED; + } + + error = db_get_histogram (classop, attr_name, &histogram_obj); + if (error != NO_ERROR) + { + return ER_FAILED; + } + + if (histogram_obj == NULL) + { + return ER_FAILED; + } + + /* get histgoram */ + error = db_get (histogram_obj, "histogram_values", &histogram_value); + if (error != NO_ERROR) + { + db_value_clear (&histogram_value); + return ER_FAILED; + } + + /* get histgoram */ + error = db_get (histogram_obj, "null_frequency", &null_frequency_value); + if (error != NO_ERROR) + { + db_value_clear (&null_frequency_value); + db_value_clear (&histogram_value); + return ER_FAILED; + } + + if (db_value_is_null (&null_frequency_value)) + { + null_frequency = 0.0; + } + else + { + null_frequency = db_get_double (&null_frequency_value); + } + + const char *histogram_blob_ptr = db_get_bit (&histogram_value, &histogram_total_length); + if (histogram_blob_ptr == NULL || histogram_total_length <= 0) + { + return ER_FAILED; + } + + /* need length of histogram_blob_ptr */ + std::string_view histogram_blob (histogram_blob_ptr, static_cast (histogram_total_length / 8)); + + hist::HistogramReader histogram_reader; + error = histogram_reader.reset (histogram_blob); + if (error != NO_ERROR) + { + return ER_FAILED; + } + + /* top border */ + fputs ("+------------------ HISTOGRAM -------------------+\n", f); + + /* column line */ + snprintf (line, sizeof (line), " column : %s (%s)", col_name, type_name); + fprintf (f, "| %-47s|\n", line); + + /* rows + sample line */ + rows_scanned = static_cast (histogram_reader.total_rows()); + + if (class_->stats->heap_num_objects <= 0 || class_->stats->heap_num_pages <= 0) + { + snprintf (line, sizeof (line), "Empty histogram for column: %s", attr_name); + fprintf (f, "| %-47s|\n", line); + fprintf (f, "+------------------------------------------------+\n"); + return NO_ERROR; + } + + if (!with_fullscan) + { + snprintf (line, sizeof (line), + " rows : %d sample : %d (%.1f%%)", + class_->stats->heap_num_objects, rows_scanned, (double) rows_scanned / class_->stats->heap_num_objects * 100.0); + } + else + { + snprintf (line, sizeof (line), + " rows : %d ", static_cast (histogram_reader.total_rows())); + } + fprintf (f, "| %-47s|\n", line); + + snprintf (line, sizeof (line), " null frequency : %.3f", null_frequency); + fprintf (f, "| %-47s|\n", line); + + snprintf (line, sizeof (line), + " buckets + mcv: %d", + static_cast (histogram_reader.bucket_count())); + fprintf (f, "| %-47s|\n", line); + + /* bottom border */ + fputs ("+------------------------------------------------+\n", f); + + const double total_rows = static_cast (histogram_reader.total_rows ()); + const int bucket_cnt = static_cast (histogram_reader.bucket_count ()); + + for (int i = 0; i < bucket_cnt; i++) + { + const int rows = static_cast (histogram_reader.bucket_rows (i)); + const double sel = + (total_rows > 0.0 + ? static_cast (rows) / total_rows + : 0.0); + + const std::int32_t ndv = + static_cast (histogram_reader.bucket_approx_ndv (i)); + const bool is_mcv = (ndv == 1); + const double cum_sel = + (total_rows > 0.0 + ? static_cast (histogram_reader.bucket_cumulative (i)) / total_rows + : 0.0); + + const char *mcv_suffix = is_mcv ? " (MCV)" : ""; + + if (i == 0) + { + std::string hi = histogram_reader.bucket_hi_dump_with_type (i, attr_type); + std::fprintf (f, + "#%02d (-inf, %s] rows=%d(%.3f) ndv=%d%s cum=%.3f\n", + i, + hi.c_str (), + rows, + sel, + ndv, + mcv_suffix, + cum_sel); + } + else + { + std::string lo = histogram_reader.bucket_hi_dump_with_type (i - 1, attr_type); + std::string hi = histogram_reader.bucket_hi_dump_with_type (i, attr_type); + std::fprintf (f, + "#%02d (%s, %s] rows=%d(%.3f) ndv=%d%s cum=%.3f\n", + i, + lo.c_str (), + hi.c_str (), + rows, + sel, + ndv, + mcv_suffix, + cum_sel); + } + } + + db_value_clear (&histogram_value); + db_value_clear (&null_frequency_value); + + return NO_ERROR; +} \ No newline at end of file diff --git a/src/histogram/histogram_cl.hpp b/src/histogram/histogram_cl.hpp new file mode 100644 index 00000000000..06a7ef03606 --- /dev/null +++ b/src/histogram/histogram_cl.hpp @@ -0,0 +1,117 @@ +/* + * Copyright 2008 Search Solution Corporation + * Copyright 2016 CUBRID Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* +* histogram_cl.hpp - Histogram class declaration +*/ + +#ifndef _HISTOGRAM_CL_HPP_ +#define _HISTOGRAM_CL_HPP_ + +#include +#include +#include +#include "thread_compat.hpp" + +// Forward declaration for PT_NODE +struct parser_node; +typedef struct parser_node PT_NODE; +typedef struct hist_stats HIST_STATS; + +/* null frequency query template */ +static const char *NULL_FREQUENCY_QUERY_TEMPLATE = + "SELECT SUM(CASE WHEN [%s] IS NULL THEN 1 ELSE 0 END) * 1.0 / NULLIF(COUNT(*), 0) AS null_frequency FROM [%s];"; +/* Use AVG instead of SUM/COUNT(*) because sampling scales only COUNT(*), not SUM. + * AVG computes ratio over the same sampled set without mixing scaled/unscaled values. */ +static const char *NULL_FREQUENCY_WITH_SAMPLING_SCAN_QUERY_TEMPLATE = + "SELECT /*+ SAMPLING_SCAN */ AVG(CASE WHEN [%s] IS NULL THEN 1.0 ELSE 0.0 END) AS null_frequency FROM [%s];"; + +/* histogram query template */ +static const char *HISTOGRAM_QUERY_TEMPLATE = + "WITH src AS (SELECT [%s] AS val FROM [%s] WHERE [%s] IS NOT NULL), " + "cnt AS (SELECT val, COUNT(*) AS c FROM src GROUP BY val), " + "mcv_ranked AS (SELECT val, c, ROW_NUMBER() OVER (ORDER BY c DESC, val) AS rn FROM cnt ORDER BY c DESC LIMIT %d), " + "non_mcv_flagged AS (SELECT val, c FROM cnt WHERE val NOT IN (SELECT val FROM mcv_ranked)), " + "hist_acc AS (SELECT val, c, SUM(c) OVER (ORDER BY val) AS cum, SUM(c) OVER () AS n FROM non_mcv_flagged), " + "param AS (SELECT CASE WHEN n > 0 THEN CEIL(n * 1.0 / %d) ELSE 1 END AS cap, n FROM hist_acc LIMIT 1), " + "hist_buckets AS (SELECT LEAST(FLOOR((cum - 1) / param.cap), %d - 1) AS bid, val, c, FALSE AS is_mcv FROM hist_acc, param), " + "mcv_buckets AS (SELECT -rn AS bid, val, c, TRUE AS is_mcv FROM mcv_ranked), " + "all_buckets AS (SELECT * FROM hist_buckets UNION ALL SELECT * FROM mcv_buckets) " + "SELECT bid, MAX(val) AS endpoint, SUM(c) AS rows_in_bucket, SUM(SUM(c)) OVER (ORDER BY MAX(val)) AS cumulative, " + "COUNT(*) AS approx_ndv, MAX(is_mcv) AS is_mcv FROM all_buckets GROUP BY bid ORDER BY MAX(val);"; +/* histogram with sampling scan query template */ +static const char *HISTOGRAM_WITH_SAMPLING_SCAN_QUERY_TEMPLATE = + "WITH src AS (SELECT /*+ SAMPLING_SCAN */ [%s] AS val FROM [%s] WHERE [%s] IS NOT NULL), " + "cnt AS (SELECT val, COUNT(*) AS c FROM src GROUP BY val), " + "mcv_ranked AS (SELECT val, c, ROW_NUMBER() OVER (ORDER BY c DESC, val) AS rn FROM cnt ORDER BY c DESC LIMIT %d), " + "non_mcv_flagged AS (SELECT val, c FROM cnt WHERE val NOT IN (SELECT val FROM mcv_ranked)), " + "hist_acc AS (SELECT val, c, SUM(c) OVER (ORDER BY val) AS cum, SUM(c) OVER () AS n FROM non_mcv_flagged), " + "param AS (SELECT CASE WHEN n > 0 THEN CEIL(n * 1.0 / %d) ELSE 1 END AS cap, n FROM hist_acc LIMIT 1), " + "hist_buckets AS (SELECT LEAST(FLOOR((cum - 1) / param.cap), %d - 1) AS bid, val, c, FALSE AS is_mcv FROM hist_acc, param), " + "mcv_buckets AS (SELECT -rn AS bid, val, c, TRUE AS is_mcv FROM mcv_ranked), " + "all_buckets AS (SELECT * FROM hist_buckets UNION ALL SELECT * FROM mcv_buckets) " + "SELECT bid, MAX(val) AS endpoint, SUM(c) AS rows_in_bucket, SUM(SUM(c)) OVER (ORDER BY MAX(val)) AS cumulative, " + "COUNT(*) AS approx_ndv, MAX(is_mcv) AS is_mcv FROM all_buckets GROUP BY bid ORDER BY MAX(val);"; + +/* histogram key kind */ +namespace hist +{ + + enum class histogram_key_kind + { + invalid, + i64, + dbl, + str, + u64 + }; + + struct histogram_key + { + histogram_key_kind kind = histogram_key_kind::invalid; + std::int64_t i64 = 0; + double dbl = 0.0; + std::string str; + std::uint64_t u64 = 0; + }; + +} // namespace hist + +/* histogram analysis functions */ +int analyze_classes (THREAD_ENTRY *thread_p, const char *tbl_name, const char *attr_name, int max_number_of_buckets, + bool with_fullscan, MOP classop); +int get_null_frequency (THREAD_ENTRY *thread_p, const char *tbl_name, const char *attr_name, bool with_fullscan, + MOP classop); +int get_histogram (THREAD_ENTRY *thread_p, const char *tbl_name, const char *attr_name, int max_number_of_buckets, + bool with_fullscan, char **histogram_blob, int *histogram_total_length); +int set_histogram (THREAD_ENTRY *thread_p, const char *tbl_name, const char *attr_name, char *histogram_blob, + int histogram_total_length, MOP classop); + +/* histogram selectivity evaluation functions */ +void histogram_get_equal_selectivity (PT_NODE *lhs, PT_NODE *rhs, double *selectivity, bool *success); +void histogram_get_comp_selectivity (PT_NODE *lhs, PT_NODE *rhs, bool is_ge, bool include_equal, double *selectivity, + bool *success); + +/* histogram utility functions */ +int db_get_histogram (MOP classop, const char *attr_name, DB_OBJECT **histogram_obj); +bool is_histogrammable_type (DB_TYPE type); +int stats_get_histogram (MOP classop, HIST_STATS **histogram); +int stats_free_histogram_and_init (HIST_STATS *histogram); +int dump_histogram (MOP classop, const char *attr_name, DB_TYPE attr_type, bool with_fullscan, int error, FILE *f); + +#endif // _HISTOGRAM_CL_HPP_ \ No newline at end of file diff --git a/src/histogram/histogram_reader.cpp b/src/histogram/histogram_reader.cpp new file mode 100644 index 00000000000..2647166648f --- /dev/null +++ b/src/histogram/histogram_reader.cpp @@ -0,0 +1,343 @@ +/* + * Copyright 2008 Search Solution Corporation + * Copyright 2016 CUBRID Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* +* histogram_reader.cpp - Histogram reader implementation +*/ + +#include "histogram_reader.hpp" +#include +#include +#include "error_manager.h" +#include "object_representation.h" + +namespace hist +{ + // ---------- get_value template specialization ---------- + template<> + std::int32_t HistogramReader::get_value (const void *ptr) const + { + return OR_GET_INT (ptr); + } + + template<> + std::int64_t HistogramReader::get_value (const void *ptr) const + { + std::int64_t value; + OR_GET_INT64 (ptr, &value); + return value; + } + + template<> + std::uint64_t HistogramReader::get_value (const void *ptr) const + { + std::uint64_t value; + OR_GET_INT64 (ptr, reinterpret_cast (&value)); + return value; + } + + template<> + double HistogramReader::get_value (const void *ptr) const + { + double value; + OR_GET_DOUBLE (ptr, &value); + return value; + } + + template<> + std::uint32_t HistogramReader::get_value (const void *ptr) const + { + return static_cast (OR_GET_INT (ptr)); + } + +// ---------- reset ---------- + int HistogramReader::reset (std::string_view blob) + { + int error = NO_ERROR; + blob_ = blob; + if (blob_.size() < sizeof (HeaderV1)) + { + error = ER_FAILED; + return error; + } + + /* read header */ + const auto *H = reinterpret_cast (blob_.data()); + if (std::string_view (H->magic, 4) != "HST1") + { + error = ER_FAILED; + return error; + } + if (get_value (&H->version) != 1) + { + error = ER_FAILED; + return error; + } + + nb_ = get_value (&H->nbuckets); + str_size_ = get_value (&H->str_size); + type_ = static_cast (get_value (&H->type)); + total_size_ = get_value (&H->total_size); + assert (total_size_ == blob_.size()); + + /* read index table for O(1) access to bucket record */ + const char *p = blob_.data() + sizeof (HeaderV1); + const char *end = blob_.data() + total_size_; + + + bucket_area_begin_ = p; + + /* find the last record */ + std::uint32_t max_off = BUCKET_RECORD_SIZE * nb_; + const char *last = bucket_area_begin_ + max_off; + + if (last > end) + { + return ER_FAILED; + } + buckets_end_ = last; + if (buckets_end_ + str_size_ != end) + { + return ER_FAILED; + } + + /* read string blob */ + str_blob_ = std::string_view{buckets_end_, static_cast (str_size_)}; + return NO_ERROR; + } + +// ---------- record navigation ---------- + const char *HistogramReader::bucket_rec (std::uint32_t i) const + { + assert (i < nb_); + std::uint32_t off = i*BUCKET_RECORD_SIZE; + const char *rec = bucket_area_begin_ + off; + assert (rec >= bucket_area_begin_ && rec < buckets_end_); + return rec; + } + + const char *HistogramReader::bucket_hi_value_ptr (std::uint32_t i) const + { + return bucket_rec (i); + } + +// ---------- access ---------- + std::int64_t HistogramReader::bucket_cumulative (std::int32_t i) const + { + if (i < 0) + { + return 0; + } + + const char *p = bucket_rec (i) + 8; + return get_value (p); + } + + std::int64_t HistogramReader::bucket_approx_ndv (std::uint32_t i) const + { + assert (i < nb_); + const char *rec = bucket_rec (i); + const char *p = rec + 16; + std::int64_t result; + result = get_value (p); + assert (result > 0); + return result; + } + + std::int64_t HistogramReader::bucket_rows (std::uint32_t i) const + { + assert (i < nb_); + const std::int64_t cur = bucket_cumulative (i); + const std::int64_t prev = (i == 0) ? 0 : bucket_cumulative (i - 1); + return cur - prev; + } + +// ---------- bucket_hi template specialization ---------- + template<> + std::int64_t HistogramReader::bucket_hi (std::int32_t i) const + { + if (i < 0) + { + return std::numeric_limits::min(); + } + + return get_value (bucket_hi_value_ptr (i)); + } + + template<> + std::int32_t HistogramReader::bucket_hi (std::int32_t i) const + { + if (i < 0) + { + return std::numeric_limits::min(); + } + + return static_cast (get_value (bucket_hi_value_ptr (i))); + } + + template<> + double HistogramReader::bucket_hi (std::int32_t i) const + { + if (i < 0) + { + return std::numeric_limits::min(); + } + + return get_value (bucket_hi_value_ptr (i)); + } + + template<> + std::string_view HistogramReader::bucket_hi (std::int32_t i) const + { + if (i < 0) + { + return std::string_view{""}; + } + + const char *p = bucket_hi_value_ptr (i); + std::uint32_t len32 = get_value (p); + std::uint32_t off32 = get_value (p + 4); + + if (len32 <= 4) // inline data + { + return std::string_view{ p+4, static_cast (len32) }; + } + assert (off32 + len32 <= str_size_); + return std::string_view{str_blob_.data() + off32, static_cast (len32)}; + } + + template<> + std::string HistogramReader::bucket_hi (std::int32_t i) const + { + if (i < 0) + { + return std::string{""}; + } + + const char *p = bucket_hi_value_ptr (static_cast (i)); + std::uint32_t len32 = get_value (p); + std::uint32_t off32 = get_value (p + 4); + + if (len32 <= 4) // inline data + { + return std::string{ p+4, static_cast (len32) }; + } + assert (off32 + len32 <= str_size_); + return std::string{str_blob_.data() + off32, static_cast (len32)}; + } + + template<> + std::uint64_t HistogramReader::bucket_hi (std::int32_t i) const + { + if (i < 0) + { + return std::numeric_limits::min(); + } + + return static_cast (get_value (bucket_hi_value_ptr (static_cast (i)))); + } + + // ---------- bucket_hi dump template specialization ---------- + template<> + std::string HistogramReader::bucket_hi_dump (std::uint32_t i) const + { + return std::to_string (get_value (bucket_hi_value_ptr (i))); + } + + template<> + std::string HistogramReader::bucket_hi_dump (std::uint32_t i) const + { + return std::to_string (static_cast (get_value (bucket_hi_value_ptr (i)))); + } + + template<> + std::string HistogramReader::bucket_hi_dump (std::uint32_t i) const + { + return std::to_string (get_value (bucket_hi_value_ptr (i))); + } + + template<> + std::string HistogramReader::bucket_hi_dump (std::uint32_t i) const + { + return std::to_string (static_cast (get_value (bucket_hi_value_ptr (i)))); + } + + template<> + std::string HistogramReader::bucket_hi_dump (std::uint32_t i) const + { + const char *p = bucket_hi_value_ptr (i); + std::uint32_t len32 = get_value (p); + std::uint32_t off32 = get_value (p + 4); + + if (len32 <= 4) // inline data + { + return std::string{ p+4, static_cast (len32) }; + } + assert (off32 + len32 <= str_size_); + return std::string{str_blob_.data() + off32, static_cast (std::min (len32, static_cast (8)))}; + } + + template<> + std::string HistogramReader::bucket_hi_dump (std::uint32_t i) const + { + const char *p = bucket_hi_value_ptr (i); + std::uint32_t len32 = get_value (p); + std::uint32_t off32 = get_value (p + 4); + + if (len32 <= 4) // inline data + { + return std::string{ p+4, static_cast (len32) }; + } + assert (off32 + len32 <= str_size_); + return std::string{str_blob_.data() + off32, static_cast (std::min (len32, static_cast (8)))}; + } + + std::string HistogramReader::bucket_hi_dump_with_type (std::uint32_t i, DB_TYPE attr_type) const + { + switch (attr_type) + { + case DB_TYPE_INTEGER: + case DB_TYPE_SHORT: + return bucket_hi_dump (i); + case DB_TYPE_FLOAT: + case DB_TYPE_DOUBLE: + case DB_TYPE_NUMERIC: + return bucket_hi_dump (i); + case DB_TYPE_BIT: + case DB_TYPE_VARBIT: + case DB_TYPE_CHAR: /* later consider for null trailing exists */ + case DB_TYPE_STRING: + return bucket_hi_dump (i); + case DB_TYPE_TIME: + case DB_TYPE_BIGINT: + return bucket_hi_dump (i); + case DB_TYPE_TIMESTAMP: + case DB_TYPE_TIMESTAMPLTZ: + case DB_TYPE_DATE: + case DB_TYPE_MONETARY: + case DB_TYPE_TIMESTAMPTZ: + case DB_TYPE_DATETIMETZ: + case DB_TYPE_DATETIMELTZ: + return bucket_hi_dump (i); + default: + assert (false); + return ""; + } + } + // ---------- get_equal_selectivity ---------- +} // namespace hist diff --git a/src/histogram/histogram_reader.hpp b/src/histogram/histogram_reader.hpp new file mode 100644 index 00000000000..a4952dfb84a --- /dev/null +++ b/src/histogram/histogram_reader.hpp @@ -0,0 +1,194 @@ +/* + * Copyright 2008 Search Solution Corporation + * Copyright 2016 CUBRID Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* +* histogram_reader.hpp - Histogram reader declaration +*/ + +#ifndef _HISTOGRAM_READER_HPP_ +#define _HISTOGRAM_READER_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include "error_manager.h" +#include +#include "dbtype.h" +namespace hist +{ + +// ---- Flat binary layout (LE) ---- +// [Header] [Index table] [Buckets area] [String blob....] +// +// Header (fixed): +// magic : 'HST2' (4B) +// version : u32 (1) +// nbuckets : u32 +// str_size : u32 +// +// +// Buckets area (variable): +// For each i in [0, nbuckets): +// data_hi : 8B (ptr or Value) +// cumulative: f64 +// approx_ndv: f64 (present only if has_ndv==1) +// +// String blob (trailing): +// str_size bytes; bucket string data points to (len, off) inside this blob. + constexpr std::uint32_t BUCKET_RECORD_SIZE = 8 + 8 + 8; // data + cumulative + approx_ndv + struct HeaderV1 + { + char magic[4]; // "HST1" + std::uint32_t version; + std::uint32_t nbuckets; + std::uint32_t str_size; + std::uint32_t type; // DB_TYPE + std::uint32_t total_size; // total size of the histogram + }; + + class HistogramReader + { + public: + HistogramReader() = default; + int create (HistogramReader &reader, std::string_view blob) + { + int error = reader.reset (blob); + return error; + } + int reset (std::string_view blob); + + std::uint64_t bucket_count() const noexcept + { + return nb_; + } + std::uint64_t total_rows() const + { + return nb_ ? bucket_cumulative (nb_ - 1) : 0; + } + + std::int64_t bucket_cumulative (std::int32_t i) const; + std::int64_t bucket_approx_ndv (std::uint32_t i) const; + + template + T bucket_hi (std::int32_t i) const; + template + std::string bucket_hi_dump (std::uint32_t i) const; + std::string bucket_hi_dump_with_type (std::uint32_t i, DB_TYPE attr_type) const; + std::int64_t bucket_rows (std::uint32_t i) const; + template + int find_bucket (const T &value) const + { + if (nb_ == 0) + { + return -1; + } + + T max_val = bucket_hi (nb_ - 1); + if (value > max_val) + { + return nb_ - 1; + } + + int lo = 0; + int hi = nb_ - 1; + + while (lo < hi) + { + int mid = lo + (hi - lo) / 2; + T hi_val = bucket_hi (mid); + + if (value <= hi_val) + { + hi = mid; + } + else + { + lo = mid + 1; + } + } + + return lo; + + } + template + bool check_value_included (std::uint32_t i, const T &value) const + { + /* not mcv */ + if (bucket_approx_ndv (i) != 1) + { + return true; + } + /* mcv */ + T mcv_val = bucket_hi (i); + if (value == mcv_val) + { + return true; + } + return false; + } + template + bool + find_bucket_and_check (const T &value, int &bucket_index) + { + bucket_index = this->find_bucket (value); + if (bucket_index == -1) + { + return false; + } + + while (!this->check_value_included (bucket_index, value)) + { + bucket_index += 1; + if (bucket_index == static_cast (nb_ - 1)) + { + return true; + } + } + + return true; + } + + private: + template + T get_value (const void *ptr) const; + + const char *bucket_rec (std::uint32_t i) const; + const char *bucket_hi_value_ptr (std::uint32_t i) const; + + private: + std::string_view blob_{}; + std::string_view str_blob_{}; + const char *bucket_area_begin_ = nullptr; + const char *buckets_end_ = nullptr; + + std::uint32_t nb_ = 0; + std::uint32_t str_size_ = 0; + std::uint32_t total_size_ = 0; + + std::uint32_t type_ = DB_TYPE_UNKNOWN; + + }; + + +} // namespace hist + +#endif // _HISTOGRAM_READER_HPP_ \ No newline at end of file diff --git a/src/object/class_object.c b/src/object/class_object.c index dcd80cbdf74..9603f302ce2 100644 --- a/src/object/class_object.c +++ b/src/object/class_object.c @@ -43,6 +43,7 @@ #include "parser.h" #include "trigger_manager.h" #include "schema_manager.h" +#include "histogram_cl.hpp" #include "dbi.h" #if defined(WINDOWS) #include "misc_string.h" @@ -6889,6 +6890,7 @@ classobj_make_class (const char *name) class_->new_ = NULL; class_->stats = NULL; + class_->histogram = NULL; class_->owner = NULL; class_->collation_id = LANG_SYS_COLLATION; class_->auth_cache = NULL; @@ -6963,6 +6965,11 @@ classobj_free_class (SM_CLASS * class_) stats_free_statistics_and_init (class_->stats); } + if (class_->histogram != NULL) + { + stats_free_histogram_and_init_and_set_null (class_->histogram); + } + if (class_->properties != NULL) { classobj_free_prop_and_init (class_->properties); diff --git a/src/object/class_object.h b/src/object/class_object.h index 761aabbb20d..28cf59f1449 100644 --- a/src/object/class_object.h +++ b/src/object/class_object.h @@ -581,6 +581,7 @@ struct sm_class_constraint #define GET_OPTION_DEDUPLICATE(opt) \ (((opt) >> OPTION_DEDUPLICATE_SHIFT) & OPTION_DEDUPLICATE_MASK) + /* * Holds information about a method argument. This will be used * in a SM_METHOD_SIGNATURE signature structure. @@ -783,6 +784,7 @@ struct sm_class SM_QUERY_SPEC *query_spec; /* virtual class query_spec information */ SM_TEMPLATE *new_; /* temporary structure */ CLASS_STATS *stats; /* server statistics, loaded on demand */ + HIST_STATS *histogram; /* column histogram, loaded on demand */ MOP owner; /* authorization object */ int collation_id; /* class collation */ @@ -1152,4 +1154,5 @@ extern SM_PARTITION *classobj_copy_partition_info (SM_PARTITION * partition_info extern int classobj_change_constraint_status (DB_SEQ * properties, SM_CLASS_CONSTRAINT * cons, SM_INDEX_STATUS index_status); +extern int classobj_check_histogram_exist (SM_ATTRIBUTE * attributes, const char *attr_name); #endif /* _CLASS_OBJECT_H_ */ diff --git a/src/object/object_accessor.c b/src/object/object_accessor.c index 8a1d68ce137..a8500f3189b 100644 --- a/src/object/object_accessor.c +++ b/src/object/object_accessor.c @@ -55,7 +55,7 @@ #include "trigger_manager.h" #include "view_transform.h" #include "network_interface_cl.h" - +#include "execute_statement.h" #include "dbtype.h" /* @@ -3675,24 +3675,26 @@ obj_make_key_value (DB_VALUE * key, const DB_VALUE * values[], int size) MOP obj_find_multi_attr (MOP op, int size, const char *attr_names[], const DB_VALUE * values[], AU_FETCHMODE fetchmode) { - SM_CLASS *class_; + int error = NO_ERROR; SM_CLASS_CONSTRAINT *cons; MOP obj = NULL; - DB_VALUE key; SM_ATTRIBUTE **attp; const char **namep; - int i; - if (op == NULL || attr_names == NULL || values == NULL || size < 1) - { - er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0); - return NULL; - } - - db_make_null (&key); - if (obj_make_key_value (&key, values, size) == NULL) - { - return NULL; + SM_CLASS *class_ = NULL; + int i = 0; + BTID *unique_btid = NULL; + DB_VALUE *unique_key = NULL; + BTREE_SEARCH result; + SCAN_OPERATION_TYPE op_type = S_SELECT; + OID *oids = NULL; + int oid_count = 0; + + DB_OTMPL *obj_tmpl = dbt_create_object_internal_for_read_only (op); + if (obj_tmpl == NULL) + { + error = ER_FAILED; + goto end_find; } if (au_fetch_class (op, &class_, AU_FETCH_READ, AU_SELECT) != NO_ERROR) @@ -3735,12 +3737,82 @@ obj_find_multi_attr (MOP op, int size, const char *attr_names[], const DB_VALUE goto end_find; } - obj = obj_find_object_by_cons_and_key (op, cons, &key, fetchmode); + + unique_btid = (BTID *) db_private_alloc (NULL, sizeof (BTID)); + if (unique_btid == NULL) + { + error = ER_FAILED; + goto end_find; + } + unique_key = (DB_VALUE *) db_private_alloc (NULL, sizeof (DB_VALUE)); + if (unique_key == NULL) + { + error = ER_FAILED; + goto end_find; + } + + BTID_COPY (unique_btid, &cons->index_btid); + db_make_null (unique_key); + + for (i = 0; i < size; i++) + { + error = dbt_put_internal (obj_tmpl, attr_names[i], (DB_VALUE *) values[i]); + if (error != NO_ERROR) + { + goto end_find; + } + } + + /* multiple key, need to create a MIDXKEY */ + error = do_create_midxkey_for_constraint (obj_tmpl, cons, unique_key); + if (error != NO_ERROR) + { + goto end_find; + } + + if (fetchmode == AU_FETCH_UPDATE) + { + op_type = S_UPDATE; + } + else + { + op_type = S_SELECT; + } + + result = + btree_find_multi_uniques (ws_oid (obj_tmpl->classobj), obj_tmpl->pruning_type, unique_btid, unique_key, 1, + op_type, &oids, &oid_count); + + if (result == BTREE_ERROR_OCCURRED) + { + error = ER_FAILED; + } + else if (result == BTREE_KEY_NOTFOUND) + { + error = ER_OBJ_OBJECT_NOT_FOUND; + } + else if (result == BTREE_KEY_FOUND) + { + obj = ws_mop (oids, NULL); + free (oids); + } end_find: - if (size > 1) /* must clear a multi-column index key */ - pr_clear_value (&key); + if (obj_tmpl != NULL) + { + dbt_abort_object (obj_tmpl); + } + if (unique_key != NULL) + { + pr_clear_value (unique_key); + db_private_free (NULL, unique_key); + } + if (unique_btid != NULL) + { + db_private_free (NULL, unique_btid); + } + assert (oid_count < 2); return obj; } diff --git a/src/object/object_primitive.c b/src/object/object_primitive.c index 755c0ca7a92..31b7aa4cc95 100644 --- a/src/object/object_primitive.c +++ b/src/object/object_primitive.c @@ -6637,11 +6637,22 @@ static int mr_index_writeval_oid (OR_BUF * buf, DB_VALUE * value) { OID *oidp = NULL; + DB_OBJECT *obj = NULL; int rc = NO_ERROR; assert (DB_VALUE_TYPE (value) == DB_TYPE_OID || DB_VALUE_TYPE (value) == DB_TYPE_OBJECT); - oidp = db_get_oid (value); + if (DB_VALUE_TYPE (value) == DB_TYPE_OBJECT) + { +#if !defined (SERVER_MODE) + obj = db_get_object (value); + oidp = WS_OID (obj); +#endif + } + else + { + oidp = db_get_oid (value); + } rc = or_put_data (buf, (char *) (&oidp->pageid), tp_Integer.disksize); if (rc == NO_ERROR) diff --git a/src/object/object_representation.c b/src/object/object_representation.c index 963a221ece4..a311e88daff 100644 --- a/src/object/object_representation.c +++ b/src/object/object_representation.c @@ -4817,6 +4817,7 @@ or_put_value (OR_BUF * buf, DB_VALUE * value, int collapse_null, int include_dom dbval_type = DB_VALUE_DOMAIN_TYPE (value); type = pr_type_from_id (dbval_type); + assert (dbval_type <= DB_TYPE_LAST); if (type == NULL) { diff --git a/src/object/object_template.c b/src/object/object_template.c index fc80dcb1aed..c4885e2311a 100644 --- a/src/object/object_template.c +++ b/src/object/object_template.c @@ -923,6 +923,184 @@ make_template (MOP object, MOP classobj) return template_ptr; } + +/* + * make_template - This initializes a new object template. + * return: new object template + * object(in): the object that the template is being created for + * classobj(in): the class of the object + * + */ + +static OBJ_TEMPLATE * +make_template_for_read_only (MOP object, MOP classobj) +{ + OBJ_TEMPLATE *template_ptr; + AU_FETCHMODE mode; + AU_TYPE auth; + SM_CLASS *class_, *base_class; + MOP base_classobj, base_object; + MOBJ obj; + OBJ_TEMPASSIGN **vec; + + base_classobj = NULL; + base_class = NULL; + base_object = NULL; + + /* fetch & lock the class with the appropriate options */ + mode = AU_FETCH_READ; + auth = AU_SELECT; + + if (au_fetch_class (classobj, &class_, mode, auth)) + { + return NULL; + } + + /* + * we only need to keep track of the base class if this is a + * virtual class, for proxies, the instances look like usual + */ + + if (class_->class_type == SM_VCLASS_CT /* a view, and... */ + && object != classobj /* we are not doing a meta class update */ ) + { + /* + * could use vid_is_updatable() if + * the instance was supplied but since this can be NULL for + * insert templates, use mq_is_updatable on the class object instead. + * NOTE: Don't call this yet, try to use mq_fetch_one_real_class() + * to perform the updatability test. + */ + if (!mq_is_updatable (classobj)) + { + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IT_NOT_UPDATABLE_STMT, 0); + return NULL; + } + + + base_classobj = mq_fetch_one_real_class (classobj); + if (base_classobj == NULL) + { + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IT_NOT_UPDATABLE_STMT, 0); + return NULL; + } + + if (au_fetch_class (base_classobj, &base_class, AU_FETCH_READ, auth)) + { + return NULL; + } + + /* get the associated base object (if this isn't a proxy) */ + if (object != NULL && !vid_is_base_instance (object)) + { + base_object = vid_get_referenced_mop (object); + } + } + + /* + * If this is an instance update, fetch & lock the instance. + * NOTE: It might be good to use AU_FETCH_WRITE and use locator_update_instance + * to set the dirty bit after the template has been successfully applied. + * + * If this is a virtual instance on a non-proxy, could be locking + * the associated instance as well. Is this already being done ? + */ + if (object != NULL && object != classobj) + { + if (au_fetch_instance (object, &obj, AU_FETCH_UPDATE, LC_FETCH_MVCC_VERSION, AU_UPDATE)) + { + return NULL; + } + + /* + * Could cache the object memory pointer this in the template as + * well but that would require that it be pinned for a long + * duration through code that we don't control. Dangerous. + */ + } + + template_ptr = (OBJ_TEMPLATE *) area_alloc (Template_area); + if (template_ptr != NULL) + { + template_ptr->object = object; + template_ptr->classobj = classobj; + + /* + * cache the class info directly in the template, will need + * to remember the transaction id and chn for validation + */ + template_ptr->class_ = class_; + + /* cache the base class if this is a virtual class template */ + template_ptr->base_classobj = base_classobj; + template_ptr->base_class = base_class; + template_ptr->base_object = base_object; + + template_ptr->tran_id = tm_Tran_index; + template_ptr->schema_id = sm_local_schema_version (); + template_ptr->assignments = NULL; + template_ptr->label = NULL; + template_ptr->traversal = 0; + template_ptr->write_lock = mode != AU_FETCH_READ; + template_ptr->traversed = 0; + template_ptr->is_old_template = 0; + template_ptr->is_class_update = (object == classobj); + template_ptr->check_uniques = obt_Check_uniques; + if (TM_TRAN_ISOLATION () >= TRAN_REPEATABLE_READ) + { + template_ptr->check_serializable_conflict = 1; + } + else + { + template_ptr->check_serializable_conflict = 0; + } + template_ptr->uniques_were_modified = 0; + template_ptr->function_key_modified = 0; + + template_ptr->shared_was_modified = 0; + template_ptr->discard_on_finish = 1; + template_ptr->fkeys_were_modified = 0; + template_ptr->force_check_not_null = 0; + template_ptr->force_flush = 0; + template_ptr->is_autoincrement_set = 0; + template_ptr->pruning_type = DB_NOT_PARTITIONED_CLASS; + /* + * Don't do this until we've initialized the other stuff; + * OTMPL_NASSIGNS relies on the "class" attribute of the template. + */ + + if (template_ptr->is_class_update) + { + template_ptr->nassigns = template_ptr->class_->class_attribute_count; + } + else + { + template_ptr->nassigns = (template_ptr->class_->att_count + template_ptr->class_->shared_count); + } + + vec = NULL; + if (template_ptr->nassigns) + { + int i; + + vec = (OBJ_TEMPASSIGN **) malloc (template_ptr->nassigns * sizeof (OBJ_TEMPASSIGN *)); + if (!vec) + { + return NULL; + } + for (i = 0; i < template_ptr->nassigns; i++) + { + vec[i] = NULL; + } + } + + template_ptr->assignments = vec; + } + + return template_ptr; +} + + /* * validate_template - This is used to validate a template before each operation * return: error code @@ -1424,6 +1602,28 @@ obt_def_object (MOP class_mop) return template_ptr; } +OBJ_TEMPLATE * +obt_def_object_for_read_only (MOP class_mop) +{ + OBJ_TEMPLATE *template_ptr = NULL; + int is_class = locator_is_class (class_mop, DB_FETCH_CLREAD_INSTWRITE); + + if (is_class < 0) + { + return NULL; + } + if (!is_class) + { + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_NOT_A_CLASS, 0); + } + else + { + template_ptr = make_template_for_read_only (NULL, class_mop); + } + + return template_ptr; +} + /* * obt_edit_object - This is used to initialize an editing template * on an existing object. diff --git a/src/object/object_template.h b/src/object/object_template.h index 4927a54092a..f0b133e57cd 100644 --- a/src/object/object_template.h +++ b/src/object/object_template.h @@ -213,6 +213,7 @@ extern bool obt_Last_insert_id_generated; /* OBJECT TEMPLATE FUNCTIONS */ extern OBJ_TEMPLATE *obt_def_object (MOP class_); +extern OBJ_TEMPLATE *obt_def_object_for_read_only (MOP class_); extern OBJ_TEMPLATE *obt_edit_object (MOP object); extern int obt_quit (OBJ_TEMPLATE * template_ptr); diff --git a/src/object/schema_class_truncator.cpp b/src/object/schema_class_truncator.cpp index a1a977177f3..f94e3cb09f3 100644 --- a/src/object/schema_class_truncator.cpp +++ b/src/object/schema_class_truncator.cpp @@ -24,6 +24,7 @@ #include "dbtype_function.h" #include "execute_statement.h" #include "network_interface_cl.h" +#include "schema_system_catalog_constants.h" #include @@ -528,7 +529,6 @@ namespace cubschema STATEMENT_ID stmt_id; DB_VALUE value; char select_query[DB_MAX_IDENTIFIER_LENGTH + 256] = { 0 }; - constexpr int CNT_CATCLS_OBJECTS = 6; DB_BIGINT cnt_refers = CNT_CATCLS_OBJECTS + 1; int au_save; @@ -538,21 +538,6 @@ namespace cubschema return ER_FAILED; } - /* - * !!CAUTION!! - * If [data_type] is DB_TYPE_OBJECT and [class_of] is NULL, it is a general object domain, but we have to check only user classes. - * To do this, we use an walkaround in which we count the number of general object domains in existing system catalogs - * and if the SELECT result is over this, we asuume that there are some general object domain in some user class. - * - * The number is now 6 and hard-coded, so we MUST consider it when add or remove a general object domain in a system class. - * If it is changed, we MUST also change the value of CNT_CATCLS_OBJECTS. - * - * We add a QA test case to confirm there are only 6 general object domains in system classes, which will help notice this constraint - * and this test case also has to be changed along if CNT_CATCLS_OBJECTS is changed. - * - * See CBRD-23983 and CBRD-25697 for the details. - */ - AU_DISABLE (au_save); (void) snprintf (select_query, sizeof (select_query), diff --git a/src/object/schema_manager.c b/src/object/schema_manager.c index a00118cd6db..70c016b10c5 100644 --- a/src/object/schema_manager.c +++ b/src/object/schema_manager.c @@ -73,6 +73,7 @@ #include "release_string.h" #include "execute_statement.h" #include "crypt_opfunc.h" +#include "histogram_cl.hpp" #include "db.h" #include "object_accessor.h" @@ -83,6 +84,7 @@ #endif /* defined (SUPPRESS_STRLEN_WARNING) */ #define SM_ADD_CONSTRAINT_SAVEPOINT_NAME "aDDcONSTRAINT" +#define SM_ADD_HISTOGRAM_SAVEPOINT_NAME "aDDhISTOGRAM" #define SM_ADD_UNIQUE_CONSTRAINT_SAVEPOINT_NAME "aDDuNIQUEcONSTRAINT" #define SM_DROP_CLASS_MOP_SAVEPOINT_NAME "dELETEcLASSmOP" #define SM_TRUNCATE_SAVEPOINT_NAME "SmtRUnCATE" @@ -3104,6 +3106,7 @@ sm_mark_system_class_for_catalog (void) CT_STORED_PROC_NAME, CT_STORED_PROC_ARGS_NAME, CT_PARTITION_NAME, + CT_DB_HISTOGRAM_NAME, CTV_CLASS_NAME, CTV_SUPER_CLASS_NAME, CTV_VCLASS_NAME, @@ -3123,6 +3126,7 @@ sm_mark_system_class_for_catalog (void) CT_COLLATION_NAME, CT_SERVER_NAME, CTV_SERVER_NAME, + CTV_DB_HISTOGRAM_NAME, NULL }; @@ -4100,6 +4104,7 @@ sm_get_class_with_statistics (MOP classop) return NULL; } + /* get the statistics of the class */ if (class_->stats == NULL) { /* it's first time to get the statistics of this class */ @@ -4137,6 +4142,40 @@ sm_get_class_with_statistics (MOP classop) } } + /* get the histogram of the class */ + if (class_->histogram == NULL) + { + if (!OID_ISTEMP (WS_OID (classop))) + { + /* make sure the class is flushed before asking for statistics, this handles the case where an index + * has been added to the class but the catalog & statistics do not reflect this fact until the class + * is flushed. We might want to flush instances as well but that shouldn't affect the statistics ? */ + if (locator_flush_class (classop) != NO_ERROR) + { + return NULL; + } + int err = stats_get_histogram (classop, &class_->histogram); + if (err != NO_ERROR) + { + stats_free_histogram_and_init (class_->histogram); + return NULL; + } + } + } + else + { + /* to do : implement timestamp check and update */ + stats_free_histogram_and_init (class_->histogram); + class_->histogram = NULL; + int err = stats_get_histogram (classop, &class_->histogram); + if (err != NO_ERROR) + { + stats_free_histogram_and_init (class_->histogram); + class_->histogram = NULL; + return NULL; + } + } + return class_; } @@ -4410,7 +4449,7 @@ sm_update_all_catalog_statistics (bool with_fullscan) CT_PARTITION_NAME, CT_SERIAL_NAME, CT_HA_APPLY_INFO_NAME, CT_COLLATION_NAME, CT_USER_NAME, CT_TRIGGER_NAME, CT_AUTHORIZATION_NAME, CT_CHARSET_NAME, CT_DUAL_NAME, - CT_SERVER_NAME, CT_SYNONYM_NAME, NULL + CT_SERVER_NAME, CT_SYNONYM_NAME, CT_DB_HISTOGRAM_NAME, NULL }; for (i = 0; classes[i] != NULL && error == NO_ERROR; i++) @@ -12467,6 +12506,12 @@ install_new_representation (MOP classop, SM_CLASS * class_, SM_TEMPLATE * flat) class_->stats = NULL; } + if (newrep && class_->histogram != NULL) + { + stats_free_histogram_and_init (class_->histogram); + class_->histogram = NULL; + } + /* formerly had classop->no_objects = 1 here, why ? */ /* now that we don't always load methods immediately after editing, must make sure that the methods_loaded flag is @@ -13550,6 +13595,8 @@ sm_delete_class_mop (MOP op, bool is_cascade_constraints) char *fk_name = NULL; const char *table_name; MOP save_user, owner; + DB_OBJECT *histogram_obj = NULL; + int au_save; int save; bool is_au_disabled = false; @@ -13652,6 +13699,32 @@ sm_delete_class_mop (MOP op, bool is_cascade_constraints) } } + AU_DISABLE (au_save); + for (att = class_->attributes; att != NULL; att = (SM_ATTRIBUTE *) att->header.next) + { + + /* class_of, key_attr */ + if (class_->attributes == NULL) + { + AU_ENABLE (au_save); + goto end; + } + + db_get_histogram (op, att->header.name, &histogram_obj); + + if (histogram_obj != NULL) + { + error = db_drop (histogram_obj); + if (error != NO_ERROR) + { + AU_ENABLE (au_save); + goto end; + } + + } + } + AU_ENABLE (au_save); + /* remove auto_increment serial object if exist */ AU_DISABLE (save); is_au_disabled = true; @@ -15480,6 +15553,107 @@ sm_save_constraint_info (SM_CONSTRAINT_INFO ** save_info, const SM_CLASS_CONSTRA return error_code; } + +int +sm_add_histogram (MOP classop, const char *attr_name, int bucket_count, bool with_fullscan) +{ + bool set_savepoint = false; + int error = NO_ERROR; + SM_CLASS *class_ = NULL; + DB_OBJECT *obj = NULL; + + if (attr_name == NULL) + { + ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS); + return error; + } + + error = tran_system_savepoint (SM_ADD_HISTOGRAM_SAVEPOINT_NAME); + if (error != NO_ERROR) + { + return error; + } + + set_savepoint = true; + error = au_fetch_class (classop, &class_, AU_FETCH_READ, AU_SELECT); + if (error != NO_ERROR) + { + goto error_exit; + } + + error = smt_check_histogram_exist (classop, attr_name); + if (error != NO_ERROR) + { + if (error == ER_LC_CLASSNAME_EXIST) + { + return error; + } + goto error_exit; + } + + error = smt_add_histogram (classop, attr_name, bucket_count, with_fullscan); + if (error != NO_ERROR) + { + goto error_exit; + } + + return error; + +error_exit: + if (set_savepoint && error != ER_TM_SERVER_DOWN_UNILATERALLY_ABORTED && error != ER_LK_UNILATERALLY_ABORTED) + { + (void) tran_abort_upto_system_savepoint (SM_ADD_HISTOGRAM_SAVEPOINT_NAME); + } + + return error; +} + + +int +sm_drop_histogram (MOP classop, const char *attr_name) +{ + bool set_savepoint = false; + int error = NO_ERROR; + SM_CLASS *class_ = NULL; + + if (attr_name == NULL) + { + ERROR0 (error, ER_OBJ_INVALID_ARGUMENTS); + return error; + } + + error = tran_system_savepoint (SM_ADD_HISTOGRAM_SAVEPOINT_NAME); + if (error != NO_ERROR) + { + return error; + } + + set_savepoint = true; + error = au_fetch_class (classop, &class_, AU_FETCH_READ, AU_SELECT); + if (error != NO_ERROR) + { + goto error_exit; + } + + error = smt_check_histogram_exist_and_delete (classop, attr_name, false); + if (error != NO_ERROR) + { + goto error_exit; + } + + return error; + +error_exit: + if (set_savepoint && error != ER_TM_SERVER_DOWN_UNILATERALLY_ABORTED && error != ER_LK_UNILATERALLY_ABORTED) + { + (void) tran_abort_upto_system_savepoint (SM_ADD_HISTOGRAM_SAVEPOINT_NAME); + } + + return error; +} + + + /* * sm_save_function_index_info() - Saves the information necessary to recreate * a function index constraint diff --git a/src/object/schema_manager.h b/src/object/schema_manager.h index dcaff6b832d..dfd929902da 100644 --- a/src/object/schema_manager.h +++ b/src/object/schema_manager.h @@ -112,6 +112,8 @@ extern int sm_add_constraint (MOP classop, DB_CONSTRAINT_TYPE constraint_type, c const char **att_names, const int *asc_desc, const int *attrs_prefix_length, int class_attributes, SM_PREDICATE_INFO * predicate_info, SM_FUNCTION_INFO * fi_info, const char *comment, SM_INDEX_STATUS index_status); +extern int sm_add_histogram (MOP classop, const char *attr_name, int bucket_count, bool with_fullscan); +extern int sm_drop_histogram (MOP classop, const char *attr_name); extern int sm_drop_constraint (MOP classop, DB_CONSTRAINT_TYPE constraint_type, const char *constraint_name, const char **att_names, bool class_attributes, bool mysql_index_name); extern int sm_drop_index (MOP classop, const char *constraint_name); diff --git a/src/object/schema_system_catalog.cpp b/src/object/schema_system_catalog.cpp index b0413e6a60e..5421cdb5f6b 100644 --- a/src/object/schema_system_catalog.cpp +++ b/src/object/schema_system_catalog.cpp @@ -77,7 +77,8 @@ namespace cubschema CT_CHARSET_NAME, // "_db_charset" CT_SERVER_NAME, // "_db_server" CT_SYNONYM_NAME, // "_db_synonym" - CT_TRIGGER_NAME, // "_db_trigger" + CT_TRIGGER_NAME, // "db_trigger" + CT_DB_HISTOGRAM_NAME, // "_db_histogram" /* currently, not implemented */ CT_RESOLUTION_NAME // "_db_resolution" @@ -109,7 +110,8 @@ namespace cubschema CTV_COLLATION_NAME, // "db_collation" CTV_CHARSET_NAME, // "db_charset" CTV_SERVER_NAME, // "db_server" - CTV_SYNONYM_NAME // "db_synonym" + CTV_SYNONYM_NAME, // "db_synonym" + CTV_DB_HISTOGRAM_NAME // "db_histogram" }; static const identifier_store sm_catalog_class_names (sm_system_class_names, false); diff --git a/src/object/schema_system_catalog_constants.h b/src/object/schema_system_catalog_constants.h index 2f033d0ac5e..252cdadfc2f 100644 --- a/src/object/schema_system_catalog_constants.h +++ b/src/object/schema_system_catalog_constants.h @@ -54,6 +54,7 @@ #define CT_DUAL_NAME "dual" #define CT_SERVER_NAME "_db_server" #define CT_SYNONYM_NAME "_db_synonym" +#define CT_DB_HISTOGRAM_NAME "_db_histogram" /* catalog vclasses */ #define CTV_CLASS_NAME "db_class" @@ -78,6 +79,7 @@ #define CTV_CHARSET_NAME "db_charset" #define CTV_SERVER_NAME "db_server" #define CTV_SYNONYM_NAME "db_synonym" +#define CTV_DB_HISTOGRAM_NAME "db_histogram" #define CT_DBCOLL_COLL_ID_COLUMN "coll_id" #define CT_DBCOLL_COLL_NAME_COLUMN "coll_name" @@ -95,4 +97,37 @@ #define SP_ATTR_TARGET_METHOD_LEN (4096) +/* + * !! CAUTION !! + * + * If [data_type] is DB_TYPE_OBJECT and [class_of] is NULL, this represents a + * general object domain. However, for correctness we must consider only + * general object domains that belong to user-defined classes. + * + * This distinction is especially important for TRUNCATE processing, where + * domain validation must ignore system-class object domains and detect only + * user-class dependencies. + * + * Since there is no direct way to distinguish system-class object domains + * from user-class ones at this point, we use a workaround: we count the + * number of general object domains that are known to exist in system catalogs, + * and if the SELECT result exceeds this number, we assume that at least one + * general object domain exists in a user class. + * + * The number of general object domains in system classes is currently 6 and + * is hard-coded. Therefore, when a general object domain is added to or + * removed from any system class, this value MUST be reviewed. + * + * If the number changes, CNT_CATCLS_OBJECTS MUST be updated accordingly. + * + * A QA test case has been added to verify that system classes contain exactly + * 6 general object domains. This test is intended to catch violations of this + * assumption early. If CNT_CATCLS_OBJECTS is modified, the corresponding QA + * test MUST also be updated. + * + * See CBRD-23983 and CBRD-25697 for details. + */ + +#define CNT_CATCLS_OBJECTS (8) /* number of general object domains in system classes */ + #endif /* _SCHEMA_SYSTEM_CATALOG_CONSTANTS_H_ */ diff --git a/src/object/schema_system_catalog_install.cpp b/src/object/schema_system_catalog_install.cpp index b82cc51425f..4d46a7823f7 100644 --- a/src/object/schema_system_catalog_install.cpp +++ b/src/object/schema_system_catalog_install.cpp @@ -286,6 +286,7 @@ catcls_init (void) ADD_TABLE_DEFINITION (CT_DUAL_NAME, system_catalog_initializer::get_dual ()); ADD_TABLE_DEFINITION (CT_SYNONYM_NAME, system_catalog_initializer::get_synonym ()); ADD_TABLE_DEFINITION (CT_SERVER_NAME, system_catalog_initializer::get_server ()); + ADD_TABLE_DEFINITION (CT_DB_HISTOGRAM_NAME, system_catalog_initializer::get_db_histogram()); ADD_VIEW_DEFINITION (CTV_CLASS_NAME, system_catalog_initializer::get_view_class ()); ADD_VIEW_DEFINITION (CTV_SUPER_CLASS_NAME, system_catalog_initializer::get_view_direct_super_class ()); @@ -309,6 +310,7 @@ catcls_init (void) ADD_VIEW_DEFINITION (CTV_CHARSET_NAME, system_catalog_initializer::get_view_charset ()); ADD_VIEW_DEFINITION (CTV_SERVER_NAME, system_catalog_initializer::get_view_server ()); ADD_VIEW_DEFINITION (CTV_SYNONYM_NAME, system_catalog_initializer::get_view_synonym ()); + ADD_VIEW_DEFINITION (CTV_DB_HISTOGRAM_NAME, system_catalog_initializer::get_view_db_histogram ()); } int @@ -391,6 +393,14 @@ namespace cubschema return s; } + const inline std::string format_varbit (const int size) + { + std::string s ("bit varying("); + s += std::to_string (size); + s += ")"; + return s; + } + const inline std::string format_numeric (const int prec, const int scale) { std::string s ("numeric("); @@ -1244,6 +1254,38 @@ namespace cubschema // initializer nullptr ); + + + } + + system_catalog_definition + system_catalog_initializer::get_db_histogram () + { +// db_class + return system_catalog_definition ( + // name + CT_DB_HISTOGRAM_NAME, + // columns + { + {"class_of", "object"}, + {"key_attr", format_varchar (255)}, + {"with_fullscan","integer"}, + {"null_frequency", "double"}, + {"histogram_values", format_varbit (1073741823) } + }, +// constraint + { + {DB_CONSTRAINT_UNIQUE, "", {"class_of", "key_attr", nullptr}, false} + }, +// authorization + { + // owner + Au_dba_user, {} + }, +// initializer + nullptr + ); + } /* ========================================================================== */ @@ -2087,5 +2129,38 @@ namespace cubschema // initializer nullptr ); + + } + + system_catalog_definition + system_catalog_initializer::get_view_db_histogram () + { +// db_class + return system_catalog_definition ( + // name + CTV_DB_HISTOGRAM_NAME, + // columns + { + {"class_of", "object"}, + {"key_attr", format_varchar (255)}, + {"with_fullscan", format_varchar (32)}, + {"null_frequency", "double"}, + {attribute_kind::QUERY_SPEC, sm_define_view_db_histogram_spec ()} + }, +// constraint + {}, +// authorization + { + // owner + Au_dba_user, + // grants + { + {Au_public_user, AU_SELECT, false} + } + }, +// initializer + nullptr + ); + } } \ No newline at end of file diff --git a/src/object/schema_system_catalog_install.hpp b/src/object/schema_system_catalog_install.hpp index 5e81ac04b05..3b133d58141 100644 --- a/src/object/schema_system_catalog_install.hpp +++ b/src/object/schema_system_catalog_install.hpp @@ -58,6 +58,7 @@ namespace cubschema static system_catalog_definition get_dual (); static system_catalog_definition get_server (); static system_catalog_definition get_synonym (); + static system_catalog_definition get_db_histogram (); // views static system_catalog_definition get_view_class (); @@ -82,6 +83,7 @@ namespace cubschema static system_catalog_definition get_view_charset (); static system_catalog_definition get_view_synonym (); static system_catalog_definition get_view_server (); + static system_catalog_definition get_view_db_histogram (); }; } @@ -108,5 +110,6 @@ const char *sm_define_view_collation_spec (void); const char *sm_define_view_charset_spec (void); const char *sm_define_view_synonym_spec (void); const char *sm_define_view_server_spec (void); +const char *sm_define_view_db_histogram_spec (void); #endif /* _SCHEMA_SYSTEM_CATALOG_INSTALL_HPP_ */ diff --git a/src/object/schema_system_catalog_install_query_spec.cpp b/src/object/schema_system_catalog_install_query_spec.cpp index 8a7532d630c..edcc1aceb63 100644 --- a/src/object/schema_system_catalog_install_query_spec.cpp +++ b/src/object/schema_system_catalog_install_query_spec.cpp @@ -1638,3 +1638,27 @@ sm_define_view_server_spec (void) return stmt; } + +const char * +sm_define_view_db_histogram_spec (void) +{ + static char stmt [2048]; + + // *INDENT-OFF* + sprintf (stmt, + "SELECT " + "[h].[class_of] AS [class_of], " + "[h].[key_attr] AS [key_attr], " + "CASE WHEN [h].[with_fullscan] = 0 THEN 'sampling scan' ELSE 'full scan' END AS [with_fullscan], " + "[h].[null_frequency] AS [null_frequency] " + "FROM " + /* CT_DB_HISTOGRAM_NAME */ + "[%s] AS [h] " + "ORDER BY " /* Is it possible to remove ORDER BY? */ + "[h].[class_of], " + "[h].[key_attr]", + CT_DB_HISTOGRAM_NAME); + // *INDENT-ON* + + return stmt; +} \ No newline at end of file diff --git a/src/object/schema_template.c b/src/object/schema_template.c index da74fcfeee1..9ee92b09bef 100644 --- a/src/object/schema_template.c +++ b/src/object/schema_template.c @@ -1981,6 +1981,176 @@ smt_check_index_exist (SM_TEMPLATE * template_, char **out_shared_cons_name, DB_ return error; } + +int +smt_check_histogram_exist (MOP classop, const char *attr_name) +{ + int error = NO_ERROR; + DB_OBJECT *histogram_class, *histogram_obj = NULL; + DB_VALUE value[2]; + DB_VALUE *value_ptrs[2] = { &value[0], &value[1] }; + const char *search_attrs[2] = { "class_of", "key_attr" }; + + histogram_class = sm_find_class (CT_DB_HISTOGRAM_NAME); + if (histogram_class == NULL) + { + error = ER_BO_MISSING_OR_INVALID_CATALOG; + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0); + goto end; + } + + /* class_of, key_attr */ + db_make_object (&value[0], classop); + db_make_string (&value[1], attr_name); + + histogram_obj = db_find_multi_unique (histogram_class, 2, (char **) search_attrs, value_ptrs, DB_FETCH_READ); + if (histogram_obj != NULL) + { + error = ER_LC_CLASSNAME_EXIST; + char error_histogram[256]; + sprintf (error_histogram, "histogram of %s(%s)", sm_get_ch_name (classop), attr_name); + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, error_histogram); + goto end; + } +end: + return error; +} + +int +smt_check_histogram_exist_and_delete (MOP classop, const char *attr_name, bool no_error_if_not_found) +{ + int error = NO_ERROR; + DB_OBJECT *histogram_class, *histogram_obj = NULL; + DB_VALUE value[2]; + DB_VALUE *value_ptrs[2] = { &value[0], &value[1] }; + const char *search_attrs[2] = { "class_of", "key_attr" }; + histogram_class = sm_find_class (CT_DB_HISTOGRAM_NAME); + if (histogram_class == NULL) + { + error = ER_BO_MISSING_OR_INVALID_CATALOG; + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0); + goto end; + } + + + /* class_of, key_attr */ + db_make_object (&value[0], classop); + db_make_string (&value[1], attr_name); + + histogram_obj = db_find_multi_unique (histogram_class, 2, (char **) search_attrs, value_ptrs, DB_FETCH_WRITE); + if (histogram_obj == NULL) + { + if (!no_error_if_not_found) + { + error = ER_LC_UNKNOWN_CLASSNAME; + char error_histogram[256]; + sprintf (error_histogram, "histogram of %s(%s)", sm_get_ch_name (classop), attr_name); + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, error_histogram); + goto end; + } + } + else + { + error = db_drop (histogram_obj); + if (error != NO_ERROR) + { + goto end; + } + } +end: + return error; +} + +int +smt_add_histogram (MOP classop, const char *attr_name, int bucket_count, bool with_fullscan) +{ + int au_save, error = NO_ERROR; + DB_OBJECT *ret_obj = NULL, *histogram_class = NULL; + DB_VALUE value; + DB_OTMPL *obj_tmpl = NULL; + double null_frequency = 0; + db_make_null (&value); + + /* temporarily disable authorization to access db_serial class */ + AU_DISABLE (au_save); + + histogram_class = sm_find_class (CT_DB_HISTOGRAM_NAME); + if (histogram_class == NULL) + { + error = ER_QPROC_DB_SERIAL_NOT_FOUND; + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0); + goto end; + } + + obj_tmpl = dbt_create_object_internal ((MOP) histogram_class); + + if (obj_tmpl == NULL) + { + error = er_errid (); + goto end; + } + + db_make_object (&value, classop); + + error = dbt_put_internal (obj_tmpl, "class_of", &value); + pr_clear_value (&value); + if (error != NO_ERROR) + { + assert (false); + goto end; + } + /* key_attr */ + db_make_string (&value, attr_name); + error = dbt_put_internal (obj_tmpl, "key_attr", &value); + pr_clear_value (&value); + if (error != NO_ERROR) + { + assert (false); + goto end; + } + + /* with_fullscan */ + db_make_int (&value, with_fullscan); + error = dbt_put_internal (obj_tmpl, "with_fullscan", &value); + pr_clear_value (&value); + if (error != NO_ERROR) + { + goto end; + } + + + db_make_double (&value, null_frequency); + error = dbt_put_internal (obj_tmpl, "null_frequency", &value); + pr_clear_value (&value); + if (error != NO_ERROR) + { + goto end; + } + + /* histogram_values */ + db_make_null (&value); + error = dbt_put_internal (obj_tmpl, "histogram_values", &value); + pr_clear_value (&value); + if (error != NO_ERROR) + { + goto end; + } + ret_obj = dbt_finish_object (obj_tmpl); + if (ret_obj == NULL) + { + assert (er_errid () != NO_ERROR); + error = er_errid (); + } + +end: + if (obj_tmpl != NULL && ret_obj == NULL) + { + dbt_abort_object (obj_tmpl); + } + AU_ENABLE (au_save); + return error; +} + /* * smt_add_constraint() - Adds the integrity constraint flags for an attribute. * return: NO_ERROR on success, non-zero for ERROR diff --git a/src/object/schema_template.h b/src/object/schema_template.h index 45be1e62f47..4b8d800b741 100644 --- a/src/object/schema_template.h +++ b/src/object/schema_template.h @@ -84,6 +84,8 @@ extern int smt_add_constraint (SM_TEMPLATE * template_, DB_CONSTRAINT_TYPE const int class_attribute, SM_FOREIGN_KEY_INFO * fk_info, SM_PREDICATE_INFO * filter_index, SM_FUNCTION_INFO * function_index, const char *comment, SM_INDEX_STATUS index_status); +extern int smt_add_histogram (MOP classop, const char *attr_name, int bucket_count, bool with_fullscan); + extern int smt_drop_constraint (SM_TEMPLATE * template_, const char **att_names, const char *constraint_name, int class_attribute, SM_ATTRIBUTE_FLAG constraint); @@ -166,6 +168,8 @@ extern int smt_check_index_exist (SM_TEMPLATE * template_, char **out_shared_con DB_CONSTRAINT_TYPE constraint_type, const char *constraint_name, const char **att_names, const int *asc_desc, const SM_PREDICATE_INFO * filter_index, const SM_FUNCTION_INFO * function_index); +extern int smt_check_histogram_exist (MOP classop, const char *attr_name); +extern int smt_check_histogram_exist_and_delete (MOP classop, const char *attr_name, bool no_error_if_not_found); #if defined(ENABLE_UNUSED_FUNCTION) extern void smt_downcase_all_class_info (void); diff --git a/src/object/transform.c b/src/object/transform.c index e4d311e55c2..ca246535cfd 100644 --- a/src/object/transform.c +++ b/src/object/transform.c @@ -522,6 +522,7 @@ CT_CLASS ct_Indexkey = { ct_indexkey_atts }; + CT_CLASS *ct_Classes[] = { &ct_Class, &ct_Attribute, diff --git a/src/optimizer/query_graph.c b/src/optimizer/query_graph.c index 70f408222a4..a453e209f90 100644 --- a/src/optimizer/query_graph.c +++ b/src/optimizer/query_graph.c @@ -2913,6 +2913,11 @@ set_seg_node (PT_NODE * attr, QO_ENV * env, BITSET * bitset) * for shared variables, and it doesn't really hurt anyone just * to ignore failures here. */ + if (attr->node_type == PT_NAME) + { + attr->info.name.histogram = seg->pt_node->info.name.histogram; + attr->info.name.null_frequency = seg->pt_node->info.name.null_frequency; + } bitset_add (bitset, QO_SEG_IDX (seg)); } @@ -5173,6 +5178,7 @@ qo_get_attr_info (QO_ENV * env, QO_SEGMENT * seg) int attr_id; QO_ATTR_CUM_STATS *cum_statsp; ATTR_STATS *attr_statsp; + int attr_hist_statsp_index = 0; BTREE_STATS *bt_statsp; int n_attrs; const char *name; @@ -5181,6 +5187,7 @@ qo_get_attr_info (QO_ENV * env, QO_SEGMENT * seg) int n_unavail_indexes; SM_CLASS_CONSTRAINT *consp; CLASS_STATS *stats; + HIST_STATS *hist_stats; bool is_reserved_name = false; if ((QO_SEG_PT_NODE (seg))->info.name.meta_class == PT_RESERVED) @@ -5249,6 +5256,7 @@ qo_get_attr_info (QO_ENV * env, QO_SEGMENT * seg) /* pointer to ATTR_STATS of CLASS_STATS of QO_CLASS_INFO_ENTRY */ stats = QO_GET_CLASS_STATS (class_info_entryp); + hist_stats = QO_GET_HIST_STATS (class_info_entryp); QO_ASSERT (env, stats != NULL); if (stats->attr_stats == NULL) { @@ -5270,8 +5278,9 @@ qo_get_attr_info (QO_ENV * env, QO_SEGMENT * seg) /* search the attribute from the class information */ attr_statsp = stats->attr_stats; + attr_hist_statsp_index = 0; n_attrs = stats->n_attrs; - for (j = 0; j < n_attrs; j++, attr_statsp++) + for (j = 0; j < n_attrs; j++, attr_statsp++, attr_hist_statsp_index++) { if (attr_statsp->id == attr_id) { @@ -5288,6 +5297,10 @@ qo_get_attr_info (QO_ENV * env, QO_SEGMENT * seg) /* set Number of Distinct Values */ attr_infop->ndv += attr_statsp->ndv; + /* set histogram */ + QO_SEG_PT_NODE (seg)->info.name.histogram = hist_stats->histogram[attr_hist_statsp_index]; + QO_SEG_PT_NODE (seg)->info.name.null_frequency = hist_stats->null_frequency[attr_hist_statsp_index]; + if (cum_statsp->valid_limits == false) { /* first time */ diff --git a/src/optimizer/query_graph.h b/src/optimizer/query_graph.h index 0afb2614bfe..3dace5db0a3 100644 --- a/src/optimizer/query_graph.h +++ b/src/optimizer/query_graph.h @@ -200,7 +200,8 @@ struct qo_index */ #define QO_GET_CLASS_STATS(entryp) \ ((entryp)->self_allocated ? (entryp)->stats : (entryp)->smclass->stats) - +#define QO_GET_HIST_STATS(entryp) \ + ((entryp)->smclass->histogram) /* * This structure is the head of a list of QO_INDEX_ENTRY index structures. * The purpose for this node is to have a place to store cumulative diff --git a/src/optimizer/query_planner.c b/src/optimizer/query_planner.c index d24a857de28..c5165c2ecd7 100644 --- a/src/optimizer/query_planner.c +++ b/src/optimizer/query_planner.c @@ -51,6 +51,7 @@ #include "network_interface_cl.h" #include "dbtype.h" #include "regu_var.hpp" +#include "histogram_cl.hpp" #define TEST_DUMP_PLAN_SCAN_COST 0 #define TEST_DUMP_PLAN_SORT_COST 0 @@ -421,29 +422,6 @@ QO_PLAN_VTBL *all_vtbls[] = { &qo_worst_plan_vtbl }; -#define DEFAULT_NULL_SELECTIVITY (double) 0.01 -#define DEFAULT_EXISTS_SELECTIVITY (double) 0.1 -#define DEFAULT_SELECTIVITY (double) 0.1 -#define DEFAULT_EQUAL_SELECTIVITY (double) 0.001 -#define DEFAULT_EQUIJOIN_SELECTIVITY (double) 0.001 -#define DEFAULT_COMP_SELECTIVITY (double) 0.1 -#define DEFAULT_BETWEEN_SELECTIVITY (double) 0.01 -#define DEFAULT_IN_SELECTIVITY (double) 0.01 -#define DEFAULT_RANGE_SELECTIVITY (double) 0.1 - -/* Structural equivalence classes for expressions */ - -typedef enum PRED_CLASS -{ - PC_ATTR, - PC_CONST, - PC_HOST_VAR, - PC_SUBQUERY, - PC_SET, - PC_OTHER, - PC_MULTI_ATTR -} PRED_CLASS; - static double qo_or_selectivity (QO_ENV * env, double lhs_sel, double rhs_sel); static double qo_and_selectivity (QO_ENV * env, double lhs_sel, double rhs_sel); @@ -460,8 +438,6 @@ static double qo_range_selectivity (QO_ENV * env, PT_NODE * pt_expr); static double qo_all_some_in_selectivity (QO_ENV * env, PT_NODE * pt_expr); -static PRED_CLASS qo_classify (PT_NODE * attr); - static int qo_index_cardinality (QO_ENV * env, PT_NODE * attr); static int qo_index_cardinality_with_dedup (QO_ENV * env, PT_NODE * attr, BITSET * seg_bitset); @@ -9718,6 +9694,8 @@ qo_equal_selectivity (QO_ENV * env, PT_NODE * pt_expr) selectivity = DEFAULT_EQUAL_SELECTIVITY; + bool success = false; + switch (pc_lhs) { case PC_ATTR: @@ -9741,9 +9719,17 @@ qo_equal_selectivity (QO_ENV * env, PT_NODE * pt_expr) selectivity = DEFAULT_EQUIJOIN_SELECTIVITY; } + /* TODO: add histogram selectivity */ break; case PC_CONST: + histogram_get_equal_selectivity (lhs, rhs, &selectivity, &success); + if (success) + { + break; + } + [[fallthrough]]; + case PC_HOST_VAR: case PC_SUBQUERY: case PC_SET: @@ -9772,6 +9758,21 @@ qo_equal_selectivity (QO_ENV * env, PT_NODE * pt_expr) break; case PC_CONST: + switch (pc_rhs) + { + case PC_ATTR: + histogram_get_equal_selectivity (rhs, lhs, &selectivity, &success); + break; + + default: + break; + } + if (success) + { + break; + } + [[fallthrough]]; + case PC_HOST_VAR: case PC_SUBQUERY: case PC_SET: @@ -9962,7 +9963,101 @@ qo_equal_selectivity (QO_ENV * env, PT_NODE * pt_expr) static double qo_comp_selectivity (QO_ENV * env, PT_NODE * pt_expr) { - return DEFAULT_COMP_SELECTIVITY; + PT_NODE *lhs, *rhs, *multi_attr; + PRED_CLASS pc_lhs, pc_rhs; + int lhs_icard, rhs_icard, icard; + double selectivity; + + lhs = pt_expr->info.expr.arg1; + rhs = pt_expr->info.expr.arg2; + + /* the class of lhs and rhs */ + pc_lhs = qo_classify (lhs); + pc_rhs = qo_classify (rhs); + + selectivity = DEFAULT_COMP_SELECTIVITY; + + bool success = false; + switch (pc_lhs) + { + case PC_ATTR: + + switch (pc_rhs) + { + case PC_ATTR: + /* TODO: add histogram selectivity */ + break; + + case PC_CONST: + if (pt_expr->info.expr.op == PT_GE) + { + histogram_get_comp_selectivity (lhs, rhs, true, true, &selectivity, &success); + } + else if (pt_expr->info.expr.op == PT_GT) + { + histogram_get_comp_selectivity (lhs, rhs, true, false, &selectivity, &success); + } + else if (pt_expr->info.expr.op == PT_LE) + { + histogram_get_comp_selectivity (lhs, rhs, false, true, &selectivity, &success); + } + else if (pt_expr->info.expr.op == PT_LT) + { + histogram_get_comp_selectivity (lhs, rhs, false, false, &selectivity, &success); + } + break; + + default: + break; + } + + break; + + case PC_CONST: + switch (pc_rhs) + { + case PC_ATTR: + if (pt_expr->info.expr.op == PT_GE) + { + histogram_get_comp_selectivity (rhs, lhs, false, false, &selectivity, &success); + } + else if (pt_expr->info.expr.op == PT_GT) + { + histogram_get_comp_selectivity (rhs, lhs, false, true, &selectivity, &success); + } + else if (pt_expr->info.expr.op == PT_LE) + { + histogram_get_comp_selectivity (rhs, lhs, true, false, &selectivity, &success); + } + else if (pt_expr->info.expr.op == PT_LT) + { + histogram_get_comp_selectivity (rhs, lhs, true, true, &selectivity, &success); + } + break; + + default: + break; + } + break; + + case PC_MULTI_ATTR: + switch (pc_rhs) + { + case PC_MULTI_ATTR: + /* (attr,attr) = (attr,attr) */ + /* TODO: add histogram selectivity */ + break; + + default: + break; + } + + break; + default: + break; + } + + return success ? selectivity : DEFAULT_COMP_SELECTIVITY; } /* @@ -9997,7 +10092,8 @@ qo_range_selectivity (QO_ENV * env, PT_NODE * pt_expr) { PT_NODE *lhs, *arg1, *arg2; PRED_CLASS pc1, pc2; - double total_selectivity, selectivity; + double total_selectivity; + double selectivity = DEFAULT_BETWEEN_SELECTIVITY; int lhs_icard = 0, rhs_icard = 0, icard = 0; PT_NODE *range_node; PT_OP_TYPE op_type; @@ -10059,9 +10155,89 @@ qo_range_selectivity (QO_ENV * env, PT_NODE * pt_expr) pc1 = qo_classify (arg1); if (op_type == PT_BETWEEN_GE_LE || op_type == PT_BETWEEN_GE_LT || op_type == PT_BETWEEN_GT_LE - || op_type == PT_BETWEEN_GT_LT) + || op_type == PT_BETWEEN_GT_LT || op_type == PT_BETWEEN_INF_LT || op_type == PT_BETWEEN_INF_LE + || op_type == PT_BETWEEN_GE_INF || op_type == PT_BETWEEN_GT_INF) { - selectivity = DEFAULT_BETWEEN_SELECTIVITY; + double selectivity_a = 0.0, selectivity_b = 0.0, selectivity_backup = selectivity; + bool success1 = false; + bool success2 = false; + switch (op_type) + { + case PT_BETWEEN_GE_LE: + { + /* selectivity = sel_le(b) - sel_lt(a) */ + histogram_get_comp_selectivity (lhs, arg1, false, false, &selectivity_a, &success1); + histogram_get_comp_selectivity (lhs, arg2, false, true, &selectivity_b, &success2); + selectivity = selectivity_b - selectivity_a; + break; + } + case PT_BETWEEN_GE_LT: + { + /* selectivity = sel_lt(b) - sel_lt(a) */ + histogram_get_comp_selectivity (lhs, arg1, false, false, &selectivity_a, &success1); + histogram_get_comp_selectivity (lhs, arg2, false, false, &selectivity_b, &success2); + selectivity = selectivity_b - selectivity_a; + break; + } + case PT_BETWEEN_GT_LE: + { + /* selectivity = sel_le(b) - sel_lt(a) */ + histogram_get_comp_selectivity (lhs, arg1, false, true, &selectivity_a, &success1); + histogram_get_comp_selectivity (lhs, arg2, false, true, &selectivity_b, &success2); + selectivity = selectivity_b - selectivity_a; + break; + } + case PT_BETWEEN_GT_LT: + { + /* selectivity = sel_lt(b) - sel_lt(a) */ + histogram_get_comp_selectivity (lhs, arg1, false, true, &selectivity_a, &success1); + histogram_get_comp_selectivity (lhs, arg2, false, false, &selectivity_b, &success2); + selectivity = selectivity_b - selectivity_a; + break; + } + case PT_BETWEEN_INF_LT: + { + histogram_get_comp_selectivity (lhs, arg1, false, false, &selectivity_a, &success1); + success2 = true; + selectivity = selectivity_a; + break; + } + case PT_BETWEEN_INF_LE: + { + histogram_get_comp_selectivity (lhs, arg1, false, true, &selectivity_a, &success1); + success2 = true; + selectivity = selectivity_a; + break; + } + case PT_BETWEEN_GT_INF: + { + histogram_get_comp_selectivity (lhs, arg1, true, false, &selectivity_a, &success1); + success2 = true; + selectivity = selectivity_a; + break; + } + case PT_BETWEEN_GE_INF: + { + histogram_get_comp_selectivity (lhs, arg1, true, true, &selectivity_a, &success1); + success2 = true; + selectivity = selectivity_a; + break; + } + default: + break; + } + if (!(success1 && success2)) + { + if (op_type == PT_BETWEEN_INF_LT || op_type == PT_BETWEEN_INF_LE || op_type == PT_BETWEEN_GE_INF + || op_type == PT_BETWEEN_GT_INF) + { + selectivity = DEFAULT_COMP_SELECTIVITY; + } + else + { + selectivity = selectivity_backup; + } + } } else if (op_type == PT_BETWEEN_EQ_NA) { @@ -10097,12 +10273,7 @@ qo_range_selectivity (QO_ENV * env, PT_NODE * pt_expr) } } } - else - { - /* PT_BETWEEN_INF_LE, PT_BETWEEN_INF_LT, PT_BETWEEN_GE_INF, and PT_BETWEEN_GT_INF have only one argument */ - selectivity = DEFAULT_COMP_SELECTIVITY; - } selectivity = MAX (selectivity, 0.0); selectivity = MIN (selectivity, 1.0); @@ -10212,7 +10383,7 @@ qo_all_some_in_selectivity (QO_ENV * env, PT_NODE * pt_expr) * return: PRED_CLASS * attr(in): pt node to classify */ -static PRED_CLASS +PRED_CLASS qo_classify (PT_NODE * attr) { switch (attr->node_type) diff --git a/src/optimizer/query_planner.h b/src/optimizer/query_planner.h index 8c1c8821fad..da434244bac 100644 --- a/src/optimizer/query_planner.h +++ b/src/optimizer/query_planner.h @@ -110,6 +110,27 @@ typedef enum QO_PLAN_SKIP_ORDERBY_CAN_USE = -2, } QO_PLAN_SKIP_ORDERBY_OPT; +#define DEFAULT_NULL_SELECTIVITY (double) 0.01 +#define DEFAULT_EXISTS_SELECTIVITY (double) 0.1 +#define DEFAULT_SELECTIVITY (double) 0.1 +#define DEFAULT_EQUAL_SELECTIVITY (double) 0.001 +#define DEFAULT_EQUIJOIN_SELECTIVITY (double) 0.001 +#define DEFAULT_COMP_SELECTIVITY (double) 0.1 +#define DEFAULT_BETWEEN_SELECTIVITY (double) 0.01 +#define DEFAULT_IN_SELECTIVITY (double) 0.01 +#define DEFAULT_RANGE_SELECTIVITY (double) 0.1 + +typedef enum PRED_CLASS +{ + PC_ATTR, + PC_CONST, + PC_HOST_VAR, + PC_SUBQUERY, + PC_SET, + PC_OTHER, + PC_MULTI_ATTR +} PRED_CLASS; + struct qo_plan { QO_INFO *info; @@ -428,6 +449,8 @@ extern int qo_has_like_recompile_candidate (QO_PLAN * plan, void *arg); extern PT_NODE *qo_plan_compute_iscan_sort_list (QO_PLAN * root, PT_NODE * group_by, bool * is_index_w_prefix, bool for_min_max_optimize); +extern PRED_CLASS qo_classify (PT_NODE * node); + extern QO_PLAN_PARALLEL_OPT_USE qo_check_hjoin_for_parallel_opt (QO_PLAN * plan); #endif /* _QUERY_PLANNER_H_ */ diff --git a/src/parser/csql_grammar.y b/src/parser/csql_grammar.y index 5469d0234b6..8bd507ef883 100644 --- a/src/parser/csql_grammar.y +++ b/src/parser/csql_grammar.y @@ -582,6 +582,7 @@ BEGIN_SUPPRESS_WARNING_BISON_FLEX %type opt_invisible %type opt_paren_plus %type opt_with_fullscan +%type with_n_buckets %type online_parallel %type comp_op %type opt_of_all_some_any @@ -662,9 +663,13 @@ BEGIN_SUPPRESS_WARNING_BISON_FLEX %type rename_class_list %type rename_class_pair %type drop_stmt +%type drop_histogram_stmt %type opt_index_column_name_list %type index_column_name_list +%type histogram_column_list +%type histogram_column %type update_statistics_stmt +%type update_histogram_stmt %type only_class_name_list %type opt_level_spec %type char_string_literal_list @@ -1127,6 +1132,7 @@ BEGIN_SUPPRESS_WARNING_BISON_FLEX %token BOOLEAN_ %token BOTH_ %token BREADTH +%token BUCKETS %token BY %token CALL %token CASCADE @@ -1233,6 +1239,7 @@ BEGIN_SUPPRESS_WARNING_BISON_FLEX %token GRANT %token GROUP_ %token HAVING +%token HISTOGRAM %token HOUR_ %token HOUR_MILLISECOND %token HOUR_SECOND @@ -1913,6 +1920,10 @@ stmt_ { $$ = $1; } | update_statistics_stmt { $$ = $1; } + | update_histogram_stmt + { $$ = $1; } + | drop_histogram_stmt + { $$ = $1; } | drop_stmt { $$ = $1; } | do_stmt @@ -4679,6 +4690,24 @@ index_column_name_list }} ; +histogram_column_list + : /* empty */ + {{ $$ = NULL; }} + + | histogram_column_list ',' histogram_column + {{ $$ = parser_make_link ($1, $3); + PARSER_SAVE_ERR_CONTEXT ($$, @$.buffer_pos)}} + | histogram_column + {{ $$ = $1; + PARSER_SAVE_ERR_CONTEXT ($$, @$.buffer_pos) }} + ; + +histogram_column + : identifier + {{ $$ = $1; + PARSER_SAVE_ERR_CONTEXT ($$, @$.buffer_pos) }} + ; + update_statistics_stmt : UPDATE STATISTICS ON_ only_class_name_list opt_with_fullscan {{ @@ -4718,6 +4747,77 @@ update_statistics_stmt }} ; +update_histogram_stmt + : ANALYZE TABLE only_class_name UPDATE HISTOGRAM ON_ histogram_column_list with_n_buckets opt_with_fullscan + {{ + PT_NODE *uhs = parser_new_node (this_parser, PT_UPDATE_HISTOGRAM); + PT_NODE *target_t = parser_new_node (this_parser, PT_SPEC); + if (uhs && target_t) + { + target_t->info.spec.entity_name = $3; + PARSER_SAVE_ERR_CONTEXT (target_t, @3.buffer_pos) + target_t->info.spec.meta_class = PT_CLASS; + uhs->info.histogram.target_table_spec = target_t; + + uhs->info.histogram.target_columns = $7; + uhs->info.histogram.bucket_count = $8; + uhs->info.histogram.with_fullscan = $9; + } + + $$ = uhs; + PARSER_SAVE_ERR_CONTEXT ($$, @$.buffer_pos) + }} + | ANALYZE TABLE only_class_name UPDATE HISTOGRAM with_n_buckets opt_with_fullscan + {{ + PT_NODE *uhs = parser_new_node (this_parser, PT_UPDATE_HISTOGRAM); + PT_NODE *target_t = parser_new_node (this_parser, PT_SPEC); + if (uhs && target_t) + { + target_t->info.spec.entity_name = $3; + PARSER_SAVE_ERR_CONTEXT (target_t, @3.buffer_pos) + target_t->info.spec.meta_class = PT_CLASS; + uhs->info.histogram.target_table_spec = target_t; + uhs->info.histogram.target_columns = NULL; + uhs->info.histogram.bucket_count = $6; + uhs->info.histogram.with_fullscan = $7; + } + + $$ = uhs; + PARSER_SAVE_ERR_CONTEXT ($$, @$.buffer_pos) + }} + ; +drop_histogram_stmt + : ANALYZE TABLE only_class_name DROP HISTOGRAM ON_ histogram_column_list + {{ + PT_NODE *dhs = parser_new_node (this_parser, PT_DROP_HISTOGRAM); + PT_NODE *target_t = parser_new_node (this_parser, PT_SPEC); + if (dhs && target_t) + { + target_t->info.spec.entity_name = $3; + PARSER_SAVE_ERR_CONTEXT (target_t, @3.buffer_pos) + target_t->info.spec.meta_class = PT_CLASS; + dhs->info.histogram.target_table_spec = target_t; + dhs->info.histogram.target_columns = $7; + } + $$ = dhs; + PARSER_SAVE_ERR_CONTEXT ($$, @$.buffer_pos) + }} + | ANALYZE TABLE only_class_name DROP HISTOGRAM + {{ + PT_NODE *dhs = parser_new_node (this_parser, PT_DROP_HISTOGRAM); + PT_NODE *target_t = parser_new_node (this_parser, PT_SPEC); + if (dhs && target_t) + { + target_t->info.spec.entity_name = $3; + PARSER_SAVE_ERR_CONTEXT (target_t, @3.buffer_pos) + target_t->info.spec.meta_class = PT_CLASS; + dhs->info.histogram.target_table_spec = target_t; + } + $$ = dhs; + PARSER_SAVE_ERR_CONTEXT ($$, @$.buffer_pos) + }} + ; + only_class_name_list : only_class_name_list ',' only_class_name {{ @@ -4754,6 +4854,18 @@ opt_with_fullscan }} ; + +with_n_buckets + : /* empty */ + {{ + $$ = 10; + }} + | WITH unsigned_integer BUCKETS + {{ + $$ = $2->info.value.data_value.i; + }} + ; + opt_of_to_eq : /* empty */ | TO diff --git a/src/parser/csql_lexer.l b/src/parser/csql_lexer.l index f5f370c711a..8b583a408f6 100644 --- a/src/parser/csql_lexer.l +++ b/src/parser/csql_lexer.l @@ -201,6 +201,7 @@ IDL [a-zA-Z0-9_] [bB][oO][oO][lL][eE][aA][nN] { begin_token(yytext); return BOOLEAN_; } [bB][oO][tT][hH] { begin_token(yytext); return BOTH_; } [bB][rR][eE][aA][dD][tT][hH] { begin_token(yytext); return BREADTH; } +[bB][uU][cC][kK][eE][tT][sS] { begin_token(yytext); return BUCKETS; } [bB][yY] { begin_token(yytext); return BY; } [bB][uU][fF][fF][eE][rR] { begin_token(yytext); csql_yylval.cptr = pt_makename(yytext); @@ -454,6 +455,7 @@ IDL [a-zA-Z0-9_] [hH][eE][aA][pP] { begin_token(yytext); csql_yylval.cptr = pt_makename(yytext); return HEAP; } +[hH][iI][sS][tT][oO][gG][rR][aA][mM] { begin_token(yytext); return HISTOGRAM; } [hH][oO][sS][tT] { begin_token(yytext); csql_yylval.cptr = pt_makename(yytext); return HOST; } diff --git a/src/parser/name_resolution.c b/src/parser/name_resolution.c index 0284b075541..a78bd093615 100644 --- a/src/parser/name_resolution.c +++ b/src/parser/name_resolution.c @@ -3294,6 +3294,22 @@ pt_bind_names (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue *continue_walk = PT_LIST_WALK; break; + case PT_UPDATE_HISTOGRAM: + case PT_DROP_HISTOGRAM: + scopestack.specs = node->info.histogram.target_table_spec; + bind_arg->scopes = &scopestack; + spec_frame.next = bind_arg->spec_frames; + spec_frame.extra_specs = NULL; + bind_arg->spec_frames = &spec_frame; + pt_bind_scope (parser, bind_arg); + + parser_walk_leaves (parser, node, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg); + + bind_arg->spec_frames = bind_arg->spec_frames->next; + bind_arg->scopes = bind_arg->scopes->next; + + *continue_walk = PT_LIST_WALK; + break; case PT_METHOD_CALL: /* * We accept two different method call syntax: @@ -6903,7 +6919,7 @@ pt_make_subclass_list (PARSER_CONTEXT * parser, DB_OBJECT * db, int line_num, in result->info.name.spec_id = id; result->info.name.meta_class = meta_class; result->info.name.partition = NULL; - + result->info.name.histogram = NULL; if ((au_fetch_class_force (db, &smclass, AU_FETCH_READ) == NO_ERROR)) { if (smclass->partition != NULL && smclass->partition->pname == NULL) diff --git a/src/parser/parse_tree.h b/src/parser/parse_tree.h index b4aac283486..ce0c03e7cdb 100644 --- a/src/parser/parse_tree.h +++ b/src/parser/parse_tree.h @@ -985,6 +985,8 @@ enum pt_node_type PT_REVOKE = CUBRID_STMT_REVOKE, PT_UPDATE_STATS = CUBRID_STMT_UPDATE_STATS, PT_GET_STATS = CUBRID_STMT_GET_STATS, + PT_UPDATE_HISTOGRAM = CUBRID_STMT_UPDATE_HISTOGRAM, + PT_DROP_HISTOGRAM = CUBRID_STMT_DROP_HISTOGRAM, PT_INSERT = CUBRID_STMT_INSERT, PT_SELECT = CUBRID_STMT_SELECT, PT_UPDATE = CUBRID_STMT_UPDATE, @@ -1614,6 +1616,7 @@ typedef struct pt_auth_cmd_info PT_AUTH_CMD_INFO; typedef struct pt_commit_work_info PT_COMMIT_WORK_INFO; typedef struct pt_create_entity_info PT_CREATE_ENTITY_INFO; typedef struct pt_index_info PT_INDEX_INFO; +typedef struct pt_histogram_info PT_HISTOGRAM_INFO; typedef struct pt_create_user_info PT_CREATE_USER_INFO; typedef struct pt_create_trigger_info PT_CREATE_TRIGGER_INFO; typedef struct pt_cte_info PT_CTE_INFO; @@ -1973,6 +1976,16 @@ struct pt_create_entity_info unsigned if_not_exists:1; /* IF NOT EXISTS clause for create table | class */ }; +/* CREATE HISTOGRAM INFO */ + +struct pt_histogram_info +{ + PT_NODE *target_table_spec; /* PT_SPEC */ + PT_NODE *target_columns; /* PT_COLUMN_LIST (PT_NAME) */ + int bucket_count; /* bucket count */ + int with_fullscan; /* with fullscan */ +}; + /* CREATE/DROP INDEX INFO */ struct pt_index_info { @@ -2659,6 +2672,8 @@ struct pt_name_info int coll_modifier; /* collation modifier = collation + 1 */ PT_RESERVED_NAME_ID reserved_id; /* used to identify reserved name */ size_t json_table_column_index; /* will be used only for json_table to gather attributes in the correct order */ + DB_VALUE *histogram; /* histogram value */ + double null_frequency; /* null frequency value */ }; /* @@ -3513,6 +3528,7 @@ union pt_statement_info PT_GRANT_INFO grant; PT_HOST_VAR_INFO host_var; PT_INDEX_INFO index; + PT_HISTOGRAM_INFO histogram; PT_INSERT_INFO insert; PT_INSERT_VALUE_INFO insert_value; PT_ISOLATION_LVL_INFO isolation_lvl; diff --git a/src/parser/parse_tree_cl.c b/src/parser/parse_tree_cl.c index ac9dc7503d1..2235e1dfc89 100644 --- a/src/parser/parse_tree_cl.c +++ b/src/parser/parse_tree_cl.c @@ -212,6 +212,7 @@ static PT_NODE *pt_apply_commit_work (PARSER_CONTEXT * parser, PT_NODE * p, void static PT_NODE *pt_apply_constraint (PARSER_CONTEXT * parser, PT_NODE * p, void *arg); static PT_NODE *pt_apply_create_entity (PARSER_CONTEXT * parser, PT_NODE * p, void *arg); static PT_NODE *pt_apply_create_index (PARSER_CONTEXT * parser, PT_NODE * p, void *arg); +static PT_NODE *pt_apply_update_histogram (PARSER_CONTEXT * parser, PT_NODE * p, void *arg); static PT_NODE *pt_apply_create_user (PARSER_CONTEXT * parser, PT_NODE * p, void *arg); static PT_NODE *pt_apply_data_default (PARSER_CONTEXT * parser, PT_NODE * p, void *arg); static PT_NODE *pt_apply_datatype (PARSER_CONTEXT * parser, PT_NODE * p, void *arg); @@ -292,6 +293,8 @@ static PT_NODE *pt_init_auth_cmd (PT_NODE * p); static PT_NODE *pt_init_constraint (PT_NODE * node); static PT_NODE *pt_init_create_entity (PT_NODE * p); static PT_NODE *pt_init_create_index (PT_NODE * p); +static PT_NODE *pt_init_update_histogram (PT_NODE * p); +static PT_NODE *pt_init_drop_histogram (PT_NODE * p); static PT_NODE *pt_init_data_default (PT_NODE * p); static PT_NODE *pt_init_datatype (PT_NODE * p); static PT_NODE *pt_init_delete (PT_NODE * p); @@ -340,6 +343,8 @@ static PARSER_VARCHAR *pt_print_constraint (PARSER_CONTEXT * parser, PT_NODE * p static PARSER_VARCHAR *pt_print_col_def_constraint (PARSER_CONTEXT * parser, PT_NODE * p); static PARSER_VARCHAR *pt_print_create_entity (PARSER_CONTEXT * parser, PT_NODE * p); static PARSER_VARCHAR *pt_print_create_index (PARSER_CONTEXT * parser, PT_NODE * p); +static PARSER_VARCHAR *pt_print_update_histogram (PARSER_CONTEXT * parser, PT_NODE * p); +static PARSER_VARCHAR *pt_print_drop_histogram (PARSER_CONTEXT * parser, PT_NODE * p); static PARSER_VARCHAR *pt_print_create_serial (PARSER_CONTEXT * parser, PT_NODE * p); static PARSER_VARCHAR *pt_print_create_stored_procedure (PARSER_CONTEXT * parser, PT_NODE * p); static PARSER_VARCHAR *pt_print_create_trigger (PARSER_CONTEXT * parser, PT_NODE * p); @@ -3075,6 +3080,10 @@ pt_show_node_type (PT_NODE * node) return "CREATE_ENTITY"; case PT_CREATE_INDEX: return "CREATE_INDEX"; + case PT_UPDATE_HISTOGRAM: + return "update_histogram"; + case PT_DROP_HISTOGRAM: + return "DROP_HISTOGRAM"; case PT_CREATE_USER: return "CREATE_USER"; case PT_CREATE_TRIGGER: @@ -5028,6 +5037,8 @@ pt_init_apply_f (void) pt_apply_func_array[PT_COMMIT_WORK] = pt_apply_commit_work; pt_apply_func_array[PT_CREATE_ENTITY] = pt_apply_create_entity; pt_apply_func_array[PT_CREATE_INDEX] = pt_apply_create_index; + pt_apply_func_array[PT_UPDATE_HISTOGRAM] = pt_apply_update_histogram; + pt_apply_func_array[PT_DROP_HISTOGRAM] = pt_apply_update_histogram; pt_apply_func_array[PT_CREATE_USER] = pt_apply_create_user; pt_apply_func_array[PT_CREATE_TRIGGER] = pt_apply_create_trigger; pt_apply_func_array[PT_CREATE_SERIAL] = pt_apply_create_serial; @@ -5162,6 +5173,8 @@ pt_init_init_f (void) pt_init_func_array[PT_COMMIT_WORK] = pt_init_func_null_function; pt_init_func_array[PT_CREATE_ENTITY] = pt_init_create_entity; pt_init_func_array[PT_CREATE_INDEX] = pt_init_create_index; + pt_init_func_array[PT_UPDATE_HISTOGRAM] = pt_init_update_histogram; + pt_init_func_array[PT_DROP_HISTOGRAM] = pt_init_drop_histogram; pt_init_func_array[PT_CREATE_USER] = pt_init_func_null_function; pt_init_func_array[PT_CREATE_TRIGGER] = pt_init_func_null_function; pt_init_func_array[PT_CREATE_SERIAL] = pt_init_func_null_function; @@ -5292,6 +5305,8 @@ pt_init_print_f (void) pt_print_func_array[PT_COMMIT_WORK] = pt_print_commit_work; pt_print_func_array[PT_CREATE_ENTITY] = pt_print_create_entity; pt_print_func_array[PT_CREATE_INDEX] = pt_print_create_index; + pt_print_func_array[PT_UPDATE_HISTOGRAM] = pt_print_update_histogram; + pt_print_func_array[PT_DROP_HISTOGRAM] = pt_print_drop_histogram; pt_print_func_array[PT_CREATE_USER] = pt_print_create_user; pt_print_func_array[PT_CREATE_TRIGGER] = pt_print_create_trigger; pt_print_func_array[PT_CREATE_SERIAL] = pt_print_create_serial; @@ -7317,6 +7332,150 @@ pt_print_create_entity (PARSER_CONTEXT * parser, PT_NODE * p) return q; } +/* update_histogram */ +/* + * pt_init_update_histogram () - + * return: + * p(in): + */ +static PT_NODE * +pt_init_update_histogram (PT_NODE * p) +{ + return p; +} + +/* update_histogram */ +/* + * pt_init_drop_histogram () - + * return: + * p(in): + */ +static PT_NODE * +pt_init_drop_histogram (PT_NODE * p) +{ + p->info.histogram.with_fullscan = 0; + p->info.histogram.bucket_count = 0; + return p; +} + +/* + * pt_apply_update_histogram () - + * return: + * parser(in): + * p(in): + * g(in): + * arg(in): + */ +static PT_NODE * +pt_apply_update_histogram (PARSER_CONTEXT * parser, PT_NODE * p, void *arg) +{ + PT_APPLY_WALK (parser, p->info.histogram.target_table_spec, arg); + PT_APPLY_WALK (parser, p->info.histogram.target_columns, arg); + return p; +} + +/* + * pt_apply_update_histogram () - + * return: + * parser(in): + * p(in): + * g(in): + * arg(in): + */ +static PARSER_VARCHAR * +pt_print_update_histogram (PARSER_CONTEXT * parser, PT_NODE * p) +{ + PARSER_VARCHAR *b = 0, *tbl = 0, *cl = 0; + unsigned int saved_cp = parser->custom_print; + PT_NODE *target_columns; + + parser->custom_print |= PT_SUPPRESS_RESOLVED; + + if (!(parser->custom_print & PT_SUPPRESS_INDEX)) + { + b = pt_append_nulstring (parser, b, "update"); + } + + b = pt_append_nulstring (parser, b, " histogram"); + + if (p->info.histogram.target_table_spec) + { + tbl = pt_print_bytes (parser, p->info.histogram.target_table_spec); + } + + if (!(parser->custom_print & PT_SUPPRESS_INDEX)) + { + b = pt_append_nulstring (parser, b, " on "); + b = pt_append_varchar (parser, b, tbl); + } + + + if (p->info.histogram.target_columns) + { + target_columns = p->info.histogram.target_columns; + cl = pt_print_bytes_l (parser, target_columns); + } + + b = pt_append_nulstring (parser, b, " ("); + b = pt_append_varchar (parser, b, cl); + b = pt_append_nulstring (parser, b, ") "); + + parser->custom_print = saved_cp; + + return b; +} + +/* + * pt_apply_update_histogram () - + * return: + * parser(in): + * p(in): + * g(in): + * arg(in): + */ +static PARSER_VARCHAR * +pt_print_drop_histogram (PARSER_CONTEXT * parser, PT_NODE * p) +{ + PARSER_VARCHAR *b = 0, *tbl = 0, *cl = 0; + unsigned int saved_cp = parser->custom_print; + PT_NODE *target_columns; + + parser->custom_print |= PT_SUPPRESS_RESOLVED; + + if (!(parser->custom_print & PT_SUPPRESS_INDEX)) + { + b = pt_append_nulstring (parser, b, "drop"); + } + + b = pt_append_nulstring (parser, b, " histogram"); + + if (p->info.histogram.target_table_spec) + { + tbl = pt_print_bytes (parser, p->info.histogram.target_table_spec); + } + + if (!(parser->custom_print & PT_SUPPRESS_INDEX)) + { + b = pt_append_nulstring (parser, b, " on "); + b = pt_append_varchar (parser, b, tbl); + } + + + if (p->info.histogram.target_columns) + { + target_columns = p->info.histogram.target_columns; + cl = pt_print_bytes_l (parser, target_columns); + } + + b = pt_append_nulstring (parser, b, " ("); + b = pt_append_varchar (parser, b, cl); + b = pt_append_nulstring (parser, b, ") "); + + parser->custom_print = saved_cp; + + return b; +} + /* CREATE_INDEX */ /* * pt_apply_create_index () - diff --git a/src/parser/parser_support.c b/src/parser/parser_support.c index f308a3dc290..7a9093b46aa 100644 --- a/src/parser/parser_support.c +++ b/src/parser/parser_support.c @@ -1515,6 +1515,8 @@ pt_is_ddl_statement (const PT_NODE * node) case PT_REMOVE_TRIGGER: case PT_RENAME_TRIGGER: case PT_UPDATE_STATS: + case PT_UPDATE_HISTOGRAM: + case PT_DROP_HISTOGRAM: /* TODO: check it */ case PT_CREATE_SERVER: case PT_DROP_SERVER: diff --git a/src/parser/semantic_check.c b/src/parser/semantic_check.c index 7e39409b91a..4d08b539f12 100644 --- a/src/parser/semantic_check.c +++ b/src/parser/semantic_check.c @@ -9061,6 +9061,75 @@ pt_check_create_index (PARSER_CONTEXT * parser, PT_NODE * node) pt_check_filter_index_expr (parser, node->info.index.column_names, node->info.index.where, db_obj); } +static void +pt_check_update_histogram (PARSER_CONTEXT * parser, PT_NODE * node) +{ + PT_NODE *name; + DB_OBJECT *db_obj; + int is_partition = DB_NOT_PARTITIONED_CLASS; + + /* check that there trying to create an histogram on a class */ + name = node->info.histogram.target_table_spec->info.spec.entity_name; + + /* We cannot create histogram of a class by using synonym names. */ + if (db_find_synonym (name->info.name.original) != NULL) + { + PT_ERRORmf (parser, name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_A_CLASS, name->info.name.original); + return; + } + else + { + /* db_find_synonym () == NULL */ + ASSERT_ERROR (); + + if (er_errid () == ER_SYNONYM_NOT_EXIST) + { + er_clear (); + } + else + { + return; + } + } + + db_obj = db_find_class (name->info.name.original); + if (db_obj == NULL) + { + PT_ERRORmf (parser, name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_A_CLASS, name->info.name.original); + return; + } + + /* make sure it's not a virtual class */ + if (db_is_class (db_obj) <= 0) + { + PT_ERRORm (parser, name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NO_INDEX_ON_VCLASS); + return; + } + + /* check if this is a partition class (TODO: not implemented) */ + if (sm_partitioned_class_type (db_obj, &is_partition, NULL, NULL) != NO_ERROR) + { + PT_ERROR (parser, node, er_msg ()); + return; + } + + if (is_partition == DB_PARTITION_CLASS) + { + PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_REQUEST); + return; + } + + name->info.name.db_object = db_obj; + + /* auth check */ + pt_check_user_owns_class (parser, name); + if (pt_has_error (parser)) + { + return; + } + +} + static void pt_check_alter_synonym (PARSER_CONTEXT * parser, PT_NODE * node) { @@ -12288,6 +12357,56 @@ pt_check_with_info (PARSER_CONTEXT * parser, PT_NODE * node, SEMANTIC_CHK_INFO * } break; + case PT_UPDATE_HISTOGRAM: + if (parser->host_var_count) + { + PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_HOSTVAR_IN_DDL); + } + else + { + sc_info_ptr->system_class = false; + node = pt_resolve_names (parser, node, sc_info_ptr); + if (!pt_has_error (parser) && node->node_type == PT_UPDATE_HISTOGRAM) + { + pt_check_update_histogram (parser, node); + } + + if (!pt_has_error (parser)) + { + node = pt_semantic_type (parser, node, info); + } + + if (node && !pt_has_error (parser)) + { + node = parser_walk_tree (parser, node, NULL, NULL, pt_semantic_check_local, sc_info_ptr); + } + } + break; + case PT_DROP_HISTOGRAM: + if (parser->host_var_count) + { + PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_HOSTVAR_IN_DDL); + } + else + { + sc_info_ptr->system_class = false; + node = pt_resolve_names (parser, node, sc_info_ptr); + if (!pt_has_error (parser) && node->node_type == PT_DROP_HISTOGRAM) + { + pt_check_update_histogram (parser, node); + } + + if (!pt_has_error (parser)) + { + node = pt_semantic_type (parser, node, info); + } + + if (node && !pt_has_error (parser)) + { + node = parser_walk_tree (parser, node, NULL, NULL, pt_semantic_check_local, sc_info_ptr); + } + } + break; case PT_SAVEPOINT: if ((node->info.savepoint.save_name) && (node->info.savepoint.save_name->info.name.meta_class == PT_PARAMETER)) { diff --git a/src/parser/xasl_generation.c b/src/parser/xasl_generation.c index 3d05705c9ea..8ab097c2f98 100644 --- a/src/parser/xasl_generation.c +++ b/src/parser/xasl_generation.c @@ -12446,6 +12446,12 @@ pt_to_class_spec_list (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * where_ NULL, where, NULL, NULL, regu_attributes_pred, regu_attributes_rest, NULL, output_val_list, regu_var_list, NULL, cache_pred, cache_rest, NULL, NO_SCHEMA, db_values_array_p, regu_attributes_reserved); + + if (access == NULL) + { + return NULL; + } + if (access_method == ACCESS_METHOD_SEQUENTIAL && PT_IS_SPEC_FLAG_SET (spec, PT_SPEC_FLAG_NO_PARALLEL_HEAP_SCAN)) { diff --git a/src/query/execute_schema.c b/src/query/execute_schema.c index 8dd9c480558..797a1331ca5 100644 --- a/src/query/execute_schema.c +++ b/src/query/execute_schema.c @@ -61,6 +61,7 @@ #include "dbtype.h" #include "jsp_cl.h" #include "msgcat_glossary.hpp" +#include "histogram_cl.hpp" #if defined (SUPPRESS_STRLEN_WARNING) #define strlen(s1) ((int) strlen(s1)) @@ -93,6 +94,11 @@ typedef enum DO_INDEX_CREATE, DO_INDEX_DROP } DO_INDEX; +typedef enum +{ + DO_HISTOGRAM_CREATE, DO_HISTOGRAM_DROP +} DO_HISTOGRAM; + typedef enum { SM_ATTR_CHG_NOT_NEEDED = 0, @@ -1784,7 +1790,8 @@ do_alter (PARSER_CONTEXT * parser, PT_NODE * alter) PT_NODE *crt_clause = NULL; bool do_semantic_checks = false; bool do_rollback = false; - + int au_save = 0; + DB_OBJECT *histogram_obj = NULL; CHECK_MODIFICATION_ERROR (); /* Multiple alter operations in a single statement need to be atomic. */ @@ -1816,7 +1823,51 @@ do_alter (PARSER_CONTEXT * parser, PT_NODE * alter) } assert (crt_result == crt_clause); } - + AU_DISABLE (au_save); + /* HANDLE HISTOGRAM DROP WHILE COLUMN MODIFY, CHANGE, RENAME, DROP */ + switch (alter_code) + { + case PT_DROP_ATTR_MTHD: + case PT_MODIFY_ATTR_MTHD: + case PT_CHANGE_ATTR: + { + if (crt_clause->info.alter.alter_clause.attr_mthd.attr_old_name != NULL) + { + const char *attr_name = crt_clause->info.alter.alter_clause.attr_mthd.attr_old_name->info.name.original; + if (attr_name != NULL) + { + db_get_histogram (crt_clause->info.alter.entity_name->info.name.db_object, attr_name, + &histogram_obj); + if (histogram_obj != NULL) + { + db_drop (histogram_obj); + } + } + } + break; + } + case PT_RENAME_ATTR_MTHD: + case PT_RENAME_ENTITY: + { + if (alter->info.alter.alter_clause.rename.old_name != NULL) + { + const char *attr_name = crt_clause->info.alter.alter_clause.rename.old_name->info.name.original; + if (attr_name != NULL) + { + db_get_histogram (crt_clause->info.alter.entity_name->info.name.db_object, attr_name, + &histogram_obj); + if (histogram_obj != NULL) + { + db_drop (histogram_obj); + } + } + } + break; + } + default: + break; + } + AU_ENABLE (au_save); switch (alter_code) { case PT_RENAME_ENTITY: @@ -4035,6 +4086,273 @@ do_alter_index (PARSER_CONTEXT * parser, const PT_NODE * statement) return error; } + + +/* + * update_or_drop_histogram_helper() - Creates or drops a histogram on a class. + * return: Error code + * parser(in): Parser context + * obj(in): Class object + * histogram_info(in): Histogram information +*/ +static int +update_or_drop_histogram_helper (PARSER_CONTEXT * parser, DB_OBJECT * const obj, + PT_HISTOGRAM_INFO * const histogram_info, DO_HISTOGRAM do_histogram) +{ + int error = NO_ERROR; + int bucket_count, nnames = 0; + bool with_fullscan = false; + char *attname = NULL; + PT_NODE *cur_column = NULL; + int is_partition = DB_NOT_PARTITIONED_CLASS; + DB_TYPE attr_type = DB_TYPE_NULL; + + /* check histogram is allowed on this class */ + error = sm_partitioned_class_type (obj, &is_partition, NULL, NULL); + if (error != NO_ERROR) + { + return error; + } + if (is_partition == DB_PARTITION_CLASS) + { + er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_NOT_ALLOWED_ACCESS_TO_PARTITION, 0); + return ER_NOT_ALLOWED_ACCESS_TO_PARTITION; + } + + /* fill infos for catlaog table */ + nnames = pt_length_of_list (histogram_info->target_columns); + bucket_count = histogram_info->bucket_count; + cur_column = histogram_info->target_columns; + with_fullscan = histogram_info->with_fullscan ? true : false; + + /* update statistics for class first */ + error = sm_update_statistics (obj, with_fullscan); + if (error != NO_ERROR) + { + return error; + } + + if (nnames == 0) + { + SM_ATTRIBUTE *att; + SM_CLASS *class_ = NULL; + error = au_fetch_class (obj, &class_, AU_FETCH_READ, AU_SELECT); + if (error != NO_ERROR) + { + return error; + } + for (att = class_->attributes; att != NULL; att = (SM_ATTRIBUTE *) att->header.next) + { + attname = (char *) att->header.name; + if (do_histogram == DO_HISTOGRAM_DROP) + { + error = sm_drop_histogram (obj, attname); + if (error != NO_ERROR) + { + return error; + } + } + else + { + /* type check for the attribute */ + attr_type = TP_DOMAIN_TYPE (att->domain); + if (!is_histogrammable_type (attr_type)) + { + error = ER_OBJ_INVALID_ARGUMENTS; + dump_histogram (obj, attname, attr_type, with_fullscan, error, stdout); + continue; + } + + /* create histogram catalog entry */ + error = sm_add_histogram (obj, attname, bucket_count, with_fullscan); + if (error != NO_ERROR) + { + if (error != ER_LC_CLASSNAME_EXIST) + { + dump_histogram (obj, attname, attr_type, with_fullscan, error, stdout); + return error; + } + } + /* update the histogram */ + error = analyze_classes (NULL, db_get_class_name (obj), attname, bucket_count, with_fullscan, obj); + if (error != NO_ERROR) + { + dump_histogram (obj, attname, attr_type, with_fullscan, error, stdout); + return error; + } + /* TODO: dump the histogram */ + error = dump_histogram (obj, attname, attr_type, with_fullscan, error, stdout); + if (error != NO_ERROR) + { + assert (false); + return error; + } + } + } + } + + for (int i = 0; i < nnames; i++) + { + attname = (char *) cur_column->info.name.original; + if (do_histogram == DO_HISTOGRAM_DROP) + { + error = sm_drop_histogram (obj, attname); + if (error != NO_ERROR) + { + return error; + } + } + else + { + /* type check for the attribute */ + DB_ATTRIBUTE *attribute; + DB_DOMAIN *attr_domain; + + attribute = db_get_attribute (obj, attname); + if (attribute == NULL) + { + error = ER_OBJ_INVALID_ARGUMENTS; + assert (false); + return error; + } + attr_domain = db_attribute_domain (attribute); + if (attr_domain == NULL) + { + error = ER_OBJ_INVALID_ARGUMENTS; + assert (false); + return error; + } + + attr_type = TP_DOMAIN_TYPE (attr_domain); + + if (!is_histogrammable_type (attr_type)) + { + error = ER_OBJ_INVALID_ARGUMENTS; + dump_histogram (obj, attname, attr_type, with_fullscan, error, stdout); + continue; + } + /* create histogram catalog entry */ + error = sm_add_histogram (obj, attname, bucket_count, with_fullscan); + if (error != NO_ERROR) + { + if (error != ER_LC_CLASSNAME_EXIST) + { + dump_histogram (obj, attname, attr_type, with_fullscan, error, stdout); + return error; + } + } + /* update the histogram */ + error = analyze_classes (NULL, db_get_class_name (obj), attname, bucket_count, with_fullscan, obj); + if (error != NO_ERROR) + { + return error; + } + /* TODO: dump the histogram */ + error = dump_histogram (obj, attname, attr_type, with_fullscan, error, stdout); + if (error != NO_ERROR) + { + assert (false); + return error; + } + } + cur_column = cur_column->next; + } + + if (error != NO_ERROR) + { + return error; + } + + return NO_ERROR; +} + + +/** + * do_update_histogram() - Create or Update a histogram on a class. + * return: Error code if it fails + * parser(in): Parser context + * statement(in): Parse tree of a create histogram statement + */ +int +do_update_histogram (PARSER_CONTEXT * parser, PT_NODE * statement) +{ + PT_NODE *cls; + DB_OBJECT *obj; + int error = NO_ERROR, save; + AU_DISABLE (save); + CHECK_MODIFICATION_ERROR (); + + /* class should be already available */ + assert (statement->info.histogram.target_table_spec); + + cls = statement->info.histogram.target_table_spec->info.spec.entity_name; + + obj = db_find_class (cls->info.name.original); + if (obj == NULL) + { + assert (er_errid () != NO_ERROR); + AU_ENABLE (save); + return er_errid (); + } + + error = update_or_drop_histogram_helper (parser, obj, &statement->info.histogram, DO_HISTOGRAM_CREATE); + + if (error != NO_ERROR) + { + assert (er_errid () != NO_ERROR); + error = er_errid (); + AU_ENABLE (save); + return error; + } + + AU_ENABLE (save); + return error; +} + + +/** + * do_update_histogram() - Creates a histogram on a class. + * return: Error code if it fails + * parser(in): Parser context + * statement(in): Parse tree of a create histogram statement + */ +int +do_drop_histogram (PARSER_CONTEXT * parser, PT_NODE * statement) +{ + PT_NODE *cls; + DB_OBJECT *obj; + int error = NO_ERROR, save; + AU_DISABLE (save); + CHECK_MODIFICATION_ERROR (); + + /* class should be already available */ + assert (statement->info.histogram.target_table_spec); + + cls = statement->info.histogram.target_table_spec->info.spec.entity_name; + + obj = db_find_class (cls->info.name.original); + if (obj == NULL) + { + assert (er_errid () != NO_ERROR); + AU_ENABLE (save); + return er_errid (); + } + + error = update_or_drop_histogram_helper (parser, obj, &statement->info.histogram, DO_HISTOGRAM_DROP); + + if (error != NO_ERROR) + { + assert (er_errid () != NO_ERROR); + error = er_errid (); + AU_ENABLE (save); + return error; + } + + AU_ENABLE (save); + return error; +} + + /* * do_create_partition() - Creates partitions * return: Error code if partitions are not created diff --git a/src/query/execute_statement.c b/src/query/execute_statement.c index d4453028c11..69a19e94d24 100644 --- a/src/query/execute_statement.c +++ b/src/query/execute_statement.c @@ -20,6 +20,7 @@ * execute_statement.c - functions to do execute */ +#include "parse_tree.h" #ident "$Id$" #include "config.h" @@ -3194,6 +3195,8 @@ do_statement (PARSER_CONTEXT * parser, PT_NODE * statement) case PT_CREATE_SERIAL: case PT_CREATE_TRIGGER: case PT_CREATE_USER: + case PT_UPDATE_HISTOGRAM: + case PT_DROP_HISTOGRAM: case PT_ALTER: case PT_ALTER_INDEX: case PT_ALTER_SERIAL: @@ -3271,6 +3274,13 @@ do_statement (PARSER_CONTEXT * parser, PT_NODE * statement) error = do_create_index (parser, statement); break; + case PT_UPDATE_HISTOGRAM: + error = do_update_histogram (parser, statement); + break; + + case PT_DROP_HISTOGRAM: + error = do_drop_histogram (parser, statement); + break; case PT_EVALUATE: error = do_evaluate (parser, statement); break; @@ -3891,6 +3901,8 @@ do_execute_statement (PARSER_CONTEXT * parser, PT_NODE * statement) case PT_CREATE_SERIAL: case PT_CREATE_TRIGGER: case PT_CREATE_USER: + case PT_UPDATE_HISTOGRAM: + case PT_DROP_HISTOGRAM: case PT_ALTER: case PT_ALTER_INDEX: case PT_ALTER_SERIAL: @@ -3964,6 +3976,12 @@ do_execute_statement (PARSER_CONTEXT * parser, PT_NODE * statement) case PT_CREATE_USER: err = do_create_user (parser, statement); break; + case PT_UPDATE_HISTOGRAM: + err = do_update_histogram (parser, statement); + break; + case PT_DROP_HISTOGRAM: + err = do_drop_histogram (parser, statement); + break; case PT_ALTER: /* err = do_alter(parser, statement); */ /* execute internal statements before and after do_alter() */ @@ -11263,7 +11281,6 @@ static PT_NODE *test_check_option (PARSER_CONTEXT * parser, PT_NODE * node, void static int insert_local (PARSER_CONTEXT * parser, PT_NODE * statement); static PT_NODE *do_create_odku_stmt (PARSER_CONTEXT * parser, PT_NODE * insert); static int do_find_unique_constraint_violations (DB_OTMPL * tmpl, bool for_update, OID ** oids, int *oids_count); -static int do_create_midxkey_for_constraint (DB_OTMPL * tmpl, SM_CLASS_CONSTRAINT * constraint, DB_VALUE * key); static int do_on_duplicate_key_update (PARSER_CONTEXT * parser, DB_OTMPL * tpl, PT_NODE * update_stmt); static int do_replace_into (PARSER_CONTEXT * parser, DB_OTMPL * tmpl, PT_NODE * spec, PT_NODE * class_specs); static int is_replace_or_odku_allowed (DB_OBJECT * obj, int *allowed); @@ -11965,7 +11982,7 @@ do_set_insert_server_not_allowed (PARSER_CONTEXT * parser, PT_NODE * node, void * constraint (in) : constraint * key (in/out) : the MIDX key */ -static int +int do_create_midxkey_for_constraint (DB_OTMPL * tmpl, SM_CLASS_CONSTRAINT * constraint, DB_VALUE * key) { DB_MIDXKEY midxkey; @@ -11998,6 +12015,7 @@ do_create_midxkey_for_constraint (DB_OTMPL * tmpl, SM_CLASS_CONSTRAINT * constra goto error_return; } + assert (attr_dom->type->id <= DB_TYPE_LAST); if (asc_desc != NULL && asc_desc[attr_count] == 1) { attr_dom->is_desc = 1; @@ -12068,10 +12086,12 @@ do_create_midxkey_for_constraint (DB_OTMPL * tmpl, SM_CLASS_CONSTRAINT * constra goto error_return; } midxkey.domain = tp_domain_cache (midxkey.domain); + assert (midxkey.domain->type->id <= DB_TYPE_LAST); midxkey.min_max_val.position = -1; midxkey.min_max_val.type = MIN_COLUMN; error = db_make_midxkey (key, &midxkey); + assert (key->domain.general_info.type <= DB_TYPE_LAST); if (error != NO_ERROR) { goto error_return; @@ -12080,6 +12100,7 @@ do_create_midxkey_for_constraint (DB_OTMPL * tmpl, SM_CLASS_CONSTRAINT * constra return NO_ERROR; error_return: + assert (false); if (midxkey.buf != NULL) { db_private_free (NULL, midxkey.buf); @@ -12095,6 +12116,7 @@ do_create_midxkey_for_constraint (DB_OTMPL * tmpl, SM_CLASS_CONSTRAINT * constra return error; } + /* * do_create_odku_stmt () - create an UPDATE statement for ON DUPLICATE KEY * UPDATE node @@ -16197,6 +16219,14 @@ do_replicate_statement (PARSER_CONTEXT * parser, PT_NODE * statement) repl_stmt.statement_type = CUBRID_STMT_DROP_INDEX; break; + case PT_UPDATE_HISTOGRAM: + repl_stmt.statement_type = CUBRID_STMT_UPDATE_HISTOGRAM; + break; + + case PT_DROP_HISTOGRAM: + repl_stmt.statement_type = CUBRID_STMT_DROP_HISTOGRAM; + break; + case PT_CREATE_SERIAL: repl_stmt.statement_type = CUBRID_STMT_CREATE_SERIAL; break; diff --git a/src/query/execute_statement.h b/src/query/execute_statement.h index ba546053791..ff622a6314d 100644 --- a/src/query/execute_statement.h +++ b/src/query/execute_statement.h @@ -119,6 +119,8 @@ extern int do_delete (PARSER_CONTEXT * parser, PT_NODE * statement); extern int do_prepare_delete (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * parent); extern int do_execute_delete (PARSER_CONTEXT * parser, PT_NODE * statement); +extern int do_update_histogram (PARSER_CONTEXT * parser, PT_NODE * statement); +extern int do_drop_histogram (PARSER_CONTEXT * parser, PT_NODE * statement); extern int do_drop (PARSER_CONTEXT * parser, PT_NODE * statement); extern int do_drop_variable (PARSER_CONTEXT * parser, PT_NODE * statement); @@ -209,4 +211,6 @@ extern int do_find_trigger_by_query (const char *name, char *buf, int buf_size); extern int do_find_synonym_by_query (const char *name, char *buf, int buf_size); extern int do_find_stored_procedure_by_query (const char *name, char *buf, int buf_size); + +extern int do_create_midxkey_for_constraint (DB_OTMPL * tmpl, SM_CLASS_CONSTRAINT * constraint, DB_VALUE * key); #endif /* _EXECUTE_STATEMENT_H_ */ diff --git a/src/query/scan_manager.c b/src/query/scan_manager.c index 36a2d151675..02bbb7f92d4 100644 --- a/src/query/scan_manager.c +++ b/src/query/scan_manager.c @@ -2898,8 +2898,14 @@ scan_open_heap_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id, return ER_FAILED; } - /* sampling_weight = total_page / sampling_page */ - hsidp->sampling.weight = MAX ((total_pages / NUMBER_OF_SAMPLING_PAGES), 1); + /* sampling_weight: default 30% sampling, minimum 100 pages, maximum 5000 pages */ + /* 30% sampling = weight approximately 3.33 (1/0.3) */ + int base_weight = 3; /* base weight for 33% sampling */ + int min_weight = (total_pages + 99) / 100; /* ensure minimum 100 pages (rounded up) */ + int max_weight = total_pages / 5000; /* limit maximum 5000 pages */ + + /* select the smaller value between base_weight and min_weight, and greater than max_weight */ + hsidp->sampling.weight = MAX (MIN (base_weight, min_weight), MAX (max_weight, 1)); } return NO_ERROR; diff --git a/src/storage/heap_file.c b/src/storage/heap_file.c index cc55a778a89..ea15ddcc297 100644 --- a/src/storage/heap_file.c +++ b/src/storage/heap_file.c @@ -39,6 +39,7 @@ #include "porting.h" #include "porting_inline.hpp" #include "record_descriptor.hpp" +#include #include "slotted_page.h" #include "overflow_file.h" #include "boot_sr.h" @@ -7889,6 +7890,25 @@ heap_get_record_data_when_all_ready (THREAD_ENTRY * thread_p, HEAP_GET_CONTEXT * return S_ERROR; } +static int +random_poisson_weight (int weight) +{ +// *INDENT-OFF* + static thread_local std::mt19937 rng { 123456789u }; // fixed seed +// *INDENT-ON* + if (weight < 1) + { + assert (false); + return 1; + } + +/* shifted version of random_poisson_weight */ + const int lambda = weight - 1; // E[1 + Poisson(lambda)] = weight + std::poisson_distribution < int >dist (lambda); + return dist (rng) + 1; // always >= 1 +} + + /* * heap_next_internal () - Retrieve of peek next object. * @@ -8114,8 +8134,9 @@ heap_next_internal (THREAD_ENTRY * thread_p, const HFID * hfid, OID * class_oid, if (sampling) { /* skip pages */ + int skip_count = random_poisson_weight (sampling->weight); if (heap_vpid_skip_next (thread_p, hfid, &scan_cache->page_watcher, &old_page_watcher, - sampling->weight, &vpid, scan_cache) == S_ERROR) + skip_count, &vpid, scan_cache) == S_ERROR) { return S_ERROR; } diff --git a/src/storage/statistics.h b/src/storage/statistics.h index 67991e7ddc5..b830b752c19 100644 --- a/src/storage/statistics.h +++ b/src/storage/statistics.h @@ -56,6 +56,14 @@ } \ while (0) +#define stats_free_histogram_and_init_and_set_null(histogram) \ + do \ + { \ + stats_free_histogram_and_init (histogram); \ + (histogram) = NULL; \ + } \ + while (0) + /* B+tree statistical information */ typedef struct btree_stats BTREE_STATS; struct btree_stats @@ -99,6 +107,14 @@ struct class_stats ATTR_STATS *attr_stats; /* pointer to the array of attribute statistics */ }; +typedef struct hist_stats HIST_STATS; +struct hist_stats +{ + int n_attrs; /* number of attributes; size of the histogram[] */ + DB_VALUE **histogram; /* column histogram , null if not exists */ + double *null_frequency; /* column null frequency , 0 if not exists */ +}; + /* Statistical Information about the attribute NDV */ typedef struct attr_ndv ATTR_NDV; struct attr_ndv diff --git a/src/transaction/boot_cl.c b/src/transaction/boot_cl.c index b5d74fb464f..a9296373f57 100644 --- a/src/transaction/boot_cl.c +++ b/src/transaction/boot_cl.c @@ -1854,6 +1854,7 @@ boot_destroy_catalog_classes (void) CT_PARTITION_NAME, CT_STORED_PROC_NAME, CT_STORED_PROC_ARGS_NAME, + CT_DB_HISTOGRAM_NAME, CTV_CLASS_NAME, CTV_SUPER_CLASS_NAME, CTV_VCLASS_NAME, @@ -1875,6 +1876,7 @@ boot_destroy_catalog_classes (void) CTV_SERVER_NAME, CT_SYNONYM_NAME, CTV_SYNONYM_NAME, + CTV_DB_HISTOGRAM_NAME, NULL }; diff --git a/src/transaction/log_applier.c b/src/transaction/log_applier.c index 33be549d20d..96477870072 100644 --- a/src/transaction/log_applier.c +++ b/src/transaction/log_applier.c @@ -5526,6 +5526,9 @@ la_apply_statement_log (LA_ITEM * item) case CUBRID_STMT_ALTER_SERIAL: case CUBRID_STMT_DROP_SERIAL: + case CUBRID_STMT_UPDATE_HISTOGRAM: + case CUBRID_STMT_DROP_HISTOGRAM: + case CUBRID_STMT_DROP_DATABASE: case CUBRID_STMT_CREATE_STORED_PROCEDURE: