diff --git a/mysql-test/suite/ctc/r/ctc_ddl_generated_columns.result b/mysql-test/suite/ctc/r/ctc_ddl_generated_columns.result index 0afdf7d19e6edf04f9655e94d8cfd982da73d2b7..1aea03501222f8aed9fdec93e73ec818b58c83fa 100644 --- a/mysql-test/suite/ctc/r/ctc_ddl_generated_columns.result +++ b/mysql-test/suite/ctc/r/ctc_ddl_generated_columns.result @@ -3,7 +3,7 @@ sidea DOUBLE, sideb DOUBLE, sidec DOUBLE AS (SQRT(sidea * sidea + sideb * sideb)) STORED ); -ERROR HY000: Cantian does not support stored generated column. +drop table triangle; CREATE TABLE triangle1 ( sidea DOUBLE, sideb DOUBLE, @@ -235,3 +235,98 @@ id select_type table partitions type possible_keys key key_len ref rows filtered Warnings: Note 1003 /* select#1 */ select `test`.`t1`.`pk` AS `pk`,`test`.`t1`.`a1` AS `a1`,`test`.`t1`.`b1` AS `b1`,`test`.`t1`.`c1` AS `c1`,`test`.`t1`.`d1` AS `d1` from `test`.`t1` where (`test`.`t1`.`b1` < 'c') drop table t1; +CREATE TABLE t1 ( +price DECIMAL(10, 2) NOT NULL, +quantity INT NOT NULL, +total_price DECIMAL(10, 2) GENERATED ALWAYS AS (price * quantity) VIRTUAL, +first_name VARCHAR(50) DEFAULT NULL, +last_name VARCHAR(50) DEFAULT NULL, +full_name VARCHAR(110) GENERATED ALWAYS AS (CONCAT(first_name, last_name)) VIRTUAL, +last_col varchar(50) DEFAULT NULL +); +insert into t1 (price, quantity, first_name, last_name, last_col) values (1.25, 5, 'Zhang', 'San', 'last_column'); +insert into t1 (price, quantity, first_name, last_name, last_col) values (10.25, 7, 'Li', 'Si', 'last_column'); +insert into t1 (price, quantity, first_name, last_name, last_col) values (0.25, 9, 'Wang', 'Wu', 'last_column'); +select * from t1; +price quantity total_price first_name last_name full_name last_col +1.25 5 6.25 Zhang San ZhangSan last_column +10.25 7 71.75 Li Si LiSi last_column +0.25 9 2.25 Wang Wu WangWu last_column +ALTER TABLE t1 ALGORITHM=COPY, DROP COLUMN total_price; +ALTER TABLE t1 DROP COLUMN full_name; +ALTER TABLE t1 DROP COLUMN last_col; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `price` decimal(10,2) NOT NULL, + `quantity` int NOT NULL, + `first_name` varchar(50) DEFAULT NULL, + `last_name` varchar(50) DEFAULT NULL +) ENGINE=CTC DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +ALTER TABLE t1 ALGORITHM=COPY, +ADD COLUMN total_price DECIMAL(10, 2) +GENERATED ALWAYS AS (price * quantity) VIRTUAL; +ALTER TABLE t1 ALGORITHM=INPLACE, +ADD COLUMN full_name VARCHAR(110) +GENERATED ALWAYS AS (CONCAT(first_name, last_name)) VIRTUAL; +ALTER TABLE t1 ALGORITHM=COPY, ADD COLUMN last_col_0 varchar(10); +ALTER TABLE t1 ALGORITHM=INPLACE, ADD COLUMN last_col_1 varchar(10); +DROP TABLE t1; +CREATE TABLE t2 ( +price DECIMAL(10, 2) NOT NULL, +quantity INT NOT NULL, +total_price DECIMAL(10, 2) GENERATED ALWAYS AS (price * quantity) STORED, +first_name VARCHAR(50) DEFAULT NULL, +last_name VARCHAR(50) DEFAULT NULL, +full_name VARCHAR(110) GENERATED ALWAYS AS (CONCAT(first_name, last_name)) VIRTUAL, +last_col varchar(50) DEFAULT NULL +); +ALTER TABLE t2 ALGORITHM=COPY, ADD INDEX idx_total (total_price); +ALTER TABLE t2 ALGORITHM=INPLACE, ADD INDEX idx_pqt (price, quantity, total_price); +DROP TABLE t2; +CREATE TABLE t1 ( +c1 INT, +c2 INT, +c3 VARCHAR(10), +c4 VARCHAR(10), +v_col_1 INT GENERATED ALWAYS AS (c1 + c1) VIRTUAL, +v_col_2 INT GENERATED ALWAYS AS (c2 + c2) VIRTUAL, +INDEX idx_c3 ((UPPER(c3))), +INDEX idx_c4 ((SUBSTR(c4, 1, 1))), +v_col_3 INT GENERATED ALWAYS AS (c1 + c2) VIRTUAL, +c5 VARCHAR(10) +); +alter table t1 add column v_col_4 INT GENERATED ALWAYS AS (c1 * c2) VIRTUAL; +DROP TABLE t1; +CREATE TABLE t1 ( +c1 INT, +c2 INT, +c3 VARCHAR(10), +c4 VARCHAR(10), +v_col_1 INT GENERATED ALWAYS AS (c1 + c1) VIRTUAL, +v_col_2 INT GENERATED ALWAYS AS (c2 + c2) VIRTUAL, +INDEX idx_c3 ((UPPER(c3))), +INDEX idx_c4 ((SUBSTR(c4, 1, 1))), +v_col_3 INT GENERATED ALWAYS AS (c1 + c2) VIRTUAL, +c5 VARCHAR(10) +); +create index idx_test on t1 ((SUBSTR(c3, 1, 2))); +alter table t1 algorithm=copy, add index idx_text_1 ((SUBSTR(c3, 1, 2))); +DROP TABLE t1; +CREATE TABLE t_gcol( +`col1` int, +`col2` int, +`col3` int, +g_col_1 INTEGER GENERATED ALWAYS AS (col3 + col3) VIRTUAL, +g_col_2 INTEGER GENERATED ALWAYS AS (col2 + col2) VIRTUAL, +g_col_3 INTEGER GENERATED ALWAYS AS (g_col_1 + g_col_2) VIRTUAL, +g_col_4 INTEGER GENERATED ALWAYS AS (col1 + g_col_3) STORED, +g_col_5 INTEGER GENERATED ALWAYS AS (g_col_3 + g_col_4) VIRTUAL +); +DROP TABLE t_gcol; +CREATE TEMPORARY TABLE temp_2 ( +price DECIMAL(10,2), +quantity INT, +total_price DECIMAL(10,2) GENERATED ALWAYS AS (price * quantity) STORED +); +drop table temp_2; diff --git a/mysql-test/suite/ctc/t/ctc_ddl_generated_columns.test b/mysql-test/suite/ctc/t/ctc_ddl_generated_columns.test index ff4c89f611207ba21b8557e466a7239d3b9f0a5d..64e86ac2f9906a3d93b85b93de41a6a60b4aaabf 100644 --- a/mysql-test/suite/ctc/t/ctc_ddl_generated_columns.test +++ b/mysql-test/suite/ctc/t/ctc_ddl_generated_columns.test @@ -1,10 +1,10 @@ #测试生成列和虚拟列 ---error 3655 CREATE TABLE triangle ( sidea DOUBLE, sideb DOUBLE, sidec DOUBLE AS (SQRT(sidea * sidea + sideb * sideb)) STORED ); +drop table triangle; CREATE TABLE triangle1 ( sidea DOUBLE, sideb DOUBLE, @@ -118,3 +118,113 @@ insert into t1(b1,c1,d1) values ('g','s','11:11:11'); analyze table t1; explain select * from t1 where b1 < 'c'; drop table t1; + +# create table with virtual generated column (vgc) +CREATE TABLE t1 ( + price DECIMAL(10, 2) NOT NULL, + quantity INT NOT NULL, + total_price DECIMAL(10, 2) GENERATED ALWAYS AS (price * quantity) VIRTUAL, + first_name VARCHAR(50) DEFAULT NULL, + last_name VARCHAR(50) DEFAULT NULL, + full_name VARCHAR(110) GENERATED ALWAYS AS (CONCAT(first_name, last_name)) VIRTUAL, + last_col varchar(50) DEFAULT NULL +); + +insert into t1 (price, quantity, first_name, last_name, last_col) values (1.25, 5, 'Zhang', 'San', 'last_column'); +insert into t1 (price, quantity, first_name, last_name, last_col) values (10.25, 7, 'Li', 'Si', 'last_column'); +insert into t1 (price, quantity, first_name, last_name, last_col) values (0.25, 9, 'Wang', 'Wu', 'last_column'); + +select * from t1; + +# drop vgc using COPY +ALTER TABLE t1 ALGORITHM=COPY, DROP COLUMN total_price; +ALTER TABLE t1 DROP COLUMN full_name; +ALTER TABLE t1 DROP COLUMN last_col; + +show create table t1; + +# add vgc using COPY and INPLACE +ALTER TABLE t1 ALGORITHM=COPY, +ADD COLUMN total_price DECIMAL(10, 2) +GENERATED ALWAYS AS (price * quantity) VIRTUAL; + +ALTER TABLE t1 ALGORITHM=INPLACE, +ADD COLUMN full_name VARCHAR(110) +GENERATED ALWAYS AS (CONCAT(first_name, last_name)) VIRTUAL; + +ALTER TABLE t1 ALGORITHM=COPY, ADD COLUMN last_col_0 varchar(10); +ALTER TABLE t1 ALGORITHM=INPLACE, ADD COLUMN last_col_1 varchar(10); + +DROP TABLE t1; + +# create table with vgc and vsc +CREATE TABLE t2 ( + price DECIMAL(10, 2) NOT NULL, + quantity INT NOT NULL, + total_price DECIMAL(10, 2) GENERATED ALWAYS AS (price * quantity) STORED, + first_name VARCHAR(50) DEFAULT NULL, + last_name VARCHAR(50) DEFAULT NULL, + full_name VARCHAR(110) GENERATED ALWAYS AS (CONCAT(first_name, last_name)) VIRTUAL, + last_col varchar(50) DEFAULT NULL +); + +# create index on vsc (suport) +ALTER TABLE t2 ALGORITHM=COPY, ADD INDEX idx_total (total_price); +ALTER TABLE t2 ALGORITHM=INPLACE, ADD INDEX idx_pqt (price, quantity, total_price); + +DROP TABLE t2; + +# create table with vgc and func_index +CREATE TABLE t1 ( + c1 INT, + c2 INT, + c3 VARCHAR(10), + c4 VARCHAR(10), + v_col_1 INT GENERATED ALWAYS AS (c1 + c1) VIRTUAL, + v_col_2 INT GENERATED ALWAYS AS (c2 + c2) VIRTUAL, + INDEX idx_c3 ((UPPER(c3))), + INDEX idx_c4 ((SUBSTR(c4, 1, 1))), + v_col_3 INT GENERATED ALWAYS AS (c1 + c2) VIRTUAL, + c5 VARCHAR(10) +); + +alter table t1 add column v_col_4 INT GENERATED ALWAYS AS (c1 * c2) VIRTUAL; +DROP TABLE t1; + +CREATE TABLE t1 ( + c1 INT, + c2 INT, + c3 VARCHAR(10), + c4 VARCHAR(10), + v_col_1 INT GENERATED ALWAYS AS (c1 + c1) VIRTUAL, + v_col_2 INT GENERATED ALWAYS AS (c2 + c2) VIRTUAL, + INDEX idx_c3 ((UPPER(c3))), + INDEX idx_c4 ((SUBSTR(c4, 1, 1))), + v_col_3 INT GENERATED ALWAYS AS (c1 + c2) VIRTUAL, + c5 VARCHAR(10) +); +# inplace default +create index idx_test on t1 ((SUBSTR(c3, 1, 2))); +alter table t1 algorithm=copy, add index idx_text_1 ((SUBSTR(c3, 1, 2))); +DROP TABLE t1; + +# NEST of virtual columns +CREATE TABLE t_gcol( +`col1` int, +`col2` int, +`col3` int, +g_col_1 INTEGER GENERATED ALWAYS AS (col3 + col3) VIRTUAL, +g_col_2 INTEGER GENERATED ALWAYS AS (col2 + col2) VIRTUAL, +g_col_3 INTEGER GENERATED ALWAYS AS (g_col_1 + g_col_2) VIRTUAL, +g_col_4 INTEGER GENERATED ALWAYS AS (col1 + g_col_3) STORED, +g_col_5 INTEGER GENERATED ALWAYS AS (g_col_3 + g_col_4) VIRTUAL +); +DROP TABLE t_gcol; + +# create temp table with vsc (support) +CREATE TEMPORARY TABLE temp_2 ( + price DECIMAL(10,2), + quantity INT, + total_price DECIMAL(10,2) GENERATED ALWAYS AS (price * quantity) STORED +); +drop table temp_2; \ No newline at end of file diff --git a/storage/ctc/ctc_srv.h b/storage/ctc/ctc_srv.h index 345be8e2edf670034e4f19b3b09cf41874b90962..66f67af1440d0e5dd2b1fa5592ee3f0b6c757448 100644 --- a/storage/ctc/ctc_srv.h +++ b/storage/ctc/ctc_srv.h @@ -253,6 +253,7 @@ typedef union { uint32_t nullable : 1; // 是否允许为空(1允许为空 0不允许) uint32_t primary : 1; // if it is a primary key (主键) uint32_t unique : 1; // 是否唯一 + uint32_t is_virtual : 1; // 是否虚拟列 uint32_t is_serial : 1; // 自增 uint32_t is_check : 1; // 检查列(取值范围)约束 http://c.biancheng.net/view/2446.html uint32_t is_ref : 1; // 指定外键 参考sql_parse_column_ref @@ -267,7 +268,7 @@ typedef union { uint32_t isKey : 1; // key关键字 uint32_t is_default_func : 1; // if default value is generated by mysql functions uint32_t is_curr_timestamp : 1; // if default value is current_timestamp - uint32_t unused_ops : 15; + uint32_t unused_ops : 14; }; uint32_t is_option_set; } ctc_column_option_set_bit; diff --git a/storage/ctc/ha_ctc.cc b/storage/ctc/ha_ctc.cc index 178c2ae7c3515b18e6c225c7aaa696fd1b764db4..8abc376175de18b41814755845ccda1d8d72210d 100644 --- a/storage/ctc/ha_ctc.cc +++ b/storage/ctc/ha_ctc.cc @@ -5302,9 +5302,9 @@ EXTER_ATTACK int ha_ctc::create(const char *name, TABLE *form, HA_CREATE_INFO *c uint32_t table_flags = 0; if (is_tmp_table) { - table_flags |= CTC_TMP_TABLE; + table_flags |= CTC_FLAG_TMP_TABLE; if (create_info->options & HA_LEX_CREATE_INTERNAL_TMP_TABLE) { - table_flags |= CTC_INTERNAL_TMP_TABLE; + table_flags |= CTC_FLAG_INTERNAL_TMP_TABLE; } ddl_ctrl.table_flags = table_flags; } diff --git a/storage/ctc/ha_ctc.h b/storage/ctc/ha_ctc.h index 44399d56bb7064af5f88dbc8ed91bc21f5123433..20d3b0249462037ac4513938dd27748e621b20e0 100644 --- a/storage/ctc/ha_ctc.h +++ b/storage/ctc/ha_ctc.h @@ -129,9 +129,9 @@ again. */ this only holds for tables created with >= MySQL-4.0.14 */ #define TMP_DIR "tmp" -#define CTC_TMP_TABLE 1 -#define CTC_INTERNAL_TMP_TABLE 2 -#define CTC_TABLE_CONTAINS_VIRCOL 4 +#define CTC_FLAG_TMP_TABLE 1 +#define CTC_FLAG_INTERNAL_TMP_TABLE 2 +#define CTC_FLAG_TABLE_CONTAINS_VIRCOL 4 /* This flag indicates the table contains virtual generated columns */ #define CTC_ANALYZE_TIME_SEC 100 #define MEM_CLASS_NUM 27 diff --git a/storage/ctc/ha_ctc_ddl.cc b/storage/ctc/ha_ctc_ddl.cc index 30205b905e7ac4afebcfba362d63c6cf7bac0723..2c6043d65504b376e0476972d3d34ee871ed52ef 100644 --- a/storage/ctc/ha_ctc_ddl.cc +++ b/storage/ctc/ha_ctc_ddl.cc @@ -91,7 +91,7 @@ int fill_delete_table_req(const char *full_path_name, const dd::Table *table_def bool is_tmp_table = ctc_is_temporary(table_def); ctc_split_normalized_name(full_path_name, db_name, SMALL_RECORD_SIZE, table_name, SMALL_RECORD_SIZE, &is_tmp_table); if (is_tmp_table) { - ddl_ctrl->table_flags |= CTC_TMP_TABLE; + ddl_ctrl->table_flags |= CTC_FLAG_TMP_TABLE; req.name = table_name; } else { req.name = const_cast(table_def->name().c_str()); @@ -1077,6 +1077,11 @@ static void ctc_fill_column_option_set(TcDb__CtcDDLColumnDef *column, Field *fie option_set->is_default_null = false; option_set->has_null = true; // 保证nullable的值是准确的 option_set->nullable = (field->is_flag_set(NOT_NULL_FLAG)) ? 0 : 1; + /* + For virtual generated columns, set flag is_virtual and no store. + For virtual stored columns, as regular stored columns. + */ + option_set->is_virtual = field->is_virtual_gcol() ? 1 : 0; if (field->is_flag_set(PRI_KEY_FLAG)) { option_set->primary = true; @@ -1135,7 +1140,15 @@ static bool ctc_ddl_fill_column_by_field( ctc_column_option_set_bit option_set; ctc_fill_column_option_set(column, field, form, &option_set); - if (ctc_is_with_default_value(field, col_obj)) { + if (option_set.is_virtual) { + /* + The expression of virtual generated columns is unused in engine, + it is used to store the location in SYS_COLUMNS. + The default value of virtual column depended on expression. + */ + option_set.is_default = 0; + option_set.is_default_null = 0; + } else if (ctc_is_with_default_value(field, col_obj)) { CTC_RETURN_IF_ERROR(ctc_ddl_fill_column_default_value(thd, column, field, fld, col_obj, &option_set, mem_start, mem_end, field_cs), false); } else { @@ -1843,17 +1856,20 @@ static int fill_create_table_req_columns_info(HA_CREATE_INFO *create_info, dd::T THD *thd, ddl_ctrl_t *ddl_ctrl, TcDb__CtcDDLCreateTableDef *req, char **mem_start, char *mem_end) { uint32_t ctc_col_idx = 0; uint32_t mysql_col_idx = 0; + uint32_t virtual_cols = 0; while (ctc_col_idx < req->n_columns) { + // When create table or alter copy algorithm, the vir_cols based on func_index at the last in the req Field *field = form->field[mysql_col_idx]; - if (field->is_gcol()) { - ddl_ctrl->table_flags |= CTC_TABLE_CONTAINS_VIRCOL; - if (field->is_virtual_gcol()) { - mysql_col_idx++; - req->n_columns--; - continue; - } - my_printf_error(ER_DISALLOWED_OPERATION, "%s", MYF(0), "Cantian does not support stored generated column."); - return HA_ERR_WRONG_COMMAND; + // The vir_cols based on func_indexs are not pushed to engine + if (field->is_field_for_functional_index()) { + mysql_col_idx++; + req->n_columns--; + continue; + } + // Virtual Generated Columns Processed + if (field->is_virtual_gcol()) { + ddl_ctrl->table_flags |= CTC_FLAG_TABLE_CONTAINS_VIRCOL; + virtual_cols++; } TcDb__CtcDDLColumnDef *column = req->columns[ctc_col_idx]; @@ -1866,8 +1882,8 @@ static int fill_create_table_req_columns_info(HA_CREATE_INFO *create_info, dd::T mysql_col_idx++; } - /*prevent only virtual columns*/ - if (req->n_columns == 0) { + // Prevent only virtual columns + if (req->n_columns == virtual_cols) { my_printf_error(ER_DISALLOWED_OPERATION, "%s", MYF(0), "Cantian does not support all columns are generated."); return HA_ERR_WRONG_COMMAND; } @@ -2436,22 +2452,23 @@ static int fill_ctc_alter_create_list(THD *thd, TABLE *altered_table, Alter_inpl { uint32_t ctc_col_idx = 0; uint32_t mysql_col_idx = 0; + uint32_t virtual_cols = 0; while (ctc_col_idx < req->n_create_list) { + // When alter table by inplace, the vir_cols based on func_indexs at the last req->n_create_list TcDb__CtcDDLColumnDef *req_create_column = req->create_list[ctc_col_idx]; const Create_field *fld = ha_alter_info->alter_info->create_list[mysql_col_idx]; - /* Generate Columns Not Processed */ - if (fld->is_gcol()) { - ddl_ctrl->table_flags |= CTC_TABLE_CONTAINS_VIRCOL; - if (fld->is_virtual_gcol()) { - req->n_create_list--; - mysql_col_idx++; - continue; - } else { - my_printf_error(ER_DISALLOWED_OPERATION, "%s", MYF(0), - "Cantian does not support stored generated column."); - return HA_ERR_WRONG_COMMAND; - } + // The vir_cols based on func_indexs are not pushed to engine + if (is_field_for_functional_index(fld)) { + mysql_col_idx++; + req->n_create_list--; + continue; + } + + // Virtual Generated Columns Processed + if (fld->is_virtual_gcol()) { + ddl_ctrl->table_flags |= CTC_FLAG_TABLE_CONTAINS_VIRCOL; + virtual_cols++; } ctc_alter_column_alter_mode alter_mode = CTC_ALTER_COLUMN_ALTER_MODE_NONE; @@ -2469,8 +2486,8 @@ static int fill_ctc_alter_create_list(THD *thd, TABLE *altered_table, Alter_inpl mysql_col_idx++; } - //prevent only virtual columns - if (req->n_create_list == 0) { + // Prevent only virtual columns + if (req->n_create_list == virtual_cols) { my_printf_error(ER_DISALLOWED_OPERATION, "%s", MYF(0), "Cantian does not support all columns are generated."); return HA_ERR_WRONG_COMMAND; }