diff options
author | unknown <sanja@askmonty.org> | 2012-12-23 23:37:11 +0200 |
---|---|---|
committer | unknown <sanja@askmonty.org> | 2012-12-23 23:37:11 +0200 |
commit | 61412c0c31138ca2e08636c10823bc55e6a7a84c (patch) | |
tree | af7b89ebb5f2334b21ab1f658d764829bfcbf7e1 /sql | |
parent | 88b59efef6ae869bac9d14cefe2982a1061d3c93 (diff) | |
parent | 082ff5931770ed70df0ec1e85f81fa880a4d9e62 (diff) | |
download | mariadb-git-61412c0c31138ca2e08636c10823bc55e6a7a84c.tar.gz |
pre-merge
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item.h | 2 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 72 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 8 | ||||
-rw-r--r-- | sql/item_create.cc | 95 | ||||
-rw-r--r-- | sql/item_create.h | 1 | ||||
-rw-r--r-- | sql/item_func.h | 2 | ||||
-rw-r--r-- | sql/item_strfunc.cc | 323 | ||||
-rw-r--r-- | sql/item_strfunc.h | 28 | ||||
-rw-r--r-- | sql/lex.h | 3 | ||||
-rw-r--r-- | sql/sql_base.cc | 1 | ||||
-rw-r--r-- | sql/sql_base.h | 1 | ||||
-rw-r--r-- | sql/sql_join_cache.cc | 988 | ||||
-rw-r--r-- | sql/sql_string.cc | 73 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 19 |
14 files changed, 946 insertions, 670 deletions
diff --git a/sql/item.h b/sql/item.h index 3136bb00394..172932434d2 100644 --- a/sql/item.h +++ b/sql/item.h @@ -523,7 +523,7 @@ public: struct st_dyncall_create_def { - Item *num, *value; + Item *key, *value; CHARSET_INFO *cs; uint len, frac; DYNAMIC_COLUMN_TYPE type; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index d950c0c1443..4db783242c7 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -6068,23 +6068,87 @@ Item* Item_equal::get_first(JOIN_TAB *context, Item *field_item) } -longlong Item_func_dyncol_exists::val_int() +longlong Item_func_dyncol_check::val_int() { char buff[STRING_BUFFER_USUAL_SIZE]; String tmp(buff, sizeof(buff), &my_charset_bin); DYNAMIC_COLUMN col; String *str; - ulonglong num; enum enum_dyncol_func_result rc; - num= args[1]->val_int(); + str= args[0]->val_str(&tmp); + if (args[0]->null_value) + goto null; + col.length= str->length(); + /* We do not change the string, so could do this trick */ + col.str= (char *)str->ptr(); + rc= mariadb_dyncol_check(&col); + if (rc < 0 && rc != ER_DYNCOL_FORMAT) + { + dynamic_column_error_message(rc); + goto null; + } + null_value= FALSE; + return rc == ER_DYNCOL_OK; + +null: + null_value= TRUE; + return 0; +} + +longlong Item_func_dyncol_exists::val_int() +{ + char buff[STRING_BUFFER_USUAL_SIZE], nmstrbuf[11]; + String tmp(buff, sizeof(buff), &my_charset_bin), + nmbuf(nmstrbuf, sizeof(nmstrbuf), system_charset_info); + DYNAMIC_COLUMN col; + String *str; + LEX_STRING buf, *name= NULL; + ulonglong num= 0; + enum enum_dyncol_func_result rc; + + if (args[1]->result_type() == INT_RESULT) + num= args[1]->val_int(); + else + { + String *nm= args[1]->val_str(&nmbuf); + if (!nm || args[1]->null_value) + { + null_value= 1; + return 1; + } + if (my_charset_same(nm->charset(), &my_charset_utf8_general_ci)) + { + buf.str= (char *) nm->ptr(); + buf.length= nm->length(); + } + else + { + uint strlen; + uint dummy_errors; + buf.str= (char *)sql_alloc((strlen= nm->length() * + my_charset_utf8_general_ci.mbmaxlen + 1)); + if (buf.str) + { + buf.length= + copy_and_convert(buf.str, strlen, &my_charset_utf8_general_ci, + nm->ptr(), nm->length(), nm->charset(), + &dummy_errors); + } + else + buf.length= 0; + } + name= &buf; + } str= args[0]->val_str(&tmp); if (args[0]->null_value || args[1]->null_value || num > UINT_MAX16) goto null; col.length= str->length(); /* We do not change the string, so could do this trick */ col.str= (char *)str->ptr(); - rc= dynamic_column_exists(&col, (uint) num); + rc= ((name == NULL) ? + mariadb_dyncol_exists(&col, (uint) num) : + mariadb_dyncol_exists_named(&col, name)); if (rc < 0) { dynamic_column_error_message(rc); diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 34d1a0bd0ae..f183ee9f8a8 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1861,6 +1861,14 @@ public: Item *neg_transformer(THD *thd); }; +class Item_func_dyncol_check :public Item_bool_func +{ +public: + Item_func_dyncol_check(Item *str) :Item_bool_func(str) {} + longlong val_int(); + const char *func_name() const { return "column_check"; } +}; + class Item_func_dyncol_exists :public Item_bool_func { public: diff --git a/sql/item_create.cc b/sql/item_create.cc index 07e7f7b7ff9..fc31b074055 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -526,6 +526,54 @@ protected: virtual ~Create_func_coercibility() {} }; +class Create_func_dyncol_check : public Create_func_arg1 +{ +public: + virtual Item *create_1_arg(THD *thd, Item *arg1); + + static Create_func_dyncol_check s_singleton; + +protected: + Create_func_dyncol_check() {} + virtual ~Create_func_dyncol_check() {} +}; + +class Create_func_dyncol_exists : public Create_func_arg2 +{ +public: + virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_dyncol_exists s_singleton; + +protected: + Create_func_dyncol_exists() {} + virtual ~Create_func_dyncol_exists() {} +}; + +class Create_func_dyncol_list : public Create_func_arg1 +{ +public: + virtual Item *create_1_arg(THD *thd, Item *arg1); + + static Create_func_dyncol_list s_singleton; + +protected: + Create_func_dyncol_list() {} + virtual ~Create_func_dyncol_list() {} +}; + +class Create_func_dyncol_json : public Create_func_arg1 +{ +public: + virtual Item *create_1_arg(THD *thd, Item *arg1); + + static Create_func_dyncol_json s_singleton; + +protected: + Create_func_dyncol_json() {} + virtual ~Create_func_dyncol_json() {} +}; + class Create_func_compress : public Create_func_arg1 { @@ -3108,6 +3156,38 @@ Create_func_coercibility::create_1_arg(THD *thd, Item *arg1) } +Create_func_dyncol_check Create_func_dyncol_check::s_singleton; + +Item* +Create_func_dyncol_check::create_1_arg(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_dyncol_check(arg1); +} + +Create_func_dyncol_exists Create_func_dyncol_exists::s_singleton; + +Item* +Create_func_dyncol_exists::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_dyncol_exists(arg1, arg2); +} + +Create_func_dyncol_list Create_func_dyncol_list::s_singleton; + +Item* +Create_func_dyncol_list::create_1_arg(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_dyncol_list(arg1); +} + +Create_func_dyncol_json Create_func_dyncol_json::s_singleton; + +Item* +Create_func_dyncol_json::create_1_arg(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_dyncol_json(arg1); +} + Create_func_concat Create_func_concat::s_singleton; Item* @@ -5252,6 +5332,10 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("CHARACTER_LENGTH") }, BUILDER(Create_func_char_length)}, { { C_STRING_WITH_LEN("CHAR_LENGTH") }, BUILDER(Create_func_char_length)}, { { C_STRING_WITH_LEN("COERCIBILITY") }, BUILDER(Create_func_coercibility)}, + { { C_STRING_WITH_LEN("COLUMN_CHECK") }, BUILDER(Create_func_dyncol_check)}, + { { C_STRING_WITH_LEN("COLUMN_EXISTS") }, BUILDER(Create_func_dyncol_exists)}, + { { C_STRING_WITH_LEN("COLUMN_LIST") }, BUILDER(Create_func_dyncol_list)}, + { { C_STRING_WITH_LEN("COLUMN_JSON") }, BUILDER(Create_func_dyncol_json)}, { { C_STRING_WITH_LEN("COMPRESS") }, BUILDER(Create_func_compress)}, { { C_STRING_WITH_LEN("CONCAT") }, BUILDER(Create_func_concat)}, { { C_STRING_WITH_LEN("CONCAT_WS") }, BUILDER(Create_func_concat_ws)}, @@ -5711,7 +5795,7 @@ static List<Item> *create_func_dyncol_prepare(THD *thd, for (uint i= 0; (def= li++) ;) { dfs[0][i++]= *def; - args->push_back(def->num); + args->push_back(def->key); args->push_back(def->value); } return args; @@ -5727,7 +5811,6 @@ Item *create_func_dyncol_create(THD *thd, List<DYNCALL_CREATE_DEF> &list) return new (thd->mem_root) Item_func_dyncol_create(*args, dfs); } - Item *create_func_dyncol_add(THD *thd, Item *str, List<DYNCALL_CREATE_DEF> &list) { @@ -5747,7 +5830,7 @@ Item *create_func_dyncol_add(THD *thd, Item *str, Item *create_func_dyncol_delete(THD *thd, Item *str, List<Item> &nums) { DYNCALL_CREATE_DEF *dfs; - Item *num; + Item *key; List_iterator_fast<Item> it(nums); List<Item> *args= new (thd->mem_root) List<Item>; @@ -5757,12 +5840,12 @@ Item *create_func_dyncol_delete(THD *thd, Item *str, List<Item> &nums) if (!args || !dfs) return NULL; - for (uint i= 0; (num= it++); i++) + for (uint i= 0; (key= it++); i++) { - dfs[i].num= num; + dfs[i].key= key; dfs[i].value= new Item_null(); dfs[i].type= DYN_COL_INT; - args->push_back(dfs[i].num); + args->push_back(dfs[i].key); args->push_back(dfs[i].value); } diff --git a/sql/item_create.h b/sql/item_create.h index ac6b0f8454f..5ecb45e9eae 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -180,5 +180,6 @@ Item *create_func_dyncol_get(THD *thd, Item *num, Item *str, Cast_target cast_type, const char *c_len, const char *c_dec, CHARSET_INFO *cs); +Item *create_func_dyncol_json(THD *thd, Item *str); #endif diff --git a/sql/item_func.h b/sql/item_func.h index 80fa0b5d634..b9b6c865bfd 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -58,7 +58,7 @@ public: NOW_FUNC, TRIG_COND_FUNC, SUSERVAR_FUNC, GUSERVAR_FUNC, COLLATE_FUNC, EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC, - NEG_FUNC, GSYSVAR_FUNC }; + NEG_FUNC, GSYSVAR_FUNC, DYNCOL_FUNC }; enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL, OPTIMIZE_EQUAL }; enum Type type() const { return FUNC_ITEM; } diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index cce99ec2991..8ea19078524 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -3777,7 +3777,8 @@ String *Item_func_uuid::val_str(String *str) Item_func_dyncol_create::Item_func_dyncol_create(List<Item> &args, DYNCALL_CREATE_DEF *dfs) - : Item_str_func(args), defs(dfs), vals(0), nums(0) + : Item_str_func(args), defs(dfs), vals(0), keys_num(NULL), keys_str(NULL), + names(FALSE), force_names(FALSE) { DBUG_ASSERT((args.elements & 0x1) == 0); // even number of arguments } @@ -3785,14 +3786,28 @@ Item_func_dyncol_create::Item_func_dyncol_create(List<Item> &args, bool Item_func_dyncol_create::fix_fields(THD *thd, Item **ref) { + uint i; bool res= Item_func::fix_fields(thd, ref); // no need Item_str_func here - vals= (DYNAMIC_COLUMN_VALUE *) alloc_root(thd->mem_root, - sizeof(DYNAMIC_COLUMN_VALUE) * - (arg_count / 2)); - nums= (uint *) alloc_root(thd->mem_root, - sizeof(uint) * (arg_count / 2)); - status_var_increment(thd->status_var.feature_dynamic_columns); - return res || vals == 0 || nums == 0; + if (!res) + { + vals= (DYNAMIC_COLUMN_VALUE *) alloc_root(thd->mem_root, + sizeof(DYNAMIC_COLUMN_VALUE) * + (arg_count / 2)); + for (i= 0; i + 1 < arg_count && args[i]->result_type() == INT_RESULT; i+= 2); + if (i + 1 < arg_count) + { + names= TRUE; + } + + keys_num= (uint *) alloc_root(thd->mem_root, + (sizeof(LEX_STRING) > sizeof(uint) ? + sizeof(LEX_STRING) : + sizeof(uint)) * + (arg_count / 2)); + keys_str= (LEX_STRING *) keys_num; + //status_var_increment(thd->status_var.feature_dynamic_columns); + } + return res || vals == 0 || keys_num == 0; } @@ -3803,13 +3818,49 @@ void Item_func_dyncol_create::fix_length_and_dec() decimals= 0; } -void Item_func_dyncol_create::prepare_arguments() +bool Item_func_dyncol_create::prepare_arguments(bool force_names_arg) { char buff[STRING_BUFFER_USUAL_SIZE]; String *res, tmp(buff, sizeof(buff), &my_charset_bin); uint column_count= (arg_count / 2); uint i; my_decimal dtmp, *dres; + force_names= force_names_arg; + + if (!(names || force_names)) + { + for (i= 0; i < column_count; i++) + { + uint valpos= i * 2 + 1; + DYNAMIC_COLUMN_TYPE type= defs[i].type; + if (type == DYN_COL_NULL) + switch (args[valpos]->field_type()) + { + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_GEOMETRY: + type= DYN_COL_STRING; + break; + default: + break; + } + + if (type == DYN_COL_STRING && + args[valpos]->type() == Item::FUNC_ITEM && + ((Item_func *)args[valpos])->functype() == DYNCOL_FUNC) + { + force_names= 1; + break; + } + } + } /* get values */ for (i= 0; i < column_count; i++) @@ -3868,7 +3919,59 @@ void Item_func_dyncol_create::prepare_arguments() break; } } - nums[i]= (uint) args[i * 2]->val_int(); + if (type == DYN_COL_STRING && + args[valpos]->type() == Item::FUNC_ITEM && + ((Item_func *)args[valpos])->functype() == DYNCOL_FUNC) + { + DBUG_ASSERT(names || force_names); + type= DYN_COL_DYNCOL; + } + if (names || force_names) + { + res= args[i * 2]->val_str(&tmp); + if (res) + { + // guaranty UTF-8 string for names + if (my_charset_same(res->charset(), &my_charset_utf8_general_ci)) + { + keys_str[i].length= res->length(); + keys_str[i].str= sql_strmake(res->ptr(), res->length()); + } + else + { + uint strlen; + uint dummy_errors; + char *str= + (char *)sql_alloc((strlen= res->length() * + my_charset_utf8_general_ci.mbmaxlen + 1)); + if (str) + { + keys_str[i].length= + copy_and_convert(str, strlen, &my_charset_utf8_general_ci, + res->ptr(), res->length(), res->charset(), + &dummy_errors); + keys_str[i].str= str; + } + else + keys_str[i].length= 0; + + } + } + else + { + keys_str[i].length= 0; + keys_str[i].str= NULL; + } + } + else + keys_num[i]= (uint) args[i * 2]->val_int(); + if (args[i * 2]->null_value) + { + /* to make cleanup possible */ + for (; i < column_count; i++) + vals[i].type= DYN_COL_NULL; + return 1; + } vals[i].type= type; switch (type) { case DYN_COL_NULL: @@ -3883,11 +3986,11 @@ void Item_func_dyncol_create::prepare_arguments() case DYN_COL_DOUBLE: vals[i].x.double_value= args[valpos]->val_real(); break; + case DYN_COL_DYNCOL: case DYN_COL_STRING: res= args[valpos]->val_str(&tmp); if (res && - (vals[i].x.string.value.str= my_strndup(res->ptr(), res->length(), - MYF(MY_WME)))) + (vals[i].x.string.value.str= sql_strmake(res->ptr(), res->length()))) { vals[i].x.string.value.length= res->length(); vals[i].x.string.charset= res->charset(); @@ -3902,7 +4005,7 @@ void Item_func_dyncol_create::prepare_arguments() case DYN_COL_DECIMAL: if ((dres= args[valpos]->val_decimal(&dtmp))) { - dynamic_column_prepare_decimal(&vals[i]); + mariadb_dyncol_prepare_decimal(&vals[i]); DBUG_ASSERT(vals[i].x.decimal.value.len == dres->len); vals[i].x.decimal.value.intg= dres->intg; vals[i].x.decimal.value.frac= dres->frac; @@ -3912,7 +4015,7 @@ void Item_func_dyncol_create::prepare_arguments() } else { - dynamic_column_prepare_decimal(&vals[i]); // just to be safe + mariadb_dyncol_prepare_decimal(&vals[i]); // just to be safe DBUG_ASSERT(args[valpos]->null_value); } break; @@ -3931,24 +4034,12 @@ void Item_func_dyncol_create::prepare_arguments() } if (vals[i].type != DYN_COL_NULL && args[valpos]->null_value) { - if (vals[i].type == DYN_COL_STRING) - my_free(vals[i].x.string.value.str); vals[i].type= DYN_COL_NULL; } } + return FALSE; } -void Item_func_dyncol_create::cleanup_arguments() -{ - uint column_count= (arg_count / 2); - uint i; - - for (i= 0; i < column_count; i++) - { - if (vals[i].type == DYN_COL_STRING) - my_free(vals[i].x.string.value.str); - } -} String *Item_func_dyncol_create::val_str(String *str) { @@ -3958,30 +4049,37 @@ String *Item_func_dyncol_create::val_str(String *str) enum enum_dyncol_func_result rc; DBUG_ASSERT((arg_count & 0x1) == 0); // even number of arguments - prepare_arguments(); - - if ((rc= dynamic_column_create_many(&col, column_count, nums, vals))) + if (prepare_arguments(FALSE)) { - dynamic_column_error_message(rc); - dynamic_column_column_free(&col); res= NULL; - null_value= TRUE; + null_value= 1; } else { - /* Move result from DYNAMIC_COLUMN to str_value */ - char *ptr; - size_t length, alloc_length; - dynamic_column_reassociate(&col, &ptr, &length, &alloc_length); - str_value.reassociate(ptr, (uint32) length, (uint32) alloc_length, - &my_charset_bin); - res= &str_value; - null_value= FALSE; + if ((rc= ((names || force_names) ? + mariadb_dyncol_create_many_named(&col, column_count, keys_str, + vals, TRUE) : + mariadb_dyncol_create_many(&col, column_count, keys_num, + vals, TRUE)))) + { + dynamic_column_error_message(rc); + dynamic_column_column_free(&col); + res= NULL; + null_value= TRUE; + } + else + { + /* Move result from DYNAMIC_COLUMN to str_value */ + char *ptr; + size_t length, alloc_length; + dynstr_reassociate(&col, &ptr, &length, &alloc_length); + str_value.reassociate(ptr, (uint32) length, (uint32) alloc_length, + &my_charset_bin); + res= &str_value; + null_value= FALSE; + } } - /* cleanup */ - cleanup_arguments(); - return res; } @@ -4007,6 +4105,7 @@ void Item_func_dyncol_create::print_arguments(String *str, case DYN_COL_DOUBLE: str->append(STRING_WITH_LEN(" AS double")); break; + case DYN_COL_DYNCOL: case DYN_COL_STRING: str->append(STRING_WITH_LEN(" AS char")); if (defs[i].cs) @@ -4044,6 +4143,40 @@ void Item_func_dyncol_create::print(String *str, str->append(')'); } +String *Item_func_dyncol_json::val_str(String *str) +{ + DYNAMIC_STRING json, col; + String *res; + enum enum_dyncol_func_result rc; + + res= args[0]->val_str(str); + if (args[0]->null_value) + goto null; + + col.str= (char *)res->ptr(); + col.length= res->length(); + if ((rc= mariadb_dyncol_json(&col, &json))) + { + dynamic_column_error_message(rc); + goto null; + } + bzero(&col, sizeof(col)); + { + /* Move result from DYNAMIC_COLUMN to str */ + char *ptr; + size_t length, alloc_length; + dynstr_reassociate(&json, &ptr, &length, &alloc_length); + str->reassociate(ptr, (uint32) length, (uint32) alloc_length, + &my_charset_utf8_general_ci); + null_value= FALSE; + } + return str; + +null: + bzero(&col, sizeof(col)); + null_value= TRUE; + return NULL; +} String *Item_func_dyncol_add::val_str(String *str) { @@ -4055,21 +4188,25 @@ String *Item_func_dyncol_add::val_str(String *str) /* We store the packed data last */ res= args[arg_count - 1]->val_str(str); - if (args[arg_count - 1]->null_value) + if (args[arg_count - 1]->null_value || + init_dynamic_string(&col, NULL, res->length() + STRING_BUFFER_USUAL_SIZE, + STRING_BUFFER_USUAL_SIZE)) goto null; - init_dynamic_string(&col, NULL, res->length() + STRING_BUFFER_USUAL_SIZE, - STRING_BUFFER_USUAL_SIZE); col.length= res->length(); memcpy(col.str, res->ptr(), col.length); - prepare_arguments(); + if (prepare_arguments(mariadb_dyncol_has_names(&col))) + goto null; - if ((rc= dynamic_column_update_many(&col, column_count, nums, vals))) + if ((rc= ((names || force_names) ? + mariadb_dyncol_update_many_named(&col, column_count, + keys_str, vals) : + mariadb_dyncol_update_many(&col, column_count, + keys_num, vals)))) { dynamic_column_error_message(rc); dynamic_column_column_free(&col); - cleanup_arguments(); goto null; } @@ -4077,16 +4214,12 @@ String *Item_func_dyncol_add::val_str(String *str) /* Move result from DYNAMIC_COLUMN to str */ char *ptr; size_t length, alloc_length; - dynamic_column_reassociate(&col, &ptr, &length, &alloc_length); + dynstr_reassociate(&col, &ptr, &length, &alloc_length); str->reassociate(ptr, (uint32) length, (uint32) alloc_length, &my_charset_bin); null_value= FALSE; } - /* cleanup */ - dynamic_column_column_free(&col); - cleanup_arguments(); - return str; null: @@ -4118,10 +4251,48 @@ bool Item_dyncol_get::get_dyn_value(DYNAMIC_COLUMN_VALUE *val, String *tmp) { DYNAMIC_COLUMN dyn_str; String *res; - longlong num; + longlong num= 0; + LEX_STRING buf, *name= NULL; + char nmstrbuf[11]; + String nmbuf(nmstrbuf, sizeof(nmstrbuf), system_charset_info); enum enum_dyncol_func_result rc; - num= args[1]->val_int(); + if (args[1]->result_type() == INT_RESULT) + num= args[1]->val_int(); + else + { + String *nm= args[1]->val_str(&nmbuf); + if (!nm || args[1]->null_value) + { + null_value= 1; + return 1; + } + + if (my_charset_same(nm->charset(), &my_charset_utf8_general_ci)) + { + buf.str= (char *) nm->ptr(); + buf.length= nm->length(); + } + else + { + uint strlen; + uint dummy_errors; + buf.str= (char *)sql_alloc((strlen= nm->length() * + my_charset_utf8_general_ci.mbmaxlen + 1)); + if (buf.str) + { + buf.length= + copy_and_convert(buf.str, strlen, &my_charset_utf8_general_ci, + nm->ptr(), nm->length(), nm->charset(), + &dummy_errors); + } + else + buf.length= 0; + } + name= &buf; + } + + if (args[1]->null_value || num < 0 || num > INT_MAX) { null_value= 1; @@ -4137,7 +4308,9 @@ bool Item_dyncol_get::get_dyn_value(DYNAMIC_COLUMN_VALUE *val, String *tmp) dyn_str.str= (char*) res->ptr(); dyn_str.length= res->length(); - if ((rc= dynamic_column_get(&dyn_str, (uint) num, val))) + if ((rc= ((name == NULL) ? + mariadb_dyncol_get(&dyn_str, (uint) num, val) : + mariadb_dyncol_get_named(&dyn_str, name, val)))) { dynamic_column_error_message(rc); null_value= 1; @@ -4169,6 +4342,7 @@ String *Item_dyncol_get::val_str(String *str_result) case DYN_COL_DOUBLE: str_result->set_real(val.x.double_value, NOT_FIXED_DEC, &my_charset_latin1); break; + case DYN_COL_DYNCOL: case DYN_COL_STRING: if ((char*) tmp.ptr() <= val.x.string.value.str && (char*) tmp.ptr() + tmp.length() >= val.x.string.value.str) @@ -4245,6 +4419,7 @@ longlong Item_dyncol_get::val_int() return 0; switch (val.type) { + case DYN_COL_DYNCOL: case DYN_COL_NULL: goto null; case DYN_COL_UINT: @@ -4325,6 +4500,7 @@ double Item_dyncol_get::val_real() return 0.0; switch (val.type) { + case DYN_COL_DYNCOL: case DYN_COL_NULL: goto null; case DYN_COL_UINT: @@ -4382,6 +4558,7 @@ my_decimal *Item_dyncol_get::val_decimal(my_decimal *decimal_value) return NULL; switch (val.type) { + case DYN_COL_DYNCOL: case DYN_COL_NULL: goto null; case DYN_COL_UINT: @@ -4441,6 +4618,7 @@ bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) return 1; // Error switch (val.type) { + case DYN_COL_DYNCOL: case DYN_COL_NULL: goto null; case DYN_COL_INT: @@ -4486,6 +4664,8 @@ null: return 1; } +void +append_identifier(THD *thd, String *packet, const char *name, uint length); void Item_dyncol_get::print(String *str, enum_query_type query_type) { @@ -4501,7 +4681,8 @@ String *Item_func_dyncol_list::val_str(String *str) { uint i; enum enum_dyncol_func_result rc; - DYNAMIC_ARRAY arr; + LEX_STRING *names= 0; + uint count; DYNAMIC_COLUMN col; String *res= args[0]->val_str(str); @@ -4510,33 +4691,37 @@ String *Item_func_dyncol_list::val_str(String *str) col.length= res->length(); /* We do not change the string, so could do this trick */ col.str= (char *)res->ptr(); - if ((rc= dynamic_column_list(&col, &arr))) + if ((rc= mariadb_dyncol_list_named(&col, &count, &names))) { + bzero(&col, sizeof(col)); dynamic_column_error_message(rc); - delete_dynamic(&arr); goto null; } + bzero(&col, sizeof(col)); /* - We support elements from 0 - 65536, so max size for one element is - 6 (including ,). + We estimate average name length as 10 */ - if (str->alloc(arr.elements * 6)) + if (str->alloc(count * 13)) goto null; str->length(0); - for (i= 0; i < arr.elements; i++) + str->set_charset(&my_charset_utf8_general_ci); + for (i= 0; i < count; i++) { - str->qs_append(*dynamic_element(&arr, i, uint*)); - if (i < arr.elements - 1) + append_identifier(current_thd, str, names[i].str, names[i].length); + if (i < count - 1) str->qs_append(','); } - null_value= FALSE; - delete_dynamic(&arr); + if (names) + my_free(names); return str; null: null_value= TRUE; + if (names) + my_free(names); return NULL; } + diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 9ed2627a518..d21c938f378 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -1001,9 +1001,10 @@ class Item_func_dyncol_create: public Item_str_func protected: DYNCALL_CREATE_DEF *defs; DYNAMIC_COLUMN_VALUE *vals; - uint *nums; - void prepare_arguments(); - void cleanup_arguments(); + uint *keys_num; + LEX_STRING *keys_str; + bool names, force_names; + bool prepare_arguments(bool force_names); void print_arguments(String *str, enum_query_type query_type); public: Item_func_dyncol_create(List<Item> &args, DYNCALL_CREATE_DEF *dfs); @@ -1012,6 +1013,7 @@ public: const char *func_name() const{ return "column_create"; } String *val_str(String *); virtual void print(String *str, enum_query_type query_type); + virtual enum Functype functype() const { return DYNCOL_FUNC; } }; @@ -1026,6 +1028,19 @@ public: virtual void print(String *str, enum_query_type query_type); }; +class Item_func_dyncol_json: public Item_str_func +{ +public: + Item_func_dyncol_json(Item *str) :Item_str_func(str) {} + const char *func_name() const{ return "column_json"; } + String *val_str(String *); + void fix_length_and_dec() + { + maybe_null= TRUE; + collation.set(&my_charset_bin); + decimals= 0; + } +}; /* The following functions is always called from an Item_cast function @@ -1036,11 +1051,9 @@ class Item_dyncol_get: public Item_str_func public: Item_dyncol_get(Item *str, Item *num) :Item_str_func(str, num) - { - max_length= MAX_DYNAMIC_COLUMN_LENGTH; - } + {} void fix_length_and_dec() - { maybe_null= 1; } + { maybe_null= 1; max_length= MAX_BLOB_WIDTH; } /* Mark that collation can change between calls */ bool dynamic_result() { return 1; } @@ -1067,3 +1080,4 @@ public: extern String my_empty_string; #endif /* ITEM_STRFUNC_INCLUDED */ + diff --git a/sql/lex.h b/sql/lex.h index dab9e2adbc4..e1dc6150cc3 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -123,11 +123,10 @@ static SYMBOL symbols[] = { { "COLUMN_NAME", SYM(COLUMN_NAME_SYM)}, { "COLUMNS", SYM(COLUMNS)}, { "COLUMN_ADD", SYM(COLUMN_ADD_SYM)}, + { "COLUMN_CHECK", SYM(COLUMN_CHECK_SYM)}, { "COLUMN_CREATE", SYM(COLUMN_CREATE_SYM)}, { "COLUMN_DELETE", SYM(COLUMN_DELETE_SYM)}, - { "COLUMN_EXISTS", SYM(COLUMN_EXISTS_SYM)}, { "COLUMN_GET", SYM(COLUMN_GET_SYM)}, - { "COLUMN_LIST", SYM(COLUMN_LIST_SYM)}, { "COMMENT", SYM(COMMENT_SYM)}, { "COMMIT", SYM(COMMIT_SYM)}, { "COMMITTED", SYM(COMMITTED_SYM)}, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index f9f6cb1ea82..86f93172568 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -9820,6 +9820,7 @@ int dynamic_column_error_message(enum_dyncol_func_result rc) switch (rc) { case ER_DYNCOL_YES: case ER_DYNCOL_OK: + case ER_DYNCOL_TRUNCATED: break; // it is not an error case ER_DYNCOL_FORMAT: my_error(ER_DYN_COL_WRONG_FORMAT, MYF(0)); diff --git a/sql/sql_base.h b/sql/sql_base.h index c19e9c68132..84f457b1ca5 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -272,6 +272,7 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db, const char *table_name); bool is_equal(const LEX_STRING *a, const LEX_STRING *b); +class Open_tables_backup; /* Functions to work with system tables. */ bool open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, Open_tables_backup *backup); diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index 07266cc9cbe..4626f30df28 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -3894,13 +3894,15 @@ int JOIN_TAB_SCAN_MRR::next() int rc= join_tab->table->file->multi_range_read_next((range_id_t*)ptr) ? -1 : 0; if (!rc) { - /* + /* If a record in in an incremental cache contains no fields then the association for the last record in cache will be equal to cache->end_pos - */ - DBUG_ASSERT((!(mrr_mode & HA_MRR_NO_ASSOCIATION))? - (cache->buff <= (uchar *) (*ptr) && - (uchar *) (*ptr) <= cache->end_pos): TRUE); + */ + /* + psergey: this makes no sense where HA_MRR_NO_ASSOC is used. + DBUG_ASSERT(cache->buff <= (uchar *) (*ptr) && + (uchar *) (*ptr) <= cache->end_pos); + */ if (join_tab->table->vfield) update_virtual_fields(join->thd, join_tab->table); } @@ -3910,663 +3912,663 @@ int JOIN_TAB_SCAN_MRR::next() static void bka_range_seq_key_info(void *init_params, uint *length, - key_part_map *map) + key_part_map *map) { - TABLE_REF *ref= &(((JOIN_CACHE*)init_params)->join_tab->ref); - *length= ref->key_length; - *map= (key_part_map(1) << ref->key_parts) - 1; +TABLE_REF *ref= &(((JOIN_CACHE*)init_params)->join_tab->ref); +*length= ref->key_length; +*map= (key_part_map(1) << ref->key_parts) - 1; } /* - Initialize retrieval of range sequence for BKA join algorithm - - SYNOPSIS - bka_range_seq_init() - init_params pointer to the BKA join cache object - n_ranges the number of ranges obtained - flags combination of MRR flags - - DESCRIPTION - The function interprets init_param as a pointer to a JOIN_CACHE_BKA - object. The function prepares for an iteration over the join keys - built for all records from the cache join buffer. - - NOTE - This function are used only as a callback function. - - RETURN VALUE - init_param value that is to be used as a parameter of bka_range_seq_next() +Initialize retrieval of range sequence for BKA join algorithm + +SYNOPSIS + bka_range_seq_init() + init_params pointer to the BKA join cache object + n_ranges the number of ranges obtained + flags combination of MRR flags + +DESCRIPTION + The function interprets init_param as a pointer to a JOIN_CACHE_BKA + object. The function prepares for an iteration over the join keys + built for all records from the cache join buffer. + +NOTE + This function are used only as a callback function. + +RETURN VALUE + init_param value that is to be used as a parameter of bka_range_seq_next() */ static range_seq_t bka_range_seq_init(void *init_param, uint n_ranges, uint flags) { - DBUG_ENTER("bka_range_seq_init"); - JOIN_CACHE_BKA *cache= (JOIN_CACHE_BKA *) init_param; - cache->reset(0); - DBUG_RETURN((range_seq_t) init_param); +DBUG_ENTER("bka_range_seq_init"); +JOIN_CACHE_BKA *cache= (JOIN_CACHE_BKA *) init_param; +cache->reset(0); +DBUG_RETURN((range_seq_t) init_param); } /* - Get the next range/key over records from the join buffer used by a BKA cache - - SYNOPSIS - bka_range_seq_next() - seq the value returned by bka_range_seq_init - range OUT reference to the next range +Get the next range/key over records from the join buffer used by a BKA cache - DESCRIPTION - The function interprets seq as a pointer to a JOIN_CACHE_BKA - object. The function returns a pointer to the range descriptor - for the key built over the next record from the join buffer. - - NOTE - This function are used only as a callback function. - - RETURN VALUE - FALSE ok, the range structure filled with info about the next range/key - TRUE no more ranges +SYNOPSIS + bka_range_seq_next() + seq the value returned by bka_range_seq_init + range OUT reference to the next range + +DESCRIPTION + The function interprets seq as a pointer to a JOIN_CACHE_BKA + object. The function returns a pointer to the range descriptor + for the key built over the next record from the join buffer. + +NOTE + This function are used only as a callback function. + +RETURN VALUE + FALSE ok, the range structure filled with info about the next range/key + TRUE no more ranges */ static bool bka_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range) { - DBUG_ENTER("bka_range_seq_next"); - JOIN_CACHE_BKA *cache= (JOIN_CACHE_BKA *) rseq; - TABLE_REF *ref= &cache->join_tab->ref; - key_range *start_key= &range->start_key; - if ((start_key->length= cache->get_next_key((uchar **) &start_key->key))) - { - start_key->keypart_map= (1 << ref->key_parts) - 1; - start_key->flag= HA_READ_KEY_EXACT; - range->end_key= *start_key; - range->end_key.flag= HA_READ_AFTER_KEY; - range->ptr= (char *) cache->get_curr_rec(); - range->range_flag= EQ_RANGE; - DBUG_RETURN(0); - } - DBUG_RETURN(1); +DBUG_ENTER("bka_range_seq_next"); +JOIN_CACHE_BKA *cache= (JOIN_CACHE_BKA *) rseq; +TABLE_REF *ref= &cache->join_tab->ref; +key_range *start_key= &range->start_key; +if ((start_key->length= cache->get_next_key((uchar **) &start_key->key))) +{ + start_key->keypart_map= (1 << ref->key_parts) - 1; + start_key->flag= HA_READ_KEY_EXACT; + range->end_key= *start_key; + range->end_key.flag= HA_READ_AFTER_KEY; + range->ptr= (char *) cache->get_curr_rec(); + range->range_flag= EQ_RANGE; + DBUG_RETURN(0); +} +DBUG_RETURN(1); } /* - Check whether range_info orders to skip the next record from BKA buffer - - SYNOPSIS - bka_range_seq_skip_record() - seq value returned by bka_range_seq_init() - range_info information about the next range - rowid [NOT USED] rowid of the record to be checked +Check whether range_info orders to skip the next record from BKA buffer - - DESCRIPTION - The function interprets seq as a pointer to a JOIN_CACHE_BKA object. - The function returns TRUE if the record with this range_info - is to be filtered out from the stream of records returned by - multi_range_read_next(). - - NOTE - This function are used only as a callback function. +SYNOPSIS + bka_range_seq_skip_record() + seq value returned by bka_range_seq_init() + range_info information about the next range + rowid [NOT USED] rowid of the record to be checked - RETURN VALUE - 1 record with this range_info is to be filtered out from the stream - of records returned by multi_range_read_next() - 0 the record is to be left in the stream + +DESCRIPTION + The function interprets seq as a pointer to a JOIN_CACHE_BKA object. + The function returns TRUE if the record with this range_info + is to be filtered out from the stream of records returned by + multi_range_read_next(). + +NOTE + This function are used only as a callback function. + +RETURN VALUE + 1 record with this range_info is to be filtered out from the stream + of records returned by multi_range_read_next() + 0 the record is to be left in the stream */ static bool bka_range_seq_skip_record(range_seq_t rseq, range_id_t range_info, uchar *rowid) { - DBUG_ENTER("bka_range_seq_skip_record"); - JOIN_CACHE_BKA *cache= (JOIN_CACHE_BKA *) rseq; - bool res= cache->get_match_flag_by_pos((uchar *) range_info) == - JOIN_CACHE::MATCH_FOUND; - DBUG_RETURN(res); +DBUG_ENTER("bka_range_seq_skip_record"); +JOIN_CACHE_BKA *cache= (JOIN_CACHE_BKA *) rseq; +bool res= cache->get_match_flag_by_pos((uchar *) range_info) == + JOIN_CACHE::MATCH_FOUND; +DBUG_RETURN(res); } /* - Check if the record combination from BKA cache matches the index condition +Check if the record combination from BKA cache matches the index condition - SYNOPSIS - bka_skip_index_tuple() - rseq value returned by bka_range_seq_init() - range_info record chain for the next range/key returned by MRR - - DESCRIPTION - This is wrapper for JOIN_CACHE_BKA::skip_index_tuple method, - see comments there. +SYNOPSIS + bka_skip_index_tuple() + rseq value returned by bka_range_seq_init() + range_info record chain for the next range/key returned by MRR + +DESCRIPTION + This is wrapper for JOIN_CACHE_BKA::skip_index_tuple method, + see comments there. - NOTE - This function is used as a RANGE_SEQ_IF::skip_index_tuple callback. - - RETURN VALUE - 0 The record combination satisfies the index condition - 1 Otherwise +NOTE + This function is used as a RANGE_SEQ_IF::skip_index_tuple callback. + +RETURN VALUE + 0 The record combination satisfies the index condition + 1 Otherwise */ static bool bka_skip_index_tuple(range_seq_t rseq, range_id_t range_info) { - DBUG_ENTER("bka_skip_index_tuple"); - JOIN_CACHE_BKA *cache= (JOIN_CACHE_BKA *) rseq; - THD *thd= cache->thd(); - bool res; - status_var_increment(thd->status_var.ha_icp_attempts); - if (!(res= cache->skip_index_tuple(range_info))) - status_var_increment(thd->status_var.ha_icp_match); - DBUG_RETURN(res); +DBUG_ENTER("bka_skip_index_tuple"); +JOIN_CACHE_BKA *cache= (JOIN_CACHE_BKA *) rseq; +THD *thd= cache->thd(); +bool res; +status_var_increment(thd->status_var.ha_icp_attempts); +if (!(res= cache->skip_index_tuple(range_info))) + status_var_increment(thd->status_var.ha_icp_match); +DBUG_RETURN(res); } /* - Prepare to read the record from BKA cache matching the current joined record - - SYNOPSIS - prepare_look_for_matches() - skip_last <-> ignore the last record in the buffer (always unused here) - - DESCRIPTION - The function prepares to iterate over records in the join cache buffer - matching the record loaded into the record buffer for join_tab when - performing join operation by BKA join algorithm. With BKA algorithms the - record loaded into the record buffer for join_tab always has a direct - reference to the matching records from the join buffer. When the regular - BKA join algorithm is employed the record from join_tab can refer to - only one such record. - The function sets the counter of the remaining records from the cache - buffer that would match the current join_tab record to 1. - - RETURN VALUE - TRUE there are no records in the buffer to iterate over - FALSE otherwise +Prepare to read the record from BKA cache matching the current joined record + +SYNOPSIS + prepare_look_for_matches() + skip_last <-> ignore the last record in the buffer (always unused here) + +DESCRIPTION + The function prepares to iterate over records in the join cache buffer + matching the record loaded into the record buffer for join_tab when + performing join operation by BKA join algorithm. With BKA algorithms the + record loaded into the record buffer for join_tab always has a direct + reference to the matching records from the join buffer. When the regular + BKA join algorithm is employed the record from join_tab can refer to + only one such record. + The function sets the counter of the remaining records from the cache + buffer that would match the current join_tab record to 1. + +RETURN VALUE + TRUE there are no records in the buffer to iterate over + FALSE otherwise */ - + bool JOIN_CACHE_BKA::prepare_look_for_matches(bool skip_last) { - if (!records) - return TRUE; - rem_records= 1; - return FALSE; +if (!records) + return TRUE; +rem_records= 1; +return FALSE; } /* - Get the record from the BKA cache matching the current joined record - - SYNOPSIS - get_next_candidate_for_match - - DESCRIPTION - This method is used for iterations over the records from the join - cache buffer when looking for matches for records from join_tab. - The method performs the necessary preparations to read the next record - from the join buffer into the record buffer by the method - read_next_candidate_for_match, or, to skip the next record from the join - buffer by the method skip_if_not_needed_match. - This implementation of the virtual method get_next_candidate_for_match - just decrements the counter of the records that are to be iterated over - and returns the value of curr_association as a reference to the position - of the beginning of the record fields in the buffer. - - RETURN VALUE - pointer to the start of the record fields in the join buffer - if the there is another record to iterate over, 0 - otherwise. +Get the record from the BKA cache matching the current joined record + +SYNOPSIS + get_next_candidate_for_match + +DESCRIPTION + This method is used for iterations over the records from the join + cache buffer when looking for matches for records from join_tab. + The method performs the necessary preparations to read the next record + from the join buffer into the record buffer by the method + read_next_candidate_for_match, or, to skip the next record from the join + buffer by the method skip_if_not_needed_match. + This implementation of the virtual method get_next_candidate_for_match + just decrements the counter of the records that are to be iterated over + and returns the value of curr_association as a reference to the position + of the beginning of the record fields in the buffer. + +RETURN VALUE + pointer to the start of the record fields in the join buffer + if the there is another record to iterate over, 0 - otherwise. */ uchar *JOIN_CACHE_BKA::get_next_candidate_for_match() { - if (!rem_records) - return 0; - rem_records--; - return curr_association; +if (!rem_records) + return 0; +rem_records--; +return curr_association; } /* - Check whether the matching record from the BKA cache is to be skipped - - SYNOPSIS - skip_next_candidate_for_match - rec_ptr pointer to the position in the join buffer right after - the previous record - - DESCRIPTION - This implementation of the virtual function just calls the - method get_match_flag_by_pos to check whether the record referenced - by ref_ptr has its match flag set to MATCH_FOUND. - - RETURN VALUE - TRUE the record referenced by rec_ptr has its match flag set to - MATCH_FOUND - FALSE otherwise +Check whether the matching record from the BKA cache is to be skipped + +SYNOPSIS + skip_next_candidate_for_match + rec_ptr pointer to the position in the join buffer right after + the previous record + +DESCRIPTION + This implementation of the virtual function just calls the + method get_match_flag_by_pos to check whether the record referenced + by ref_ptr has its match flag set to MATCH_FOUND. + +RETURN VALUE + TRUE the record referenced by rec_ptr has its match flag set to + MATCH_FOUND + FALSE otherwise */ bool JOIN_CACHE_BKA::skip_next_candidate_for_match(uchar *rec_ptr) { - return join_tab->check_only_first_match() && - (get_match_flag_by_pos(rec_ptr) == MATCH_FOUND); +return join_tab->check_only_first_match() && + (get_match_flag_by_pos(rec_ptr) == MATCH_FOUND); } /* - Read the next record from the BKA join cache buffer when looking for matches - - SYNOPSIS - read_next_candidate_for_match - rec_ptr pointer to the position in the join buffer right after - the previous record - - DESCRIPTION - This implementation of the virtual method read_next_candidate_for_match - calls the method get_record_by_pos to read the record referenced by rec_ptr - from the join buffer into the record buffer. If this record refers to - fields in the other join buffers the call of get_record_by_po ensures that - these fields are read into the corresponding record buffers as well. - This function is supposed to be called after a successful call of - the method get_next_candidate_for_match. - - RETURN VALUE - none +Read the next record from the BKA join cache buffer when looking for matches + +SYNOPSIS + read_next_candidate_for_match + rec_ptr pointer to the position in the join buffer right after + the previous record + +DESCRIPTION + This implementation of the virtual method read_next_candidate_for_match + calls the method get_record_by_pos to read the record referenced by rec_ptr + from the join buffer into the record buffer. If this record refers to + fields in the other join buffers the call of get_record_by_po ensures that + these fields are read into the corresponding record buffers as well. + This function is supposed to be called after a successful call of + the method get_next_candidate_for_match. + +RETURN VALUE + none */ void JOIN_CACHE_BKA::read_next_candidate_for_match(uchar *rec_ptr) { - get_record_by_pos(rec_ptr); +get_record_by_pos(rec_ptr); } /* - Initialize the BKA join cache +Initialize the BKA join cache - SYNOPSIS - init +SYNOPSIS + init - DESCRIPTION - The function initializes the cache structure. It is supposed to be called - right after a constructor for the JOIN_CACHE_BKA. +DESCRIPTION + The function initializes the cache structure. It is supposed to be called + right after a constructor for the JOIN_CACHE_BKA. - NOTES - The function first constructs a companion object of the type - JOIN_TAB_SCAN_MRR, then it calls the init method of the parent class. - - RETURN VALUE - 0 initialization with buffer allocations has been succeeded - 1 otherwise +NOTES + The function first constructs a companion object of the type + JOIN_TAB_SCAN_MRR, then it calls the init method of the parent class. + +RETURN VALUE + 0 initialization with buffer allocations has been succeeded + 1 otherwise */ int JOIN_CACHE_BKA::init() { - int res; - bool check_only_first_match= join_tab->check_only_first_match(); +int res; +bool check_only_first_match= join_tab->check_only_first_match(); - RANGE_SEQ_IF rs_funcs= { bka_range_seq_key_info, - bka_range_seq_init, - bka_range_seq_next, - check_only_first_match ? - bka_range_seq_skip_record : 0, - bka_skip_index_tuple }; +RANGE_SEQ_IF rs_funcs= { bka_range_seq_key_info, + bka_range_seq_init, + bka_range_seq_next, + check_only_first_match ? + bka_range_seq_skip_record : 0, + bka_skip_index_tuple }; - DBUG_ENTER("JOIN_CACHE_BKA::init"); +DBUG_ENTER("JOIN_CACHE_BKA::init"); - JOIN_TAB_SCAN_MRR *jsm; - if (!(join_tab_scan= jsm= new JOIN_TAB_SCAN_MRR(join, join_tab, - mrr_mode, rs_funcs))) - DBUG_RETURN(1); +JOIN_TAB_SCAN_MRR *jsm; +if (!(join_tab_scan= jsm= new JOIN_TAB_SCAN_MRR(join, join_tab, + mrr_mode, rs_funcs))) + DBUG_RETURN(1); - if ((res= JOIN_CACHE::init())) - DBUG_RETURN(res); +if ((res= JOIN_CACHE::init())) + DBUG_RETURN(res); - if (use_emb_key) - jsm->mrr_mode |= HA_MRR_MATERIALIZED_KEYS; +if (use_emb_key) + jsm->mrr_mode |= HA_MRR_MATERIALIZED_KEYS; - DBUG_RETURN(0); +DBUG_RETURN(0); } /* - Get the key built over the next record from BKA join buffer - - SYNOPSIS - get_next_key() - key pointer to the buffer where the key value is to be placed - - DESCRIPTION - The function reads key fields from the current record in the join buffer. - and builds the key value out of these fields that will be used to access - the 'join_tab' table. Some of key fields may belong to previous caches. - They are accessed via record references to the record parts stored in the - previous join buffers. The other key fields always are placed right after - the flag fields of the record. - If the key is embedded, which means that its value can be read directly - from the join buffer, then *key is set to the beginning of the key in - this buffer. Otherwise the key is built in the join_tab->ref->key_buff. - The function returns the length of the key if it succeeds ro read it. - If is assumed that the functions starts reading at the position of - the record length which is provided for each records in a BKA cache. - After the key is built the 'pos' value points to the first position after - the current record. - The function just skips the records with MATCH_IMPOSSIBLE in the - match flag field if there is any. - The function returns 0 if the initial position is after the beginning - of the record fields for last record from the join buffer. - - RETURN VALUE - length of the key value - if the starting value of 'pos' points to - the position before the fields for the last record, - 0 - otherwise. +Get the key built over the next record from BKA join buffer + +SYNOPSIS + get_next_key() + key pointer to the buffer where the key value is to be placed + +DESCRIPTION + The function reads key fields from the current record in the join buffer. + and builds the key value out of these fields that will be used to access + the 'join_tab' table. Some of key fields may belong to previous caches. + They are accessed via record references to the record parts stored in the + previous join buffers. The other key fields always are placed right after + the flag fields of the record. + If the key is embedded, which means that its value can be read directly + from the join buffer, then *key is set to the beginning of the key in + this buffer. Otherwise the key is built in the join_tab->ref->key_buff. + The function returns the length of the key if it succeeds ro read it. + If is assumed that the functions starts reading at the position of + the record length which is provided for each records in a BKA cache. + After the key is built the 'pos' value points to the first position after + the current record. + The function just skips the records with MATCH_IMPOSSIBLE in the + match flag field if there is any. + The function returns 0 if the initial position is after the beginning + of the record fields for last record from the join buffer. + +RETURN VALUE + length of the key value - if the starting value of 'pos' points to + the position before the fields for the last record, + 0 - otherwise. */ uint JOIN_CACHE_BKA::get_next_key(uchar ** key) { - uint len; - uint32 rec_len; - uchar *init_pos; - JOIN_CACHE *cache; - +uint len; +uint32 rec_len; +uchar *init_pos; +JOIN_CACHE *cache; + start: - /* Any record in a BKA cache is prepended with its length */ - DBUG_ASSERT(with_length); - - if ((pos+size_of_rec_len) > last_rec_pos || !records) - return 0; +/* Any record in a BKA cache is prepended with its length */ +DBUG_ASSERT(with_length); + +if ((pos+size_of_rec_len) > last_rec_pos || !records) + return 0; - /* Read the length of the record */ - rec_len= get_rec_length(pos); - pos+= size_of_rec_len; - init_pos= pos; +/* Read the length of the record */ +rec_len= get_rec_length(pos); +pos+= size_of_rec_len; +init_pos= pos; - /* Read a reference to the previous cache if any */ - if (prev_cache) - pos+= prev_cache->get_size_of_rec_offset(); +/* Read a reference to the previous cache if any */ +if (prev_cache) + pos+= prev_cache->get_size_of_rec_offset(); - curr_rec_pos= pos; +curr_rec_pos= pos; - /* Read all flag fields of the record */ - read_flag_fields(); +/* Read all flag fields of the record */ +read_flag_fields(); - if (with_match_flag && - (Match_flag) curr_rec_pos[0] == MATCH_IMPOSSIBLE ) - { - pos= init_pos+rec_len; - goto start; - } - - if (use_emb_key) - { - /* An embedded key is taken directly from the join buffer */ - *key= pos; - len= emb_key_length; - } - else +if (with_match_flag && + (Match_flag) curr_rec_pos[0] == MATCH_IMPOSSIBLE ) +{ + pos= init_pos+rec_len; + goto start; +} + +if (use_emb_key) +{ + /* An embedded key is taken directly from the join buffer */ + *key= pos; + len= emb_key_length; +} +else +{ + /* Read key arguments from previous caches if there are any such fields */ + if (external_key_arg_fields) { - /* Read key arguments from previous caches if there are any such fields */ - if (external_key_arg_fields) - { - uchar *rec_ptr= curr_rec_pos; - uint key_arg_count= external_key_arg_fields; - CACHE_FIELD **copy_ptr= blob_ptr-key_arg_count; - for (cache= prev_cache; key_arg_count; cache= cache->prev_cache) - { - uint len= 0; + uchar *rec_ptr= curr_rec_pos; + uint key_arg_count= external_key_arg_fields; + CACHE_FIELD **copy_ptr= blob_ptr-key_arg_count; + for (cache= prev_cache; key_arg_count; cache= cache->prev_cache) + { + uint len= 0; + DBUG_ASSERT(cache); + rec_ptr= cache->get_rec_ref(rec_ptr); + while (!cache->referenced_fields) + { + cache= cache->prev_cache; DBUG_ASSERT(cache); rec_ptr= cache->get_rec_ref(rec_ptr); - while (!cache->referenced_fields) - { - cache= cache->prev_cache; - DBUG_ASSERT(cache); - rec_ptr= cache->get_rec_ref(rec_ptr); - } - while (key_arg_count && - cache->read_referenced_field(*copy_ptr, rec_ptr, &len)) - { - copy_ptr++; - --key_arg_count; - } + } + while (key_arg_count && + cache->read_referenced_field(*copy_ptr, rec_ptr, &len)) + { + copy_ptr++; + --key_arg_count; } } - - /* - Read the other key arguments from the current record. The fields for - these arguments are always first in the sequence of the record's fields. - */ - CACHE_FIELD *copy= field_descr+flag_fields; - CACHE_FIELD *copy_end= copy+local_key_arg_fields; - bool blob_in_rec_buff= blob_data_is_in_rec_buff(curr_rec_pos); - for ( ; copy < copy_end; copy++) - read_record_field(copy, blob_in_rec_buff); - - /* Build the key over the fields read into the record buffers */ - TABLE_REF *ref= &join_tab->ref; - cp_buffer_from_ref(join->thd, join_tab->table, ref); - *key= ref->key_buff; - len= ref->key_length; } + + /* + Read the other key arguments from the current record. The fields for + these arguments are always first in the sequence of the record's fields. + */ + CACHE_FIELD *copy= field_descr+flag_fields; + CACHE_FIELD *copy_end= copy+local_key_arg_fields; + bool blob_in_rec_buff= blob_data_is_in_rec_buff(curr_rec_pos); + for ( ; copy < copy_end; copy++) + read_record_field(copy, blob_in_rec_buff); + + /* Build the key over the fields read into the record buffers */ + TABLE_REF *ref= &join_tab->ref; + cp_buffer_from_ref(join->thd, join_tab->table, ref); + *key= ref->key_buff; + len= ref->key_length; +} - pos= init_pos+rec_len; +pos= init_pos+rec_len; - return len; +return len; } /* - Check the index condition of the joined table for a record from the BKA cache - - SYNOPSIS - skip_index_tuple() - range_info pointer to the record returned by MRR - - DESCRIPTION - This function is invoked from MRR implementation to check if an index - tuple matches the index condition. It is used in the case where the index - condition actually depends on both columns of the used index and columns - from previous tables. - - NOTES - Accessing columns of the previous tables requires special handling with - BKA. The idea of BKA is to collect record combinations in a buffer and - then do a batch of ref access lookups, i.e. by the time we're doing a - lookup its previous-records-combination is not in prev_table->record[0] - but somewhere in the join buffer. - We need to get it from there back into prev_table(s)->record[0] before we - can evaluate the index condition, and that's why we need this function - instead of regular IndexConditionPushdown. - - NOTES - Possible optimization: - Before we unpack the record from a previous table - check if this table is used in the condition. - If so then unpack the record otherwise skip the unpacking. - This should be done by a special virtual method - get_partial_record_by_pos(). +Check the index condition of the joined table for a record from the BKA cache - RETURN VALUE - 1 the record combination does not satisfies the index condition - 0 otherwise +SYNOPSIS + skip_index_tuple() + range_info pointer to the record returned by MRR + +DESCRIPTION + This function is invoked from MRR implementation to check if an index + tuple matches the index condition. It is used in the case where the index + condition actually depends on both columns of the used index and columns + from previous tables. + +NOTES + Accessing columns of the previous tables requires special handling with + BKA. The idea of BKA is to collect record combinations in a buffer and + then do a batch of ref access lookups, i.e. by the time we're doing a + lookup its previous-records-combination is not in prev_table->record[0] + but somewhere in the join buffer. + We need to get it from there back into prev_table(s)->record[0] before we + can evaluate the index condition, and that's why we need this function + instead of regular IndexConditionPushdown. + +NOTES + Possible optimization: + Before we unpack the record from a previous table + check if this table is used in the condition. + If so then unpack the record otherwise skip the unpacking. + This should be done by a special virtual method + get_partial_record_by_pos(). + +RETURN VALUE + 1 the record combination does not satisfies the index condition + 0 otherwise */ bool JOIN_CACHE_BKA::skip_index_tuple(range_id_t range_info) { - DBUG_ENTER("JOIN_CACHE_BKA::skip_index_tuple"); - get_record_by_pos((uchar*)range_info); - DBUG_RETURN(!join_tab->cache_idx_cond->val_int()); +DBUG_ENTER("JOIN_CACHE_BKA::skip_index_tuple"); +get_record_by_pos((uchar*)range_info); +DBUG_RETURN(!join_tab->cache_idx_cond->val_int()); } /* - Initialize retrieval of range sequence for the BKAH join algorithm - - SYNOPSIS - bkah_range_seq_init() - init_params pointer to the BKAH join cache object - n_ranges the number of ranges obtained - flags combination of MRR flags - - DESCRIPTION - The function interprets init_param as a pointer to a JOIN_CACHE_BKAH - object. The function prepares for an iteration over distinct join keys - built over the records from the cache join buffer. - - NOTE - This function are used only as a callback function. - - RETURN VALUE - init_param value that is to be used as a parameter of - bkah_range_seq_next() +Initialize retrieval of range sequence for the BKAH join algorithm + +SYNOPSIS + bkah_range_seq_init() + init_params pointer to the BKAH join cache object + n_ranges the number of ranges obtained + flags combination of MRR flags + +DESCRIPTION + The function interprets init_param as a pointer to a JOIN_CACHE_BKAH + object. The function prepares for an iteration over distinct join keys + built over the records from the cache join buffer. + +NOTE + This function are used only as a callback function. + +RETURN VALUE + init_param value that is to be used as a parameter of + bkah_range_seq_next() */ static range_seq_t bkah_range_seq_init(void *init_param, uint n_ranges, uint flags) { - DBUG_ENTER("bkah_range_seq_init"); - JOIN_CACHE_BKAH *cache= (JOIN_CACHE_BKAH *) init_param; - cache->reset(0); - DBUG_RETURN((range_seq_t) init_param); +DBUG_ENTER("bkah_range_seq_init"); +JOIN_CACHE_BKAH *cache= (JOIN_CACHE_BKAH *) init_param; +cache->reset(0); +DBUG_RETURN((range_seq_t) init_param); } /* - Get the next range/key over records from the join buffer of a BKAH cache - - SYNOPSIS - bkah_range_seq_next() - seq value returned by bkah_range_seq_init() - range OUT reference to the next range +Get the next range/key over records from the join buffer of a BKAH cache - DESCRIPTION - The function interprets seq as a pointer to a JOIN_CACHE_BKAH - object. The function returns a pointer to the range descriptor - for the next unique key built over records from the join buffer. - - NOTE - This function are used only as a callback function. - - RETURN VALUE - FALSE ok, the range structure filled with info about the next range/key - TRUE no more ranges +SYNOPSIS + bkah_range_seq_next() + seq value returned by bkah_range_seq_init() + range OUT reference to the next range + +DESCRIPTION + The function interprets seq as a pointer to a JOIN_CACHE_BKAH + object. The function returns a pointer to the range descriptor + for the next unique key built over records from the join buffer. + +NOTE + This function are used only as a callback function. + +RETURN VALUE + FALSE ok, the range structure filled with info about the next range/key + TRUE no more ranges */ static bool bkah_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range) { - DBUG_ENTER("bkah_range_seq_next"); - JOIN_CACHE_BKAH *cache= (JOIN_CACHE_BKAH *) rseq; - TABLE_REF *ref= &cache->join_tab->ref; - key_range *start_key= &range->start_key; - if ((start_key->length= cache->get_next_key((uchar **) &start_key->key))) - { - start_key->keypart_map= (1 << ref->key_parts) - 1; - start_key->flag= HA_READ_KEY_EXACT; - range->end_key= *start_key; - range->end_key.flag= HA_READ_AFTER_KEY; - range->ptr= (char *) cache->get_curr_key_chain(); - range->range_flag= EQ_RANGE; - DBUG_RETURN(0); - } - DBUG_RETURN(1); +DBUG_ENTER("bkah_range_seq_next"); +JOIN_CACHE_BKAH *cache= (JOIN_CACHE_BKAH *) rseq; +TABLE_REF *ref= &cache->join_tab->ref; +key_range *start_key= &range->start_key; +if ((start_key->length= cache->get_next_key((uchar **) &start_key->key))) +{ + start_key->keypart_map= (1 << ref->key_parts) - 1; + start_key->flag= HA_READ_KEY_EXACT; + range->end_key= *start_key; + range->end_key.flag= HA_READ_AFTER_KEY; + range->ptr= (char *) cache->get_curr_key_chain(); + range->range_flag= EQ_RANGE; + DBUG_RETURN(0); +} +DBUG_RETURN(1); } /* - Check whether range_info orders to skip the next record from BKAH join buffer +Check whether range_info orders to skip the next record from BKAH join buffer - SYNOPSIS - bkah_range_seq_skip_record() - seq value returned by bkah_range_seq_init() - range_info information about the next range/key returned by MRR - rowid [NOT USED] rowid of the record to be checked (not used) - - DESCRIPTION - The function interprets seq as a pointer to a JOIN_CACHE_BKAH - object. The function returns TRUE if the record with this range_info - is to be filtered out from the stream of records returned by - multi_range_read_next(). - - NOTE - This function are used only as a callback function. - - RETURN VALUE - 1 record with this range_info is to be filtered out from the stream - of records returned by multi_range_read_next() - 0 the record is to be left in the stream +SYNOPSIS + bkah_range_seq_skip_record() + seq value returned by bkah_range_seq_init() + range_info information about the next range/key returned by MRR + rowid [NOT USED] rowid of the record to be checked (not used) + +DESCRIPTION + The function interprets seq as a pointer to a JOIN_CACHE_BKAH + object. The function returns TRUE if the record with this range_info + is to be filtered out from the stream of records returned by + multi_range_read_next(). + +NOTE + This function are used only as a callback function. + +RETURN VALUE + 1 record with this range_info is to be filtered out from the stream + of records returned by multi_range_read_next() + 0 the record is to be left in the stream */ static bool bkah_range_seq_skip_record(range_seq_t rseq, range_id_t range_info, - uchar *rowid) + uchar *rowid) { - DBUG_ENTER("bkah_range_seq_skip_record"); - JOIN_CACHE_BKAH *cache= (JOIN_CACHE_BKAH *) rseq; - bool res= cache->check_all_match_flags_for_key((uchar *) range_info); - DBUG_RETURN(res); +DBUG_ENTER("bkah_range_seq_skip_record"); +JOIN_CACHE_BKAH *cache= (JOIN_CACHE_BKAH *) rseq; +bool res= cache->check_all_match_flags_for_key((uchar *) range_info); +DBUG_RETURN(res); } - + /* - Check if the record combination from BKAH cache matches the index condition +Check if the record combination from BKAH cache matches the index condition - SYNOPSIS - bkah_skip_index_tuple() - rseq value returned by bka_range_seq_init() - range_info record chain for the next range/key returned by MRR - - DESCRIPTION - This is wrapper for JOIN_CACHE_BKA_UNIQUE::skip_index_tuple method, - see comments there. +SYNOPSIS + bkah_skip_index_tuple() + rseq value returned by bka_range_seq_init() + range_info record chain for the next range/key returned by MRR + +DESCRIPTION + This is wrapper for JOIN_CACHE_BKA_UNIQUE::skip_index_tuple method, + see comments there. - NOTE - This function is used as a RANGE_SEQ_IF::skip_index_tuple callback. - - RETURN VALUE - 0 some records from the chain satisfy the index condition - 1 otherwise +NOTE + This function is used as a RANGE_SEQ_IF::skip_index_tuple callback. + +RETURN VALUE + 0 some records from the chain satisfy the index condition + 1 otherwise */ static bool bkah_skip_index_tuple(range_seq_t rseq, range_id_t range_info) { - DBUG_ENTER("bka_unique_skip_index_tuple"); - JOIN_CACHE_BKAH *cache= (JOIN_CACHE_BKAH *) rseq; - THD *thd= cache->thd(); - bool res; - status_var_increment(thd->status_var.ha_icp_attempts); - if (!(res= cache->skip_index_tuple(range_info))) - status_var_increment(thd->status_var.ha_icp_match); - DBUG_RETURN(res); +DBUG_ENTER("bka_unique_skip_index_tuple"); +JOIN_CACHE_BKAH *cache= (JOIN_CACHE_BKAH *) rseq; +THD *thd= cache->thd(); +bool res; +status_var_increment(thd->status_var.ha_icp_attempts); +if (!(res= cache->skip_index_tuple(range_info))) + status_var_increment(thd->status_var.ha_icp_match); +DBUG_RETURN(res); } /* - Prepare to read record from BKAH cache matching the current joined record - - SYNOPSIS - prepare_look_for_matches() - skip_last <-> ignore the last record in the buffer (always unused here) - - DESCRIPTION - The function prepares to iterate over records in the join cache buffer - matching the record loaded into the record buffer for join_tab when - performing join operation by BKAH join algorithm. With BKAH algorithm, if - association labels are used, then record loaded into the record buffer - for join_tab always has a direct reference to the chain of the mathing - records from the join buffer. If association labels are not used then - then the chain of the matching records is obtained by the call of the - get_key_chain_by_join_key function. - - RETURN VALUE - TRUE there are no records in the buffer to iterate over - FALSE otherwise +Prepare to read record from BKAH cache matching the current joined record + +SYNOPSIS + prepare_look_for_matches() + skip_last <-> ignore the last record in the buffer (always unused here) + +DESCRIPTION + The function prepares to iterate over records in the join cache buffer + matching the record loaded into the record buffer for join_tab when + performing join operation by BKAH join algorithm. With BKAH algorithm, if + association labels are used, then record loaded into the record buffer + for join_tab always has a direct reference to the chain of the mathing + records from the join buffer. If association labels are not used then + then the chain of the matching records is obtained by the call of the + get_key_chain_by_join_key function. + +RETURN VALUE + TRUE there are no records in the buffer to iterate over + FALSE otherwise */ - + bool JOIN_CACHE_BKAH::prepare_look_for_matches(bool skip_last) { - last_matching_rec_ref_ptr= next_matching_rec_ref_ptr= 0; - if (no_association && - !(curr_matching_chain= get_matching_chain_by_join_key())) +last_matching_rec_ref_ptr= next_matching_rec_ref_ptr= 0; +if (no_association && + !(curr_matching_chain= get_matching_chain_by_join_key())) //psergey: added '!' return 1; last_matching_rec_ref_ptr= get_next_rec_ref(curr_matching_chain); return 0; diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 89536b93feb..126139cd219 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -768,79 +768,6 @@ String *copy_if_not_alloced(String *to,String *from,uint32 from_length) Help functions ****************************************************************************/ -/* - copy a string from one character set to another - - SYNOPSIS - copy_and_convert() - to Store result here - to_cs Character set of result string - from Copy from here - from_length Length of from string - from_cs From character set - - NOTES - 'to' must be big enough as form_length * to_cs->mbmaxlen - - RETURN - length of bytes copied to 'to' -*/ - - -static uint32 -copy_and_convert_extended(char *to, uint32 to_length, CHARSET_INFO *to_cs, - const char *from, uint32 from_length, - CHARSET_INFO *from_cs, - uint *errors) -{ - int cnvres; - my_wc_t wc; - const uchar *from_end= (const uchar*) from+from_length; - char *to_start= to; - uchar *to_end= (uchar*) to+to_length; - my_charset_conv_mb_wc mb_wc= from_cs->cset->mb_wc; - my_charset_conv_wc_mb wc_mb= to_cs->cset->wc_mb; - uint error_count= 0; - - while (1) - { - if ((cnvres= (*mb_wc)(from_cs, &wc, (uchar*) from, - from_end)) > 0) - from+= cnvres; - else if (cnvres == MY_CS_ILSEQ) - { - error_count++; - from++; - wc= '?'; - } - else if (cnvres > MY_CS_TOOSMALL) - { - /* - A correct multibyte sequence detected - But it doesn't have Unicode mapping. - */ - error_count++; - from+= (-cnvres); - wc= '?'; - } - else - break; // Not enough characters - -outp: - if ((cnvres= (*wc_mb)(to_cs, wc, (uchar*) to, to_end)) > 0) - to+= cnvres; - else if (cnvres == MY_CS_ILUNI && wc != '?') - { - error_count++; - wc= '?'; - goto outp; - } - else - break; - } - *errors= error_count; - return (uint32) (to - to_start); -} /* diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 0d7d0dee1da..1fd6f5150cb 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -883,11 +883,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token COLLATION_SYM /* SQL-2003-N */ %token COLUMNS %token COLUMN_ADD_SYM +%token COLUMN_CHECK_SYM %token COLUMN_CREATE_SYM %token COLUMN_DELETE_SYM -%token COLUMN_EXISTS_SYM %token COLUMN_GET_SYM -%token COLUMN_LIST_SYM %token COLUMN_SYM /* SQL-2003-R */ %token COLUMN_NAME_SYM /* SQL-2003-N */ %token COMMENT_SYM @@ -8403,7 +8402,7 @@ dyncall_create_element: alloc_root(YYTHD->mem_root, sizeof(DYNCALL_CREATE_DEF)); if ($$ == NULL) MYSQL_YYABORT; - $$->num= $1; + $$->key= $1; $$->value= $3; $$->type= (DYNAMIC_COLUMN_TYPE)$4; $$->cs= lex->charset; @@ -8957,16 +8956,9 @@ function_call_nonkeyword: MYSQL_YYABORT; } | - COLUMN_EXISTS_SYM '(' expr ',' expr ')' + COLUMN_CHECK_SYM '(' expr ')' { - $$= new (YYTHD->mem_root) Item_func_dyncol_exists($3, $5); - if ($$ == NULL) - MYSQL_YYABORT; - } - | - COLUMN_LIST_SYM '(' expr ')' - { - $$= new (YYTHD->mem_root) Item_func_dyncol_list($3); + $$= new (YYTHD->mem_root) Item_func_dyncol_check($3); if ($$ == NULL) MYSQL_YYABORT; } @@ -13114,11 +13106,10 @@ keyword: | CHECKPOINT_SYM {} | CLOSE_SYM {} | COLUMN_ADD_SYM {} + | COLUMN_CHECK_SYM {} | COLUMN_CREATE_SYM {} | COLUMN_DELETE_SYM {} - | COLUMN_EXISTS_SYM {} | COLUMN_GET_SYM {} - | COLUMN_LIST_SYM {} | COMMENT_SYM {} | COMMIT_SYM {} | CONTAINS_SYM {} |