diff options
author | Sergei Golubchik <sergii@pisem.net> | 2010-04-08 14:10:05 +0200 |
---|---|---|
committer | Sergei Golubchik <sergii@pisem.net> | 2010-04-08 14:10:05 +0200 |
commit | e24e1668bc112afe4b4f6b3dc4d5b8d10635f60b (patch) | |
tree | 9c586042547cc441792db76369ef92eaa7deb47c /sql/create_options.cc | |
parent | 8e122db92d0a60670e421903c32213524cd54fdf (diff) | |
download | mariadb-git-e24e1668bc112afe4b4f6b3dc4d5b8d10635f60b.tar.gz |
MWL#43 CREATE TABLE options (by Sanja)
Docs/sp-imp-spec.txt:
New sql_mode added.
include/my_base.h:
Flag in frm of create options.
libmysqld/CMakeLists.txt:
New files added.
libmysqld/Makefile.am:
New files added.
mysql-test/r/events_bugs.result:
New sql_mode added.
mysql-test/r/information_schema.result:
New sql_mode added.
mysql-test/r/sp.result:
New sql_mode added.
mysql-test/r/system_mysql_db.result:
New sql_mode added.
mysql-test/suite/funcs_1/r/is_columns_mysql.result:
New sql_mode added.
mysql-test/suite/funcs_1/r/is_columns_mysql_embedded.result:
New sql_mode added.
mysql-test/t/events_bugs.test:
New sql_mode added.
mysql-test/t/sp.test:
New sql_mode added.
scripts/mysql_system_tables.sql:
New sql_mode added.
scripts/mysql_system_tables_fix.sql:
New sql_mode added.
sql/CMakeLists.txt:
New files added.
sql/Makefile.am:
New files added.
sql/event_db_repository.cc:
New sql_mode added.
sql/field.cc:
Create options support added.
sql/field.h:
Create options support added.
sql/ha_partition.cc:
Create options support added.
sql/handler.cc:
Create options support added.
sql/handler.h:
Create options support added.
sql/log_event.h:
New sql_mode added.
sql/mysql_priv.h:
New sql_mode added.
sql/mysqld.cc:
New sql_mode added.
sql/share/errmsg.txt:
New error messages added.
sql/sp.cc:
New sql_mode added.
sql/sp_head.cc:
Create options support added.
sql/sql_class.cc:
Create options support added.
Debug added.
sql/sql_class.h:
Create options support added.
sql/sql_insert.cc:
my_safe_a* moved to mysqld_priv.h
sql/sql_lex.h:
Create options support added.
sql/sql_parse.cc:
Create options support added.
sql/sql_show.cc:
Create options support added.
sql/sql_table.cc:
Create options support added.
sql/sql_view.cc:
New sql_mode added.
sql/sql_yacc.yy:
Create options support added.
sql/structs.h:
Create options support added.
sql/table.cc:
Create options support added.
sql/table.h:
Create options support added.
sql/unireg.cc:
Create options support added.
storage/example/ha_example.cc:
Create options example.
storage/example/ha_example.h:
Create options example.
storage/pbxt/src/discover_xt.cc:
Create options support added.
Diffstat (limited to 'sql/create_options.cc')
-rw-r--r-- | sql/create_options.cc | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/sql/create_options.cc b/sql/create_options.cc new file mode 100644 index 00000000000..f4868f95330 --- /dev/null +++ b/sql/create_options.cc @@ -0,0 +1,612 @@ +/* Copyright (C) 2010 Monty Program Ab + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file + + Engine defined options of tables/fields/keys in CREATE/ALTER TABLE. +*/ + +#include "mysql_priv.h" +#include "create_options.h" +#include <my_getopt.h> + +#define FRM_QUOTED_VALUE 0x8000 + +/** + Links this item to the given list end + + @param start The list beginning or NULL + @param end The list last element or does not matter +*/ + +void engine_option_value::link(engine_option_value **start, + engine_option_value **end) +{ + DBUG_ENTER("engine_option_value::link"); + DBUG_PRINT("enter", ("name: '%s' (%u) value: '%s' (%u)", + name.str, (uint) name.length, + value.str, (uint) value.length)); + engine_option_value *opt; + /* check duplicates to avoid writing them to frm*/ + for(opt= *start; + opt && ((opt->parsed && !opt->value.str) || + my_strnncoll(system_charset_info, + (uchar *)name.str, name.length, + (uchar*)opt->name.str, opt->name.length)); + opt= opt->next) /* no-op */; + if (opt) + { + opt->value.str= NULL; /* remove previous value */ + opt->parsed= TRUE; /* and don't issue warnings for it anymore */ + } + /* + Add this option to the end of the list + + @note: We add even if it is opt->value.str == NULL because it can be + ALTER TABLE to remove the option. + */ + if (*start) + { + (*end)->next= this; + *end= this; + } + else + { + /* + note that is *start == 0, the value of *end does not matter, + it can be uninitialized. + */ + *start= *end= this; + } + DBUG_VOID_RETURN; +} + +static bool report_wrong_value(THD *thd, const char *name, const char *val, + my_bool suppress_warning) +{ + if (!(thd->variables.sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS)) + { + my_error(ER_BAD_OPTION_VALUE, MYF(0), val, name); + return 1; + } + + /* + We may need to suppress warnings to avoid duplicate messages + about the same option (option list is parsed more than once during + CREATE/ALTER table). + Errors are not suppressed, as they abort the execution on the first parsing. + */ + if (!suppress_warning) + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_BAD_OPTION_VALUE, + ER(ER_BAD_OPTION_VALUE), val, name); + return 0; +} + +static bool report_unknown_option(THD *thd, engine_option_value *val, + my_bool suppress_warning) +{ + DBUG_ENTER("report_unknown_option"); + if (val->parsed) + { + DBUG_PRINT("info", ("parsed => exiting")); + DBUG_RETURN(FALSE); + } + + if (!(thd->variables.sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS)) + { + my_error(ER_UNKNOWN_OPTION, MYF(0), val->name.str); + DBUG_RETURN(TRUE); + } + + if (!suppress_warning) + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_UNKNOWN_OPTION, + ER(ER_UNKNOWN_OPTION), + val->name.str); + DBUG_RETURN(FALSE); +} + +static bool set_one_value(ha_create_table_option *opt, + THD *thd, LEX_STRING *value, void *base, + my_bool suppress_warning, + MEM_ROOT *root) +{ + DBUG_ENTER("set_one_value"); + DBUG_PRINT("enter", ("opt: 0x%lx type: %u name '%s' value: '%s'", + (ulong) opt, + opt->type, opt->name, + (value->str ? value->str : "<DEFAULT>"))); + switch (opt->type) + { + case HA_OPTION_TYPE_ULL: + { + ulonglong *val= (ulonglong*)((char*)base + opt->offset); + if (!value->str) + { + *val= opt->def_value; + DBUG_RETURN(0); + } + + my_option optp= { opt->name, 1, 0, (uchar **)val, 0, 0, GET_ULL, + REQUIRED_ARG, opt->def_value, opt->min_value, opt->max_value, + 0, opt->block_size, 0}; + + ulonglong orig_val= strtoull(value->str, NULL, 10); + my_bool unused; + *val= orig_val; + *val= getopt_ull_limit_value(*val, &optp, &unused); + if (*val == orig_val) + DBUG_RETURN(0); + + DBUG_RETURN(report_wrong_value(thd, opt->name, value->str, + suppress_warning)); + } + case HA_OPTION_TYPE_STRING: + { + char **val= (char **)((char *)base + opt->offset); + if (!value->str) + { + *val= 0; + DBUG_RETURN(0); + } + + if (!(*val= strmake_root(root, value->str, value->length))) + DBUG_RETURN(1); + DBUG_RETURN(0); + } + case HA_OPTION_TYPE_ENUM: + { + uint *val= (uint *)((char *)base + opt->offset), num; + + *val= opt->def_value; + if (!value->str) + DBUG_RETURN(0); + + const char *start= opt->values, *end; + + num= 0; + while (*start) + { + for (end=start; + *end && *end != ','; + end+= my_mbcharlen(system_charset_info, *end)) /* no-op */; + if (!my_strnncoll(system_charset_info, + (uchar*)start, end-start, + (uchar*)value->str, value->length)) + { + *val= num; + DBUG_RETURN(0); + } + if (*end) *end++; + start= end; + num++; + } + + DBUG_RETURN(report_wrong_value(thd, opt->name, value->str, + suppress_warning)); + } + case HA_OPTION_TYPE_BOOL: + { + bool *val= (bool *)((char *)base + opt->offset); + *val= opt->def_value; + + if (!value->str) + DBUG_RETURN(0); + + if (!my_strnncoll(system_charset_info, + (const uchar*)"NO", 2, + (uchar *)value->str, value->length) || + !my_strnncoll(system_charset_info, + (const uchar*)"OFF", 3, + (uchar *)value->str, value->length) || + !my_strnncoll(system_charset_info, + (const uchar*)"0", 1, + (uchar *)value->str, value->length)) + { + *val= FALSE; + DBUG_RETURN(FALSE); + } + + if (!my_strnncoll(system_charset_info, + (const uchar*)"YES", 3, + (uchar *)value->str, value->length) || + !my_strnncoll(system_charset_info, + (const uchar*)"ON", 2, + (uchar *)value->str, value->length) || + !my_strnncoll(system_charset_info, + (const uchar*)"1", 1, + (uchar *)value->str, value->length)) + { + *val= TRUE; + DBUG_RETURN(FALSE); + } + + DBUG_RETURN(report_wrong_value(thd, opt->name, value->str, + suppress_warning)); + } + } + DBUG_ASSERT(0); + my_error(ER_UNKNOWN_ERROR, MYF(0)); + DBUG_RETURN(1); +} + +static const size_t ha_option_type_sizeof[]= +{ sizeof(ulonglong), sizeof(char *), sizeof(uint), sizeof(bool)}; + +/** + Creates option structure and parses list of options in it + + @param thd thread handler + @param option_struct where to store pointer on the option struct + @param option_list list of options given by user + @param rules list of option description by engine + @param suppress_warning second parse so we do not need warnings + @param root MEM_ROOT where allocate memory + + @retval TRUE Error + @retval FALSE OK +*/ + +my_bool parse_option_list(THD* thd, void **option_struct, + engine_option_value *option_list, + ha_create_table_option *rules, + my_bool suppress_warning, + MEM_ROOT *root) +{ + ha_create_table_option *opt; + size_t option_struct_size= 0; + engine_option_value *val= option_list; + DBUG_ENTER("parse_option_list"); + DBUG_PRINT("enter", + ("struct: 0x%lx list: 0x%lx rules: 0x%lx suppres %u root 0x%lx", + (ulong) *option_struct, (ulong)option_list, (ulong)rules, + (uint) suppress_warning, (ulong) root)); + + if (rules) + { + LEX_STRING default_val= {NULL, 0}; + for (opt= rules; opt->name; opt++) + set_if_bigger(option_struct_size, opt->offset + + ha_option_type_sizeof[opt->type]); + + *option_struct= alloc_root(root, option_struct_size); + + /* set all values to default */ + for (opt= rules; opt->name; opt++) + set_one_value(opt, thd, &default_val, *option_struct, + suppress_warning, root); + } + + for (; val; val= val->next) + { + for (opt= rules; opt && opt->name; opt++) + { + if (my_strnncoll(system_charset_info, + (uchar*)opt->name, opt->name_length, + (uchar*)val->name.str, val->name.length)) + continue; + + if (set_one_value(opt, thd, &val->value, + *option_struct, suppress_warning || val->parsed, root)) + DBUG_RETURN(TRUE); + val->parsed= true; + break; + } + if (report_unknown_option(thd, val, suppress_warning)) + DBUG_RETURN(TRUE); + val->parsed= true; + } + + DBUG_RETURN(FALSE); +} + + +/** + Parses all table/fields/keys options + + @param thd thread handler + @param file handler of the table + @parem share descriptor of the table + + @retval TRUE Error + @retval FALSE OK +*/ + +my_bool parse_engine_table_options(THD *thd, handlerton *ht, + TABLE_SHARE *share) +{ + MEM_ROOT *root= &share->mem_root; + DBUG_ENTER("parse_engine_table_options"); + + if (parse_option_list(thd, &share->option_struct, share->option_list, + ht->table_options, TRUE, root)) + DBUG_RETURN(TRUE); + + for (Field **field= share->field; *field; field++) + { + if (parse_option_list(thd, &(*field)->option_struct, (*field)->option_list, + ht->field_options, TRUE, root)) + DBUG_RETURN(TRUE); + } + + for (uint index= 0; index < share->keys; index ++) + { + if (parse_option_list(thd, &share->key_info[index].option_struct, + share->key_info[index].option_list, + ht->index_options, TRUE, root)) + DBUG_RETURN(TRUE); + } + + DBUG_RETURN(FALSE); +} + + +/** + Returns representation length of key and value in the frm file +*/ + +uint engine_option_value::frm_length() +{ + /* + 1 byte - name length + 2 bytes - value length + + if value.str is NULL, this option is not written to frm (=DEFAULT) + */ + return value.str ? 1 + name.length + 2 + value.length : 0; +} + + +/** + Returns length of representation of option list in the frm file +*/ + +static uint option_list_frm_length(engine_option_value *opt) +{ + uint res= 0; + + for (; opt; opt= opt->next) + res+= opt->frm_length(); + + return res; +} + + +/** + Calculates length of options image in the .frm + + @param table_option_list list of table options + @param create_fields field descriptors list + @param keys number of keys + @param key_info array of key descriptors + + @returns length of image in frm +*/ + +uint engine_table_options_frm_length(engine_option_value *table_option_list, + List<Create_field> &create_fields, + uint keys, KEY *key_info) +{ + List_iterator<Create_field> it(create_fields); + Create_field *field; + uint res, index; + DBUG_ENTER("engine_table_options_frm_length"); + + res= option_list_frm_length(table_option_list); + + while ((field= it++)) + res+= option_list_frm_length(field->option_list); + + for (index= 0; index < keys; index++, key_info++) + res+= option_list_frm_length(key_info->option_list); + + /* + if there's at least one option somewhere (res > 0) + we write option lists for all fields and keys, zero-terminated. + If there're no options we write nothing at all (backward compatibility) + */ + DBUG_RETURN(res ? res + 1 + create_fields.elements + keys : 0); +} + + +/** + Writes image of the key and value to the frm image buffer + + @param buff pointer to the buffer free space beginning + + @returns pointer to byte after last recorded in the buffer +*/ + +uchar *engine_option_value::frm_image(uchar *buff) +{ + if (value.str) + { + *buff++= name.length; + memcpy(buff, name.str, name.length); + buff+= name.length; + int2store(buff, value.length | (quoted_value ? FRM_QUOTED_VALUE : 0)); + buff+= 2; + memcpy(buff, (const uchar *) value.str, value.length); + buff+= value.length; + } + return buff; +} + +/** + Writes image of the key and value to the frm image buffer + + @param buff pointer to the buffer to store the options in + @param opt list of options; + + @returns pointer to the end of the stored data in the buffer +*/ +static uchar *option_list_frm_image(uchar *buff, engine_option_value *opt) +{ + for (; opt; opt= opt->next) + buff= opt->frm_image(buff); + + *buff++= 0; + return buff; +} + + +/** + Writes options image in the .frm buffer + + @param buff pointer to the buffer + @param table_option_list list of table options + @param create_fields field descriptors list + @param keys number of keys + @param key_info array of key descriptors + + @returns pointer to byte after last recorded in the buffer +*/ + +uchar *engine_table_options_frm_image(uchar *buff, + engine_option_value *table_option_list, + List<Create_field> &create_fields, + uint keys, KEY *key_info) +{ + List_iterator<Create_field> it(create_fields); + Create_field *field; + KEY *key_info_end= key_info + keys; + DBUG_ENTER("engine_table_options_frm_image"); + + buff= option_list_frm_image(buff, table_option_list); + + while ((field= it++)) + buff= option_list_frm_image(buff, field->option_list); + + while (key_info < key_info_end) + buff= option_list_frm_image(buff, (key_info++)->option_list); + + DBUG_RETURN(buff); +} + +/** + Reads name and value from buffer, then link it in the list + + @param buff the buffer to read from + @param start The list beginning or NULL + @param end The list last element or does not matter + @param root MEM_ROOT for allocating + + @returns pointer to byte after last recorded in the buffer +*/ +uchar *engine_option_value::frm_read(const uchar *buff, engine_option_value **start, + engine_option_value **end, MEM_ROOT *root) +{ + LEX_STRING name, value; + uint len; + + name.length= buff[0]; + buff++; + if (!(name.str= strmake_root(root, (const char*)buff, name.length))) + return NULL; + buff+= name.length; + len= uint2korr(buff); + value.length= len & ~FRM_QUOTED_VALUE; + buff+= 2; + if (!(value.str= strmake_root(root, (const char*)buff, value.length))) + return NULL; + buff+= value.length; + + engine_option_value *ptr=new (root) + engine_option_value(name, value, len & FRM_QUOTED_VALUE, start, end); + if (!ptr) + return NULL; + + return (uchar *)buff; +} + + +/** + Reads options from this buffer + + @param buff the buffer to read from + @param length buffer length + @param share table descriptor + @param root MEM_ROOT for allocating + + @retval TRUE Error + @retval FALSE OK +*/ + +my_bool engine_table_options_frm_read(const uchar *buff, uint length, + TABLE_SHARE *share) +{ + const uchar *buff_end= buff + length; + engine_option_value *end; + MEM_ROOT *root= &share->mem_root; + uint count; + DBUG_ENTER("engine_table_options_frm_read"); + + while (buff < buff_end && *buff) + { + if (!(buff= engine_option_value::frm_read(buff, &share->option_list, &end, + root))) + DBUG_RETURN(TRUE); + } + buff++; + + for (count=0; count < share->fields; count++) + { + while (buff < buff_end && *buff) + { + if (!(buff= engine_option_value::frm_read(buff, + &share->field[count]->option_list, + &end, root))) + DBUG_RETURN(TRUE); + } + buff++; + } + + for (count=0; count < share->keys; count++) + { + while (buff < buff_end && *buff) + { + if (!(buff= engine_option_value::frm_read(buff, + &share->key_info[count].option_list, + &end, root))) + DBUG_RETURN(TRUE); + } + buff++; + } + + DBUG_RETURN(buff != buff_end); +} + +/** + Merges two lists of engine_option_value's with duplicate removal. +*/ + +engine_option_value *merge_engine_table_options(engine_option_value *first, + engine_option_value *second, + MEM_ROOT *root) +{ + engine_option_value *end, *opt; + DBUG_ENTER("merge_engine_table_options"); + + /* find last element */ + if (first && second) + for (end= first; end->next; end= end->next) /* no-op */; + + for (opt= second; opt; opt= opt->next) + new (root) engine_option_value(opt->name, opt->value, opt->quoted_value, + &first, &end); + DBUG_RETURN(first); +} |