diff options
Diffstat (limited to 'sql')
91 files changed, 4260 insertions, 2088 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt new file mode 100755 index 00000000000..b01871872ce --- /dev/null +++ b/sql/CMakeLists.txt @@ -0,0 +1,114 @@ +SET(CMAKE_CXX_FLAGS_DEBUG + "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX -DUSE_SYMDIR /Zi") +SET(CMAKE_C_FLAGS_DEBUG + "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX -DUSE_SYMDIR /Zi") +SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /MAP /MAPINFO:EXPORTS") + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/extra/yassl/include + ${CMAKE_SOURCE_DIR}/sql + ${CMAKE_SOURCE_DIR}/regex + ${CMAKE_SOURCE_DIR}/zlib + ${CMAKE_SOURCE_DIR}/bdb/build_win32 + ${CMAKE_SOURCE_DIR}/bdb/dbinc) + +SET_SOURCE_FILES_PROPERTIES(${CMAKE_SOURCE_DIR}/sql/message.rc + ${CMAKE_SOURCE_DIR}/sql/message.h + ${CMAKE_SOURCE_DIR}/sql/sql_yacc.h + ${CMAKE_SOURCE_DIR}/sql/sql_yacc.cc + ${CMAKE_SOURCE_DIR}/include/mysql_version.h + ${CMAKE_SOURCE_DIR}/sql/lex_hash.h + ${PROJECT_SOURCE_DIR}/include/mysqld_error.h + ${PROJECT_SOURCE_DIR}/include/mysqld_ername.h + ${PROJECT_SOURCE_DIR}/include/sql_state.h + PROPERTIES GENERATED 1) + +ADD_DEFINITIONS(-DHAVE_INNOBASE -DMYSQL_SERVER + -D_CONSOLE -DHAVE_DLOPEN) + +ADD_EXECUTABLE(mysqld ../sql-common/client.c derror.cc des_key_file.cc + discover.cc ../libmysql/errmsg.c field.cc field_conv.cc + filesort.cc gstream.cc ha_blackhole.cc + ha_archive.cc ha_heap.cc ha_myisam.cc ha_myisammrg.cc + ha_innodb.cc ha_federated.cc ha_berkeley.cc ha_blackhole.cc + handler.cc hash_filo.cc hash_filo.h + hostname.cc init.cc item.cc item_buff.cc item_cmpfunc.cc + item_create.cc item_func.cc item_geofunc.cc item_row.cc + item_strfunc.cc item_subselect.cc item_sum.cc item_timefunc.cc + item_uniq.cc key.cc log.cc lock.cc log_event.cc message.rc + message.h mf_iocache.cc my_decimal.cc ../sql-common/my_time.c + ../myisammrg/myrg_rnext_same.c mysqld.cc net_serv.cc + nt_servc.cc nt_servc.h opt_range.cc opt_range.h opt_sum.cc + ../sql-common/pack.c parse_file.cc password.c procedure.cc + protocol.cc records.cc repl_failsafe.cc set_var.cc + slave.cc sp.cc sp_cache.cc sp_head.cc sp_pcontext.cc + sp_rcontext.cc spatial.cc sql_acl.cc sql_analyse.cc sql_base.cc + sql_cache.cc sql_class.cc sql_client.cc sql_crypt.cc sql_crypt.h + sql_cursor.cc sql_db.cc sql_delete.cc sql_derived.cc sql_do.cc + sql_error.cc sql_handler.cc sql_help.cc sql_insert.cc sql_lex.cc + sql_list.cc sql_load.cc sql_manager.cc sql_map.cc sql_parse.cc + sql_prepare.cc sql_rename.cc + sql_repl.cc sql_select.cc sql_show.cc sql_state.c sql_string.cc + sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc + sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc + time.cc tztime.cc uniques.cc unireg.cc + ../sql-common/my_user.c + sql_locale.cc + ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc + ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h + ${PROJECT_SOURCE_DIR}/include/mysqld_error.h + ${PROJECT_SOURCE_DIR}/include/mysqld_ername.h + ${PROJECT_SOURCE_DIR}/include/sql_state.h + ${PROJECT_SOURCE_DIR}/include/mysql_version.h + ${PROJECT_SOURCE_DIR}/sql/lex_hash.h) + +TARGET_LINK_LIBRARIES(mysqld heap myisam myisammrg mysys yassl zlib dbug yassl + taocrypt strings vio regex wsock32) + +IF(WITH_EXAMPLE_STORAGE_ENGINE) + TARGET_LINK_LIBRARIES(mysqld example) +ENDIF(WITH_EXAMPLE_STORAGE_ENGINE) + +IF(WITH_INNOBASE_STORAGE_ENGINE) + TARGET_LINK_LIBRARIES(mysqld innobase) +ENDIF(WITH_INNOBASE_STORAGE_ENGINE) + +IF(WITH_BERKELEY_STORAGE_ENGINE) + TARGET_LINK_LIBRARIES(mysqld bdb) +ENDIF(WITH_BERKELEY_STORAGE_ENGINE) + + +ADD_DEPENDENCIES(mysqld GenError) + +# Sql Parser custom command +ADD_CUSTOM_COMMAND( + SOURCE ${PROJECT_SOURCE_DIR}/sql/sql_yacc.yy + OUTPUT ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc + COMMAND bison.exe ARGS -y -p MYSQL --defines=sql_yacc.h + --output=sql_yacc.cc sql_yacc.yy + DEPENDS ${PROJECT_SOURCE_DIR}/sql/sql_yacc.yy) + +ADD_CUSTOM_COMMAND( + OUTPUT ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h + COMMAND echo + DEPENDS ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc +) + +# Windows message file +ADD_CUSTOM_COMMAND( + SOURCE ${PROJECT_SOURCE_DIR}/sql/message.mc + OUTPUT message.rc message.h + COMMAND mc ARGS ${PROJECT_SOURCE_DIR}/sql/message.mc + DEPENDS ${PROJECT_SOURCE_DIR}/sql/message.mc) + +# Gen_lex_hash +ADD_EXECUTABLE(gen_lex_hash gen_lex_hash.cc) +TARGET_LINK_LIBRARIES(gen_lex_hash dbug mysqlclient wsock32) +GET_TARGET_PROPERTY(GEN_LEX_HASH_EXE gen_lex_hash LOCATION) +ADD_CUSTOM_COMMAND( + OUTPUT ${PROJECT_SOURCE_DIR}/sql/lex_hash.h + COMMAND ${GEN_LEX_HASH_EXE} ARGS > lex_hash.h + DEPENDS ${GEN_LEX_HASH_EXE} +) + +ADD_DEPENDENCIES(mysqld gen_lex_hash) diff --git a/sql/Makefile.am b/sql/Makefile.am index 416f0faf1a6..98c8fe784eb 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -116,9 +116,11 @@ DEFS = -DMYSQL_SERVER \ @DEFS@ BUILT_SOURCES = sql_yacc.cc sql_yacc.h lex_hash.h -EXTRA_DIST = udf_example.cc $(BUILT_SOURCES) -DISTCLEANFILES = lex_hash.h -AM_YFLAGS = -d +EXTRA_DIST = $(BUILT_SOURCES) nt_servc.cc nt_servc.h \ + message.mc examples/CMakeLists.txt CMakeLists.txt +DISTCLEANFILES = lex_hash.h sql_yacc.output + +AM_YFLAGS = -d --debug --verbose mysql_tzinfo_to_sql.cc: rm -f mysql_tzinfo_to_sql.cc @@ -155,7 +157,7 @@ lex_hash.h: gen_lex_hash$(EXEEXT) # For testing of udf_example.so noinst_LTLIBRARIES= udf_example.la -udf_example_la_SOURCES= udf_example.cc +udf_example_la_SOURCES= udf_example.c udf_example_la_LDFLAGS= -module -rpath $(pkglibdir) diff --git a/sql/examples/CMakeLists.txt b/sql/examples/CMakeLists.txt new file mode 100755 index 00000000000..d3cc430ef40 --- /dev/null +++ b/sql/examples/CMakeLists.txt @@ -0,0 +1,11 @@ +SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") +SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/sql + ${CMAKE_SOURCE_DIR}/extra/yassl/include + ${CMAKE_SOURCE_DIR}/regex) + +IF(WITH_EXAMPLE_STORAGE_ENGINE) +ADD_LIBRARY(example ha_example.cc) +ADD_DEPENDENCIES(example GenError) +ENDIF(WITH_EXAMPLE_STORAGE_ENGINE) diff --git a/sql/examples/ha_tina.cc b/sql/examples/ha_tina.cc index 524ce5eb693..f727cefc6d0 100644 --- a/sql/examples/ha_tina.cc +++ b/sql/examples/ha_tina.cc @@ -205,16 +205,18 @@ static TINA_SHARE *get_share(const char *table_name, TABLE *table) share->table_name_length=length; share->table_name=tmp_name; strmov(share->table_name,table_name); - fn_format(data_file_name, table_name, "", ".CSV",MY_REPLACE_EXT|MY_UNPACK_FILENAME); + fn_format(data_file_name, table_name, "", ".CSV", + MY_REPLACE_EXT | MY_UNPACK_FILENAME); + + if ((share->data_file= my_open(data_file_name, O_RDWR|O_APPEND, + MYF(0))) == -1) + goto error; + if (my_hash_insert(&tina_open_tables, (byte*) share)) goto error; thr_lock_init(&share->lock); pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST); - if ((share->data_file= my_open(data_file_name, O_RDWR|O_APPEND, - MYF(0))) == -1) - goto error2; - /* We only use share->data_file for writing, so we scan to the end to append */ if (my_seek(share->data_file, 0, SEEK_END, MYF(0)) == MY_FILEPOS_ERROR) goto error2; @@ -233,6 +235,7 @@ error3: error2: thr_lock_delete(&share->lock); pthread_mutex_destroy(&share->mutex); + hash_delete(&tina_open_tables, (byte*) share); error: pthread_mutex_unlock(&tina_mutex); my_free((gptr) share, MYF(0)); diff --git a/sql/field.cc b/sql/field.cc index 2cf4a2f83af..4860f6ea3da 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4387,6 +4387,24 @@ Field_timestamp::Field_timestamp(char *ptr_arg, uint32 len_arg, } +Field_timestamp::Field_timestamp(bool maybe_null_arg, + const char *field_name_arg, + struct st_table *table_arg, CHARSET_INFO *cs) + :Field_str((char*) 0, 19, maybe_null_arg ? (uchar*) "": 0, 0, + NONE, field_name_arg, table_arg, cs) +{ + /* For 4.0 MYD and 4.0 InnoDB compatibility */ + flags|= ZEROFILL_FLAG | UNSIGNED_FLAG; + if (table && !table->timestamp_field && + unireg_check != NONE) + { + /* This timestamp has auto-update */ + table->timestamp_field= this; + flags|=TIMESTAMP_FLAG; + } +} + + /* Get auto-set type for TIMESTAMP field. @@ -4606,7 +4624,7 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) thd->time_zone_used= 1; temp= time_tmp.year % 100; - if (temp < YY_PART_YEAR) + if (temp < YY_PART_YEAR - 1) { *to++= '2'; *to++= '0'; @@ -6136,15 +6154,26 @@ Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table, Field *new_field; if (type() != MYSQL_TYPE_VAR_STRING || keep_type) - return Field::new_field(root, new_table, keep_type); + new_field= Field::new_field(root, new_table, keep_type); + else + { - /* - Old VARCHAR field which should be modified to a VARCHAR on copy - This is done to ensure that ALTER TABLE will convert old VARCHAR fields - to now VARCHAR fields. - */ - return new Field_varstring(field_length, maybe_null(), - field_name, new_table, charset()); + /* + Old VARCHAR field which should be modified to a VARCHAR on copy + This is done to ensure that ALTER TABLE will convert old VARCHAR fields + to now VARCHAR fields. + */ + new_field= new Field_varstring(field_length, maybe_null(), + field_name, new_table, charset()); + /* + Normally orig_table is different from table only if field was created + via ::new_field. Here we alter the type of field, so ::new_field is + not applicable. But we still need to preserve the original field + metadata for the client-server protocol. + */ + new_field->orig_table= orig_table; + } + return new_field; } /**************************************************************************** @@ -6394,6 +6423,11 @@ void Field_varstring::sql_type(String &res) const } +uint32 Field_varstring::data_length(const char *from) +{ + return length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr); +} + /* Functions to create a packed row. Here the number of length bytes are depending on the given max_length @@ -8265,7 +8299,8 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, comment= *fld_comment; /* - Set flag if this field doesn't have a default value + Set NO_DEFAULT_VALUE_FLAG if this field doesn't have a default value and + it is NOT NULL, not an AUTO_INCREMENT field and not a TIMESTAMP. */ if (!fld_default_value && !(fld_type_modifier & AUTO_INCREMENT_FLAG) && (fld_type_modifier & NOT_NULL_FLAG) && fld_type != FIELD_TYPE_TIMESTAMP) @@ -8342,12 +8377,28 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, /* Allow empty as default value. */ String str,*res; res= fld_default_value->val_str(&str); - if (res->length()) + /* + A default other than '' is always an error, and any non-NULL + specified default is an error in strict mode. + */ + if (res->length() || (thd->variables.sql_mode & + (MODE_STRICT_TRANS_TABLES | + MODE_STRICT_ALL_TABLES))) { my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), fld_name); /* purecov: inspected */ DBUG_RETURN(TRUE); } + else + { + /* + Otherwise a default of '' is just a warning. + */ + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_BLOB_CANT_HAVE_DEFAULT, + ER(ER_BLOB_CANT_HAVE_DEFAULT), + fld_name); + } def= 0; } flags|= BLOB_FLAG; diff --git a/sql/field.h b/sql/field.h index 09638b9a979..65e747e9d2f 100644 --- a/sql/field.h +++ b/sql/field.h @@ -144,6 +144,11 @@ public: table, which is located on disk). */ virtual uint32 pack_length_in_rec() const { return pack_length(); } + + /* + data_length() return the "real size" of the data in memory. + */ + virtual uint32 data_length(const char *from) { return pack_length(); } virtual uint32 sort_length() const { return pack_length(); } virtual void reset(void) { bzero(ptr,pack_length()); } virtual void reset_fields() {} @@ -780,6 +785,8 @@ public: enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, CHARSET_INFO *cs); + Field_timestamp(bool maybe_null_arg, const char *field_name_arg, + struct st_table *table_arg, CHARSET_INFO *cs); enum_field_types type() const { return FIELD_TYPE_TIMESTAMP;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } enum Item_result cmp_type () const { return INT_RESULT; } @@ -1100,6 +1107,7 @@ public: int key_cmp(const byte *str, uint length); uint packed_col_length(const char *to, uint length); uint max_packed_col_length(uint max_length); + uint32 data_length(const char *from); uint size_of() const { return sizeof(*this); } enum_field_types real_type() const { return MYSQL_TYPE_VARCHAR; } bool has_charset(void) const @@ -1128,6 +1136,21 @@ public: { flags|= BLOB_FLAG; } + Field_blob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, + struct st_table *table_arg, CHARSET_INFO *cs, bool set_packlength) + :Field_longstr((char*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0, + NONE, field_name_arg, table_arg, cs) + { + flags|= BLOB_FLAG; + packlength= 4; + if (set_packlength) + { + uint32 char_length= len_arg/cs->mbmaxlen; + packlength= char_length <= 255 ? 1 : + char_length <= 65535 ? 2 : + char_length <= 16777215 ? 3 : 4; + } + } enum_field_types type() const { return FIELD_TYPE_BLOB;} enum ha_base_keytype key_type() const { return binary() ? HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2; } diff --git a/sql/gen_lex_hash.cc b/sql/gen_lex_hash.cc index 7e0b178f7af..5a8bd48d699 100644 --- a/sql/gen_lex_hash.cc +++ b/sql/gen_lex_hash.cc @@ -442,13 +442,17 @@ int main(int argc,char **argv) if (get_options(argc,(char **) argv)) exit(1); + /* Broken up to indicate that it's not advice to you, gentle reader. */ + printf("/*\n\n Do " "not " "edit " "this " "file " "directly!\n\n*/\n"); + printf("/* Copyright (C) 2001-2004 MySQL AB\n\ This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n\ and you are welcome to modify and redistribute it under the GPL license\n\ \n*/\n\n"); - printf("/* This code is generated by gen_lex_hash.cc that seeks for\ - a perfect\nhash function */\n\n"); + /* Broken up to indicate that it's not advice to you, gentle reader. */ + printf("/* Do " "not " "edit " "this " "file! This is generated by " + "gen_lex_hash.cc\nthat seeks for a perfect hash function */\n\n"); printf("#include \"lex.h\"\n\n"); calc_length(); @@ -468,6 +472,14 @@ static inline SYMBOL *get_hash_symbol(const char *s,\n\ {\n\ register uchar *hash_map;\n\ register const char *cur_str= s;\n\ +\n\ + if (len == 0) {\n\ + DBUG_PRINT(\"warning\", (\"get_hash_symbol() received a request for a zero-length symbol, which is probably a mistake.\"));\ + return(NULL);\n\ + }\n" +); + + printf("\ if (function){\n\ if (len>sql_functions_max_len) return 0;\n\ hash_map= sql_functions_map;\n\ @@ -498,7 +510,10 @@ static inline SYMBOL *get_hash_symbol(const char *s,\n\ cur_struct= uint4korr(hash_map+\n\ (((uint16)cur_struct + cur_char - first_char)*4));\n\ cur_str++;\n\ - }\n\ + }\n" +); + + printf("\ }else{\n\ if (len>symbols_max_len) return 0;\n\ hash_map= symbols_map;\n\ diff --git a/sql/ha_archive.cc b/sql/ha_archive.cc index 3885defb4d5..3a30f404a40 100644 --- a/sql/ha_archive.cc +++ b/sql/ha_archive.cc @@ -120,6 +120,8 @@ static bool archive_inited= FALSE; /* Variables for archive share methods */ pthread_mutex_t archive_mutex; static HASH archive_open_tables; +static z_off_t max_zfile_size; +static int zoffset_size; /* The file extension */ #define ARZ ".ARZ" // The data file @@ -203,6 +205,8 @@ bool archive_db_init() } else { + zoffset_size= 2 << ((zlibCompileFlags() >> 6) & 3); + max_zfile_size= (z_off_t) (~(1 << (zoffset_size * 8 - 1))); archive_inited= TRUE; DBUG_RETURN(FALSE); } @@ -240,7 +244,7 @@ ha_archive::ha_archive(TABLE *table_arg) buffer.set((char *)byte_buffer, IO_SIZE, system_charset_info); /* The size of the offset value we will use for position() */ - ref_length = 2 << ((zlibCompileFlags() >> 6) & 3); + ref_length = zoffset_size; DBUG_ASSERT(ref_length <= sizeof(z_off_t)); } @@ -480,7 +484,8 @@ int ha_archive::init_archive_writer() DBUG_RETURN(1); } share->archive_write_open= TRUE; - + info(HA_STATUS_TIME); + share->approx_file_size= data_file_length; DBUG_RETURN(0); } @@ -614,7 +619,7 @@ int ha_archive::create(const char *name, TABLE *table_arg, error= my_errno; goto error; } - if ((archive= gzdopen(create_file, "wb")) == NULL) + if ((archive= gzdopen(dup(create_file), "wb")) == NULL) { error= errno; goto error2; @@ -651,10 +656,21 @@ error: */ int ha_archive::real_write_row(byte *buf, gzFile writer) { - z_off_t written; + z_off_t written, total_row_length; uint *ptr, *end; DBUG_ENTER("ha_archive::real_write_row"); - + total_row_length= table->s->reclength; + for (ptr= table->s->blob_field, end= ptr + table->s->blob_fields; + ptr != end; ptr++) + total_row_length+= ((Field_blob*) table->field[*ptr])->get_length(); + if (share->approx_file_size > max_zfile_size - total_row_length) + { + info(HA_STATUS_TIME); + share->approx_file_size= data_file_length; + if (share->approx_file_size > max_zfile_size - total_row_length) + DBUG_RETURN(HA_ERR_RECORD_FILE_FULL); + } + share->approx_file_size+= total_row_length; written= gzwrite(writer, buf, table->s->reclength); DBUG_PRINT("ha_archive::real_write_row", ("Wrote %d bytes expected %d", written, table->s->reclength)); if (!delayed_insert || !bulk_insert) @@ -710,6 +726,28 @@ int ha_archive::write_row(byte *buf) if (init_archive_writer()) DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); + /* + Varchar structures are constant in size but are not cleaned up request + to request. The following sets all unused space to null to improve + compression. + */ + for (Field **field=table->field ; *field ; field++) + { + DBUG_PRINT("archive",("Pack is %d\n", (*field)->pack_length())); + DBUG_PRINT("archive",("MyPack is %d\n", (*field)->data_length((char*) buf + (*field)->offset()))); + if ((*field)->real_type() == MYSQL_TYPE_VARCHAR) + { + uint actual_length= (*field)->data_length((char*) buf + (*field)->offset()); + uint offset= (*field)->offset() + actual_length + + (actual_length > 255 ? 2 : 1); + DBUG_PRINT("archive",("Offset is %d -> %d\n", actual_length, offset)); + /* + if ((*field)->pack_length() + (*field)->offset() != offset) + bzero(buf + offset, (size_t)((*field)->pack_length() + (actual_length > 255 ? 2 : 1) - (*field)->data_length)); + */ + } + } + share->rows_recorded++; rc= real_write_row(buf, share->archive_write); pthread_mutex_unlock(&share->mutex); diff --git a/sql/ha_archive.h b/sql/ha_archive.h index 2bac9fa605e..564b9f03bf5 100644 --- a/sql/ha_archive.h +++ b/sql/ha_archive.h @@ -38,6 +38,7 @@ typedef struct st_archive_share { bool dirty; /* Flag for if a flush should occur */ bool crashed; /* Meta file is crashed */ ha_rows rows_recorded; /* Number of rows in tables */ + z_off_t approx_file_size; /* Approximate archive data file size */ } ARCHIVE_SHARE; /* diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc index 72af402a0dc..4209bc93d30 100644 --- a/sql/ha_berkeley.cc +++ b/sql/ha_berkeley.cc @@ -953,7 +953,10 @@ int ha_berkeley::write_row(byte * record) if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) table->timestamp_field->set_time(); if (table->next_number_field && record == table->record[0]) - update_auto_increment(); + { + if ((error= update_auto_increment())) + DBUG_RETURN(error); + } if ((error=pack_row(&row, record,1))) DBUG_RETURN(error); /* purecov: inspected */ diff --git a/sql/ha_heap.cc b/sql/ha_heap.cc index 79d4575ff1b..3aaa0287098 100644 --- a/sql/ha_heap.cc +++ b/sql/ha_heap.cc @@ -176,7 +176,10 @@ int ha_heap::write_row(byte * buf) if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) table->timestamp_field->set_time(); if (table->next_number_field && buf == table->record[0]) - update_auto_increment(); + { + if ((res= update_auto_increment())) + return res; + } res= heap_write(file,buf); if (!res && (++records_changed*HEAP_STATS_UPDATE_THRESHOLD > file->s->records)) diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 6aadce0191a..4e73d4fb285 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -2190,8 +2190,7 @@ ha_innobase::open( "have forgotten\nto delete the corresponding " ".frm files of InnoDB tables, or you\n" "have moved .frm files to another database?\n" - "Look from section 15.1 of " - "http://www.innodb.com/ibman.html\n" + "See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n" "how you can resolve the problem.\n", norm_name); free_share(share); @@ -2208,8 +2207,7 @@ ha_innobase::open( "Have you deleted the .ibd file from the " "database directory under\nthe MySQL datadir, " "or have you used DISCARD TABLESPACE?\n" - "Look from section 15.1 of " - "http://www.innodb.com/ibman.html\n" + "See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n" "how you can resolve the problem.\n", norm_name); free_share(share); @@ -3254,7 +3252,8 @@ no_commit: /* We must use the handler code to update the auto-increment value to be sure that we increment it correctly. */ - update_auto_increment(); + if ((error= update_auto_increment())) + goto func_exit; auto_inc_used = 1; } @@ -5384,13 +5383,14 @@ ha_innobase::info( for (i = 0; i < table->s->keys; i++) { if (index == NULL) { ut_print_timestamp(stderr); - sql_print_error("Table %s contains less " + sql_print_error("Table %s contains fewer " "indexes inside InnoDB than " "are defined in the MySQL " ".frm file. Have you mixed up " ".frm files from different " - "installations? See section " - "15.1 at http://www.innodb.com/ibman.html", + "installations? See " +"http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n", + ib_table->name); break; } @@ -5399,17 +5399,11 @@ ha_innobase::info( if (j + 1 > index->n_uniq) { ut_print_timestamp(stderr); - sql_print_error("Index %s of %s has " - "%lu columns unique " - "inside InnoDB, but " - "MySQL is asking " - "statistics for %lu " - "columns. Have you " - "mixed up .frm files " - "from different " - "installations? See " - "section 15.1 at " - "http://www.innodb.com/ibman.html", + sql_print_error( +"Index %s of %s has %lu columns unique inside InnoDB, but MySQL is asking " +"statistics for %lu columns. Have you mixed up .frm files from different " +"installations? " +"See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n", index->name, ib_table->name, (unsigned long) @@ -5943,14 +5937,6 @@ ha_innobase::start_stmt( innobase_release_stat_resources(trx); - if (trx->isolation_level <= TRX_ISO_READ_COMMITTED - && trx->global_read_view) { - /* At low transaction isolation levels we let - each consistent read set its own snapshot */ - - read_view_close_for_mysql(trx); - } - prebuilt->sql_stat_start = TRUE; prebuilt->hint_need_to_fetch_extra_cols = 0; prebuilt->read_just_key = 0; @@ -6215,7 +6201,7 @@ ha_innobase::transactional_table_lock( "table %s does not exist.\n" "Have you deleted the .ibd file from the database directory under\n" "the MySQL datadir?" -"Look from section 15.1 of http://www.innodb.com/ibman.html\n" +"See http://dev.mysql.com/doc/refman/5.0/en/innodb-troubleshooting.html\n" "how you can resolve the problem.\n", prebuilt->table->name); DBUG_RETURN(HA_ERR_CRASHED); @@ -6684,17 +6670,17 @@ ha_innobase::store_lock( && !thd->tablespace_op && thd->lex->sql_command != SQLCOM_TRUNCATE && thd->lex->sql_command != SQLCOM_OPTIMIZE + #ifdef __WIN__ - /* - for alter table on win32 for succesfull operation - completion it is used TL_WRITE(=10) lock instead of - TL_WRITE_ALLOW_READ(=6), however here in innodb handler - TL_WRITE is lifted to TL_WRITE_ALLOW_WRITE, which causes - race condition when several clients do alter table - simultaneously (bug #17264). This fix avoids the problem. - */ - && thd->lex->sql_command != SQLCOM_ALTER_TABLE + /* For alter table on win32 for succesful operation + completion it is used TL_WRITE(=10) lock instead of + TL_WRITE_ALLOW_READ(=6), however here in innodb handler + TL_WRITE is lifted to TL_WRITE_ALLOW_WRITE, which causes + race condition when several clients do alter table + simultaneously (bug #17264). This fix avoids the problem. */ + && thd->lex->sql_command != SQLCOM_ALTER_TABLE #endif + && thd->lex->sql_command != SQLCOM_CREATE_TABLE) { lock_type = TL_WRITE_ALLOW_WRITE; diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 876cd33ec9c..fc3bd3d3b08 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -324,7 +324,11 @@ int ha_myisam::write_row(byte * buf) or a new row, then update the auto_increment value in the record. */ if (table->next_number_field && buf == table->record[0]) - update_auto_increment(); + { + int error; + if ((error= update_auto_increment())) + return error; + } return mi_write(file,buf); } diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index 0b6e05fcbd4..cb11d9b0452 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -139,7 +139,11 @@ int ha_myisammrg::write_row(byte * buf) if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) table->timestamp_field->set_time(); if (table->next_number_field && buf == table->record[0]) - update_auto_increment(); + { + int error; + if ((error= update_auto_increment())) + return error; + } return myrg_write(file,buf); } diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 1af677fa754..5448b08b74f 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -35,6 +35,7 @@ // options from from mysqld.cc extern my_bool opt_ndb_optimized_node_selection; extern const char *opt_ndbcluster_connectstring; +extern ulong opt_ndb_cache_check_time; // Default value for parallelism static const int parallelism= 0; @@ -228,13 +229,15 @@ static int ndb_to_mysql_error(const NdbError *err) inline -int execute_no_commit(ha_ndbcluster *h, NdbTransaction *trans) +int execute_no_commit(ha_ndbcluster *h, NdbTransaction *trans, + bool force_release) { #ifdef NOT_USED int m_batch_execute= 0; if (m_batch_execute) return 0; #endif + h->release_completed_operations(trans, force_release); return trans->execute(NdbTransaction::NoCommit, NdbTransaction::AbortOnError, h->m_force_send); @@ -267,13 +270,15 @@ int execute_commit(THD *thd, NdbTransaction *trans) } inline -int execute_no_commit_ie(ha_ndbcluster *h, NdbTransaction *trans) +int execute_no_commit_ie(ha_ndbcluster *h, NdbTransaction *trans, + bool force_release) { #ifdef NOT_USED int m_batch_execute= 0; if (m_batch_execute) return 0; #endif + h->release_completed_operations(trans, force_release); return trans->execute(NdbTransaction::NoCommit, NdbTransaction::AO_IgnoreError, h->m_force_send); @@ -290,6 +295,7 @@ Thd_ndb::Thd_ndb() all= NULL; stmt= NULL; error= 0; + query_state&= NDB_QUERY_NORMAL; } Thd_ndb::~Thd_ndb() @@ -1443,7 +1449,7 @@ int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf) if ((res= define_read_attrs(buf, op))) DBUG_RETURN(res); - if (execute_no_commit_ie(this,trans) != 0) + if (execute_no_commit_ie(this,trans,false) != 0) { table->status= STATUS_NOT_FOUND; DBUG_RETURN(ndb_err(trans)); @@ -1490,7 +1496,7 @@ int ha_ndbcluster::complemented_pk_read(const byte *old_data, byte *new_data) ERR_RETURN(trans->getNdbError()); } } - if (execute_no_commit(this,trans) != 0) + if (execute_no_commit(this,trans,false) != 0) { table->status= STATUS_NOT_FOUND; DBUG_RETURN(ndb_err(trans)); @@ -1630,7 +1636,7 @@ int ha_ndbcluster::peek_indexed_rows(const byte *record) } last= trans->getLastDefinedOperation(); if (first) - res= execute_no_commit_ie(this,trans); + res= execute_no_commit_ie(this,trans,false); else { // Table has no keys @@ -1679,7 +1685,7 @@ int ha_ndbcluster::unique_index_read(const byte *key, if ((res= define_read_attrs(buf, op))) DBUG_RETURN(res); - if (execute_no_commit_ie(this,trans) != 0) + if (execute_no_commit_ie(this,trans,false) != 0) { table->status= STATUS_NOT_FOUND; DBUG_RETURN(ndb_err(trans)); @@ -1727,7 +1733,7 @@ inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor) */ if (m_ops_pending && m_blobs_pending) { - if (execute_no_commit(this,trans) != 0) + if (execute_no_commit(this,trans,false) != 0) DBUG_RETURN(ndb_err(trans)); m_ops_pending= 0; m_blobs_pending= FALSE; @@ -1759,7 +1765,7 @@ inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor) { if (m_transaction_on) { - if (execute_no_commit(this,trans) != 0) + if (execute_no_commit(this,trans,false) != 0) DBUG_RETURN(-1); } else @@ -2063,7 +2069,7 @@ int ha_ndbcluster::ordered_index_scan(const key_range *start_key, DBUG_RETURN(res); } - if (execute_no_commit(this,trans) != 0) + if (execute_no_commit(this,trans,false) != 0) DBUG_RETURN(ndb_err(trans)); DBUG_RETURN(next_result(buf)); @@ -2096,7 +2102,7 @@ int ha_ndbcluster::full_table_scan(byte *buf) if ((res= define_read_attrs(buf, op))) DBUG_RETURN(res); - if (execute_no_commit(this,trans) != 0) + if (execute_no_commit(this,trans,false) != 0) DBUG_RETURN(ndb_err(trans)); DBUG_PRINT("exit", ("Scan started successfully")); DBUG_RETURN(next_result(buf)); @@ -2125,9 +2131,11 @@ int ha_ndbcluster::write_row(byte *record) if (has_auto_increment) { THD *thd= table->in_use; + int error; m_skip_auto_increment= FALSE; - update_auto_increment(); + if ((error= update_auto_increment())) + DBUG_RETURN(error); /* Ensure that handler is always called for auto_increment values */ thd->next_insert_id= 0; m_skip_auto_increment= !auto_increment_column_changed; @@ -2228,7 +2236,7 @@ int ha_ndbcluster::write_row(byte *record) m_bulk_insert_not_flushed= FALSE; if (m_transaction_on) { - if (execute_no_commit(this,trans) != 0) + if (execute_no_commit(this,trans,false) != 0) { m_skip_auto_increment= TRUE; no_uncommitted_rows_execute_failure(); @@ -2428,7 +2436,7 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data) } // Execute update operation - if (!cursor && execute_no_commit(this,trans) != 0) { + if (!cursor && execute_no_commit(this,trans,false) != 0) { no_uncommitted_rows_execute_failure(); DBUG_RETURN(ndb_err(trans)); } @@ -2499,7 +2507,7 @@ int ha_ndbcluster::delete_row(const byte *record) } // Execute delete operation - if (execute_no_commit(this,trans) != 0) { + if (execute_no_commit(this,trans,false) != 0) { no_uncommitted_rows_execute_failure(); DBUG_RETURN(ndb_err(trans)); } @@ -2928,6 +2936,26 @@ int ha_ndbcluster::close_scan() NdbScanOperation *cursor= m_active_cursor ? m_active_cursor : m_multi_cursor; + if (m_lock_tuple) + { + /* + Lock level m_lock.type either TL_WRITE_ALLOW_WRITE + (SELECT FOR UPDATE) or TL_READ_WITH_SHARED_LOCKS (SELECT + LOCK WITH SHARE MODE) and row was not explictly unlocked + with unlock_row() call + */ + NdbOperation *op; + // Lock row + DBUG_PRINT("info", ("Keeping lock on scanned row")); + + if (!(op= cursor->lockCurrentTuple())) + { + m_lock_tuple= false; + ERR_RETURN(trans->getNdbError()); + } + m_ops_pending++; + } + m_lock_tuple= false; if (m_ops_pending) { /* @@ -2935,7 +2963,7 @@ int ha_ndbcluster::close_scan() deleteing/updating transaction before closing the scan */ DBUG_PRINT("info", ("ops_pending: %d", m_ops_pending)); - if (execute_no_commit(this,trans) != 0) { + if (execute_no_commit(this,trans,false) != 0) { no_uncommitted_rows_execute_failure(); DBUG_RETURN(ndb_err(trans)); } @@ -3345,7 +3373,7 @@ int ha_ndbcluster::end_bulk_insert() m_bulk_insert_not_flushed= FALSE; if (m_transaction_on) { - if (execute_no_commit(this, trans) != 0) + if (execute_no_commit(this, trans,false) != 0) { no_uncommitted_rows_execute_failure(); my_errno= error= ndb_err(trans); @@ -3500,7 +3528,14 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) if (lock_type != F_UNLCK) { DBUG_PRINT("info", ("lock_type != F_UNLCK")); - if (!thd->transaction.on) + if (thd->lex->sql_command == SQLCOM_LOAD) + { + m_transaction_on= FALSE; + /* Would be simpler if has_transactions() didn't always say "yes" */ + thd->options|= OPTION_STATUS_NO_TRANS_UPDATE; + thd->no_trans_update= TRUE; + } + else if (!thd->transaction.on) m_transaction_on= FALSE; else m_transaction_on= thd->variables.ndb_use_transactions; @@ -3518,6 +3553,7 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) ERR_RETURN(ndb->getNdbError()); no_uncommitted_rows_reset(thd); thd_ndb->stmt= trans; + thd_ndb->query_state&= NDB_QUERY_NORMAL; trans_register_ha(thd, FALSE, &ndbcluster_hton); } else @@ -3533,6 +3569,7 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) ERR_RETURN(ndb->getNdbError()); no_uncommitted_rows_reset(thd); thd_ndb->all= trans; + thd_ndb->query_state&= NDB_QUERY_NORMAL; trans_register_ha(thd, TRUE, &ndbcluster_hton); /* @@ -3739,6 +3776,7 @@ int ha_ndbcluster::start_stmt(THD *thd, thr_lock_type lock_type) thd_ndb->stmt= trans; trans_register_ha(thd, FALSE, &ndbcluster_hton); } + thd_ndb->query_state&= NDB_QUERY_NORMAL; m_active_trans= trans; // Start of statement @@ -4147,10 +4185,15 @@ static void ndb_set_fragmentation(NDBTAB &tab, TABLE *form, uint pk_length) acc_row_size+= 4 + /*safety margin*/ 4; #endif ulonglong acc_fragment_size= 512*1024*1024; + /* + * if not --with-big-tables then max_rows is ulong + * the warning in this case is misleading though + */ + ulonglong big_max_rows = (ulonglong)max_rows; #if MYSQL_VERSION_ID >= 50100 - no_fragments= (max_rows*acc_row_size)/acc_fragment_size+1; + no_fragments= (big_max_rows*acc_row_size)/acc_fragment_size+1; #else - no_fragments= ((max_rows*acc_row_size)/acc_fragment_size+1 + no_fragments= ((big_max_rows*acc_row_size)/acc_fragment_size+1 +1/*correct rounding*/)/2; #endif } @@ -5210,6 +5253,7 @@ bool ndbcluster_init() pthread_cond_init(&COND_ndb_util_thread, NULL); + ndb_cache_check_time = opt_ndb_cache_check_time; // Create utility thread pthread_t tmp; if (pthread_create(&tmp, &connection_attrib, ndb_util_thread_func, 0)) @@ -5986,6 +6030,30 @@ int ha_ndbcluster::write_ndb_file() DBUG_RETURN(error); } +void +ha_ndbcluster::release_completed_operations(NdbTransaction *trans, + bool force_release) +{ + if (trans->hasBlobOperation()) + { + /* We are reading/writing BLOB fields, + releasing operation records is unsafe + */ + return; + } + if (!force_release) + { + if (get_thd_ndb(current_thd)->query_state & NDB_QUERY_MULTI_READ_RANGE) + { + /* We are batching reads and have not consumed all fetched + rows yet, releasing operation records is unsafe + */ + return; + } + } + trans->releaseCompletedOperations(); +} + int ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, KEY_MULTI_RANGE *ranges, @@ -6000,6 +6068,7 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, NDB_INDEX_TYPE index_type= get_index_type(active_index); ulong reclength= table->s->reclength; NdbOperation* op; + Thd_ndb *thd_ndb= get_thd_ndb(current_thd); if (uses_blob_value(m_retrieve_all_fields)) { @@ -6013,7 +6082,7 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, sorted, buffer)); } - + thd_ndb->query_state|= NDB_QUERY_MULTI_READ_RANGE; m_disable_multi_read= FALSE; /** @@ -6160,7 +6229,7 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, */ m_current_multi_operation= lastOp ? lastOp->next() : m_active_trans->getFirstDefinedOperation(); - if (!(res= execute_no_commit_ie(this, m_active_trans))) + if (!(res= execute_no_commit_ie(this, m_active_trans, true))) { m_multi_range_defined= multi_range_curr; multi_range_curr= ranges; diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index 01950c2b00f..cfb12981b98 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -435,6 +435,11 @@ class Ndb_cond_traverse_context Ndb_rewrite_context *rewrite_stack; }; +typedef enum ndb_query_state_bits { + NDB_QUERY_NORMAL = 0, + NDB_QUERY_MULTI_READ_RANGE = 1 +} NDB_QUERY_STATE_BITS; + /* Place holder for ha_ndbcluster thread specific data */ @@ -451,6 +456,7 @@ class Thd_ndb NdbTransaction *stmt; int error; List<NDB_SHARE> changed_tables; + uint query_state; }; class ha_ndbcluster: public handler @@ -672,8 +678,8 @@ private: NdbScanOperation* op); friend int execute_commit(ha_ndbcluster*, NdbTransaction*); - friend int execute_no_commit(ha_ndbcluster*, NdbTransaction*); - friend int execute_no_commit_ie(ha_ndbcluster*, NdbTransaction*); + friend int execute_no_commit(ha_ndbcluster*, NdbTransaction*, bool); + friend int execute_no_commit_ie(ha_ndbcluster*, NdbTransaction*, bool); NdbTransaction *m_active_trans; NdbScanOperation *m_active_cursor; @@ -716,6 +722,8 @@ private: bool m_force_send; ha_rows m_autoincrement_prefetch; bool m_transaction_on; + void release_completed_operations(NdbTransaction*, bool); + Ndb_cond_stack *m_cond_stack; bool m_disable_multi_read; byte *m_multi_range_result_ptr; diff --git a/sql/handler.cc b/sql/handler.cc index 81981a5dcc6..4accc746664 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -302,7 +302,9 @@ handler *get_new_handler(TABLE *table, MEM_ROOT *alloc, enum db_type db_type) #endif case DB_TYPE_MRG_MYISAM: case DB_TYPE_MRG_ISAM: - return new (alloc) ha_myisammrg(table); + if (have_merge_db == SHOW_OPTION_YES) + return new (alloc) ha_myisammrg(table); + return NULL; #ifdef HAVE_BERKELEY_DB case DB_TYPE_BERKELEY_DB: if (have_berkeley_db == SHOW_OPTION_YES) @@ -427,6 +429,8 @@ static int ha_init_errors(void) SETMSG(HA_ERR_TABLE_DEF_CHANGED, ER(ER_TABLE_DEF_CHANGED)); SETMSG(HA_ERR_TABLE_NEEDS_UPGRADE, ER(ER_TABLE_NEEDS_UPGRADE)); SETMSG(HA_ERR_TABLE_READONLY, ER(ER_OPEN_AS_READONLY)); + SETMSG(HA_ERR_AUTOINC_READ_FAILED, ER(ER_AUTOINC_READ_FAILED)); + SETMSG(HA_ERR_AUTOINC_ERANGE, ER(ER_WARN_DATA_OUT_OF_RANGE)); /* Register the error messages for use with my_error(). */ return my_error_register(errmsgs, HA_ERR_FIRST, HA_ERR_LAST); @@ -1549,7 +1553,10 @@ prev_insert_id(ulonglong nr, struct system_variables *variables) RETURN 0 ok - 1 get_auto_increment() was called and returned ~(ulonglong) 0 + HA_ERR_AUTOINC_READ_FAILED + get_auto_increment() was called and returned ~(ulonglong) 0 + HA_ERR_AUTOINC_ERANGE + storing value in field caused strict mode failure. IMPLEMENTATION @@ -1593,13 +1600,12 @@ prev_insert_id(ulonglong nr, struct system_variables *variables) thd->next_insert_id is cleared after it's been used for a statement. */ -bool handler::update_auto_increment() +int handler::update_auto_increment() { ulonglong nr; THD *thd= table->in_use; struct system_variables *variables= &thd->variables; bool auto_increment_field_not_null; - bool result= 0; DBUG_ENTER("handler::update_auto_increment"); /* @@ -1623,7 +1629,7 @@ bool handler::update_auto_increment() if (!(nr= thd->next_insert_id)) { if ((nr= get_auto_increment()) == ~(ulonglong) 0) - result= 1; // Mark failure + DBUG_RETURN(HA_ERR_AUTOINC_READ_FAILED); // Mark failure if (variables->auto_increment_increment != 1) nr= next_insert_id(nr-1, variables); @@ -1643,6 +1649,7 @@ bool handler::update_auto_increment() if (likely(!table->next_number_field->store((longlong) nr, TRUE))) thd->insert_id((ulonglong) nr); else + if (thd->killed != THD::KILL_BAD_DATA) /* did we fail strict mode? */ { /* overflow of the field; we'll use the max value, however we try to @@ -1653,6 +1660,8 @@ bool handler::update_auto_increment() if (unlikely(table->next_number_field->store((longlong) nr, TRUE))) thd->insert_id(nr= table->next_number_field->val_int()); } + else + DBUG_RETURN(HA_ERR_AUTOINC_ERANGE); /* We can't set next_insert_id if the auto-increment key is not the @@ -1673,7 +1682,7 @@ bool handler::update_auto_increment() /* Mark that we generated a new value */ auto_increment_column_changed=1; - DBUG_RETURN(result); + DBUG_RETURN(0); } /* @@ -1871,6 +1880,12 @@ void handler::print_error(int error, myf errflag) case HA_ERR_TABLE_READONLY: textno= ER_OPEN_AS_READONLY; break; + case HA_ERR_AUTOINC_READ_FAILED: + textno= ER_AUTOINC_READ_FAILED; + break; + case HA_ERR_AUTOINC_ERANGE: + textno= ER_WARN_DATA_OUT_OF_RANGE; + break; default: { /* The error was "unknown" to this function. diff --git a/sql/handler.h b/sql/handler.h index 471bb08b748..481a98be285 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -566,7 +566,7 @@ public: virtual handler *clone(MEM_ROOT *mem_root); int ha_open(const char *name, int mode, int test_if_locked); void adjust_next_insert_id_after_explicit_value(ulonglong nr); - bool update_auto_increment(); + int update_auto_increment(); virtual void print_error(int error, myf errflag); virtual bool get_error_message(int error, String *buf); uint get_dup_key(int error); diff --git a/sql/item.cc b/sql/item.cc index 511ea1ffb44..de78a2539a7 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -305,6 +305,7 @@ Item::Item(): maybe_null=null_value=with_sum_func=unsigned_flag=0; decimals= 0; max_length= 0; with_subselect= 0; + cmp_context= (Item_result)-1; /* Put item in free list so that we can free all items at end */ THD *thd= current_thd; @@ -343,7 +344,8 @@ Item::Item(THD *thd, Item *item): unsigned_flag(item->unsigned_flag), with_sum_func(item->with_sum_func), fixed(item->fixed), - collation(item->collation) + collation(item->collation), + cmp_context(item->cmp_context) { next= thd->free_list; // Put in free list thd->free_list= this; @@ -420,6 +422,49 @@ void Item::rename(char *new_name) } +/* + Traverse item tree possibly transforming it (replacing items). + + SYNOPSIS + Item::transform() + transformer functor that performs transformation of a subtree + arg opaque argument passed to the functor + + DESCRIPTION + This function is designed to ease transformation of Item trees. + + Re-execution note: every such transformation is registered for + rollback by THD::change_item_tree() and is rolled back at the end + of execution by THD::rollback_item_tree_changes(). + + Therefore: + + - this function can not be used at prepared statement prepare + (in particular, in fix_fields!), as only permanent + transformation of Item trees are allowed at prepare. + + - the transformer function shall allocate new Items in execution + memory root (thd->mem_root) and not anywhere else: allocated + items will be gone in the end of execution. + + If you don't need to transform an item tree, but only traverse + it, please use Item::walk() instead. + + + RETURN VALUE + Returns pointer to the new subtree root. THD::change_item_tree() + should be called for it if transformation took place, i.e. if a + pointer to newly allocated item is returned. +*/ + +Item* Item::transform(Item_transformer transformer, byte *arg) +{ + DBUG_ASSERT(!current_thd->is_stmt_prepare()); + + return (this->*transformer)(arg); +} + + Item_ident::Item_ident(Name_resolution_context *context_arg, const char *db_name_arg,const char *table_name_arg, const char *field_name_arg) @@ -573,6 +618,7 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) } if (cs->ctype) { + uint orig_len= length; /* This will probably need a better implementation in the future: a function in CHARSET_INFO structure. @@ -582,6 +628,11 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) length--; str++; } + if (orig_len != length && !is_autogenerated_name) + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_REMOVED_SPACES, ER(ER_REMOVED_SPACES), + str + length - orig_len); + } if (!my_charset_same(cs, system_charset_info)) { @@ -1418,7 +1469,8 @@ bool agg_item_charsets(DTCollation &coll, const char *fname, In case we're in statement prepare, create conversion item in its memory: it will be reused on each execute. */ - arena= thd->activate_stmt_arena_if_needed(&backup); + arena= thd->is_stmt_prepare() ? thd->activate_stmt_arena_if_needed(&backup) + : NULL; for (i= 0, arg= args; i < nargs; i++, arg+= item_sep) { @@ -1453,7 +1505,7 @@ bool agg_item_charsets(DTCollation &coll, const char *fname, been created in prepare. In this case register the change for rollback. */ - if (arena && arena->is_conventional()) + if (arena) *arg= conv; else thd->change_item_tree(arg, conv); @@ -3737,13 +3789,48 @@ Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal) /* + Check whether a field can be substituted by an equal item + + SYNOPSIS + equal_fields_propagator() + arg - *arg != NULL <-> the field is in the context where + substitution for an equal item is valid + + DESCRIPTION + The function checks whether a substitution of the field + occurrence for an equal item is valid. + + NOTES + The following statement is not always true: + x=y => F(x)=F(x/y). + This means substitution of an item for an equal item not always + yields an equavalent condition. + Here's an example: + 'a'='a ' + (LENGTH('a')=1) != (LENGTH('a ')=2) + Such a substitution is surely valid if either the substituted + field is not of a STRING type or if it is an argument of + a comparison predicate. + + RETURN + TRUE substitution is valid + FALSE otherwise +*/ + +bool Item_field::subst_argument_checker(byte **arg) +{ + return (result_type() != STRING_RESULT) || (*arg); +} + + +/* Set a pointer to the multiple equality the field reference belongs to (if any) SYNOPSIS equal_fields_propagator() - arg - reference to list of multiple equalities where - the field (this object) is to be looked for + arg - reference to list of multiple equalities where + the field (this object) is to be looked for DESCRIPTION The function looks for a multiple equality containing the field item @@ -3755,7 +3842,7 @@ Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal) NOTES This function is supposed to be called as a callback parameter in calls - of the transform method. + of the compile method. RETURN VALUES pointer to the replacing constant item, if the field item was substituted @@ -3770,7 +3857,19 @@ Item *Item_field::equal_fields_propagator(byte *arg) Item *item= 0; if (item_equal) item= item_equal->get_const(); - if (!item) + /* + Disable const propagation for items used in different comparison contexts. + This must be done because, for example, Item_hex_string->val_int() is not + the same as (Item_hex_string->val_str() in BINARY column)->val_int(). + We cannot simply disable the replacement in a particular context ( + e.g. <bin_col> = <int_col> AND <bin_col> = <hex_string>) since + Items don't know the context they are in and there are functions like + IF (<hex_string>, 'yes', 'no'). + The same problem occurs when comparing a DATE/TIME field with a + DATE/TIME represented as an int and as a string. + */ + if (!item || + (cmp_context != (Item_result)-1 && item->cmp_context != cmp_context)) item= this; return item; } @@ -3781,11 +3880,11 @@ Item *Item_field::equal_fields_propagator(byte *arg) See comments in Arg_comparator::set_compare_func() for details */ -Item *Item_field::set_no_const_sub(byte *arg) +bool Item_field::set_no_const_sub(byte *arg) { if (field->charset() != &my_charset_bin) no_const_subst=1; - return this; + return FALSE; } @@ -3897,7 +3996,9 @@ Field *Item::make_string_field(TABLE *table) if (max_length/collation.collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB) return new Field_blob(max_length, maybe_null, name, table, collation.collation); - if (max_length > 0) + /* Item_type_holder holds the exact type, do not change it */ + if (max_length > 0 && + (type() != Item::TYPE_HOLDER || field_type() != MYSQL_TYPE_STRING)) return new Field_varstring(max_length, maybe_null, name, table, collation.collation); return new Field_string(max_length, maybe_null, name, table, @@ -3961,6 +4062,7 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table) case MYSQL_TYPE_TIME: return new Field_time(maybe_null, name, table, &my_charset_bin); case MYSQL_TYPE_TIMESTAMP: + return new Field_timestamp(maybe_null, name, table, &my_charset_bin); case MYSQL_TYPE_DATETIME: return new Field_datetime(maybe_null, name, table, &my_charset_bin); case MYSQL_TYPE_YEAR: @@ -3984,7 +4086,11 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table) case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_GEOMETRY: - return new Field_blob(max_length, maybe_null, name, table, + if (this->type() == Item::TYPE_HOLDER) + return new Field_blob(max_length, maybe_null, name, table, + collation.collation, 1); + else + return new Field_blob(max_length, maybe_null, name, table, collation.collation); break; // Blob handled outside of case } @@ -5280,6 +5386,31 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions) } +/* + This method like the walk method traverses the item tree, but at the + same time it can replace some nodes in the tree +*/ + +Item *Item_default_value::transform(Item_transformer transformer, byte *args) +{ + DBUG_ASSERT(!current_thd->is_stmt_prepare()); + + Item *new_item= arg->transform(transformer, args); + if (!new_item) + return 0; + + /* + THD::change_item_tree() should be called only if the tree was + really transformed, i.e. when a new item has been created. + Otherwise we'll be allocating a lot of unnecessary memory for + change records at each execution. + */ + if (arg != new_item) + current_thd->change_item_tree(&arg, new_item); + return (this->*transformer)(args); +} + + bool Item_insert_value::eq(const Item *item, bool binary_cmp) const { return item->type() == INSERT_VALUE_ITEM && @@ -6047,14 +6178,13 @@ bool Item_type_holder::join_types(THD *thd, Item *item) max_length= my_decimal_precision_to_length(precision, decimals, unsigned_flag); } - else - max_length= max(max_length, display_length(item)); - + switch (Field::result_merge_type(fld_type)) { case STRING_RESULT: { const char *old_cs, *old_derivation; + uint32 old_max_chars= max_length / collation.collation->mbmaxlen; old_cs= collation.collation->name; old_derivation= collation.derivation_name(); if (collation.aggregate(item->collation, MY_COLL_ALLOW_CONV)) @@ -6066,6 +6196,14 @@ bool Item_type_holder::join_types(THD *thd, Item *item) "UNION"); DBUG_RETURN(TRUE); } + /* + To figure out max_length, we have to take into account possible + expansion of the size of the values because of character set + conversions. + */ + max_length= max(old_max_chars * collation.collation->mbmaxlen, + display_length(item) / item->collation.collation->mbmaxlen * + collation.collation->mbmaxlen); break; } case REAL_RESULT: @@ -6084,7 +6222,8 @@ bool Item_type_holder::join_types(THD *thd, Item *item) max_length= (fld_type == MYSQL_TYPE_FLOAT) ? FLT_DIG+6 : DBL_DIG+7; break; } - default:; + default: + max_length= max(max_length, display_length(item)); }; maybe_null|= item->maybe_null; get_full_info(item); @@ -6145,7 +6284,7 @@ uint32 Item_type_holder::display_length(Item *item) case MYSQL_TYPE_DOUBLE: return 53; case MYSQL_TYPE_NULL: - return 4; + return 0; case MYSQL_TYPE_LONGLONG: return 20; case MYSQL_TYPE_INT24: diff --git a/sql/item.h b/sql/item.h index b7b9f972f67..0cfb0b01fd8 100644 --- a/sql/item.h +++ b/sql/item.h @@ -410,7 +410,19 @@ public: }; -typedef bool (Item::*Item_processor)(byte *arg); +typedef bool (Item::*Item_processor) (byte *arg); +/* + Analyzer function + SYNOPSIS + argp in/out IN: Analysis parameter + OUT: Parameter to be passed to the transformer + + RETURN + TRUE Invoke the transformer + FALSE Don't do it + +*/ +typedef bool (Item::*Item_analyzer) (byte **argp); typedef Item* (Item::*Item_transformer) (byte *arg); typedef void (*Cond_traverser) (const Item *item, void *arg); @@ -465,7 +477,7 @@ public: my_bool with_subselect; /* If this item is a subselect or some of its arguments is or contains a subselect */ - + Item_result cmp_context; /* Comparison context */ // alloc & destruct is done as start of select using sql_alloc Item(); /* @@ -697,9 +709,16 @@ public: Any new item which can be NULL must implement this call. */ virtual bool is_null() { return 0; } + /* - it is "top level" item of WHERE clause and we do not need correct NULL - handling + Inform the item that there will be no distinction between its result + being FALSE or NULL. + + NOTE + This function will be called for eg. Items that are top-level AND-parts + of the WHERE clause. Items implementing this function (currently + Item_cond_and and subquery-related item) enable special optimizations + when they are "top level". */ virtual void top_level_item() {} /* @@ -727,9 +746,30 @@ public: return (this->*processor)(arg); } - virtual Item* transform(Item_transformer transformer, byte *arg) + virtual Item* transform(Item_transformer transformer, byte *arg); + + /* + This function performs a generic "compilation" of the Item tree. + The process of compilation is assumed to go as follows: + + compile() + { + if (this->*some_analyzer(...)) + { + compile children if any; + this->*some_transformer(...); + } + } + + i.e. analysis is performed top-down while transformation is done + bottom-up. + */ + virtual Item* compile(Item_analyzer analyzer, byte **arg_p, + Item_transformer transformer, byte *arg_t) { - return (this->*transformer)(arg); + if ((this->*analyzer) (arg_p)) + return ((this->*transformer) (arg_t)); + return 0; } virtual void traverse_cond(Cond_traverser traverser, @@ -745,9 +785,16 @@ public: virtual bool find_item_in_field_list_processor(byte *arg) { return 0; } virtual bool change_context_processor(byte *context) { return 0; } virtual bool reset_query_id_processor(byte *query_id) { return 0; } + virtual bool is_expensive_processor(byte *arg) { return 0; } + virtual bool subst_argument_checker(byte **arg) + { + if (*arg) + *arg= NULL; + return TRUE; + } virtual Item *equal_fields_propagator(byte * arg) { return this; } - virtual Item *set_no_const_sub(byte *arg) { return this; } + virtual bool set_no_const_sub(byte *arg) { return FALSE; } virtual Item *replace_equal_field(byte * arg) { return this; } /* @@ -1246,8 +1293,9 @@ public: return field->can_be_compared_as_longlong(); } Item_equal *find_item_equal(COND_EQUAL *cond_equal); + bool subst_argument_checker(byte **arg); Item *equal_fields_propagator(byte *arg); - Item *set_no_const_sub(byte *arg); + bool set_no_const_sub(byte *arg); Item *replace_equal_field(byte *arg); inline uint32 max_disp_length() { return field->max_length(); } Item_field *filed_for_view_update() { return this; } @@ -2108,18 +2156,7 @@ public: (this->*processor)(args); } - /* - This method like the walk method traverses the item tree, but - at the same time it can replace some nodes in the tree - */ - Item *transform(Item_transformer transformer, byte *args) - { - Item *new_item= arg->transform(transformer, args); - if (!new_item) - return 0; - arg= new_item; - return (this->*transformer)(args); - } + Item *transform(Item_transformer transformer, byte *args); }; /* @@ -2147,7 +2184,11 @@ public: { return Item_field::save_in_field(field_arg, no_conversions); } - table_map used_tables() const { return (table_map)0L; } + /* + We use RAND_TABLE_BIT to prevent Item_insert_value from + being treated as a constant and precalculated before execution + */ + table_map used_tables() const { return RAND_TABLE_BIT; } bool walk(Item_processor processor, byte *args) { diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 98453899375..780d70d51dc 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -77,123 +77,14 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems) This function aggregates result types from the array of items. Found type supposed to be used later for comparison of values of these items. Aggregation itself is performed by the item_cmp_type() function. - - NOTES - Aggregation rules: - If there are DATE/TIME fields/functions in the list and no string - fields/functions in the list then: - The INT_RESULT type will be used for aggregation instead of original - result type of any DATE/TIME field/function in the list - All constant items in the list will be converted to a DATE/TIME using - found field or result field of found function. - - Implementation notes: - The code is equivalent to: - 1. Check the list for presence of a STRING field/function. - Collect the is_const flag. - 2. Get a Field* object to use for type coercion - 3. Perform type conversion. - 1 and 2 are implemented in 2 loops. The first searches for a DATE/TIME - field/function and checks presence of a STRING field/function. - The second loop works only if a DATE/TIME field/function is found. - It checks presence of a STRING field/function in the rest of the list. - - TODO - 1) The current implementation can produce false comparison results for - expressions like: - date_time_field BETWEEN string_field_with_dates AND string_constant - if the string_constant will omit some of leading zeroes. - In order to fully implement correct comparison of DATE/TIME the new - DATETIME_RESULT result type should be introduced and agg_cmp_type() - should return the DATE/TIME field used for the conversion. Later - this field can be used by comparison functions like Item_func_between to - convert string values to ints on the fly and thus return correct results. - This modification will affect functions BETWEEN, IN and CASE. - - 2) If in the list a DATE field/function and a DATETIME field/function - are present in the list then the first found field/function will be - used for conversion. This may lead to wrong results and probably should - be fixed. */ static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems) { uint i; - Item::Type res= (Item::Type)0; - /* Used only for date/time fields, max_length = 19 */ - char buff[20]; - uchar null_byte; - Field *field= NULL; - - /* Search for date/time fields/functions */ - for (i= 0; i < nitems; i++) - { - if (!items[i]->result_as_longlong()) - { - /* Do not convert anything if a string field/function is present */ - if (!items[i]->const_item() && items[i]->result_type() == STRING_RESULT) - { - i= nitems; - break; - } - continue; - } - if ((res= items[i]->real_item()->type()) == Item::FIELD_ITEM && - items[i]->result_type() != INT_RESULT) - { - field= ((Item_field *)items[i]->real_item())->field; - break; - } - else if (res == Item::FUNC_ITEM) - { - field= items[i]->tmp_table_field_from_field_type(0); - if (field) - field->move_field(buff, &null_byte, 0); - break; - } - } - if (field) - { - /* Check the rest of the list for presence of a string field/function. */ - for (i++ ; i < nitems; i++) - { - if (!items[i]->const_item() && items[i]->result_type() == STRING_RESULT && - !items[i]->result_as_longlong()) - { - if (res == Item::FUNC_ITEM) - delete field; - field= 0; - break; - } - } - } - /* - If the first item is a date/time function then its result should be - compared as int - */ - if (field) - /* Suppose we are comparing dates */ - type[0]= INT_RESULT; - else - type[0]= items[0]->result_type(); - - for (i= 0; i < nitems ; i++) - { - Item_result result= items[i]->result_type(); - /* - Use INT_RESULT as result type for DATE/TIME fields/functions and - for constants successfully converted to DATE/TIME - */ - if (field && - ((!items[i]->const_item() && items[i]->result_as_longlong()) || - (items[i]->const_item() && convert_constant_item(thd, field, - &items[i])))) - result= INT_RESULT; - type[0]= item_cmp_type(type[0], result); - } - - if (res == Item::FUNC_ITEM && field) - delete field; + type[0]= items[0]->result_type(); + for (i= 1 ; i < nitems ; i++) + type[0]= item_cmp_type(type[0], items[i]->result_type()); } @@ -397,7 +288,8 @@ void Item_bool_func2::fix_length_and_dec() agg_arg_charsets(coll, args, 2, MY_COLL_CMP_CONV, 1)) return; - + args[0]->cmp_context= args[1]->cmp_context= + item_cmp_type(args[0]->result_type(), args[1]->result_type()); // Make a special case of compare with fields to get nicer DATE comparisons if (functype() == LIKE_FUNC) // Disable conversion in case of LIKE function. @@ -418,6 +310,7 @@ void Item_bool_func2::fix_length_and_dec() { cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, INT_RESULT); // Works for all types. + args[0]->cmp_context= args[1]->cmp_context= INT_RESULT; return; } } @@ -432,6 +325,7 @@ void Item_bool_func2::fix_length_and_dec() { cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, INT_RESULT); // Works for all types. + args[0]->cmp_context= args[1]->cmp_context= INT_RESULT; return; } } @@ -500,8 +394,8 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) which would be transformed to: WHERE col= 'j' */ - (*a)->transform(&Item::set_no_const_sub, (byte*) 0); - (*b)->transform(&Item::set_no_const_sub, (byte*) 0); + (*a)->walk(&Item::set_no_const_sub, (byte*) 0); + (*b)->walk(&Item::set_no_const_sub, (byte*) 0); } break; } @@ -1209,9 +1103,32 @@ void Item_func_between::fix_length_and_dec() if (!args[0] || !args[1] || !args[2]) return; agg_cmp_type(thd, &cmp_type, args, 3); + if (cmp_type == STRING_RESULT && + agg_arg_charsets(cmp_collation, args, 3, MY_COLL_CMP_CONV, 1)) + return; - if (cmp_type == STRING_RESULT) - agg_arg_charsets(cmp_collation, args, 3, MY_COLL_CMP_CONV, 1); + /* + Make a special case of compare with date/time and longlong fields. + They are compared as integers, so for const item this time-consuming + conversion can be done only once, not for every single comparison + */ + if (args[0]->type() == FIELD_ITEM && + thd->lex->sql_command != SQLCOM_CREATE_VIEW && + thd->lex->sql_command != SQLCOM_SHOW_CREATE) + { + Field *field=((Item_field*) args[0])->field; + if (field->can_be_compared_as_longlong()) + { + /* + The following can't be recoded with || as convert_constant_item + changes the argument + */ + if (convert_constant_item(thd, field,&args[1])) + cmp_type=INT_RESULT; // Works for all types. + if (convert_constant_item(thd, field,&args[2])) + cmp_type=INT_RESULT; // Works for all types. + } + } } @@ -2735,16 +2652,16 @@ bool Item_cond::walk(Item_processor processor, byte *arg) SYNOPSIS transform() - transformer the transformer callback function to be applied to the nodes - of the tree of the object - arg parameter to be passed to the transformer + transformer the transformer callback function to be applied to the nodes + of the tree of the object + arg parameter to be passed to the transformer DESCRIPTION - The function recursively applies the transform method with the - same transformer to each member item of the condition list. + The function recursively applies the transform method to each + member item of the condition list. If the call of the method for a member item returns a new item the old item is substituted for a new one. - After this the transform method is applied to the root node + After this the transformer is applied to the root node of the Item_cond object. RETURN VALUES @@ -2753,6 +2670,8 @@ bool Item_cond::walk(Item_processor processor, byte *arg) Item *Item_cond::transform(Item_transformer transformer, byte *arg) { + DBUG_ASSERT(!current_thd->is_stmt_prepare()); + List_iterator<Item> li(list); Item *item; while ((item= li++)) @@ -2760,12 +2679,68 @@ Item *Item_cond::transform(Item_transformer transformer, byte *arg) Item *new_item= item->transform(transformer, arg); if (!new_item) return 0; + + /* + THD::change_item_tree() should be called only if the tree was + really transformed, i.e. when a new item has been created. + Otherwise we'll be allocating a lot of unnecessary memory for + change records at each execution. + */ if (new_item != item) - li.replace(new_item); + current_thd->change_item_tree(li.ref(), new_item); } return Item_func::transform(transformer, arg); } + +/* + Compile Item_cond object with a processor and a transformer callback functions + + SYNOPSIS + compile() + analyzer the analyzer callback function to be applied to the nodes + of the tree of the object + arg_p in/out parameter to be passed to the analyzer + transformer the transformer callback function to be applied to the nodes + of the tree of the object + arg_t parameter to be passed to the transformer + + DESCRIPTION + First the function applies the analyzer to the root node of + the Item_func object. Then if the analyzer succeeeds (returns TRUE) + the function recursively applies the compile method to member + item of the condition list. + If the call of the method for a member item returns a new item + the old item is substituted for a new one. + After this the transformer is applied to the root node + of the Item_cond object. + + RETURN VALUES + Item returned as the result of transformation of the root node +*/ + +Item *Item_cond::compile(Item_analyzer analyzer, byte **arg_p, + Item_transformer transformer, byte *arg_t) +{ + if (!(this->*analyzer)(arg_p)) + return 0; + + List_iterator<Item> li(list); + Item *item; + while ((item= li++)) + { + /* + The same parameter value of arg_p must be passed + to analyze any argument of the condition formula. + */ + byte *arg_v= *arg_p; + Item *new_item= item->compile(analyzer, &arg_v, transformer, arg_t); + if (new_item && new_item != item) + li.replace(new_item); + } + return Item_func::transform(transformer, arg_t); +} + void Item_cond::traverse_cond(Cond_traverser traverser, void *arg, traverse_order order) { @@ -3656,6 +3631,28 @@ Item *Item_cond_or::neg_transformer(THD *thd) /* NOT(a OR b OR ...) -> */ } +Item *Item_func_nop_all::neg_transformer(THD *thd) +{ + /* "NOT (e $cmp$ ANY (SELECT ...)) -> e $rev_cmp$" ALL (SELECT ...) */ + Item_func_not_all *new_item= new Item_func_not_all(args[0]); + Item_allany_subselect *allany= (Item_allany_subselect*)args[0]; + allany->func= allany->func_creator(FALSE); + allany->all= !allany->all; + allany->upper_item= new_item; + return new_item; +} + +Item *Item_func_not_all::neg_transformer(THD *thd) +{ + /* "NOT (e $cmp$ ALL (SELECT ...)) -> e $rev_cmp$" ANY (SELECT ...) */ + Item_func_nop_all *new_item= new Item_func_nop_all(args[0]); + Item_allany_subselect *allany= (Item_allany_subselect*)args[0]; + allany->all= !allany->all; + allany->func= allany->func_creator(TRUE); + allany->upper_item= new_item; + return new_item; +} + Item *Item_func_eq::negated_item() /* a = b -> a != b */ { return new Item_func_ne(args[0], args[1]); @@ -3984,6 +3981,8 @@ bool Item_equal::walk(Item_processor processor, byte *arg) Item *Item_equal::transform(Item_transformer transformer, byte *arg) { + DBUG_ASSERT(!current_thd->is_stmt_prepare()); + List_iterator<Item_field> it(fields); Item *item; while ((item= it++)) @@ -3991,8 +3990,15 @@ Item *Item_equal::transform(Item_transformer transformer, byte *arg) Item *new_item= item->transform(transformer, arg); if (!new_item) return 0; + + /* + THD::change_item_tree() should be called only if the tree was + really transformed, i.e. when a new item has been created. + Otherwise we'll be allocating a lot of unnecessary memory for + change records at each execution. + */ if (new_item != item) - it.replace((Item_field *) new_item); + current_thd->change_item_tree((Item **) it.ref(), new_item); } return Item_func::transform(transformer, arg); } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index a2b10eacc79..f2c43833bd9 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -240,6 +240,7 @@ public: } Item *neg_transformer(THD *thd); virtual Item *negated_item(); + bool subst_argument_checker(byte **arg) { return TRUE; } }; class Item_func_not :public Item_bool_func @@ -311,6 +312,7 @@ public: void set_sum_test(Item_sum_hybrid *item) { test_sum_item= item; }; void set_sub_test(Item_maxmin_subselect *item) { test_sub_item= item; }; bool empty_underlying_subquery(); + Item *neg_transformer(THD *thd); }; @@ -321,6 +323,7 @@ public: Item_func_nop_all(Item *a) :Item_func_not_all(a) {} longlong val_int(); const char *func_name() const { return "<nop>"; } + Item *neg_transformer(THD *thd); }; @@ -1169,6 +1172,9 @@ public: Item *transform(Item_transformer transformer, byte *arg); void traverse_cond(Cond_traverser, void *arg, traverse_order order); void neg_arguments(THD *thd); + bool subst_argument_checker(byte **arg) { return TRUE; } + Item *compile(Item_analyzer analyzer, byte **arg_p, + Item_transformer transformer, byte *arg_t); }; diff --git a/sql/item_create.cc b/sql/item_create.cc index 7d57757432e..e0e18094705 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -450,6 +450,7 @@ Item *create_func_cast(Item *a, Cast_target cast_type, int len, int dec, CHARSET_INFO *cs) { Item *res; + int tmp_len; LINT_INIT(res); switch (cast_type) { @@ -460,7 +461,13 @@ Item *create_func_cast(Item *a, Cast_target cast_type, int len, int dec, case ITEM_CAST_TIME: res= new Item_time_typecast(a); break; case ITEM_CAST_DATETIME: res= new Item_datetime_typecast(a); break; case ITEM_CAST_DECIMAL: - res= new Item_decimal_typecast(a, (len>0) ? len : 10, dec ? dec : 2); + tmp_len= (len>0) ? len : 10; + if (tmp_len < dec) + { + my_error(ER_M_BIGGER_THAN_D, MYF(0), ""); + return 0; + } + res= new Item_decimal_typecast(a, tmp_len, dec ? dec : 2); break; case ITEM_CAST_CHAR: res= new Item_char_typecast(a, len, cs ? cs : diff --git a/sql/item_func.cc b/sql/item_func.cc index 1d906b300b6..2e594c74031 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -234,22 +234,21 @@ void Item_func::traverse_cond(Cond_traverser traverser, } - /* Transform an Item_func object with a transformer callback function SYNOPSIS transform() - transformer the transformer callback function to be applied to the nodes - of the tree of the object - argument parameter to be passed to the transformer + transformer the transformer callback function to be applied to the nodes + of the tree of the object + argument parameter to be passed to the transformer DESCRIPTION - The function recursively applies the transform method with the - same transformer to each argument the function. - If the call of the method for a member item returns a new item + The function recursively applies the transform method to each + argument of the Item_func node. + If the call of the method for an argument item returns a new item the old item is substituted for a new one. - After this the transform method is applied to the root node + After this the transformer is applied to the root node of the Item_func object. RETURN VALUES @@ -258,6 +257,8 @@ void Item_func::traverse_cond(Cond_traverser traverser, Item *Item_func::transform(Item_transformer transformer, byte *argument) { + DBUG_ASSERT(!current_thd->is_stmt_prepare()); + if (arg_count) { Item **arg,**arg_end; @@ -266,6 +267,13 @@ Item *Item_func::transform(Item_transformer transformer, byte *argument) Item *new_item= (*arg)->transform(transformer, argument); if (!new_item) return 0; + + /* + THD::change_item_tree() should be called only if the tree was + really transformed, i.e. when a new item has been created. + Otherwise we'll be allocating a lot of unnecessary memory for + change records at each execution. + */ if (*arg != new_item) current_thd->change_item_tree(arg, new_item); } @@ -274,6 +282,55 @@ Item *Item_func::transform(Item_transformer transformer, byte *argument) } +/* + Compile Item_func object with a processor and a transformer callback functions + + SYNOPSIS + compile() + analyzer the analyzer callback function to be applied to the nodes + of the tree of the object + arg_p in/out parameter to be passed to the processor + transformer the transformer callback function to be applied to the nodes + of the tree of the object + arg_t parameter to be passed to the transformer + + DESCRIPTION + First the function applies the analyzer to the root node of + the Item_func object. Then if the analizer succeeeds (returns TRUE) + the function recursively applies the compile method to each argument + of the Item_func node. + If the call of the method for an argument item returns a new item + the old item is substituted for a new one. + After this the transformer is applied to the root node + of the Item_func object. + + RETURN VALUES + Item returned as the result of transformation of the root node +*/ + +Item *Item_func::compile(Item_analyzer analyzer, byte **arg_p, + Item_transformer transformer, byte *arg_t) +{ + if (!(this->*analyzer)(arg_p)) + return 0; + if (arg_count) + { + Item **arg,**arg_end; + for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++) + { + /* + The same parameter value of arg_p must be passed + to analyze any argument of the condition formula. + */ + byte *arg_v= *arg_p; + Item *new_item= (*arg)->compile(analyzer, &arg_v, transformer, arg_t); + if (new_item && *arg != new_item) + current_thd->change_item_tree(arg, new_item); + } + } + return (this->*transformer)(arg_t); +} + /* See comments in Item_cmp_func::split_sum_func() */ void Item_func::split_sum_func(THD *thd, Item **ref_pointer_array, @@ -398,6 +455,13 @@ Field *Item_func::tmp_table_field(TABLE *t_arg) return res; } + +bool Item_func::is_expensive_processor(byte *arg) +{ + return is_expensive(); +} + + my_decimal *Item_func::val_decimal(my_decimal *decimal_value) { DBUG_ASSERT(fixed); @@ -541,7 +605,7 @@ void Item_func::signal_divide_by_null() Item *Item_func::get_tmp_table_item(THD *thd) { - if (!with_sum_func && !const_item()) + if (!with_sum_func && !const_item() && functype() != SUSERVAR_FUNC) return new Item_field(result_field); return copy_or_same(thd); } @@ -3406,6 +3470,7 @@ static user_var_entry *get_variable(HASH *hash, LEX_STRING &name, entry->length=0; entry->update_query_id=0; entry->collation.set(NULL, DERIVATION_IMPLICIT); + entry->unsigned_flag= 0; /* If we are here, we were called from a SET or a query which sets a variable. Imagine it is this: @@ -3492,6 +3557,7 @@ Item_func_set_user_var::fix_length_and_dec() type - type of new value cs - charset info for new value dv - derivation for new value + unsigned_arg - indiates if a value of type INT_RESULT is unsigned RETURN VALUE False - success, True - failure @@ -3499,7 +3565,8 @@ Item_func_set_user_var::fix_length_and_dec() static bool update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length, - Item_result type, CHARSET_INFO *cs, Derivation dv) + Item_result type, CHARSET_INFO *cs, Derivation dv, + bool unsigned_arg) { if (set_null) { @@ -3547,6 +3614,7 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length, ((my_decimal*)entry->value)->fix_buffer_pointer(); entry->length= length; entry->collation.set(cs, dv); + entry->unsigned_flag= unsigned_arg; } entry->type=type; return 0; @@ -3555,7 +3623,8 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length, bool Item_func_set_user_var::update_hash(void *ptr, uint length, Item_result type, - CHARSET_INFO *cs, Derivation dv) + CHARSET_INFO *cs, Derivation dv, + bool unsigned_arg) { /* If we set a variable explicitely to NULL then keep the old @@ -3564,7 +3633,7 @@ Item_func_set_user_var::update_hash(void *ptr, uint length, Item_result type, if ((null_value= args[0]->null_value) && null_item) type= entry->type; // Don't change type of item if (::update_hash(entry, (null_value= args[0]->null_value), - ptr, length, type, cs, dv)) + ptr, length, type, cs, dv, unsigned_arg)) { current_thd->fatal_error(); // Probably end of memory null_value= 1; @@ -3646,7 +3715,10 @@ String *user_var_entry::val_str(my_bool *null_value, String *str, str->set(*(double*) value, decimals, &my_charset_bin); break; case INT_RESULT: - str->set(*(longlong*) value, &my_charset_bin); + if (!unsigned_flag) + str->set(*(longlong*) value, &my_charset_bin); + else + str->set(*(ulonglong*) value, &my_charset_bin); break; case DECIMAL_RESULT: my_decimal2string(E_DEC_FATAL_ERROR, (my_decimal *)value, 0, 0, 0, str); @@ -3704,29 +3776,38 @@ my_decimal *user_var_entry::val_decimal(my_bool *null_value, my_decimal *val) */ bool -Item_func_set_user_var::check() +Item_func_set_user_var::check(bool use_result_field) { DBUG_ENTER("Item_func_set_user_var::check"); + if (use_result_field) + DBUG_ASSERT(result_field); switch (cached_result_type) { case REAL_RESULT: { - save_result.vreal= args[0]->val_real(); + save_result.vreal= use_result_field ? result_field->val_real() : + args[0]->val_real(); break; } case INT_RESULT: { - save_result.vint= args[0]->val_int(); + save_result.vint= use_result_field ? result_field->val_int() : + args[0]->val_int(); + unsigned_flag= use_result_field ? ((Field_num*)result_field)->unsigned_flag: + args[0]->unsigned_flag; break; } case STRING_RESULT: { - save_result.vstr= args[0]->val_str(&value); + save_result.vstr= use_result_field ? result_field->val_str(&value) : + args[0]->val_str(&value); break; } case DECIMAL_RESULT: { - save_result.vdec= args[0]->val_decimal(&decimal_buff); + save_result.vdec= use_result_field ? + result_field->val_decimal(&decimal_buff) : + args[0]->val_decimal(&decimal_buff); break; } case ROW_RESULT: @@ -3766,36 +3847,37 @@ Item_func_set_user_var::update() case REAL_RESULT: { res= update_hash((void*) &save_result.vreal,sizeof(save_result.vreal), - REAL_RESULT, &my_charset_bin, DERIVATION_IMPLICIT); + REAL_RESULT, &my_charset_bin, DERIVATION_IMPLICIT, 0); break; } case INT_RESULT: { res= update_hash((void*) &save_result.vint, sizeof(save_result.vint), - INT_RESULT, &my_charset_bin, DERIVATION_IMPLICIT); + INT_RESULT, &my_charset_bin, DERIVATION_IMPLICIT, + unsigned_flag); break; } case STRING_RESULT: { if (!save_result.vstr) // Null value res= update_hash((void*) 0, 0, STRING_RESULT, &my_charset_bin, - DERIVATION_IMPLICIT); + DERIVATION_IMPLICIT, 0); else res= update_hash((void*) save_result.vstr->ptr(), save_result.vstr->length(), STRING_RESULT, save_result.vstr->charset(), - DERIVATION_IMPLICIT); + DERIVATION_IMPLICIT, 0); break; } case DECIMAL_RESULT: { if (!save_result.vdec) // Null value res= update_hash((void*) 0, 0, DECIMAL_RESULT, &my_charset_bin, - DERIVATION_IMPLICIT); + DERIVATION_IMPLICIT, 0); else res= update_hash((void*) save_result.vdec, sizeof(my_decimal), DECIMAL_RESULT, - &my_charset_bin, DERIVATION_IMPLICIT); + &my_charset_bin, DERIVATION_IMPLICIT, 0); break; } case ROW_RESULT: @@ -3811,7 +3893,7 @@ Item_func_set_user_var::update() double Item_func_set_user_var::val_real() { DBUG_ASSERT(fixed == 1); - check(); + check(0); update(); // Store expression return entry->val_real(&null_value); } @@ -3819,7 +3901,7 @@ double Item_func_set_user_var::val_real() longlong Item_func_set_user_var::val_int() { DBUG_ASSERT(fixed == 1); - check(); + check(0); update(); // Store expression return entry->val_int(&null_value); } @@ -3827,7 +3909,7 @@ longlong Item_func_set_user_var::val_int() String *Item_func_set_user_var::val_str(String *str) { DBUG_ASSERT(fixed == 1); - check(); + check(0); update(); // Store expression return entry->val_str(&null_value, str, decimals); } @@ -3836,7 +3918,7 @@ String *Item_func_set_user_var::val_str(String *str) my_decimal *Item_func_set_user_var::val_decimal(my_decimal *val) { DBUG_ASSERT(fixed == 1); - check(); + check(0); update(); // Store expression return entry->val_decimal(&null_value, val); } @@ -3861,6 +3943,29 @@ void Item_func_set_user_var::print_as_stmt(String *str) str->append(')'); } +bool Item_func_set_user_var::send(Protocol *protocol, String *str_arg) +{ + if (result_field) + { + check(1); + update(); + return protocol->store(result_field); + } + return Item::send(protocol, str_arg); +} + +void Item_func_set_user_var::make_field(Send_field *tmp_field) +{ + if (result_field) + { + result_field->make_field(tmp_field); + DBUG_ASSERT(tmp_field->table_name != 0); + if (Item::name) + tmp_field->col_name=Item::name; // Use user supplied name + } + else + Item::make_field(tmp_field); +} String * Item_func_get_user_var::val_str(String *str) @@ -4126,7 +4231,7 @@ bool Item_func_get_user_var::set_value(THD *thd, Item_func_set_user_var is not fixed after construction, call fix_fields(). */ - return (!suv || suv->fix_fields(thd, it) || suv->check() || suv->update()); + return (!suv || suv->fix_fields(thd, it) || suv->check(0) || suv->update()); } @@ -4151,7 +4256,7 @@ bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref) void Item_user_var_as_out_param::set_null_value(CHARSET_INFO* cs) { if (::update_hash(entry, TRUE, 0, 0, STRING_RESULT, cs, - DERIVATION_IMPLICIT)) + DERIVATION_IMPLICIT, 0 /* unsigned_arg */)) current_thd->fatal_error(); // Probably end of memory } @@ -4160,7 +4265,7 @@ void Item_user_var_as_out_param::set_value(const char *str, uint length, CHARSET_INFO* cs) { if (::update_hash(entry, FALSE, (void*)str, length, STRING_RESULT, cs, - DERIVATION_IMPLICIT)) + DERIVATION_IMPLICIT, 0 /* unsigned_arg */)) current_thd->fatal_error(); // Probably end of memory } @@ -4830,7 +4935,9 @@ Item_func_sp::execute_impl(THD *thd, Field *return_value_fld) { bool err_status= TRUE; Sub_statement_state statement_state; - Security_context *save_security_ctx= thd->security_ctx, *save_ctx_func; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + Security_context *save_security_ctx= thd->security_ctx; +#endif DBUG_ENTER("Item_func_sp::execute_impl"); @@ -4841,7 +4948,7 @@ Item_func_sp::execute_impl(THD *thd, Field *return_value_fld) thd->security_ctx= context->security_ctx; } #endif - if (find_and_check_access(thd, EXECUTE_ACL, &save_ctx_func)) + if (find_and_check_access(thd)) goto error; /* @@ -4853,13 +4960,11 @@ Item_func_sp::execute_impl(THD *thd, Field *return_value_fld) err_status= m_sp->execute_function(thd, args, arg_count, return_value_fld); thd->restore_sub_statement_state(&statement_state); -#ifndef NO_EMBEDDED_ACCESS_CHECKS - sp_restore_security_context(thd, save_ctx_func); error: +#ifndef NO_EMBEDDED_ACCESS_CHECKS thd->security_ctx= save_security_ctx; -#else -error: #endif + DBUG_RETURN(err_status); } @@ -4976,70 +5081,38 @@ Item_func_sp::tmp_table_field(TABLE *t_arg) SYNOPSIS find_and_check_access() thd thread handler - want_access requested access - save backup of security context RETURN FALSE Access granted TRUE Requested access can't be granted or function doesn't exists - In this case security context is not changed and *save = 0 NOTES Checks if requested access to function can be granted to user. If function isn't found yet, it searches function first. If function can't be found or user don't have requested access error is raised. - If security context sp_ctx is provided and access can be granted then - switch back to previous context isn't performed. - In case of access error or if context is not provided then - find_and_check_access() switches back to previous security context. */ bool -Item_func_sp::find_and_check_access(THD *thd, ulong want_access, - Security_context **save) +Item_func_sp::find_and_check_access(THD *thd) { - bool res= TRUE; - - *save= 0; // Safety if error if (! m_sp && ! (m_sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, m_name, &thd->sp_func_cache, TRUE))) { my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str); - goto error; + return TRUE; } #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (check_routine_access(thd, want_access, + if (check_routine_access(thd, EXECUTE_ACL, m_sp->m_db.str, m_sp->m_name.str, 0, FALSE)) - goto error; - - sp_change_security_context(thd, m_sp, save); - /* - If we changed context to run as another user, we need to check the - access right for the new context again as someone may have deleted - this person the right to use the procedure - - TODO: - Cache if the definer has the right to use the object on the first - usage and only reset the cache if someone does a GRANT statement - that 'may' affect this. - */ - if (*save && - check_routine_access(thd, want_access, - m_sp->m_db.str, m_sp->m_name.str, 0, FALSE)) - { - sp_restore_security_context(thd, *save); - *save= 0; // Safety - goto error; - } + return TRUE; #endif - res= FALSE; // no error -error: - return res; + return FALSE; } + bool Item_func_sp::fix_fields(THD *thd, Item **ref) { @@ -5050,19 +5123,23 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) { /* Here we check privileges of the stored routine only during view - creation, in order to validate the view. A runtime check is perfomed - in Item_func_sp::execute(), and this method is not called during - context analysis. We do not need to restore the security context - changed in find_and_check_access because all view structures created - in CREATE VIEW are not used for execution. Notice, that during view - creation we do not infer into stored routine bodies and do not check - privileges of its statements, which would probably be a good idea - especially if the view has SQL SECURITY DEFINER and the used stored - procedure has SQL SECURITY DEFINER + creation, in order to validate the view. A runtime check is + perfomed in Item_func_sp::execute(), and this method is not + called during context analysis. Notice, that during view + creation we do not infer into stored routine bodies and do not + check privileges of its statements, which would probably be a + good idea especially if the view has SQL SECURITY DEFINER and + the used stored procedure has SQL SECURITY DEFINER. */ - Security_context *save_ctx; - if (!(res= find_and_check_access(thd, EXECUTE_ACL, &save_ctx))) - sp_restore_security_context(thd, save_ctx); + res= find_and_check_access(thd); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + Security_context *save_secutiry_ctx; + if (!res && !(res= set_routine_security_ctx(thd, m_sp, false, + &save_secutiry_ctx))) + { + sp_restore_security_context(thd, save_secutiry_ctx); + } +#endif /* ! NO_EMBEDDED_ACCESS_CHECKS */ } return res; } diff --git a/sql/item_func.h b/sql/item_func.h index 2ca4be9f3f2..177daf0311f 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -54,8 +54,8 @@ public: SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN, NOT_FUNC, NOT_ALL_FUNC, NOW_FUNC, TRIG_COND_FUNC, - GUSERVAR_FUNC, COLLATE_FUNC, - EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP }; + SUSERVAR_FUNC, GUSERVAR_FUNC, COLLATE_FUNC, + EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC }; enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL, OPTIMIZE_EQUAL }; enum Type type() const { return FUNC_ITEM; } @@ -156,7 +156,10 @@ public: { return (null_value=args[0]->get_time(ltime)); } - bool is_null() { (void) val_int(); return null_value; } + bool is_null() { + (void) val_int(); /* Discard result. It sets null_value as side-effect. */ + return null_value; + } void signal_divide_by_null(); friend class udf_handler; Field *tmp_table_field() { return result_field; } @@ -184,8 +187,12 @@ public: } bool walk(Item_processor processor, byte *arg); Item *transform(Item_transformer transformer, byte *arg); + Item* compile(Item_analyzer analyzer, byte **arg_p, + Item_transformer transformer, byte *arg_t); void traverse_cond(Cond_traverser traverser, void * arg, traverse_order order); + bool is_expensive_processor(byte *arg); + virtual bool is_expensive() { return 0; } }; @@ -930,6 +937,7 @@ public: Item_udf_func(udf_func *udf_arg, List<Item> &list) :Item_func(list), udf(udf_arg) {} const char *func_name() const { return udf.name(); } + enum Functype functype() const { return UDF_FUNC; } bool fix_fields(THD *thd, Item **ref) { DBUG_ASSERT(fixed == 0); @@ -942,6 +950,7 @@ public: void cleanup(); Item_result result_type () const { return udf.result_type(); } table_map not_null_tables() const { return 0; } + bool is_expensive() { return 1; } }; @@ -1154,21 +1163,22 @@ class Item_func_set_user_var :public Item_func String *vstr; my_decimal *vdec; } save_result; - String save_buff; - public: LEX_STRING name; // keep it public Item_func_set_user_var(LEX_STRING a,Item *b) :Item_func(b), cached_result_type(INT_RESULT), name(a) {} + enum Functype functype() const { return SUSERVAR_FUNC; } double val_real(); longlong val_int(); String *val_str(String *str); my_decimal *val_decimal(my_decimal *); - bool update_hash(void *ptr, uint length, enum Item_result type, - CHARSET_INFO *cs, Derivation dv); - bool check(); + bool update_hash(void *ptr, uint length, enum Item_result type, + CHARSET_INFO *cs, Derivation dv, bool unsigned_arg); + bool send(Protocol *protocol, String *str_arg); + void make_field(Send_field *tmp_field); + bool check(bool use_result_field); bool update(); enum Item_result result_type () const { return cached_result_type; } bool fix_fields(THD *thd, Item **ref); @@ -1465,11 +1475,11 @@ public: { context= (Name_resolution_context *)cntx; return FALSE; } void fix_length_and_dec(); - bool find_and_check_access(THD * thd, ulong want_access, - Security_context **backup); + bool find_and_check_access(THD * thd); virtual enum Functype functype() const { return FUNC_SP; } bool fix_fields(THD *thd, Item **ref); + bool is_expensive() { return 1; } }; diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 2b92e72e728..c5200e26cb7 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -25,6 +25,12 @@ #ifdef HAVE_SPATIAL #include <m_ctype.h> +Field *Item_geometry_func::tmp_table_field(TABLE *t_arg) +{ + return new Field_geom(max_length, maybe_null, name, t_arg, + (Field::geometry_type) get_geometry_type()); +} + void Item_geometry_func::fix_length_and_dec() { collation.set(&my_charset_bin); @@ -32,6 +38,10 @@ void Item_geometry_func::fix_length_and_dec() max_length=MAX_BLOB_WIDTH; } +int Item_geometry_func::get_geometry_type() const +{ + return (int)Field::GEOM_GEOMETRY; +} String *Item_func_geometry_from_text::val_str(String *str) { @@ -152,6 +162,12 @@ String *Item_func_geometry_type::val_str(String *str) } +int Item_func_envelope::get_geometry_type() const +{ + return (int) Field::GEOM_POLYGON; +} + + String *Item_func_envelope::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -176,6 +192,12 @@ String *Item_func_envelope::val_str(String *str) } +int Item_func_centroid::get_geometry_type() const +{ + return (int) Field::GEOM_POINT; +} + + String *Item_func_centroid::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -310,6 +332,12 @@ err: */ +int Item_func_point::get_geometry_type() const +{ + return (int) Field::GEOM_POINT; +} + + String *Item_func_point::val_str(String *str) { DBUG_ASSERT(fixed == 1); diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h index 1f64fdba609..4848f59301d 100644 --- a/sql/item_geofunc.h +++ b/sql/item_geofunc.h @@ -33,6 +33,8 @@ public: Item_geometry_func(List<Item> &list) :Item_str_func(list) {} void fix_length_and_dec(); enum_field_types field_type() const { return MYSQL_TYPE_GEOMETRY; } + Field *tmp_table_field(TABLE *t_arg); + virtual int get_geometry_type() const; }; class Item_func_geometry_from_text: public Item_geometry_func @@ -89,6 +91,7 @@ public: Item_func_centroid(Item *a): Item_geometry_func(a) {} const char *func_name() const { return "centroid"; } String *val_str(String *); + int get_geometry_type() const; }; class Item_func_envelope: public Item_geometry_func @@ -97,6 +100,7 @@ public: Item_func_envelope(Item *a): Item_geometry_func(a) {} const char *func_name() const { return "envelope"; } String *val_str(String *); + int get_geometry_type() const; }; class Item_func_point: public Item_geometry_func @@ -106,6 +110,7 @@ public: Item_func_point(Item *a, Item *b, Item *srid): Item_geometry_func(a, b, srid) {} const char *func_name() const { return "point"; } String *val_str(String *); + int get_geometry_type() const; }; class Item_func_spatial_decomp: public Item_geometry_func diff --git a/sql/item_row.cc b/sql/item_row.cc index f5c8d511025..d7591b9865d 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -154,12 +154,22 @@ bool Item_row::walk(Item_processor processor, byte *arg) Item *Item_row::transform(Item_transformer transformer, byte *arg) { + DBUG_ASSERT(!current_thd->is_stmt_prepare()); + for (uint i= 0; i < arg_count; i++) { Item *new_item= items[i]->transform(transformer, arg); if (!new_item) return 0; - items[i]= new_item; + + /* + THD::change_item_tree() should be called only if the tree was + really transformed, i.e. when a new item has been created. + Otherwise we'll be allocating a lot of unnecessary memory for + change records at each execution. + */ + if (items[i] != new_item) + current_thd->change_item_tree(&items[i], new_item); } return (this->*transformer)(arg); } diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 8bc1bfaf7dd..46a96c28d88 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -124,6 +124,7 @@ String *Item_func_md5::val_str(String *str) { DBUG_ASSERT(fixed == 1); String * sptr= args[0]->val_str(str); + str->set_charset(&my_charset_bin); if (sptr) { my_MD5_CTX context; @@ -170,6 +171,7 @@ String *Item_func_sha::val_str(String *str) { DBUG_ASSERT(fixed == 1); String * sptr= args[0]->val_str(str); + str->set_charset(&my_charset_bin); if (sptr) /* If we got value different from NULL */ { SHA1_CONTEXT context; /* Context used to generate SHA1 hash */ @@ -752,44 +754,47 @@ String *Item_func_reverse::val_str(String *str) { DBUG_ASSERT(fixed == 1); String *res = args[0]->val_str(str); - char *ptr,*end; + char *ptr, *end, *tmp; if ((null_value=args[0]->null_value)) return 0; /* An empty string is a special case as the string pointer may be null */ if (!res->length()) return &my_empty_string; - res=copy_if_not_alloced(str,res,res->length()); - ptr = (char *) res->ptr(); - end=ptr+res->length(); + if (tmp_value.alloced_length() < res->length() && + tmp_value.realloc(res->length())) + { + null_value= 1; + return 0; + } + tmp_value.length(res->length()); + tmp_value.set_charset(res->charset()); + ptr= (char *) res->ptr(); + end= ptr + res->length(); + tmp= (char *) tmp_value.ptr() + tmp_value.length(); #ifdef USE_MB if (use_mb(res->charset())) { - String tmpstr; - tmpstr.copy(*res); - char *tmp = (char *) tmpstr.ptr() + tmpstr.length(); register uint32 l; while (ptr < end) { - if ((l=my_ismbchar(res->charset(), ptr,end))) - tmp-=l, memcpy(tmp,ptr,l), ptr+=l; + if ((l= my_ismbchar(res->charset(),ptr,end))) + { + tmp-= l; + memcpy(tmp,ptr,l); + ptr+= l; + } else - *--tmp=*ptr++; + *--tmp= *ptr++; } - memcpy((char *) res->ptr(),(char *) tmpstr.ptr(), res->length()); } else #endif /* USE_MB */ { - char tmp; while (ptr < end) - { - tmp=*ptr; - *ptr++=*--end; - *end=tmp; - } + *--tmp= *ptr++; } - return res; + return &tmp_value; } @@ -1038,7 +1043,7 @@ String *Item_func_left::val_str(String *str) long length =(long) args[1]->val_int(); uint char_pos; - if ((null_value=args[0]->null_value)) + if ((null_value=(args[0]->null_value || args[1]->null_value))) return 0; if (length <= 0) return &my_empty_string; @@ -1078,7 +1083,7 @@ String *Item_func_right::val_str(String *str) String *res =args[0]->val_str(str); long length =(long) args[1]->val_int(); - if ((null_value=args[0]->null_value)) + if ((null_value=(args[0]->null_value || args[1]->null_value))) return 0; /* purecov: inspected */ if (length <= 0) return &my_empty_string; /* purecov: inspected */ @@ -1500,6 +1505,23 @@ void Item_func_trim::fix_length_and_dec() } } +void Item_func_trim::print(String *str) +{ + if (arg_count == 1) + { + Item_func::print(str); + return; + } + str->append(Item_func_trim::func_name()); + str->append('('); + str->append(mode_name()); + str->append(' '); + args[1]->print(str); + str->append(STRING_WITH_LEN(" from ")); + args[0]->print(str); + str->append(')'); +} + /* Item_func_password */ @@ -1585,7 +1607,7 @@ String *Item_func_encrypt::val_str(String *str) null_value= 1; return 0; } - str->set(tmp,(uint) strlen(tmp),res->charset()); + str->set(tmp, (uint) strlen(tmp), &my_charset_bin); str->copy(); pthread_mutex_unlock(&LOCK_crypt); return str; @@ -2021,7 +2043,7 @@ String *Item_func_make_set::val_str(String *str) return &my_empty_string; result= &tmp_str; } - if (tmp_str.append(',') || tmp_str.append(*res)) + if (tmp_str.append(STRING_WITH_LEN(","), &my_charset_bin) || tmp_str.append(*res)) return &my_empty_string; } } @@ -2031,6 +2053,26 @@ String *Item_func_make_set::val_str(String *str) } +Item *Item_func_make_set::transform(Item_transformer transformer, byte *arg) +{ + DBUG_ASSERT(!current_thd->is_stmt_prepare()); + + Item *new_item= item->transform(transformer, arg); + if (!new_item) + return 0; + + /* + THD::change_item_tree() should be called only if the tree was + really transformed, i.e. when a new item has been created. + Otherwise we'll be allocating a lot of unnecessary memory for + change records at each execution. + */ + if (item != new_item) + current_thd->change_item_tree(&item, new_item); + return Item_str_func::transform(transformer, arg); +} + + void Item_func_make_set::print(String *str) { str->append(STRING_WITH_LEN("make_set(")); @@ -2339,17 +2381,33 @@ String *Item_func_conv::val_str(String *str) abs(to_base) > 36 || abs(to_base) < 2 || abs(from_base) > 36 || abs(from_base) < 2 || !(res->length())) { - null_value=1; - return 0; + null_value= 1; + return NULL; } - null_value=0; + null_value= 0; unsigned_flag= !(from_base < 0); - if (from_base < 0) - dec= my_strntoll(res->charset(),res->ptr(),res->length(),-from_base,&endptr,&err); + + if (args[0]->field_type() == MYSQL_TYPE_BIT) + { + /* + Special case: The string representation of BIT doesn't resemble the + decimal representation, so we shouldn't change it to string and then to + decimal. + */ + dec= args[0]->val_int(); + } else - dec= (longlong) my_strntoull(res->charset(),res->ptr(),res->length(),from_base,&endptr,&err); - ptr= longlong2str(dec,ans,to_base); - if (str->copy(ans,(uint32) (ptr-ans), default_charset())) + { + if (from_base < 0) + dec= my_strntoll(res->charset(), res->ptr(), res->length(), + -from_base, &endptr, &err); + else + dec= (longlong) my_strntoull(res->charset(), res->ptr(), res->length(), + from_base, &endptr, &err); + } + + ptr= longlong2str(dec, ans, to_base); + if (str->copy(ans, (uint32) (ptr-ans), default_charset())) return &my_empty_string; return str; } @@ -2679,8 +2737,12 @@ String* Item_func_export_set::val_str(String* str) } break; case 3: - sep_buf.set(STRING_WITH_LEN(","), default_charset()); - sep = &sep_buf; + { + /* errors is not checked - assume "," can always be converted */ + uint errors; + sep_buf.copy(STRING_WITH_LEN(","), &my_charset_bin, collation.collation, &errors); + sep = &sep_buf; + } break; default: DBUG_ASSERT(0); // cannot happen @@ -2965,6 +3027,16 @@ String *Item_func_uncompress::val_str(String *str) if (res->is_empty()) return res; + /* If length is less than 4 bytes, data is corrupt */ + if (res->length() <= 4) + { + push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_ZLIB_Z_DATA_ERROR, + ER(ER_ZLIB_Z_DATA_ERROR)); + goto err; + } + + /* Size of uncompressed data is stored as first 4 bytes of field */ new_size= uint4korr(res->ptr()) & 0x3FFFFFFF; if (new_size > current_thd->variables.max_allowed_packet) { diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index a2204f22822..528180b803d 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -43,7 +43,10 @@ class Item_func_md5 :public Item_str_func { String tmp_value; public: - Item_func_md5(Item *a) :Item_str_func(a) {} + Item_func_md5(Item *a) :Item_str_func(a) + { + collation.set(&my_charset_bin); + } String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "md5"; } @@ -53,7 +56,10 @@ public: class Item_func_sha :public Item_str_func { public: - Item_func_sha(Item *a) :Item_str_func(a) {} + Item_func_sha(Item *a) :Item_str_func(a) + { + collation.set(&my_charset_bin); + } String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "sha"; } @@ -102,6 +108,7 @@ public: class Item_func_reverse :public Item_str_func { + String tmp_value; public: Item_func_reverse(Item *a) :Item_str_func(a) {} String *val_str(String *); @@ -232,6 +239,8 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "trim"; } + void print(String *str); + virtual const char *mode_name() const { return "both"; } }; @@ -242,6 +251,7 @@ public: Item_func_ltrim(Item *a) :Item_func_trim(a) {} String *val_str(String *); const char *func_name() const { return "ltrim"; } + const char *mode_name() const { return "leading"; } }; @@ -252,6 +262,7 @@ public: Item_func_rtrim(Item *a) :Item_func_trim(a) {} String *val_str(String *); const char *func_name() const { return "rtrim"; } + const char *mode_name() const { return "trailing"; } }; @@ -320,9 +331,21 @@ public: class Item_func_encrypt :public Item_str_func { String tmp_value; + + /* Encapsulate common constructor actions */ + void constructor_helper() + { + collation.set(&my_charset_bin); + } public: - Item_func_encrypt(Item *a) :Item_str_func(a) {} - Item_func_encrypt(Item *a, Item *b): Item_str_func(a,b) {} + Item_func_encrypt(Item *a) :Item_str_func(a) + { + constructor_helper(); + } + Item_func_encrypt(Item *a, Item *b): Item_str_func(a,b) + { + constructor_helper(); + } String *val_str(String *); void fix_length_and_dec() { maybe_null=1; max_length = 13; } const char *func_name() const { return "encrypt"; } @@ -470,14 +493,7 @@ public: return item->walk(processor, arg) || Item_str_func::walk(processor, arg); } - Item *transform(Item_transformer transformer, byte *arg) - { - Item *new_item= item->transform(transformer, arg); - if (!new_item) - return 0; - item= new_item; - return Item_str_func::transform(transformer, arg); - } + Item *transform(Item_transformer transformer, byte *arg); void print(String *str); }; @@ -723,7 +739,7 @@ public: void fix_length_and_dec(); bool eq(const Item *item, bool binary_cmp) const; const char *func_name() const { return "collate"; } - enum Functype func_type() const { return COLLATE_FUNC; } + enum Functype functype() const { return COLLATE_FUNC; } void print(String *str); Item_field *filed_for_view_update() { @@ -803,7 +819,7 @@ class Item_func_uncompress: public Item_str_func String buffer; public: Item_func_uncompress(Item *a): Item_str_func(a){} - void fix_length_and_dec(){max_length= MAX_BLOB_WIDTH;} + void fix_length_and_dec(){ maybe_null= 1; max_length= MAX_BLOB_WIDTH; } const char *func_name() const{return "uncompress";} String *val_str(String *) ZLIB_DEPENDED_FUNCTION }; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index dda1b46ab10..0ad517609c9 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -572,14 +572,14 @@ Item_in_subselect::Item_in_subselect(Item * left_exp, } Item_allany_subselect::Item_allany_subselect(Item * left_exp, - Comp_creator *fn, + chooser_compare_func_creator fc, st_select_lex *select_lex, bool all_arg) - :Item_in_subselect(), all(all_arg) + :Item_in_subselect(), func_creator(fc), all(all_arg) { DBUG_ENTER("Item_in_subselect::Item_in_subselect"); left_expr= left_exp; - func= fn; + func= func_creator(all_arg); init(select_lex, new select_exists_subselect(this)); max_columns= 1; abort_on_null= 0; @@ -802,7 +802,8 @@ Item_in_subselect::single_value_transformer(JOIN *join, if (!select_lex->group_list.elements && !select_lex->having && !select_lex->with_sum_func && - !(select_lex->next_select())) + !(select_lex->next_select()) && + select_lex->table_list.elements) { Item_sum_hybrid *item; nesting_map save_allow_sum_func; @@ -1097,24 +1098,23 @@ Item_in_subselect::row_value_transformer(JOIN *join) DBUG_RETURN(RES_ERROR); Item *item_eq= new Item_func_eq(new - Item_direct_ref(&select_lex->context, - (*optimizer->get_cache())-> - addr(i), - (char *)"<no matter>", - (char *)in_left_expr_name), + Item_ref(&select_lex->context, + (*optimizer->get_cache())-> + addr(i), + (char *)"<no matter>", + (char *)in_left_expr_name), new - Item_direct_ref(&select_lex->context, - select_lex->ref_pointer_array + i, - (char *)"<no matter>", - (char *)"<list ref>") + Item_ref(&select_lex->context, + select_lex->ref_pointer_array + i, + (char *)"<no matter>", + (char *)"<list ref>") ); Item *item_isnull= new Item_func_isnull(new - Item_direct_ref(&select_lex->context, - select_lex-> - ref_pointer_array+i, - (char *)"<no matter>", - (char *)"<list ref>") + Item_ref(&select_lex->context, + select_lex->ref_pointer_array+i, + (char *)"<no matter>", + (char *)"<list ref>") ); having_item= and_items(having_item, @@ -1124,11 +1124,11 @@ Item_in_subselect::row_value_transformer(JOIN *join) new Item_is_not_null_test(this, new - Item_direct_ref(&select_lex->context, - select_lex-> - ref_pointer_array + i, - (char *)"<no matter>", - (char *)"<list ref>") + Item_ref(&select_lex->context, + select_lex-> + ref_pointer_array + i, + (char *)"<no matter>", + (char *)"<list ref>") ) ); item_having_part2->top_level_item(); @@ -1184,11 +1184,11 @@ Item_in_subselect::row_value_transformer(JOIN *join) new Item_is_not_null_test(this, new - Item_direct_ref(&select_lex->context, - select_lex-> - ref_pointer_array + i, - (char *)"<no matter>", - (char *)"<list ref>") + Item_ref(&select_lex->context, + select_lex-> + ref_pointer_array + i, + (char *)"<no matter>", + (char *)"<list ref>") ) ); item_isnull= new @@ -1510,6 +1510,7 @@ static Item_result set_row(List<Item> &item_list, Item *item, item->max_length= sel_item->max_length; res_type= sel_item->result_type(); item->decimals= sel_item->decimals; + item->unsigned_flag= sel_item->unsigned_flag; *maybe_null= sel_item->maybe_null; if (!(row[i]= Item_cache::get_cache(res_type))) return STRING_RESULT; // we should return something diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 293408dc09e..45df4f3880d 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -269,14 +269,13 @@ public: /* ALL/ANY/SOME subselect */ class Item_allany_subselect :public Item_in_subselect { -protected: - Comp_creator *func; - public: + chooser_compare_func_creator func_creator; + Comp_creator *func; bool all; - Item_allany_subselect(Item * left_expr, Comp_creator *f, - st_select_lex *select_lex, bool all); + Item_allany_subselect(Item * left_expr, chooser_compare_func_creator fc, + st_select_lex *select_lex, bool all); // only ALL subquery has upper not subs_type substype() { return all?ALL_SUBS:ANY_SUBS; } diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 4d70debb966..bcd8270e52f 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -290,7 +290,9 @@ Item_sum::Item_sum(THD *thd, Item_sum *item): void Item_sum::mark_as_sum_func() { - current_thd->lex->current_select->with_sum_func= 1; + SELECT_LEX *cur_select= current_thd->lex->current_select; + cur_select->n_sum_items++; + cur_select->with_sum_func= 1; with_sum_func= 1; } @@ -377,7 +379,13 @@ Field *Item_sum::create_tmp_field(bool group, TABLE *table, case INT_RESULT: return new Field_longlong(max_length,maybe_null,name,table,unsigned_flag); case STRING_RESULT: - if (max_length/collation.collation->mbmaxlen > 255 && convert_blob_length) + /* + Make sure that the blob fits into a Field_varstring which has + 2-byte lenght. + */ + if (max_length/collation.collation->mbmaxlen > 255 && + max_length/collation.collation->mbmaxlen < UINT_MAX16 && + convert_blob_length) return new Field_varstring(convert_blob_length, maybe_null, name, table, collation.collation); @@ -1256,9 +1264,6 @@ Field *Item_sum_variance::create_tmp_field(bool group, TABLE *table, sizeof(double)*2) + sizeof(longlong), 0, name, table, &my_charset_bin); } - if (hybrid_type == DECIMAL_RESULT) - return new Field_new_decimal(max_length, maybe_null, name, table, - decimals, unsigned_flag); return new Field_double(max_length, maybe_null,name,table,decimals); } diff --git a/sql/item_sum.h b/sql/item_sum.h index f4ff257aa4e..f1ea95214de 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -688,7 +688,7 @@ public: { return sample ? "var_samp(" : "variance("; } Item *copy_or_same(THD* thd); Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length); - enum Item_result result_type () const { return hybrid_type; } + enum Item_result result_type () const { return REAL_RESULT; } }; class Item_sum_std; diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 9e1962835c8..30230005f6e 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -224,7 +224,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, tmp= (char*) val + min(2, val_len); l_time->day= (int) my_strtoll10(val, &tmp, &error); /* Skip 'st, 'nd, 'th .. */ - val= tmp + min((int) (end-tmp), 2); + val= tmp + min((int) (val_end-tmp), 2); break; /* Hour */ @@ -1704,14 +1704,12 @@ uint Item_func_date_format::format_length(const String *format) case 'u': /* week (00..52), where week starts with Monday */ case 'V': /* week 1..53 used with 'x' */ case 'v': /* week 1..53 used with 'x', where week starts with Monday */ - case 'H': /* hour (00..23) */ case 'y': /* year, numeric, 2 digits */ case 'm': /* month, numeric */ case 'd': /* day (of the month), numeric */ case 'h': /* hour (01..12) */ case 'I': /* --||-- */ case 'i': /* minutes, numeric */ - case 'k': /* hour ( 0..23) */ case 'l': /* hour ( 1..12) */ case 'p': /* locale's AM or PM */ case 'S': /* second (00..61) */ @@ -1720,6 +1718,10 @@ uint Item_func_date_format::format_length(const String *format) case 'e': /* day (0..31) */ size += 2; break; + case 'k': /* hour ( 0..23) */ + case 'H': /* hour (00..23; value > 23 OK, padding always 2-digit) */ + size += 7; /* docs allow > 23, range depends on sizeof(unsigned int) */ + break; case 'r': /* time, 12-hour (hh:mm:ss [AP]M) */ size += 11; break; @@ -2877,6 +2879,8 @@ longlong Item_func_timestamp_diff::val_int() { uint year_beg, year_end, month_beg, month_end, day_beg, day_end; uint years= 0; + uint second_beg, second_end, microsecond_beg, microsecond_end; + if (neg == -1) { year_beg= ltime2.year; @@ -2885,6 +2889,10 @@ longlong Item_func_timestamp_diff::val_int() month_end= ltime1.month; day_beg= ltime2.day; day_end= ltime1.day; + second_beg= ltime2.hour * 3600 + ltime2.minute * 60 + ltime2.second; + second_end= ltime1.hour * 3600 + ltime1.minute * 60 + ltime1.second; + microsecond_beg= ltime2.second_part; + microsecond_end= ltime1.second_part; } else { @@ -2894,6 +2902,10 @@ longlong Item_func_timestamp_diff::val_int() month_end= ltime2.month; day_beg= ltime1.day; day_end= ltime2.day; + second_beg= ltime1.hour * 3600 + ltime1.minute * 60 + ltime1.second; + second_end= ltime2.hour * 3600 + ltime2.minute * 60 + ltime2.second; + microsecond_beg= ltime1.second_part; + microsecond_end= ltime2.second_part; } /* calc years */ @@ -2907,8 +2919,13 @@ longlong Item_func_timestamp_diff::val_int() months+= 12 - (month_beg - month_end); else months+= (month_end - month_beg); + if (day_end < day_beg) months-= 1; + else if ((day_end == day_beg) && + ((second_end < second_beg) || + (second_end == second_beg && microsecond_end < microsecond_beg))) + months-= 1; } switch (int_type) { diff --git a/sql/lock.cc b/sql/lock.cc index 97a080c5634..90ddcc957a2 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -854,6 +854,7 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list) TABLE *table; char key[MAX_DBKEY_LENGTH]; char *db= table_list->db; + int table_in_key_offset; uint key_length; HASH_SEARCH_STATE state; DBUG_ENTER("lock_table_name"); @@ -861,8 +862,9 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list) safe_mutex_assert_owner(&LOCK_open); - key_length=(uint) (strmov(strmov(key,db)+1,table_list->table_name) - -key)+ 1; + table_in_key_offset= strmov(key, db) - key + 1; + key_length= (uint)(strmov(key + table_in_key_offset, table_list->table_name) + - key) + 1; /* Only insert the table if we haven't insert it already */ @@ -883,6 +885,7 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list) table->s= &table->share_not_to_be_used; memcpy((table->s->table_cache_key= (char*) (table+1)), key, key_length); table->s->db= table->s->table_cache_key; + table->s->table_name= table->s->table_cache_key + table_in_key_offset; table->s->key_length=key_length; table->in_use=thd; table->locked_by_name=1; diff --git a/sql/log.cc b/sql/log.cc index ebd1d10d8b7..1cd01865f9f 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -723,13 +723,18 @@ shutdown the MySQL server and restart it.", name, errno); int MYSQL_LOG::get_current_log(LOG_INFO* linfo) { pthread_mutex_lock(&LOCK_log); + int ret = raw_get_current_log(linfo); + pthread_mutex_unlock(&LOCK_log); + return ret; +} + +int MYSQL_LOG::raw_get_current_log(LOG_INFO* linfo) +{ strmake(linfo->log_file_name, log_file_name, sizeof(linfo->log_file_name)-1); linfo->pos = my_b_tell(&log_file); - pthread_mutex_unlock(&LOCK_log); return 0; } - /* Move all data up in a file in an filename index file @@ -2385,6 +2390,12 @@ void print_buffer_to_nt_eventlog(enum loglevel level, char *buff, void */ +#ifdef EMBEDDED_LIBRARY +void vprint_msg_to_log(enum loglevel level __attribute__((unused)), + const char *format __attribute__((unused)), + va_list argsi __attribute__((unused))) +{} +#else /*!EMBEDDED_LIBRARY*/ void vprint_msg_to_log(enum loglevel level, const char *format, va_list args) { char buff[1024]; @@ -2400,6 +2411,7 @@ void vprint_msg_to_log(enum loglevel level, const char *format, va_list args) DBUG_VOID_RETURN; } +#endif /*EMBEDDED_LIBRARY*/ void sql_print_error(const char *format, ...) diff --git a/sql/log_event.cc b/sql/log_event.cc index cf5dbb1e77c..219434ab218 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3846,7 +3846,7 @@ int User_var_log_event::exec_event(struct st_relay_log_info* rli) a single record and with a single column. Thus, like a column value, it could always have IMPLICIT derivation. */ - e.update_hash(val, val_len, type, charset, DERIVATION_IMPLICIT); + e.update_hash(val, val_len, type, charset, DERIVATION_IMPLICIT, 0); free_root(thd->mem_root,0); rli->inc_event_relay_log_pos(); diff --git a/sql/message.mc b/sql/message.mc new file mode 100644 index 00000000000..a1a7c8cff7e --- /dev/null +++ b/sql/message.mc @@ -0,0 +1,8 @@ +MessageId = 100 +Severity = Error +Facility = Application +SymbolicName = MSG_DEFAULT +Language = English +%1For more information, see Help and Support Center at http://www.mysql.com. + + diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index c9ae743addd..f293c769d75 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -105,6 +105,15 @@ typedef struct my_locale_st TYPELIB *ab_month_names; TYPELIB *day_names; TYPELIB *ab_day_names; +#ifdef __cplusplus + my_locale_st(const char *name_par, const char *descr_par, bool is_ascii_par, + TYPELIB *month_names_par, TYPELIB *ab_month_names_par, + TYPELIB *day_names_par, TYPELIB *ab_day_names_par) : + name(name_par), description(descr_par), is_ascii(is_ascii_par), + month_names(month_names_par), ab_month_names(ab_month_names_par), + day_names(day_names_par), ab_day_names(ab_day_names_par) + {} +#endif } MY_LOCALE; extern MY_LOCALE my_locale_en_US; @@ -416,6 +425,7 @@ void view_store_options(THD *thd, st_table_list *table, String *buff); #define TL_OPTION_UPDATING 1 #define TL_OPTION_FORCE_INDEX 2 #define TL_OPTION_IGNORE_LEAVES 4 +#define TL_OPTION_ALIAS 8 /* Some portable defines */ @@ -520,9 +530,11 @@ enum enum_var_type OPT_DEFAULT= 0, OPT_SESSION, OPT_GLOBAL }; class sys_var; +class Comp_creator; +typedef Comp_creator* (*chooser_compare_func_creator)(bool invert); #include "item.h" extern my_decimal decimal_zero; -typedef Comp_creator* (*chooser_compare_func_creator)(bool invert); + /* sql_parse.cc */ void free_items(Item *item); void cleanup_items(Item *item); @@ -555,6 +567,8 @@ void get_default_definer(THD *thd, LEX_USER *definer); LEX_USER *create_default_definer(THD *thd); LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name); LEX_USER *get_current_user(THD *thd, LEX_USER *user); +bool check_string_length(CHARSET_INFO *cs, LEX_STRING *str, + const char *err_msg, uint max_length); enum enum_mysql_completiontype { ROLLBACK_RELEASE=-2, ROLLBACK=1, ROLLBACK_AND_CHAIN=7, @@ -865,8 +879,6 @@ bool mysqld_show_create_db(THD *thd, char *dbname, HA_CREATE_INFO *create); void mysqld_list_processes(THD *thd,const char *user,bool verbose); int mysqld_show_status(THD *thd); int mysqld_show_variables(THD *thd,const char *wild); -int mysql_find_files(THD *thd,List<char> *files, const char *db, - const char *path, const char *wild, bool dir); bool mysqld_show_storage_engines(THD *thd); bool mysqld_show_privileges(THD *thd); bool mysqld_show_column_types(THD *thd); @@ -974,6 +986,7 @@ bool setup_tables_and_check_access (THD *thd, TABLE_LIST *tables, Item **conds, TABLE_LIST **leaves, bool select_insert, + ulong want_access_first, ulong want_access); int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, List<Item> *sum_func_list, uint wild_num); @@ -1138,7 +1151,10 @@ uint check_word(TYPELIB *lib, const char *val, const char *end, bool is_keyword(const char *name, uint len); #define MY_DB_OPT_FILE "db.opt" +bool check_db_dir_existence(const char *db_name); bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create); +bool load_db_opt_by_name(THD *thd, const char *db_name, + HA_CREATE_INFO *db_create_info); bool my_dbopt_init(void); void my_dbopt_cleanup(void); void my_dbopt_free(void); @@ -1323,6 +1339,10 @@ extern handlerton ndbcluster_hton; extern SHOW_COMP_OPTION have_ndbcluster; #endif +/* MRG_MYISAM handler is always built, but may be skipped */ +extern handlerton myisammrg_hton; +#define have_merge_db myisammrg_hton.state + extern SHOW_COMP_OPTION have_isam; extern SHOW_COMP_OPTION have_raid, have_openssl, have_symlink, have_dlopen; extern SHOW_COMP_OPTION have_query_cache; @@ -1494,6 +1514,9 @@ void free_list(I_List <i_string> *list); /* sql_yacc.cc */ extern int MYSQLparse(void *thd); +#ifndef DBUG_OFF +extern void turn_parser_debug_on(); +#endif /* frm_crypt.cc */ #ifdef HAVE_CRYPTED_FRM diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 429bdee17d6..8a4f212340d 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -300,7 +300,7 @@ static bool lower_case_table_names_used= 0; static bool volatile select_thread_in_use, signal_thread_in_use; static bool volatile ready_to_exit; static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0; -static my_bool opt_bdb, opt_isam, opt_ndbcluster; +static my_bool opt_bdb, opt_isam, opt_ndbcluster, opt_merge; static my_bool opt_short_log_format= 0; static uint kill_cached_threads, wake_thread; static ulong killed_threads, thread_created; @@ -713,7 +713,7 @@ static void close_connections(void) { if (ip_sock != INVALID_SOCKET) { - (void) shutdown(ip_sock,2); + (void) shutdown(ip_sock, SHUT_RDWR); (void) closesocket(ip_sock); ip_sock= INVALID_SOCKET; } @@ -745,7 +745,7 @@ static void close_connections(void) #ifdef HAVE_SYS_UN_H if (unix_sock != INVALID_SOCKET) { - (void) shutdown(unix_sock,2); + (void) shutdown(unix_sock, SHUT_RDWR); (void) closesocket(unix_sock); (void) unlink(mysqld_unix_port); unix_sock= INVALID_SOCKET; @@ -848,7 +848,7 @@ static void close_server_sock() { ip_sock=INVALID_SOCKET; DBUG_PRINT("info",("calling shutdown on TCP/IP socket")); - VOID(shutdown(tmp_sock,2)); + VOID(shutdown(tmp_sock, SHUT_RDWR)); #if defined(__NETWARE__) /* The following code is disabled for normal systems as it causes MySQL @@ -863,7 +863,7 @@ static void close_server_sock() { unix_sock=INVALID_SOCKET; DBUG_PRINT("info",("calling shutdown on unix socket")); - VOID(shutdown(tmp_sock,2)); + VOID(shutdown(tmp_sock, SHUT_RDWR)); #if defined(__NETWARE__) /* The following code is disabled for normal systems as it may cause MySQL @@ -1409,7 +1409,7 @@ static void network_init(void) uint waited; uint this_wait; uint retry; - DBUG_ENTER("server_init"); + DBUG_ENTER("network_init"); LINT_INIT(ret); set_ports(); @@ -2392,10 +2392,8 @@ static int my_message_sql(uint error, const char *str, myf MyFlags) if ((thd= current_thd)) { if (thd->spcont && - thd->spcont->find_handler(error, MYSQL_ERROR::WARN_LEVEL_ERROR)) + thd->spcont->handle_error(error, MYSQL_ERROR::WARN_LEVEL_ERROR, thd)) { - if (! thd->spcont->found_handler_here()) - thd->net.report_error= 1; /* Make "select" abort correctly */ DBUG_RETURN(0); } @@ -2738,9 +2736,8 @@ static int init_common_variables(const char *conf_file_name, int argc, get corrupted if accesses with names of different case. */ DBUG_PRINT("info", ("lower_case_table_names: %d", lower_case_table_names)); - if (!lower_case_table_names && - (lower_case_file_system= - (test_if_case_insensitive(mysql_real_data_home) == 1))) + lower_case_file_system= test_if_case_insensitive(mysql_real_data_home); + if (!lower_case_table_names && lower_case_file_system == 1) { if (lower_case_table_names_used) { @@ -2770,6 +2767,11 @@ You should consider changing lower_case_table_names to 1 or 2", mysql_real_data_home); lower_case_table_names= 0; } + else + { + lower_case_file_system= + (test_if_case_insensitive(mysql_real_data_home) == 1); + } /* Reset table_alias_charset, now that lower_case_table_names is set. */ table_alias_charset= (lower_case_table_names ? @@ -4086,7 +4088,7 @@ pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused))) if (req.sink) ((void (*)(int))req.sink)(req.fd); - (void) shutdown(new_sock,2); + (void) shutdown(new_sock, SHUT_RDWR); (void) closesocket(new_sock); continue; } @@ -4101,7 +4103,7 @@ pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused))) if (getsockname(new_sock,&dummy, &dummyLen) < 0) { sql_perror("Error on new connection socket"); - (void) shutdown(new_sock,2); + (void) shutdown(new_sock, SHUT_RDWR); (void) closesocket(new_sock); continue; } @@ -4113,7 +4115,7 @@ pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused))) if (!(thd= new THD)) { - (void) shutdown(new_sock,2); + (void) shutdown(new_sock, SHUT_RDWR); VOID(closesocket(new_sock)); continue; } @@ -4127,7 +4129,7 @@ pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused))) vio_delete(vio_tmp); else { - (void) shutdown(new_sock,2); + (void) shutdown(new_sock, SHUT_RDWR); (void) closesocket(new_sock); } delete thd; @@ -4648,7 +4650,8 @@ enum options_mysqld OPT_OLD_STYLE_USER_LIMITS, OPT_LOG_SLOW_ADMIN_STATEMENTS, OPT_TABLE_LOCK_WAIT_TIMEOUT, - OPT_PORT_OPEN_TIMEOUT + OPT_PORT_OPEN_TIMEOUT, + OPT_MERGE }; @@ -5101,6 +5104,9 @@ master-ssl", #endif /* HAVE_REPLICATION */ {"memlock", OPT_MEMLOCK, "Lock mysqld in memory.", (gptr*) &locked_in_memory, (gptr*) &locked_in_memory, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"merge", OPT_MERGE, "Enable Merge storage engine. Disable with \ +--skip-merge.", + (gptr*) &opt_merge, (gptr*) &opt_merge, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0}, {"myisam-recover", OPT_MYISAM_RECOVER, "Syntax: myisam-recover[=option[,option...]], where option can be DEFAULT, BACKUP, FORCE or QUICK.", (gptr*) &myisam_recover_options_str, (gptr*) &myisam_recover_options_str, 0, @@ -6883,6 +6889,11 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), global_system_variables.tx_isolation= (type-1); break; } + case OPT_MERGE: + if (opt_merge) + have_merge_db= SHOW_OPTION_YES; + else + have_merge_db= SHOW_OPTION_DISABLED; #ifdef HAVE_BERKELEY_DB case OPT_BDB_NOSYNC: /* Deprecated option */ @@ -7032,10 +7043,10 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), } switch (method-1) { case 0: - method_conv= MI_STATS_METHOD_NULLS_EQUAL; + method_conv= MI_STATS_METHOD_NULLS_NOT_EQUAL; break; case 1: - method_conv= MI_STATS_METHOD_NULLS_NOT_EQUAL; + method_conv= MI_STATS_METHOD_NULLS_EQUAL; break; case 2: method_conv= MI_STATS_METHOD_IGNORE_NULLS; diff --git a/sql/net_serv.cc b/sql/net_serv.cc index cf9dc6e3f60..1601f7e5177 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -96,8 +96,11 @@ extern uint test_flags; extern ulong bytes_sent, bytes_received, net_big_packet_count; extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received; #ifndef MYSQL_INSTANCE_MANAGER -extern void query_cache_insert(NET *net, const char *packet, ulong length); +#ifdef HAVE_QUERY_CACHE #define USE_QUERY_CACHE +extern void query_cache_init_query(NET *net); +extern void query_cache_insert(NET *net, const char *packet, ulong length); +#endif // HAVE_QUERY_CACHE #define update_statistics(A) A #endif /* MYSQL_INSTANCE_MANGER */ #endif /* defined(MYSQL_SERVER) && !defined(MYSQL_INSTANCE_MANAGER) */ @@ -133,7 +136,11 @@ my_bool my_net_init(NET *net, Vio* vio) net->compress=0; net->reading_or_writing=0; net->where_b = net->remain_in_buf=0; net->last_errno=0; - net->query_cache_query=0; +#ifdef USE_QUERY_CACHE + query_cache_init_query(net); +#else + net->query_cache_query= 0; +#endif net->report_error= 0; if (vio != 0) /* If real connection */ @@ -552,10 +559,8 @@ net_real_write(NET *net,const char *packet,ulong len) my_bool net_blocking = vio_is_blocking(net->vio); DBUG_ENTER("net_real_write"); -#if defined(MYSQL_SERVER) && defined(HAVE_QUERY_CACHE) \ - && !defined(MYSQL_INSTANCE_MANAGER) - if (net->query_cache_query != 0) - query_cache_insert(net, packet, len); +#if defined(MYSQL_SERVER) && defined(USE_QUERY_CACHE) + query_cache_insert(net, packet, len); #endif if (net->error == 2) @@ -855,7 +860,7 @@ my_real_read(NET *net, ulong *complen) #endif /* EXTRA_DEBUG */ } #if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER) - if (vio_should_retry(net->vio)) + if (vio_errno(net->vio) == SOCKET_EINTR) { DBUG_PRINT("warning",("Interrupted read. Retrying...")); continue; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 5866fe731cf..79cfbc72fe7 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -442,6 +442,7 @@ typedef struct st_qsel_param { uint fields_bitmap_size; MY_BITMAP needed_fields; /* bitmask of fields needed by the query */ + MY_BITMAP tmp_covered_fields; key_map *needed_reg; /* ptr to SQL_SELECT::needed_reg */ @@ -1762,6 +1763,7 @@ static int fill_used_fields_bitmap(PARAM *param) param->fields_bitmap_size= (table->s->fields/8 + 1); uchar *tmp; uint pk; + param->tmp_covered_fields.bitmap= 0; if (!(tmp= (uchar*)alloc_root(param->mem_root,param->fields_bitmap_size)) || bitmap_init(¶m->needed_fields, tmp, param->fields_bitmap_size*8, FALSE)) @@ -3199,11 +3201,14 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, /*I=set of all covering indexes */ ror_scan_mark= tree->ror_scans; - uchar buf[MAX_KEY/8+1]; - MY_BITMAP covered_fields; - if (bitmap_init(&covered_fields, buf, nbits, FALSE)) + MY_BITMAP *covered_fields= ¶m->tmp_covered_fields; + if (!covered_fields->bitmap) + covered_fields->bitmap= (uchar*)alloc_root(param->mem_root, + param->fields_bitmap_size); + if (!covered_fields->bitmap || + bitmap_init(covered_fields, covered_fields->bitmap, nbits, FALSE)) DBUG_RETURN(0); - bitmap_clear_all(&covered_fields); + bitmap_clear_all(covered_fields); double total_cost= 0.0f; ha_rows records=0; @@ -3222,7 +3227,7 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, */ for (ROR_SCAN_INFO **scan= ror_scan_mark; scan != ror_scans_end; ++scan) { - bitmap_subtract(&(*scan)->covered_fields, &covered_fields); + bitmap_subtract(&(*scan)->covered_fields, covered_fields); (*scan)->used_fields_covered= bitmap_bits_set(&(*scan)->covered_fields); (*scan)->first_uncovered_field= @@ -3244,8 +3249,8 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, if (total_cost > read_time) DBUG_RETURN(NULL); /* F=F-covered by first(I) */ - bitmap_union(&covered_fields, &(*ror_scan_mark)->covered_fields); - all_covered= bitmap_is_subset(¶m->needed_fields, &covered_fields); + bitmap_union(covered_fields, &(*ror_scan_mark)->covered_fields); + all_covered= bitmap_is_subset(¶m->needed_fields, covered_fields); } while ((++ror_scan_mark < ror_scans_end) && !all_covered); if (!all_covered || (ror_scan_mark - tree->ror_scans) == 1) @@ -3577,25 +3582,37 @@ static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func, break; case Item_func::BETWEEN: - if (inv) - { - tree= get_ne_mm_tree(param, cond_func, field, cond_func->arguments()[1], - cond_func->arguments()[2], cmp_type); - } - else + { + if (!value) { - tree= get_mm_parts(param, cond_func, field, Item_func::GE_FUNC, - cond_func->arguments()[1],cmp_type); - if (tree) + if (inv) + { + tree= get_ne_mm_tree(param, cond_func, field, cond_func->arguments()[1], + cond_func->arguments()[2], cmp_type); + } + else { - tree= tree_and(param, tree, get_mm_parts(param, cond_func, field, - Item_func::LE_FUNC, - cond_func->arguments()[2], - cmp_type)); + tree= get_mm_parts(param, cond_func, field, Item_func::GE_FUNC, + cond_func->arguments()[1],cmp_type); + if (tree) + { + tree= tree_and(param, tree, get_mm_parts(param, cond_func, field, + Item_func::LE_FUNC, + cond_func->arguments()[2], + cmp_type)); + } } } + else + tree= get_mm_parts(param, cond_func, field, + (inv ? + (value == (Item*)1 ? Item_func::GT_FUNC : + Item_func::LT_FUNC): + (value == (Item*)1 ? Item_func::LE_FUNC : + Item_func::GE_FUNC)), + cond_func->arguments()[0], cmp_type); break; - + } case Item_func::IN_FUNC: { Item_func_in *func=(Item_func_in*) cond_func; @@ -3605,41 +3622,33 @@ static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func, if (func->array && func->cmp_type != ROW_RESULT) { /* - We get here for conditions in form "t.key NOT IN (c1, c2, ...)" - (where c{i} are constants). - Our goal is to produce a SEL_ARG graph that represents intervals: + We get here for conditions in form "t.key NOT IN (c1, c2, ...)", + where c{i} are constants. Our goal is to produce a SEL_TREE that + represents intervals: ($MIN<t.key<c1) OR (c1<t.key<c2) OR (c2<t.key<c3) OR ... (*) where $MIN is either "-inf" or NULL. - The most straightforward way to handle NOT IN would be to convert - it to "(t.key != c1) AND (t.key != c2) AND ..." and let the range - optimizer to build SEL_ARG graph from that. However that will cause - the range optimizer to use O(N^2) memory (it's a bug, not filed), - and people do use big NOT IN lists (see BUG#15872). Also, for big - NOT IN lists constructing/using graph (*) does not make the query - faster. - - So, we will handle NOT IN manually in the following way: - * if the number of entries in the NOT IN list is less then - NOT_IN_IGNORE_THRESHOLD, we will construct SEL_ARG graph (*) - manually. - * Otherwise, we will construct a smaller graph: for - "t.key NOT IN (c1,...cN)" we construct a graph representing - ($MIN < t.key) OR (cN < t.key) // here sequence of c_i is - // ordered. - - A note about partially-covering indexes: for those (e.g. for - "a CHAR(10), KEY(a(5))") the handling is correct (albeit not very - efficient): - Instead of "t.key < c1" we get "t.key <= prefix-val(c1)". - Combining the intervals in (*) together, we get: - (-inf<=t.key<=c1) OR (c1<=t.key<=c2) OR (c2<=t.key<=c3) OR ... - i.e. actually we get intervals combined into one interval: - (-inf<=t.key<=+inf). This doesn't make much sense but it doesn't - cause any problems. + The most straightforward way to produce it is to convert NOT IN + into "(t.key != c1) AND (t.key != c2) AND ... " and let the range + analyzer to build SEL_TREE from that. The problem is that the + range analyzer will use O(N^2) memory (which is probably a bug), + and people do use big NOT IN lists (e.g. see BUG#15872, BUG#21282), + will run out of memory. + + Another problem with big lists like (*) is that a big list is + unlikely to produce a good "range" access, while considering that + range access will require expensive CPU calculations (and for + MyISAM even index accesses). In short, big NOT IN lists are rarely + worth analyzing. + + Considering the above, we'll handle NOT IN as follows: + * if the number of entries in the NOT IN list is less than + NOT_IN_IGNORE_THRESHOLD, construct the SEL_TREE (*) manually. + * Otherwise, don't produce a SEL_TREE. */ +#define NOT_IN_IGNORE_THRESHOLD 1000 MEM_ROOT *tmp_root= param->mem_root; param->thd->mem_root= param->old_root; /* @@ -3653,9 +3662,9 @@ static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func, Item *value_item= func->array->create_item(); param->thd->mem_root= tmp_root; - if (!value_item) + if (func->array->count > NOT_IN_IGNORE_THRESHOLD || !value_item) break; - + /* Get a SEL_TREE for "(-inf|NULL) < X < c_0" interval. */ uint i=0; do @@ -3674,45 +3683,39 @@ static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func, tree= NULL; break; } -#define NOT_IN_IGNORE_THRESHOLD 1000 SEL_TREE *tree2; - if (func->array->count < NOT_IN_IGNORE_THRESHOLD) + for (; i < func->array->count; i++) { - for (; i < func->array->count; i++) + if (func->array->compare_elems(i, i-1)) { - if (func->array->compare_elems(i, i-1)) + /* Get a SEL_TREE for "-inf < X < c_i" interval */ + func->array->value_to_item(i, value_item); + tree2= get_mm_parts(param, cond_func, field, Item_func::LT_FUNC, + value_item, cmp_type); + if (!tree2) { - /* Get a SEL_TREE for "-inf < X < c_i" interval */ - func->array->value_to_item(i, value_item); - tree2= get_mm_parts(param, cond_func, field, Item_func::LT_FUNC, - value_item, cmp_type); - if (!tree2) - { - tree= NULL; - break; - } + tree= NULL; + break; + } - /* Change all intervals to be "c_{i-1} < X < c_i" */ - for (uint idx= 0; idx < param->keys; idx++) + /* Change all intervals to be "c_{i-1} < X < c_i" */ + for (uint idx= 0; idx < param->keys; idx++) + { + SEL_ARG *new_interval, *last_val; + if (((new_interval= tree2->keys[idx])) && + ((last_val= tree->keys[idx]->last()))) { - SEL_ARG *new_interval, *last_val; - if (((new_interval= tree2->keys[idx])) && - ((last_val= tree->keys[idx]->last()))) - { - new_interval->min_value= last_val->max_value; - new_interval->min_flag= NEAR_MIN; - } + new_interval->min_value= last_val->max_value; + new_interval->min_flag= NEAR_MIN; } - /* - The following doesn't try to allocate memory so no need to - check for NULL. - */ - tree= tree_or(param, tree, tree2); } + /* + The following doesn't try to allocate memory so no need to + check for NULL. + */ + tree= tree_or(param, tree, tree2); } } - else - func->array->value_to_item(func->array->count - 1, value_item); if (tree && tree->type != SEL_TREE::IMPOSSIBLE) { @@ -3777,7 +3780,118 @@ static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func, } DBUG_RETURN(tree); +} + + +/* + Build conjunction of all SEL_TREEs for a simple predicate applying equalities + + SYNOPSIS + get_full_func_mm_tree() + param PARAM from SQL_SELECT::test_quick_select + cond_func item for the predicate + field_item field in the predicate + value constant in the predicate + (for BETWEEN it contains the number of the field argument, + for IN it's always 0) + inv TRUE <> NOT cond_func is considered + (makes sense only when cond_func is BETWEEN or IN) + + DESCRIPTION + For a simple SARGable predicate of the form (f op c), where f is a field and + c is a constant, the function builds a conjunction of all SEL_TREES that can + be obtained by the substitution of f for all different fields equal to f. + + NOTES + If the WHERE condition contains a predicate (fi op c), + then not only SELL_TREE for this predicate is built, but + the trees for the results of substitution of fi for + each fj belonging to the same multiple equality as fi + are built as well. + E.g. for WHERE t1.a=t2.a AND t2.a > 10 + a SEL_TREE for t2.a > 10 will be built for quick select from t2 + and + a SEL_TREE for t1.a > 10 will be built for quick select from t1. + + A BETWEEN predicate of the form (fi [NOT] BETWEEN c1 AND c2) is treated + in a similar way: we build a conjuction of trees for the results + of all substitutions of fi for equal fj. + Yet a predicate of the form (c BETWEEN f1i AND f2i) is processed + differently. It is considered as a conjuction of two SARGable + predicates (f1i <= c) and (f2i <=c) and the function get_full_func_mm_tree + is called for each of them separately producing trees for + AND j (f1j <=c ) and AND j (f2j <= c) + After this these two trees are united in one conjunctive tree. + It's easy to see that the same tree is obtained for + AND j,k (f1j <=c AND f2k<=c) + which is equivalent to + AND j,k (c BETWEEN f1j AND f2k). + The validity of the processing of the predicate (c NOT BETWEEN f1i AND f2i) + which equivalent to (f1i > c OR f2i < c) is not so obvious. Here the + function get_full_func_mm_tree is called for (f1i > c) and (f2i < c) + producing trees for AND j (f1j > c) and AND j (f2j < c). Then this two + trees are united in one OR-tree. The expression + (AND j (f1j > c) OR AND j (f2j < c) + is equivalent to the expression + AND j,k (f1j > c OR f2k < c) + which is just a translation of + AND j,k (c NOT BETWEEN f1j AND f2k) + + In the cases when one of the items f1, f2 is a constant c1 we do not create + a tree for it at all. It works for BETWEEN predicates but does not + work for NOT BETWEEN predicates as we have to evaluate the expression + with it. If it is TRUE then the other tree can be completely ignored. + We do not do it now and no trees are built in these cases for + NOT BETWEEN predicates. + + As to IN predicates only ones of the form (f IN (c1,...,cn)), + where f1 is a field and c1,...,cn are constant, are considered as + SARGable. We never try to narrow the index scan using predicates of + the form (c IN (c1,...,f,...,cn)). + + RETURN + Pointer to the tree representing the built conjunction of SEL_TREEs +*/ + +static SEL_TREE *get_full_func_mm_tree(PARAM *param, Item_func *cond_func, + Item_field *field_item, Item *value, + bool inv) +{ + SEL_TREE *tree= 0; + SEL_TREE *ftree= 0; + table_map ref_tables= 0; + table_map param_comp= ~(param->prev_tables | param->read_tables | + param->current_table); + DBUG_ENTER("get_full_func_mm_tree"); + for (uint i= 0; i < cond_func->arg_count; i++) + { + Item *arg= cond_func->arguments()[i]->real_item(); + if (arg != field_item) + ref_tables|= arg->used_tables(); + } + Field *field= field_item->field; + Item_result cmp_type= field->cmp_type(); + if (!((ref_tables | field->table->map) & param_comp)) + ftree= get_func_mm_tree(param, cond_func, field, value, cmp_type, inv); + Item_equal *item_equal= field_item->item_equal; + if (item_equal) + { + Item_equal_iterator it(*item_equal); + Item_field *item; + while ((item= it++)) + { + Field *f= item->field; + if (field->eq(f)) + continue; + if (!((ref_tables | f->table->map) & param_comp)) + { + tree= get_func_mm_tree(param, cond_func, f, value, cmp_type, inv); + ftree= !ftree ? tree : tree_and(param, ftree, tree); + } + } + } + DBUG_RETURN(ftree); } /* make a select tree of all keys in condition */ @@ -3788,7 +3902,7 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) SEL_TREE *ftree= 0; Item_field *field_item= 0; bool inv= FALSE; - Item *value; + Item *value= 0; DBUG_ENTER("get_mm_tree"); if (cond->type() == Item::COND_ITEM) @@ -3868,10 +3982,37 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) switch (cond_func->functype()) { case Item_func::BETWEEN: - if (cond_func->arguments()[0]->real_item()->type() != Item::FIELD_ITEM) - DBUG_RETURN(0); - field_item= (Item_field*) (cond_func->arguments()[0]->real_item()); - value= NULL; + if (cond_func->arguments()[0]->real_item()->type() == Item::FIELD_ITEM) + { + field_item= (Item_field*) (cond_func->arguments()[0]->real_item()); + ftree= get_full_func_mm_tree(param, cond_func, field_item, NULL, inv); + } + + /* + Concerning the code below see the NOTES section in + the comments for the function get_full_func_mm_tree() + */ + for (uint i= 1 ; i < cond_func->arg_count ; i++) + { + + if (cond_func->arguments()[i]->real_item()->type() == Item::FIELD_ITEM) + { + field_item= (Item_field*) (cond_func->arguments()[i]->real_item()); + SEL_TREE *tmp= get_full_func_mm_tree(param, cond_func, + field_item, (Item*) i, inv); + if (inv) + tree= !tree ? tmp : tree_or(param, tree, tmp); + else + tree= tree_and(param, tree, tmp); + } + else if (inv) + { + tree= 0; + break; + } + } + + ftree = tree_and(param, ftree, tree); break; case Item_func::IN_FUNC: { @@ -3879,7 +4020,7 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) if (func->key_item()->real_item()->type() != Item::FIELD_ITEM) DBUG_RETURN(0); field_item= (Item_field*) (func->key_item()->real_item()); - value= NULL; + ftree= get_full_func_mm_tree(param, cond_func, field_item, NULL, inv); break; } case Item_func::MULT_EQUAL_FUNC: @@ -3918,47 +4059,9 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) } else DBUG_RETURN(0); + ftree= get_full_func_mm_tree(param, cond_func, field_item, value, inv); } - /* - If the where condition contains a predicate (ti.field op const), - then not only SELL_TREE for this predicate is built, but - the trees for the results of substitution of ti.field for - each tj.field belonging to the same multiple equality as ti.field - are built as well. - E.g. for WHERE t1.a=t2.a AND t2.a > 10 - a SEL_TREE for t2.a > 10 will be built for quick select from t2 - and - a SEL_TREE for t1.a > 10 will be built for quick select from t1. - */ - - for (uint i= 0; i < cond_func->arg_count; i++) - { - Item *arg= cond_func->arguments()[i]->real_item(); - if (arg != field_item) - ref_tables|= arg->used_tables(); - } - Field *field= field_item->field; - Item_result cmp_type= field->cmp_type(); - if (!((ref_tables | field->table->map) & param_comp)) - ftree= get_func_mm_tree(param, cond_func, field, value, cmp_type, inv); - Item_equal *item_equal= field_item->item_equal; - if (item_equal) - { - Item_equal_iterator it(*item_equal); - Item_field *item; - while ((item= it++)) - { - Field *f= item->field; - if (field->eq(f)) - continue; - if (!((ref_tables | f->table->map) & param_comp)) - { - tree= get_func_mm_tree(param, cond_func, f, value, cmp_type, inv); - ftree= !ftree ? tree : tree_and(param, ftree, tree); - } - } - } DBUG_RETURN(ftree); } @@ -4023,6 +4126,7 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, MEM_ROOT *alloc= param->mem_root; char *str; ulong orig_sql_mode; + int err; DBUG_ENTER("get_mm_leaf"); /* @@ -4174,7 +4278,13 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, (field->type() == FIELD_TYPE_DATE || field->type() == FIELD_TYPE_DATETIME)) field->table->in_use->variables.sql_mode|= MODE_INVALID_DATES; - if (value->save_in_field_no_warnings(field, 1) < 0) + err= value->save_in_field_no_warnings(field, 1); + if (err > 0 && field->cmp_type() != value->result_type()) + { + tree= 0; + goto end; + } + if (err < 0) { field->table->in_use->variables.sql_mode= orig_sql_mode; /* This happens when we try to insert a NULL field in a not null column */ @@ -6611,7 +6721,6 @@ int QUICK_RANGE_SELECT::get_next() } } - /* Get the next record with a different prefix. @@ -9265,7 +9374,6 @@ static void print_ror_scans_arr(TABLE *table, const char *msg, DBUG_VOID_RETURN; } - /***************************************************************************** ** Print a quick range for debugging ** TODO: diff --git a/sql/opt_range.h b/sql/opt_range.h index cdb00ea7d0c..9474f2d469f 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -668,7 +668,7 @@ private: #ifdef NOT_USED bool test_if_null_range(QUICK_RANGE *range, uint used_key_parts); #endif - int reset(void) { next=0; rev_it.rewind(); return 0; } + int reset(void) { rev_it.rewind(); return QUICK_RANGE_SELECT::reset(); } List<QUICK_RANGE> rev_ranges; List_iterator<QUICK_RANGE> rev_it; }; diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 1f6190241a3..d17c42bca38 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -191,7 +191,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) Type of range for the key part for this field will be returned in range_fl. */ - if ((outer_tables & table->map) || + if (table->file->inited || (outer_tables & table->map) || !find_key_for_maxmin(0, &ref, item_field->field, conds, &range_fl, &prefix_len)) { @@ -278,7 +278,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) Type of range for the key part for this field will be returned in range_fl. */ - if ((outer_tables & table->map) || + if (table->file->inited || (outer_tables & table->map) || !find_key_for_maxmin(1, &ref, item_field->field, conds, &range_fl, &prefix_len)) { diff --git a/sql/protocol.cc b/sql/protocol.cc index 650bd8fc58f..5de24ebdcb3 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -53,8 +53,18 @@ bool Protocol_prep::net_store_data(const char *from, uint length) } - /* Send a error string to client */ +/* + Send a error string to client + + Design note: + net_printf_error and net_send_error are low-level functions + that shall be used only when a new connection is being + established or at server startup. + For SIGNAL/RESIGNAL and GET DIAGNOSTICS functionality it's + critical that every error that can be intercepted is issued in one + place only, my_message_sql. +*/ void net_send_error(THD *thd, uint sql_errno, const char *err) { NET *net= &thd->net; @@ -64,19 +74,15 @@ void net_send_error(THD *thd, uint sql_errno, const char *err) err ? err : net->last_error[0] ? net->last_error : "NULL")); + DBUG_ASSERT(!thd->spcont); + if (net && net->no_send_error) { thd->clear_error(); DBUG_PRINT("info", ("sending error messages prohibited")); DBUG_VOID_RETURN; } - if (thd->spcont && thd->spcont->find_handler(sql_errno, - MYSQL_ERROR::WARN_LEVEL_ERROR)) - { - if (! thd->spcont->found_handler_here()) - thd->net.report_error= 1; /* Make "select" abort correctly */ - DBUG_VOID_RETURN; - } + thd->query_error= 1; // needed to catch query errors during replication if (!err) { @@ -117,6 +123,15 @@ void net_send_error(THD *thd, uint sql_errno, const char *err) Write error package and flush to client It's a little too low level, but I don't want to use another buffer for this + + Design note: + + net_printf_error and net_send_error are low-level functions + that shall be used only when a new connection is being + established or at server startup. + For SIGNAL/RESIGNAL and GET DIAGNOSTICS functionality it's + critical that every error that can be intercepted is issued in one + place only, my_message_sql. */ void @@ -136,6 +151,8 @@ net_printf_error(THD *thd, uint errcode, ...) DBUG_ENTER("net_printf_error"); DBUG_PRINT("enter",("message: %u",errcode)); + DBUG_ASSERT(!thd->spcont); + if (net && net->no_send_error) { thd->clear_error(); @@ -143,13 +160,6 @@ net_printf_error(THD *thd, uint errcode, ...) DBUG_VOID_RETURN; } - if (thd->spcont && thd->spcont->find_handler(errcode, - MYSQL_ERROR::WARN_LEVEL_ERROR)) - { - if (! thd->spcont->found_handler_here()) - thd->net.report_error= 1; /* Make "select" abort correctly */ - DBUG_VOID_RETURN; - } thd->query_error= 1; // needed to catch query errors during replication #ifndef EMBEDDED_LIBRARY query_cache_abort(net); // Safety @@ -322,7 +332,7 @@ static char eof_buff[1]= { (char) 254 }; /* Marker for end of fields */ 254 Marker (1 byte) warning_count Stored in 2 bytes; New in 4.1 protocol status_flag Stored in 2 bytes; - For flags like SERVER_STATUS_MORE_RESULTS + For flags like SERVER_MORE_RESULTS_EXISTS Note that the warning count will not be sent if 'no_flush' is set as we don't want to report the warning count until all data is sent to the diff --git a/sql/set_var.cc b/sql/set_var.cc index 8e3fda0a164..c667e2f2bcc 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -509,7 +509,8 @@ static sys_var_thd_bit sys_sql_big_tables("sql_big_tables", 0, static sys_var_thd_bit sys_big_selects("sql_big_selects", 0, set_option_bit, OPTION_BIG_SELECTS); -static sys_var_thd_bit sys_log_off("sql_log_off", 0, +static sys_var_thd_bit sys_log_off("sql_log_off", + check_log_update, set_option_bit, OPTION_LOG_OFF); static sys_var_thd_bit sys_log_update("sql_log_update", @@ -869,6 +870,7 @@ struct show_var_st init_vars[]= { {"have_geometry", (char*) &have_geometry, SHOW_HAVE}, {"have_innodb", (char*) &have_innodb, SHOW_HAVE}, {"have_isam", (char*) &have_isam, SHOW_HAVE}, + {"have_merge_engine", (char*) &have_merge_db, SHOW_HAVE}, {"have_ndbcluster", (char*) &have_ndbcluster, SHOW_HAVE}, {"have_openssl", (char*) &have_openssl, SHOW_HAVE}, {"have_query_cache", (char*) &have_query_cache, SHOW_HAVE}, @@ -3250,7 +3252,7 @@ int set_var_user::check(THD *thd) 0 can be passed as last argument (reference on item) */ return (user_var_item->fix_fields(thd, (Item**) 0) || - user_var_item->check()) ? -1 : 0; + user_var_item->check(0)) ? -1 : 0; } diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 4b2408be6d0..a097b438fe0 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -54,7 +54,7 @@ ER_CANT_CREATE_FILE cze "Nemohu vytvo-Bøit soubor '%-.64s' (chybový kód: %d)" dan "Kan ikke oprette filen '%-.64s' (Fejlkode: %d)" nla "Kan file '%-.64s' niet aanmaken (Errcode: %d)" - eng "Can't create file '%-.64s' (errno: %d)" + eng "Can't create file '%-.200s' (errno: %d)" est "Ei suuda luua faili '%-.64s' (veakood: %d)" fre "Ne peut créer le fichier '%-.64s' (Errcode: %d)" ger "Kann Datei '%-.64s' nicht erzeugen (Fehler: %d)" @@ -278,7 +278,7 @@ ER_CANT_GET_STAT cze "Nemohu z-Bískat stav '%-.64s' (chybový kód: %d)" dan "Kan ikke læse status af '%-.64s' (Fejlkode: %d)" nla "Kan de status niet krijgen van '%-.64s' (Errcode: %d)" - eng "Can't get status of '%-.64s' (errno: %d)" + eng "Can't get status of '%-.200s' (errno: %d)" jps "'%-.64s' ‚̃XƒeƒCƒ^ƒX‚ª“¾‚ç‚ê‚Ü‚¹‚ñ. (errno: %d)", est "Ei suuda lugeda '%-.64s' olekut (veakood: %d)" fre "Ne peut obtenir le status de '%-.64s' (Errcode: %d)" @@ -353,7 +353,7 @@ ER_CANT_OPEN_FILE cze "Nemohu otev-Bøít soubor '%-.64s' (chybový kód: %d)" dan "Kan ikke åbne fil: '%-.64s' (Fejlkode: %d)" nla "Kan de file '%-.64s' niet openen (Errcode: %d)" - eng "Can't open file: '%-.64s' (errno: %d)" + eng "Can't open file: '%-.200s' (errno: %d)" jps "'%-.64s' ƒtƒ@ƒCƒ‹‚ðŠJ‚Ž–‚ª‚Å‚«‚Ü‚¹‚ñ (errno: %d)", est "Ei suuda avada faili '%-.64s' (veakood: %d)" fre "Ne peut ouvrir le fichier: '%-.64s' (Errcode: %d)" @@ -378,7 +378,7 @@ ER_FILE_NOT_FOUND cze "Nemohu naj-Bít soubor '%-.64s' (chybový kód: %d)" dan "Kan ikke finde fila: '%-.64s' (Fejlkode: %d)" nla "Kan de file: '%-.64s' niet vinden (Errcode: %d)" - eng "Can't find file: '%-.64s' (errno: %d)" + eng "Can't find file: '%-.200s' (errno: %d)" jps "'%-.64s' ƒtƒ@ƒCƒ‹‚ðŒ©•t‚¯‚鎖‚ª‚Å‚«‚Ü‚¹‚ñ.(errno: %d)", est "Ei suuda leida faili '%-.64s' (veakood: %d)" fre "Ne peut trouver le fichier: '%-.64s' (Errcode: %d)" @@ -549,7 +549,7 @@ ER_ERROR_ON_READ cze "Chyba p-Bøi ètení souboru '%-.64s' (chybový kód: %d)" dan "Fejl ved læsning af '%-.64s' (Fejlkode: %d)" nla "Fout bij het lezen van file '%-.64s' (Errcode: %d)" - eng "Error reading file '%-.64s' (errno: %d)" + eng "Error reading file '%-.200s' (errno: %d)" jps "'%-.64s' ƒtƒ@ƒCƒ‹‚̓ǂݞ‚݃Gƒ‰[ (errno: %d)", est "Viga faili '%-.64s' lugemisel (veakood: %d)" fre "Erreur en lecture du fichier '%-.64s' (Errcode: %d)" @@ -599,7 +599,7 @@ ER_ERROR_ON_WRITE cze "Chyba p-Bøi zápisu do souboru '%-.64s' (chybový kód: %d)" dan "Fejl ved skriving av filen '%-.64s' (Fejlkode: %d)" nla "Fout bij het wegschrijven van file '%-.64s' (Errcode: %d)" - eng "Error writing file '%-.64s' (errno: %d)" + eng "Error writing file '%-.200s' (errno: %d)" jps "'%-.64s' ƒtƒ@ƒCƒ‹‚ð‘‚Ž–‚ª‚Å‚«‚Ü‚¹‚ñ (errno: %d)", est "Viga faili '%-.64s' kirjutamisel (veakood: %d)" fre "Erreur d'écriture du fichier '%-.64s' (Errcode: %d)" @@ -772,7 +772,7 @@ ER_NOT_FORM_FILE cze "Nespr-Bávná informace v souboru '%-.64s'" dan "Forkert indhold i: '%-.64s'" nla "Verkeerde info in file: '%-.64s'" - eng "Incorrect information in file: '%-.64s'" + eng "Incorrect information in file: '%-.200s'" jps "ƒtƒ@ƒCƒ‹ '%-.64s' ‚Ì info ‚ªŠÔˆá‚Á‚Ä‚¢‚邿‚¤‚Å‚·", est "Vigane informatsioon failis '%-.64s'" fre "Information erronnée dans le fichier: '%-.64s'" @@ -797,7 +797,7 @@ ER_NOT_KEYFILE cze "Nespr-Bávný klíè pro tabulku '%-.64s'; pokuste se ho opravit" dan "Fejl i indeksfilen til tabellen '%-.64s'; prøv at reparere den" nla "Verkeerde zoeksleutel file voor tabel: '%-.64s'; probeer het te repareren" - eng "Incorrect key file for table '%-.64s'; try to repair it" + eng "Incorrect key file for table '%-.200s'; try to repair it" jps "'%-.64s' ƒe[ƒuƒ‹‚Ì key file ‚ªŠÔˆá‚Á‚Ä‚¢‚邿‚¤‚Å‚·. C•œ‚ð‚µ‚Ä‚‚¾‚³‚¢", est "Tabeli '%-.64s' võtmefail on vigane; proovi seda parandada" fre "Index corrompu dans la table: '%-.64s'; essayez de le réparer" @@ -2044,7 +2044,7 @@ ER_TEXTFILE_NOT_READABLE cze "Soubor '%-.64s' mus-Bí být v adresáøi databáze nebo èitelný pro v¹echny" dan "Filen '%-.64s' skal være i database-folderen og kunne læses af alle" nla "Het bestand '%-.64s' dient in de database directory voor the komen of leesbaar voor iedereen te zijn." - eng "The file '%-.64s' must be in the database directory or be readable by all" + eng "The file '%-.128s' must be in the database directory or be readable by all" jps "ƒtƒ@ƒCƒ‹ '%-.64s' ‚Í databse ‚Ì directory ‚É‚ ‚é‚©‘S‚Ẵ†[ƒU[‚ª“Ç‚ß‚é‚æ‚¤‚É‹–‰Â‚³‚ê‚Ä‚¢‚È‚¯‚ê‚΂Ȃè‚Ü‚¹‚ñ.", est "Fail '%-.64s' peab asuma andmebaasi kataloogis või olema kõigile loetav" fre "Le fichier '%-.64s' doit être dans le répertoire de la base et lisible par tous" @@ -2069,7 +2069,7 @@ ER_FILE_EXISTS_ERROR cze "Soubor '%-.64s' ji-B¾ existuje" dan "Filen '%-.64s' eksisterer allerede" nla "Het bestand '%-.64s' bestaat reeds" - eng "File '%-.80s' already exists" + eng "File '%-.200s' already exists" jps "File '%-.64s' ‚ÍŠù‚É‘¶Ý‚µ‚Ü‚·", est "Fail '%-.80s' juba eksisteerib" fre "Le fichier '%-.64s' existe déjà" @@ -2345,7 +2345,7 @@ ER_NO_UNIQUE_LOGFILE cze "Nemohu vytvo-Bøit jednoznaèné jméno logovacího souboru %s.(1-999)\n" dan "Kan ikke lave unikt log-filnavn %s.(1-999)\n" nla "Het is niet mogelijk een unieke naam te maken voor de logfile %s.(1-999)\n" - eng "Can't generate a unique log-filename %-.64s.(1-999)\n" + eng "Can't generate a unique log-filename %-.200s.(1-999)\n" est "Ei suuda luua unikaalset logifaili nime %-.64s.(1-999)\n" fre "Ne peut générer un unique nom de journal %s.(1-999)\n" ger "Kann keinen eindeutigen Dateinamen für die Logdatei %-.64s(1-999) erzeugen\n" @@ -3813,7 +3813,7 @@ ER_WRONG_MRG_TABLE cze "V-B¹echny tabulky v MERGE tabulce nejsou definovány stejnì" dan "Tabellerne i MERGE er ikke defineret ens" nla "Niet alle tabellen in de MERGE tabel hebben identieke gedefinities" - eng "All tables in the MERGE table are not identically defined" + eng "Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exists" est "Kõik tabelid MERGE tabeli määratluses ei ole identsed" fre "Toutes les tables de la table de type MERGE n'ont pas la même définition" ger "Nicht alle Tabellen in der MERGE-Tabelle sind gleich definiert" @@ -5218,7 +5218,7 @@ ER_FPARSER_BAD_HEADER rus "îÅ×ÅÒÎÙÊ ÚÁÇÏÌÏ×ÏË ÔÉÐÁ ÆÁÊÌÁ '%-.64s'" ukr "îÅצÒÎÉÊ ÚÁÇÏÌÏ×ÏË ÔÉÐÕ Õ ÆÁÊ̦ '%-.64s'" ER_FPARSER_EOF_IN_COMMENT - eng "Unexpected end of file while parsing comment '%-.64s'" + eng "Unexpected end of file while parsing comment '%-.200s'" ger "Unerwartetes Dateiende beim Parsen des Kommentars '%-.64s'" rus "îÅÏÖÉÄÁÎÎÙÊ ËÏÎÅà ÆÁÊÌÁ × ËÏÍÅÎÔÁÒÉÉ '%-.64s'" ukr "îÅÓÐÏĦ×ÁÎÎÉÊ Ë¦ÎÅÃØ ÆÁÊÌÕ Õ ËÏÍÅÎÔÁÒ¦ '%-.64s'" @@ -5387,7 +5387,7 @@ ER_LOGGING_PROHIBIT_CHANGING_OF eng "Binary logging and replication forbid changing the global server %s" ger "Binärlogs und Replikation verhindern Wechsel des globalen Servers %s" ER_NO_FILE_MAPPING - eng "Can't map file: %-.64s, errno: %d" + eng "Can't map file: %-.200s, errno: %d" ger "Kann Datei nicht abbilden: %-.64s, Fehler: %d" ER_WRONG_MAGIC eng "Wrong magic in %-.64s" @@ -5621,3 +5621,13 @@ ER_TABLE_CANT_HANDLE_SPKEYS eng "The used table type doesn't support SPATIAL indexes" ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA eng "Triggers can not be created on system tables" +ER_REMOVED_SPACES + eng "Leading spaces are removed from name '%s'" +ER_AUTOINC_READ_FAILED + eng "Failed to read auto-increment value from storage engine" +ER_USERNAME + eng "user name" +ER_HOSTNAME + eng "host name" +ER_WRONG_STRING_LENGTH + eng "String '%-.70s' is too long for %s (should be no longer than %d)" diff --git a/sql/slave.cc b/sql/slave.cc index b284f4a6a16..55cff94a179 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3051,7 +3051,7 @@ static ulong read_event(MYSQL* mysql, MASTER_INFO *mi, bool* suppress_warnings) return packet_error; #endif - len = net_safe_read(mysql); + len = cli_safe_read(mysql); if (len == packet_error || (long) len < 1) { if (mysql_errno(mysql) == ER_NET_READ_INTERRUPTED) @@ -3226,7 +3226,7 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) rli->is_until_satisfied()) { char buf[22]; - sql_print_error("Slave SQL thread stopped because it reached its" + sql_print_information("Slave SQL thread stopped because it reached its" " UNTIL position %s", llstr(rli->until_pos(), buf)); /* Setting abort_slave flag because we do not want additional message about @@ -3717,15 +3717,12 @@ err: write_ignored_events_info_to_relay_log(thd, mi); thd->proc_info = "Waiting for slave mutex on exit"; pthread_mutex_lock(&mi->run_lock); - mi->slave_running = 0; - mi->io_thd = 0; /* Forget the relay log's format */ delete mi->rli.relay_log.description_event_for_queue; mi->rli.relay_log.description_event_for_queue= 0; // TODO: make rpl_status part of MASTER_INFO change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE); - mi->abort_slave = 0; // TODO: check if this is needed DBUG_ASSERT(thd->net.buff != 0); net_end(&thd->net); // destructor will not free it, because net.vio is 0 close_thread_tables(thd, 0); @@ -3733,8 +3730,11 @@ err: THD_CHECK_SENTRY(thd); delete thd; pthread_mutex_unlock(&LOCK_thread_count); - pthread_cond_broadcast(&mi->stop_cond); // tell the world we are done + mi->abort_slave= 0; + mi->slave_running= 0; + mi->io_thd= 0; pthread_mutex_unlock(&mi->run_lock); + pthread_cond_broadcast(&mi->stop_cond); // tell the world we are done #ifndef DBUG_OFF if (abort_slave_event_count && !events_till_abort) goto slave_begin; diff --git a/sql/slave.h b/sql/slave.h index c355f7172a9..dee134aaa0c 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -406,7 +406,7 @@ typedef struct st_master_info /* the variables below are needed because we can change masters on the fly */ char master_log_name[FN_REFLEN]; char host[HOSTNAME_LENGTH+1]; - char user[USERNAME_LENGTH+1]; + char user[USERNAME_BYTE_LENGTH+1]; char password[MAX_PASSWORD_LENGTH+1]; my_bool ssl; // enables use of SSL connection if true char ssl_ca[FN_REFLEN], ssl_capath[FN_REFLEN], ssl_cert[FN_REFLEN]; diff --git a/sql/sp.cc b/sql/sp.cc index 553465ebff8..63175b110fa 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -404,16 +404,16 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, { LEX *old_lex= thd->lex, newlex; String defstr; - char old_db_buf[NAME_LEN+1]; + char old_db_buf[NAME_BYTE_LEN+1]; LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) }; bool dbchanged; ulong old_sql_mode= thd->variables.sql_mode; ha_rows old_select_limit= thd->variables.select_limit; sp_rcontext *old_spcont= thd->spcont; - char definer_user_name_holder[USERNAME_LENGTH + 1]; + char definer_user_name_holder[USERNAME_BYTE_LENGTH + 1]; LEX_STRING_WITH_INIT definer_user_name(definer_user_name_holder, - USERNAME_LENGTH); + USERNAME_BYTE_LENGTH); char definer_host_name_holder[HOSTNAME_LENGTH + 1]; LEX_STRING_WITH_INIT definer_host_name(definer_host_name_holder, @@ -495,6 +495,13 @@ sp_returns_type(THD *thd, String &result, sp_head *sp) table.s = &table.share_not_to_be_used; field= sp->create_result_field(0, 0, &table); field->sql_type(result); + + if (field->has_charset()) + { + result.append(STRING_WITH_LEN(" CHARSET ")); + result.append(field->charset()->csname); + } + delete field; } @@ -504,7 +511,7 @@ db_create_routine(THD *thd, int type, sp_head *sp) int ret; TABLE *table; char definer[USER_HOST_BUFF_SIZE]; - char old_db_buf[NAME_LEN+1]; + char old_db_buf[NAME_BYTE_LEN+1]; LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) }; bool dbchanged; DBUG_ENTER("db_create_routine"); @@ -626,7 +633,10 @@ db_create_routine(THD *thd, int type, sp_head *sp) log_query.append(STRING_WITH_LEN("CREATE ")); append_definer(thd, &log_query, &thd->lex->definer->user, &thd->lex->definer->host); - log_query.append(thd->lex->stmt_definition_begin); + log_query.append(thd->lex->stmt_definition_begin, + (char *)sp->m_body_begin - + thd->lex->stmt_definition_begin + + sp->m_body.length); /* Such a statement can always go directly to binlog, no trans cache */ Query_log_event qinfo(thd, log_query.c_ptr(), log_query.length(), 0, @@ -974,6 +984,11 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp, sp_head *new_sp; const char *returns= ""; char definer[USER_HOST_BUFF_SIZE]; + + /* + String buffer for RETURNS data type must have system charset; + 64 -- size of "returns" column of mysql.proc. + */ String retstr(64); DBUG_PRINT("info", ("found: 0x%lx", (ulong)sp)); @@ -991,6 +1006,12 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp, } DBUG_RETURN(sp->m_first_free_instance); } + /* + Actually depth could be +1 than the actual value in case a SP calls + SHOW CREATE PROCEDURE. Hence, the linked list could hold up to one more + instance. + */ + level= sp->m_last_cached_sp->m_recursion_level + 1; if (level > depth) { @@ -1160,19 +1181,22 @@ sp_update_procedure(THD *thd, sp_name *name, st_sp_chistics *chistics) int sp_show_create_procedure(THD *thd, sp_name *name) { + int ret= SP_KEY_NOT_FOUND; sp_head *sp; DBUG_ENTER("sp_show_create_procedure"); DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str)); + /* + Increase the recursion limit for this statement. SHOW CREATE PROCEDURE + does not do actual recursion. + */ + thd->variables.max_sp_recursion_depth++; if ((sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name, &thd->sp_proc_cache, FALSE))) - { - int ret= sp->show_create_procedure(thd); + ret= sp->show_create_procedure(thd); - DBUG_RETURN(ret); - } - - DBUG_RETURN(SP_KEY_NOT_FOUND); + thd->variables.max_sp_recursion_depth--; + DBUG_RETURN(ret); } @@ -1846,7 +1870,6 @@ sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db, bool no_access_check, bool *dbchangedp) { int ret; - static char empty_c_string[1]= {0}; /* used for not defined db */ DBUG_ENTER("sp_use_new_db"); DBUG_PRINT("enter", ("newdb: %s", new_db.str)); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 9965c48935a..e5b0b1e606e 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -230,6 +230,12 @@ sp_get_flags_for_command(LEX *lex) else flags= sp_head::HAS_COMMIT_OR_ROLLBACK; break; + case SQLCOM_FLUSH: + flags= sp_head::HAS_SQLCOM_FLUSH; + break; + case SQLCOM_RESET: + flags= sp_head::HAS_SQLCOM_RESET; + break; case SQLCOM_CREATE_INDEX: case SQLCOM_CREATE_DB: case SQLCOM_CREATE_VIEW: @@ -470,7 +476,7 @@ sp_head::init(LEX *lex) lex->trg_table_fields.empty(); my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8); m_param_begin= m_param_end= m_body_begin= 0; - m_qname.str= m_db.str= m_name.str= m_params.str= + m_qname.str= m_db.str= m_name.str= m_params.str= m_body.str= m_defstr.str= 0; m_qname.length= m_db.length= m_name.length= m_params.length= m_body.length= m_defstr.length= 0; @@ -478,29 +484,42 @@ sp_head::init(LEX *lex) DBUG_VOID_RETURN; } + +void +sp_head::init_sp_name(THD *thd, sp_name *spname) +{ + DBUG_ENTER("sp_head::init_sp_name"); + + /* Must be initialized in the parser. */ + + DBUG_ASSERT(spname && spname->m_db.str && spname->m_db.length); + + /* We have to copy strings to get them into the right memroot. */ + + m_db.length= spname->m_db.length; + m_db.str= strmake_root(thd->mem_root, spname->m_db.str, spname->m_db.length); + + m_name.length= spname->m_name.length; + m_name.str= strmake_root(thd->mem_root, spname->m_name.str, + spname->m_name.length); + + if (spname->m_qname.length == 0) + spname->init_qname(thd); + + m_qname.length= spname->m_qname.length; + m_qname.str= strmake_root(thd->mem_root, spname->m_qname.str, + m_qname.length); +} + + void -sp_head::init_strings(THD *thd, LEX *lex, sp_name *name) +sp_head::init_strings(THD *thd, LEX *lex) { DBUG_ENTER("sp_head::init_strings"); uchar *endp; /* Used to trim the end */ /* During parsing, we must use thd->mem_root */ MEM_ROOT *root= thd->mem_root; - DBUG_ASSERT(name); - /* Must be initialized in the parser */ - DBUG_ASSERT(name->m_db.str && name->m_db.length); - - /* We have to copy strings to get them into the right memroot */ - m_db.length= name->m_db.length; - m_db.str= strmake_root(root, name->m_db.str, name->m_db.length); - m_name.length= name->m_name.length; - m_name.str= strmake_root(root, name->m_name.str, name->m_name.length); - - if (name->m_qname.length == 0) - name->init_qname(thd); - m_qname.length= name->m_qname.length; - m_qname.str= strmake_root(root, name->m_qname.str, m_qname.length); - if (m_param_begin && m_param_end) { m_params.length= m_param_end - m_param_begin; @@ -514,10 +533,7 @@ sp_head::init_strings(THD *thd, LEX *lex, sp_name *name) Trim "garbage" at the end. This is sometimes needed with the "/ * ! VERSION... * /" wrapper in dump files. */ - while (m_body_begin < endp && - (endp[-1] <= ' ' || endp[-1] == '*' || - endp[-1] == '/' || endp[-1] == ';')) - endp-= 1; + endp= skip_rear_comments(m_body_begin, endp); m_body.length= endp - m_body_begin; m_body.str= strmake_root(root, (char *)m_body_begin, m_body.length); @@ -908,7 +924,7 @@ bool sp_head::execute(THD *thd) { DBUG_ENTER("sp_head::execute"); - char old_db_buf[NAME_LEN+1]; + char old_db_buf[NAME_BYTE_LEN+1]; LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) }; bool dbchanged; sp_rcontext *ctx; @@ -1097,6 +1113,7 @@ sp_head::execute(THD *thd) thd->restore_active_arena(&execute_arena, &backup_arena); + thd->spcont->pop_all_cursors(); // To avoid memory leaks after an error /* Restore all saved */ old_packet.swap(thd->packet); @@ -1158,6 +1175,161 @@ sp_head::execute(THD *thd) m_first_instance->m_first_free_instance->m_recursion_level == m_recursion_level + 1)); m_first_instance->m_first_free_instance= this; + + DBUG_RETURN(err_status); +} + + +#ifndef NO_EMBEDDED_ACCESS_CHECKS +/* + set_routine_security_ctx() changes routine security context, and + checks if there is an EXECUTE privilege in new context. If there is + no EXECUTE privilege, it changes the context back and returns a + error. + + SYNOPSIS + set_routine_security_ctx() + thd thread handle + sp stored routine to change the context for + is_proc TRUE is procedure, FALSE if function + save_ctx pointer to an old security context + + RETURN + TRUE if there was a error, and the context wasn't changed. + FALSE if the context was changed. +*/ + +bool +set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, + Security_context **save_ctx) +{ + *save_ctx= 0; + if (sp_change_security_context(thd, sp, save_ctx)) + return TRUE; + + /* + If we changed context to run as another user, we need to check the + access right for the new context again as someone may have revoked + the right to use the procedure from this user. + + TODO: + Cache if the definer has the right to use the object on the + first usage and only reset the cache if someone does a GRANT + statement that 'may' affect this. + */ + if (*save_ctx && + check_routine_access(thd, EXECUTE_ACL, + sp->m_db.str, sp->m_name.str, is_proc, FALSE)) + { + sp_restore_security_context(thd, *save_ctx); + *save_ctx= 0; + return TRUE; + } + + return FALSE; +} +#endif // ! NO_EMBEDDED_ACCESS_CHECKS + + +/* + Execute a trigger: + - changes security context for triggers + - switch to new memroot + - call sp_head::execute + - restore old memroot + - restores security context + + SYNOPSIS + sp_head::execute_trigger() + thd Thread handle + db database name + table table name + grant_info GRANT_INFO structure to be filled with + information about definer's privileges + on subject table + + RETURN + FALSE on success + TRUE on error +*/ + +bool +sp_head::execute_trigger(THD *thd, const char *db, const char *table, + GRANT_INFO *grant_info) +{ + sp_rcontext *octx = thd->spcont; + sp_rcontext *nctx = NULL; + bool err_status= FALSE; + MEM_ROOT call_mem_root; + Query_arena call_arena(&call_mem_root, Query_arena::INITIALIZED_FOR_SP); + Query_arena backup_arena; + + DBUG_ENTER("sp_head::execute_trigger"); + DBUG_PRINT("info", ("trigger %s", m_name.str)); + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + Security_context *save_ctx; + if (sp_change_security_context(thd, this, &save_ctx)) + DBUG_RETURN(TRUE); + + /* + NOTE: TRIGGER_ACL should be used here. + */ + if (check_global_access(thd, SUPER_ACL)) + { + sp_restore_security_context(thd, save_ctx); + DBUG_RETURN(TRUE); + } + + /* + Fetch information about table-level privileges to GRANT_INFO + structure for subject table. Check of privileges that will use it + and information about column-level privileges will happen in + Item_trigger_field::fix_fields(). + */ + fill_effective_table_privileges(thd, grant_info, db, table); +#endif // NO_EMBEDDED_ACCESS_CHECKS + + /* + Prepare arena and memroot for objects which lifetime is whole + duration of trigger call (sp_rcontext, it's tables and items, + sp_cursor and Item_cache holders for case expressions). We can't + use caller's arena/memroot for those objects because in this case + some fixed amount of memory will be consumed for each trigger + invocation and so statements which involve lot of them will hog + memory. + + TODO: we should create sp_rcontext once per command and reuse it + on subsequent executions of a trigger. + */ + init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0); + thd->set_n_backup_active_arena(&call_arena, &backup_arena); + + if (!(nctx= new sp_rcontext(m_pcont, 0, octx)) || + nctx->init(thd)) + { + err_status= TRUE; + goto err_with_cleanup; + } + +#ifndef DBUG_OFF + nctx->sp= this; +#endif + + thd->spcont= nctx; + + err_status= execute(thd); + +err_with_cleanup: + thd->restore_active_arena(&call_arena, &backup_arena); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + sp_restore_security_context(thd, save_ctx); +#endif // NO_EMBEDDED_ACCESS_CHECKS + delete nctx; + call_arena.free_items(); + free_root(&call_mem_root, MYF(0)); + thd->spcont= octx; + DBUG_RETURN(err_status); } @@ -1165,8 +1337,12 @@ sp_head::execute(THD *thd) /* Execute a function: - evaluate parameters + - changes security context for SUID routines + - switch to new memroot - call sp_head::execute + - restore old memroot - evaluate the return value + - restores security context SYNOPSIS sp_head::execute_function() @@ -1293,6 +1469,15 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, } thd->spcont= nctx; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + Security_context *save_security_ctx; + if (set_routine_security_ctx(thd, this, FALSE, &save_security_ctx)) + { + err_status= TRUE; + goto err_with_cleanup; + } +#endif + binlog_save_options= thd->options; if (need_binlog_call) { @@ -1333,7 +1518,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, reset_dynamic(&thd->user_var_events); } - if (m_type == TYPE_ENUM_FUNCTION && !err_status) + if (!err_status) { /* We need result only in function but not in trigger */ @@ -1344,8 +1529,9 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, } } - - nctx->pop_all_cursors(); // To avoid memory leaks after an error +#ifndef NO_EMBEDDED_ACCESS_CHECKS + sp_restore_security_context(thd, save_security_ctx); +#endif err_with_cleanup: delete nctx; @@ -1368,8 +1554,10 @@ err_with_cleanup: The function does the following steps: - Set all parameters + - changes security context for SUID routines - call sp_head::execute - copy back values of INOUT and OUT parameters + - restores security context RETURN FALSE on success @@ -1490,6 +1678,12 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) thd->spcont= nctx; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + Security_context *save_security_ctx= 0; + if (!err_status) + err_status= set_routine_security_ctx(thd, this, TRUE, &save_security_ctx); +#endif + if (!err_status) err_status= execute(thd); @@ -1534,10 +1728,14 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) } } +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (save_security_ctx) + sp_restore_security_context(thd, save_security_ctx); +#endif + if (!save_spcont) delete octx; - nctx->pop_all_cursors(); // To avoid memory leaks after an error delete nctx; thd->spcont= save_spcont; @@ -1674,14 +1872,18 @@ sp_head::fill_field_definition(THD *thd, LEX *lex, enum enum_field_types field_type, create_field *field_def) { + HA_CREATE_INFO sp_db_info; LEX_STRING cmt = { 0, 0 }; uint unused1= 0; int unused2= 0; + load_db_opt_by_name(thd, m_db.str, &sp_db_info); + if (field_def->init(thd, (char*) "", field_type, lex->length, lex->dec, lex->type, (Item*) 0, (Item*) 0, &cmt, 0, &lex->interval_list, - (lex->charset ? lex->charset : default_charset_info), + (lex->charset ? lex->charset : + sp_db_info.default_table_charset), lex->uint_geom_type)) return TRUE; @@ -1755,8 +1957,8 @@ sp_head::set_info(longlong created, longlong modified, void sp_head::set_definer(const char *definer, uint definerlen) { - char user_name_holder[USERNAME_LENGTH + 1]; - LEX_STRING_WITH_INIT user_name(user_name_holder, USERNAME_LENGTH); + char user_name_holder[USERNAME_BYTE_LENGTH + 1]; + LEX_STRING_WITH_INIT user_name(user_name_holder, USERNAME_BYTE_LENGTH); char host_name_holder[HOSTNAME_LENGTH + 1]; LEX_STRING_WITH_INIT host_name(host_name_holder, HOSTNAME_LENGTH); diff --git a/sql/sp_head.h b/sql/sp_head.h index 073cca2cd12..7f2da69aa0c 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -115,7 +115,9 @@ public: IS_INVOKED= 32, // Is set if this sp_head is being used HAS_SET_AUTOCOMMIT_STMT= 64,// Is set if a procedure with 'set autocommit' /* Is set if a procedure with COMMIT (implicit or explicit) | ROLLBACK */ - HAS_COMMIT_OR_ROLLBACK= 128 + HAS_COMMIT_OR_ROLLBACK= 128, + HAS_SQLCOM_RESET= 2048, + HAS_SQLCOM_FLUSH= 4096 }; /* TYPE_ENUM_FUNCTION, TYPE_ENUM_PROCEDURE or TYPE_ENUM_TRIGGER */ @@ -193,9 +195,13 @@ public: void init(LEX *lex); + /* Copy sp name from parser. */ + void + init_sp_name(THD *thd, sp_name *spname); + // Initialize strings after parsing header void - init_strings(THD *thd, LEX *lex, sp_name *name); + init_strings(THD *thd, LEX *lex); int create(THD *thd); @@ -207,6 +213,10 @@ public: destroy(); bool + execute_trigger(THD *thd, const char *db, const char *table, + GRANT_INFO *grant_onfo); + + bool execute_function(THD *thd, Item **args, uint argcount, Field *return_fld); bool @@ -327,14 +337,16 @@ public: my_error(ER_SP_NO_RETSET, MYF(0), where); else if (m_flags & HAS_SET_AUTOCOMMIT_STMT) my_error(ER_SP_CANT_SET_AUTOCOMMIT, MYF(0)); - else if (m_type != TYPE_ENUM_PROCEDURE && - (m_flags & sp_head::HAS_COMMIT_OR_ROLLBACK)) - { + else if (m_flags & HAS_COMMIT_OR_ROLLBACK) my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); - return TRUE; - } + else if (m_flags & HAS_SQLCOM_RESET) + my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "RESET"); + else if (m_flags & HAS_SQLCOM_FLUSH) + my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "FLUSH"); + return test(m_flags & - (CONTAINS_DYNAMIC_SQL|MULTI_RESULTS|HAS_SET_AUTOCOMMIT_STMT)); + (CONTAINS_DYNAMIC_SQL|MULTI_RESULTS|HAS_SET_AUTOCOMMIT_STMT| + HAS_COMMIT_OR_ROLLBACK|HAS_SQLCOM_RESET|HAS_SQLCOM_FLUSH)); } #ifndef DBUG_OFF @@ -1149,6 +1161,10 @@ sp_change_security_context(THD *thd, sp_head *sp, Security_context **backup); void sp_restore_security_context(THD *thd, Security_context *backup); + +bool +set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, + Security_context **save_ctx); #endif /* NO_EMBEDDED_ACCESS_CHECKS */ TABLE_LIST * diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 3bc27a029d0..67ee5459bb4 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -260,6 +260,65 @@ sp_rcontext::find_handler(uint sql_errno, return TRUE; } +/* + Handle the error for a given errno. + The severity of the error is adjusted depending of the current sql_mode. + If an handler is present for the error (see find_handler()), + this function will return true. + If a handler is found and if the severity of the error indicate + that the current instruction executed should abort, + the flag thd->net.report_error is also set. + This will cause the execution of the current instruction in a + sp_instr* to fail, and give control to the handler code itself + in the sp_head::execute() loop. + + SYNOPSIS + sql_errno The error code + level Warning level + thd The current thread + - thd->net.report_error is an optional output. + + RETURN + TRUE if a handler was found. + FALSE if no handler was found. +*/ +bool +sp_rcontext::handle_error(uint sql_errno, + MYSQL_ERROR::enum_warning_level level, + THD *thd) +{ + bool handled= FALSE; + MYSQL_ERROR::enum_warning_level elevated_level= level; + + + /* Depending on the sql_mode of execution, + warnings may be considered errors */ + if ((level == MYSQL_ERROR::WARN_LEVEL_WARN) && + thd->really_abort_on_warning()) + { + elevated_level= MYSQL_ERROR::WARN_LEVEL_ERROR; + } + + if (find_handler(sql_errno, elevated_level)) + { + if (elevated_level == MYSQL_ERROR::WARN_LEVEL_ERROR) + { + /* + Forces to abort the current instruction execution. + NOTE: This code is altering the original meaning of + the net.report_error flag (send an error to the client). + In the context of stored procedures with error handlers, + the flag is reused to cause error propagation, + until the error handler is reached. + No messages will be sent to the client in that context. + */ + thd->net.report_error= 1; + } + handled= TRUE; + } + + return handled; +} void sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i) diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 30521f6da84..5e03aa60d23 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -128,6 +128,12 @@ class sp_rcontext : public Sql_alloc bool find_handler(uint sql_errno,MYSQL_ERROR::enum_warning_level level); + // If there is an error handler for this error, handle it and return TRUE. + bool + handle_error(uint sql_errno, + MYSQL_ERROR::enum_warning_level level, + THD *thd); + // Returns handler type and sets *ip to location if one was found inline int found_handler(uint *ip, uint *fp) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index f7dac349d8a..010a5c33b96 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -54,7 +54,7 @@ static byte* acl_entry_get_key(acl_entry *entry,uint *length, } #define IP_ADDR_STRLEN (3+1+3+1+3+1+3) -#define ACL_KEY_LENGTH (IP_ADDR_STRLEN+1+NAME_LEN+1+USERNAME_LENGTH+1) +#define ACL_KEY_LENGTH (IP_ADDR_STRLEN+1+NAME_BYTE_LEN+1+USERNAME_BYTE_LENGTH+1) static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs; static MEM_ROOT mem, memex; @@ -149,8 +149,8 @@ my_bool acl_init(bool dont_read_acl_tables) acl_cache= new hash_filo(ACL_CACHE_SIZE, 0, 0, (hash_get_key) acl_entry_get_key, (hash_free_key) free, - /* Use the case sensitive "binary" charset */ - &my_charset_bin); + lower_case_file_system ? + system_charset_info : &my_charset_bin); if (dont_read_acl_tables) { DBUG_RETURN(0); /* purecov: tested */ @@ -197,7 +197,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) READ_RECORD read_record_info; my_bool return_val= 1; bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE; - char tmp_name[NAME_LEN+1]; + char tmp_name[NAME_BYTE_LEN+1]; int password_length; DBUG_ENTER("acl_load"); @@ -874,6 +874,7 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, sql_print_information("X509 issuer mismatch: should be '%s' " "but is '%s'", acl_user->x509_issuer, ptr); free(ptr); + user_access=NO_ACCESS; break; } user_access= acl_user->access; @@ -889,11 +890,13 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, if (strcmp(acl_user->x509_subject,ptr)) { if (global_system_variables.log_warnings) - sql_print_information("X509 subject mismatch: '%s' vs '%s'", + sql_print_information("X509 subject mismatch: should be '%s' but is '%s'", acl_user->x509_subject, ptr); + free(ptr); + user_access=NO_ACCESS; + break; } - else - user_access= acl_user->access; + user_access= acl_user->access; free(ptr); } break; @@ -2261,7 +2264,7 @@ static GRANT_NAME *name_hash_search(HASH *name_hash, const char *user, const char *tname, bool exact) { - char helping [NAME_LEN*2+USERNAME_LENGTH+3]; + char helping [NAME_BYTE_LEN*2+USERNAME_BYTE_LENGTH+3]; uint len; GRANT_NAME *grant_name,*found=0; HASH_SEARCH_STATE state; @@ -2900,14 +2903,6 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list, result= TRUE; continue; } - if (Str->host.length > HOSTNAME_LENGTH || - Str->user.length > USERNAME_LENGTH) - { - my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER), - MYF(0)); - result= TRUE; - continue; - } /* Create user if needed */ error=replace_user_table(thd, tables[0].table, *Str, 0, revoke_grant, create_new_users, @@ -3112,15 +3107,6 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, result= TRUE; continue; } - if (Str->host.length > HOSTNAME_LENGTH || - Str->user.length > USERNAME_LENGTH) - { - if (!no_error) - my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER), - MYF(0)); - result= TRUE; - continue; - } /* Create user if needed */ error=replace_user_table(thd, tables[0].table, *Str, 0, revoke_grant, create_new_users, @@ -3181,7 +3167,7 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, { List_iterator <LEX_USER> str_list (list); LEX_USER *Str, *tmp_Str; - char tmp_db[NAME_LEN+1]; + char tmp_db[NAME_BYTE_LEN+1]; bool create_new_users=0; TABLE_LIST tables[2]; DBUG_ENTER("mysql_grant"); @@ -3246,14 +3232,6 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, result= TRUE; continue; } - if (Str->host.length > HOSTNAME_LENGTH || - Str->user.length > USERNAME_LENGTH) - { - my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER), - MYF(0)); - result= -1; - continue; - } if (replace_user_table(thd, tables[0].table, *Str, (!db ? rights : 0), revoke_grant, create_new_users, test(thd->variables.sql_mode & @@ -3787,9 +3765,24 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref, if (table_ref->view || table_ref->field_translation) { /* View or derived information schema table. */ + ulong view_privs; grant= &(table_ref->grant); db_name= table_ref->view_db.str; table_name= table_ref->view_name.str; + if (table_ref->belong_to_view && + (thd->lex->sql_command == SQLCOM_SHOW_FIELDS || + thd->lex->sql_command == SQLCOM_SHOW_CREATE)) + { + view_privs= get_column_grant(thd, grant, db_name, table_name, name); + if (view_privs & VIEW_ANY_ACL) + { + table_ref->belong_to_view->allowed_show= TRUE; + return FALSE; + } + table_ref->belong_to_view->allowed_show= FALSE; + my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0)); + return TRUE; + } } else { @@ -3874,7 +3867,7 @@ err2: bool check_grant_db(THD *thd,const char *db) { Security_context *sctx= thd->security_ctx; - char helping [NAME_LEN+USERNAME_LENGTH+2]; + char helping [NAME_BYTE_LEN+USERNAME_BYTE_LENGTH+2]; uint len; bool error= 1; @@ -4144,14 +4137,6 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) DBUG_RETURN(TRUE); } - if (lex_user->host.length > HOSTNAME_LENGTH || - lex_user->user.length > USERNAME_LENGTH) - { - my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER), - MYF(0)); - DBUG_RETURN(TRUE); - } - rw_rdlock(&LOCK_grant); VOID(pthread_mutex_lock(&acl_cache->lock)); @@ -4678,6 +4663,32 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) DBUG_RETURN(0); } +ACL_USER *check_acl_user(LEX_USER *user_name, + uint *acl_acl_userdx) +{ + ACL_USER *acl_user= 0; + uint counter; + + safe_mutex_assert_owner(&acl_cache->lock); + + for (counter= 0 ; counter < acl_users.elements ; counter++) + { + const char *user,*host; + acl_user= dynamic_element(&acl_users, counter, ACL_USER*); + if (!(user=acl_user->user)) + user= ""; + if (!(host=acl_user->host.hostname)) + host= ""; + if (!strcmp(user_name->user.str,user) && + !my_strcasecmp(system_charset_info, user_name->host.str, host)) + break; + } + if (counter == acl_users.elements) + return 0; + + *acl_acl_userdx= counter; + return acl_user; +} /* Modify a privilege table. @@ -4726,7 +4737,6 @@ static int modify_grant_table(TABLE *table, Field *host_field, DBUG_RETURN(error); } - /* Handle a privilege table. @@ -5220,7 +5230,8 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list) { result= TRUE; continue; - } + } + /* Search all in-memory structures and grant tables for a mention of the new user name. @@ -5361,7 +5372,7 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list) result= TRUE; } } - + /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */ rebuild_check_host(); @@ -5413,8 +5424,6 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) } if (!find_acl_user(lex_user->host.str, lex_user->user.str, TRUE)) { - sql_print_error("REVOKE ALL PRIVILEGES, GRANT: User '%s'@'%s' does not " - "exists", lex_user->user.str, lex_user->host.str); result= -1; continue; } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 5383bb52aaa..c29c610b200 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -34,7 +34,8 @@ HASH open_cache; /* Used by mysql_test */ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, const char *name, const char *alias, - TABLE_LIST *table_list, MEM_ROOT *mem_root); + TABLE_LIST *table_list, MEM_ROOT *mem_root, + uint flags); static void free_cache_entry(TABLE *entry); static void mysql_rm_tmp_tables(void); static bool open_new_frm(THD *thd, const char *path, const char *alias, @@ -1108,7 +1109,7 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; if (open_unireg_entry(thd, table, db, table_name, table_name, 0, - thd->mem_root) || + thd->mem_root, 0) || !(table->s->table_cache_key= memdup_root(&table->mem_root, (char*) key, key_length))) { @@ -1311,7 +1312,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, VOID(pthread_mutex_lock(&LOCK_open)); if (!open_unireg_entry(thd, table, table_list->db, table_list->table_name, - alias, table_list, mem_root)) + alias, table_list, mem_root, 0)) { DBUG_ASSERT(table_list->view != 0); VOID(pthread_mutex_unlock(&LOCK_open)); @@ -1391,6 +1392,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, else { TABLE_SHARE *share; + int error; /* Free cache if too big */ while (open_cache.records > table_cache_size && unused_tables) VOID(hash_delete(&open_cache,(byte*) unused_tables)); /* purecov: tested */ @@ -1401,9 +1403,12 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_RETURN(NULL); } - if (open_unireg_entry(thd, table, table_list->db, table_list->table_name, - alias, table_list, mem_root) || - (!table_list->view && + error= open_unireg_entry(thd, table, table_list->db, + table_list->table_name, + alias, table_list, mem_root, + (flags & OPEN_VIEW_NO_PARSE)); + if ((error > 0) || + (!table_list->view && !error && !(table->s->table_cache_key= memdup_root(&table->mem_root, (char*) key, key_length)))) @@ -1413,8 +1418,15 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_RETURN(NULL); } - if (table_list->view) + if (table_list->view || error < 0) { + /* + VIEW not really opened, only frm were read. + Set 1 as a flag here + */ + if (error < 0) + table_list->view= (st_lex*)1; + my_free((gptr)table, MYF(0)); VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_RETURN(0); // VIEW @@ -1521,7 +1533,7 @@ bool reopen_table(TABLE *table,bool locked) safe_mutex_assert_owner(&LOCK_open); if (open_unireg_entry(table->in_use, &tmp, db, table_name, - table->alias, 0, table->in_use->mem_root)) + table->alias, 0, table->in_use->mem_root, 0)) goto end; free_io_cache(table); @@ -1851,6 +1863,8 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name) alias Alias name table_desc TABLE_LIST descriptor (used with views) mem_root temporary mem_root for parsing + flags the OPEN_VIEW_NO_PARSE flag to be passed to + openfrm()/open_new_frm() NOTES Extra argument for open is taken from thd->open_options @@ -1861,7 +1875,8 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name) */ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, const char *name, const char *alias, - TABLE_LIST *table_desc, MEM_ROOT *mem_root) + TABLE_LIST *table_desc, MEM_ROOT *mem_root, + uint flags) { char path[FN_REFLEN]; int error; @@ -1873,14 +1888,16 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX | HA_TRY_READ_ONLY | NO_ERR_ON_NEW_FRM), - READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, + READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD | + (flags & OPEN_VIEW_NO_PARSE), thd->open_options, entry)) && (error != 5 || (fn_format(path, path, 0, reg_ext, MY_UNPACK_FILENAME), open_new_frm(thd, path, alias, db, name, (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX | HA_TRY_READ_ONLY), - READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, + READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD | + (flags & OPEN_VIEW_NO_PARSE), thd->open_options, entry, table_desc, mem_root)))) { @@ -1962,7 +1979,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, } if (error == 5) - DBUG_RETURN(0); // we have just opened VIEW + DBUG_RETURN((flags & OPEN_VIEW_NO_PARSE)? -1 : 0); // we have just opened VIEW /* We can't mark all tables in 'mysql' database as system since we don't @@ -4041,36 +4058,48 @@ store_top_level_join_columns(THD *thd, TABLE_LIST *table_ref, if (table_ref->nested_join) { List_iterator_fast<TABLE_LIST> nested_it(table_ref->nested_join->join_list); - TABLE_LIST *cur_left_neighbor= nested_it++; - TABLE_LIST *cur_right_neighbor= NULL; + TABLE_LIST *same_level_left_neighbor= nested_it++; + TABLE_LIST *same_level_right_neighbor= NULL; + /* Left/right-most neighbors, possibly at higher levels in the join tree. */ + TABLE_LIST *real_left_neighbor, *real_right_neighbor; - while (cur_left_neighbor) + while (same_level_left_neighbor) { - TABLE_LIST *cur_table_ref= cur_left_neighbor; - cur_left_neighbor= nested_it++; + TABLE_LIST *cur_table_ref= same_level_left_neighbor; + same_level_left_neighbor= nested_it++; /* The order of RIGHT JOIN operands is reversed in 'join list' to transform it into a LEFT JOIN. However, in this procedure we need the join operands in their lexical order, so below we reverse the - join operands. Notice that this happens only in the first loop, and - not in the second one, as in the second loop cur_left_neighbor == NULL. - This is the correct behavior, because the second loop - sets cur_table_ref reference correctly after the join operands are + join operands. Notice that this happens only in the first loop, + and not in the second one, as in the second loop + same_level_left_neighbor == NULL. + This is the correct behavior, because the second loop sets + cur_table_ref reference correctly after the join operands are swapped in the first loop. */ - if (cur_left_neighbor && + if (same_level_left_neighbor && cur_table_ref->outer_join & JOIN_TYPE_RIGHT) { /* This can happen only for JOIN ... ON. */ DBUG_ASSERT(table_ref->nested_join->join_list.elements == 2); - swap_variables(TABLE_LIST*, cur_left_neighbor, cur_table_ref); + swap_variables(TABLE_LIST*, same_level_left_neighbor, cur_table_ref); } + /* + Pick the parent's left and right neighbors if there are no immediate + neighbors at the same level. + */ + real_left_neighbor= (same_level_left_neighbor) ? + same_level_left_neighbor : left_neighbor; + real_right_neighbor= (same_level_right_neighbor) ? + same_level_right_neighbor : right_neighbor; + if (cur_table_ref->nested_join && store_top_level_join_columns(thd, cur_table_ref, - cur_left_neighbor, cur_right_neighbor)) + real_left_neighbor, real_right_neighbor)) goto err; - cur_right_neighbor= cur_table_ref; + same_level_right_neighbor= cur_table_ref; } } @@ -4424,7 +4453,20 @@ bool setup_tables(THD *thd, Name_resolution_context *context, uint tablenr= 0; DBUG_ENTER("setup_tables"); - context->table_list= context->first_name_resolution_table= tables; + /* + Due to the various call paths that lead to setup_tables() it may happen + that context->table_list and context->first_name_resolution_table can be + NULL (this is typically done when creating TABLE_LISTs internally). + TODO: + Investigate all cases when this my happen, initialize the name resolution + context correctly in all those places, and remove the context reset below. + */ + if (!context->table_list || !context->first_name_resolution_table) + { + /* Test whether the context is in a consistent state. */ + DBUG_ASSERT(!context->first_name_resolution_table && !context->table_list); + context->table_list= context->first_name_resolution_table= tables; + } /* this is used for INSERT ... SELECT. @@ -4534,9 +4576,11 @@ bool setup_tables_and_check_access(THD *thd, TABLE_LIST *tables, Item **conds, TABLE_LIST **leaves, bool select_insert, + ulong want_access_first, ulong want_access) { TABLE_LIST *leaves_tmp = NULL; + bool first_table= true; if (setup_tables (thd, context, from_clause, tables, conds, &leaves_tmp, select_insert)) @@ -4546,13 +4590,16 @@ bool setup_tables_and_check_access(THD *thd, *leaves = leaves_tmp; for (; leaves_tmp; leaves_tmp= leaves_tmp->next_leaf) + { if (leaves_tmp->belong_to_view && - check_single_table_access(thd, want_access, leaves_tmp)) + check_single_table_access(thd, first_table ? want_access_first : + want_access, leaves_tmp)) { tables->hide_view_error(thd); return TRUE; } - + first_table= false; + } return FALSE; } @@ -4947,12 +4994,17 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values, bool ignore_errors) { List_iterator_fast<Item> f(fields),v(values); - Item *value; + Item *value, *fld; Item_field *field; DBUG_ENTER("fill_record"); - while ((field=(Item_field*) f++)) + while ((fld= f++)) { + if (!(field= fld->filed_for_view_update())) + { + my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), fld->name); + DBUG_RETURN(TRUE); + } value=v++; Field *rfield= field->field; TABLE *table= rfield->table; @@ -5362,7 +5414,8 @@ open_new_frm(THD *thd, const char *path, const char *alias, my_error(ER_WRONG_OBJECT, MYF(0), db, table_name, "BASE TABLE"); goto err; } - if (mysql_make_view(thd, parser, table_desc)) + if (mysql_make_view(thd, parser, table_desc, + (prgflag & OPEN_VIEW_NO_PARSE))) goto err; } else diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index f8f7bde3a62..ff033b69f98 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -565,21 +565,62 @@ byte *query_cache_query_get_key(const byte *record, uint *length, *****************************************************************************/ /* + Note on double-check locking (DCL) usage. + + Below, in query_cache_insert(), query_cache_abort() and + query_cache_end_of_result() we use what is called double-check + locking (DCL) for NET::query_cache_query. I.e. we test it first + without a lock, and, if positive, test again under the lock. + + This means that if we see 'NET::query_cache_query == 0' without a + lock we will skip the operation. But this is safe here: when we + started to cache a query, we called Query_cache::store_query(), and + NET::query_cache_query was set to non-zero in this thread (and the + thread always sees results of its memory operations, mutex or not). + If later we see 'NET::query_cache_query == 0' without locking a + mutex, that may only mean that some other thread have reset it by + invalidating the query. Skipping the operation in this case is the + right thing to do, as NET::query_cache_query won't get non-zero for + this query again. + + See also comments in Query_cache::store_query() and + Query_cache::send_result_to_client(). + + NOTE, however, that double-check locking is not applicable in + 'invalidate' functions, as we may erroneously skip invalidation, + because the thread doing invalidation may never see non-zero + NET::query_cache_query. +*/ + + +void query_cache_init_query(NET *net) +{ + /* + It is safe to initialize 'NET::query_cache_query' without a lock + here, because before it will be accessed from different threads it + will be set in this thread under a lock, and access from the same + thread is always safe. + */ + net->query_cache_query= 0; +} + + +/* Insert the packet into the query cache. - This should only be called if net->query_cache_query != 0 */ void query_cache_insert(NET *net, const char *packet, ulong length) { DBUG_ENTER("query_cache_insert"); + /* See the comment on double-check locking usage above. */ + if (net->query_cache_query == 0) + DBUG_VOID_RETURN; + STRUCT_LOCK(&query_cache.structure_guard_mutex); - /* - It is very unlikely that following condition is TRUE (it is possible - only if other thread is resizing cache), so we check it only after guard - mutex lock - */ - if (unlikely(query_cache.query_cache_size == 0)) + + if (unlikely(query_cache.query_cache_size == 0 || + query_cache.flush_in_progress)) { STRUCT_UNLOCK(&query_cache.structure_guard_mutex); DBUG_VOID_RETURN; @@ -616,10 +657,10 @@ void query_cache_insert(NET *net, const char *packet, ulong length) header->result(result); header->last_pkt_nr= net->pkt_nr; BLOCK_UNLOCK_WR(query_block); + DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0);); } else STRUCT_UNLOCK(&query_cache.structure_guard_mutex); - DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0);); DBUG_VOID_RETURN; } @@ -628,33 +669,33 @@ void query_cache_abort(NET *net) { DBUG_ENTER("query_cache_abort"); - if (net->query_cache_query != 0) // Quick check on unlocked structure + /* See the comment on double-check locking usage above. */ + if (net->query_cache_query == 0) + DBUG_VOID_RETURN; + + STRUCT_LOCK(&query_cache.structure_guard_mutex); + + if (unlikely(query_cache.query_cache_size == 0 || + query_cache.flush_in_progress)) { - STRUCT_LOCK(&query_cache.structure_guard_mutex); - /* - It is very unlikely that following condition is TRUE (it is possible - only if other thread is resizing cache), so we check it only after guard - mutex lock - */ - if (unlikely(query_cache.query_cache_size == 0)) - { - STRUCT_UNLOCK(&query_cache.structure_guard_mutex); - DBUG_VOID_RETURN; - } + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + DBUG_VOID_RETURN; + } - Query_cache_block *query_block = ((Query_cache_block*) - net->query_cache_query); - if (query_block) // Test if changed by other thread - { - DUMP(&query_cache); - BLOCK_LOCK_WR(query_block); - // The following call will remove the lock on query_block - query_cache.free_query(query_block); - } - net->query_cache_query=0; + Query_cache_block *query_block= ((Query_cache_block*) + net->query_cache_query); + if (query_block) // Test if changed by other thread + { + DUMP(&query_cache); + BLOCK_LOCK_WR(query_block); + // The following call will remove the lock on query_block + query_cache.free_query(query_block); + net->query_cache_query= 0; DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); - STRUCT_UNLOCK(&query_cache.structure_guard_mutex); } + + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + DBUG_VOID_RETURN; } @@ -663,60 +704,65 @@ void query_cache_end_of_result(THD *thd) { DBUG_ENTER("query_cache_end_of_result"); - if (thd->net.query_cache_query != 0) // Quick check on unlocked structure - { + /* See the comment on double-check locking usage above. */ + if (thd->net.query_cache_query == 0) + DBUG_VOID_RETURN; + #ifdef EMBEDDED_LIBRARY - query_cache_insert(&thd->net, (char*)thd, - emb_count_querycache_size(thd)); + query_cache_insert(&thd->net, (char*)thd, + emb_count_querycache_size(thd)); #endif - STRUCT_LOCK(&query_cache.structure_guard_mutex); - /* - It is very unlikely that following condition is TRUE (it is possible - only if other thread is resizing cache), so we check it only after guard - mutex lock - */ - if (unlikely(query_cache.query_cache_size == 0)) - { - STRUCT_UNLOCK(&query_cache.structure_guard_mutex); - DBUG_VOID_RETURN; - } - Query_cache_block *query_block = ((Query_cache_block*) - thd->net.query_cache_query); - if (query_block) - { - DUMP(&query_cache); - BLOCK_LOCK_WR(query_block); - Query_cache_query *header = query_block->query(); - Query_cache_block *last_result_block = header->result()->prev; - ulong allign_size = ALIGN_SIZE(last_result_block->used); - ulong len = max(query_cache.min_allocation_unit, allign_size); - if (last_result_block->length >= query_cache.min_allocation_unit + len) - query_cache.split_block(last_result_block,len); - STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + STRUCT_LOCK(&query_cache.structure_guard_mutex); + + if (unlikely(query_cache.query_cache_size == 0 || + query_cache.flush_in_progress)) + { + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + DBUG_VOID_RETURN; + } + + Query_cache_block *query_block= ((Query_cache_block*) + thd->net.query_cache_query); + if (query_block) + { + DUMP(&query_cache); + BLOCK_LOCK_WR(query_block); + Query_cache_query *header= query_block->query(); + Query_cache_block *last_result_block= header->result()->prev; + ulong allign_size= ALIGN_SIZE(last_result_block->used); + ulong len= max(query_cache.min_allocation_unit, allign_size); + if (last_result_block->length >= query_cache.min_allocation_unit + len) + query_cache.split_block(last_result_block,len); #ifndef DBUG_OFF - if (header->result() == 0) - { - DBUG_PRINT("error", ("end of data whith no result. query '%s'", - header->query())); - query_cache.wreck(__LINE__, ""); - DBUG_VOID_RETURN; - } -#endif - header->found_rows(current_thd->limit_found_rows); - header->result()->type = Query_cache_block::RESULT; - header->writer(0); - BLOCK_UNLOCK_WR(query_block); - } - else + if (header->result() == 0) { - // Cache was flushed or resized and query was deleted => do nothing + DBUG_PRINT("error", ("end of data whith no result. query '%s'", + header->query())); + query_cache.wreck(__LINE__, ""); + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + + DBUG_VOID_RETURN; } - thd->net.query_cache_query=0; - DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0);); +#endif + header->found_rows(current_thd->limit_found_rows); + header->result()->type= Query_cache_block::RESULT; + header->writer(0); + thd->net.query_cache_query= 0; + DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); + + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + + BLOCK_UNLOCK_WR(query_block); + } + else + { + // Cache was flushed or resized and query was deleted => do nothing + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); } + DBUG_VOID_RETURN; } @@ -762,8 +808,7 @@ ulong Query_cache::resize(ulong query_cache_size_arg) query_cache_size_arg)); DBUG_ASSERT(initialized); STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size > 0) - free_cache(); + free_cache(); query_cache_size= query_cache_size_arg; ::query_cache_size= init_cache(); STRUCT_UNLOCK(&structure_guard_mutex); @@ -784,7 +829,15 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) TABLE_COUNTER_TYPE local_tables; ulong tot_length; DBUG_ENTER("Query_cache::store_query"); - if (query_cache_size == 0 || thd->locked_tables) + /* + Testing 'query_cache_size' without a lock here is safe: the thing + we may loose is that the query won't be cached, but we save on + mutex locking in the case when query cache is disabled or the + query is uncachable. + + See also a note on double-check locking usage above. + */ + if (thd->locked_tables || query_cache_size == 0) DBUG_VOID_RETURN; uint8 tables_type= 0; @@ -836,9 +889,9 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu", acquiring the query cache mutex. */ ha_release_temporary_latches(thd); - STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size == 0) + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size == 0 || flush_in_progress) { STRUCT_UNLOCK(&structure_guard_mutex); DBUG_VOID_RETURN; @@ -912,11 +965,12 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu", double_linked_list_simple_include(query_block, &queries_blocks); inserts++; queries_in_cache++; - STRUCT_UNLOCK(&structure_guard_mutex); - net->query_cache_query= (gptr) query_block; header->writer(net); header->tables_type(tables_type); + + STRUCT_UNLOCK(&structure_guard_mutex); + // init_n_lock make query block locked BLOCK_UNLOCK_WR(query_block); } @@ -970,12 +1024,16 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) Query_cache_query_flags flags; DBUG_ENTER("Query_cache::send_result_to_client"); - if (query_cache_size == 0 || thd->locked_tables || - thd->variables.query_cache_type == 0) - goto err; + /* + Testing 'query_cache_size' without a lock here is safe: the thing + we may loose is that the query won't be served from cache, but we + save on mutex locking in the case when query cache is disabled. - /* Check that we haven't forgot to reset the query cache variables */ - DBUG_ASSERT(thd->net.query_cache_query == 0); + See also a note on double-check locking usage above. + */ + if (thd->locked_tables || thd->variables.query_cache_type == 0 || + query_cache_size == 0) + goto err; if (!thd->lex->safe_to_cache_query) { @@ -1011,11 +1069,15 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) } STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size == 0) + if (query_cache_size == 0 || flush_in_progress) { DBUG_PRINT("qcache", ("query cache disabled")); goto err_unlock; } + + /* Check that we haven't forgot to reset the query cache variables */ + DBUG_ASSERT(thd->net.query_cache_query == 0); + Query_cache_block *query_block; tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE; @@ -1241,45 +1303,43 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used, my_bool using_transactions) { DBUG_ENTER("Query_cache::invalidate (table list)"); - if (query_cache_size > 0) + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0 && !flush_in_progress) { - STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size > 0) - { - DUMP(this); + DUMP(this); - using_transactions = using_transactions && - (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); - for (; tables_used; tables_used= tables_used->next_local) - { - DBUG_ASSERT(!using_transactions || tables_used->table!=0); - if (tables_used->derived) - continue; - if (using_transactions && - (tables_used->table->file->table_cache_type() == - HA_CACHE_TBL_TRANSACT)) - /* - Tables_used->table can't be 0 in transaction. - Only 'drop' invalidate not opened table, but 'drop' - force transaction finish. - */ - thd->add_changed_table(tables_used->table); - else - invalidate_table(tables_used); - } + using_transactions= using_transactions && + (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); + for (; tables_used; tables_used= tables_used->next_local) + { + DBUG_ASSERT(!using_transactions || tables_used->table!=0); + if (tables_used->derived) + continue; + if (using_transactions && + (tables_used->table->file->table_cache_type() == + HA_CACHE_TBL_TRANSACT)) + /* + Tables_used->table can't be 0 in transaction. + Only 'drop' invalidate not opened table, but 'drop' + force transaction finish. + */ + thd->add_changed_table(tables_used->table); + else + invalidate_table(tables_used); } - STRUCT_UNLOCK(&structure_guard_mutex); } + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_VOID_RETURN; } void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used) { DBUG_ENTER("Query_cache::invalidate (changed table list)"); - if (query_cache_size > 0 && tables_used) + if (tables_used) { STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size > 0) + if (query_cache_size > 0 && !flush_in_progress) { DUMP(this); for (; tables_used; tables_used= tables_used->next) @@ -1309,10 +1369,10 @@ void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used) void Query_cache::invalidate_locked_for_write(TABLE_LIST *tables_used) { DBUG_ENTER("Query_cache::invalidate_locked_for_write"); - if (query_cache_size > 0 && tables_used) + if (tables_used) { STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size > 0) + if (query_cache_size > 0 && !flush_in_progress) { DUMP(this); for (; tables_used; tables_used= tables_used->next_local) @@ -1336,21 +1396,19 @@ void Query_cache::invalidate(THD *thd, TABLE *table, { DBUG_ENTER("Query_cache::invalidate (table)"); - if (query_cache_size > 0) + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0 && !flush_in_progress) { - STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size > 0) - { - using_transactions = using_transactions && - (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); - if (using_transactions && - (table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT)) - thd->add_changed_table(table); - else - invalidate_table(table); - } - STRUCT_UNLOCK(&structure_guard_mutex); + using_transactions= using_transactions && + (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); + if (using_transactions && + (table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT)) + thd->add_changed_table(table); + else + invalidate_table(table); } + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_VOID_RETURN; } @@ -1359,20 +1417,18 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length, { DBUG_ENTER("Query_cache::invalidate (key)"); - if (query_cache_size > 0) + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0 && !flush_in_progress) { - using_transactions = using_transactions && + using_transactions= using_transactions && (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); if (using_transactions) // used for innodb => has_transactions() is TRUE thd->add_changed_table(key, key_length); else - { - STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size > 0) - invalidate_table((byte*)key, key_length); - STRUCT_UNLOCK(&structure_guard_mutex); - } + invalidate_table((byte*)key, key_length); } + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_VOID_RETURN; } @@ -1383,38 +1439,36 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length, void Query_cache::invalidate(char *db) { DBUG_ENTER("Query_cache::invalidate (db)"); - if (query_cache_size > 0) + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0 && !flush_in_progress) { - STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size > 0) - { - DUMP(this); + DUMP(this); restart_search: - if (tables_blocks) + if (tables_blocks) + { + Query_cache_block *curr= tables_blocks; + Query_cache_block *next; + do { - Query_cache_block *curr= tables_blocks; - Query_cache_block *next; - do - { - next= curr->next; - if (strcmp(db, (char*)(curr->table()->db())) == 0) - invalidate_table(curr); - /* - invalidate_table can freed block on which point 'next' (if - table of this block used only in queries which was deleted - by invalidate_table). As far as we do not allocate new blocks - and mark all headers of freed blocks as 'FREE' (even if they are - merged with other blocks) we can just test type of block - to be sure that block is not deleted - */ - if (next->type == Query_cache_block::FREE) - goto restart_search; - curr= next; - } while (curr != tables_blocks); - } + next= curr->next; + if (strcmp(db, (char*)(curr->table()->db())) == 0) + invalidate_table(curr); + /* + invalidate_table can freed block on which point 'next' (if + table of this block used only in queries which was deleted + by invalidate_table). As far as we do not allocate new blocks + and mark all headers of freed blocks as 'FREE' (even if they are + merged with other blocks) we can just test type of block + to be sure that block is not deleted + */ + if (next->type == Query_cache_block::FREE) + goto restart_search; + curr= next; + } while (curr != tables_blocks); } - STRUCT_UNLOCK(&structure_guard_mutex); } + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_VOID_RETURN; } @@ -1422,23 +1476,22 @@ void Query_cache::invalidate(char *db) void Query_cache::invalidate_by_MyISAM_filename(const char *filename) { DBUG_ENTER("Query_cache::invalidate_by_MyISAM_filename"); - if (query_cache_size > 0) + + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0 && !flush_in_progress) { /* Calculate the key outside the lock to make the lock shorter */ char key[MAX_DBKEY_LENGTH]; uint32 db_length; uint key_length= filename_2_table_key(key, filename, &db_length); - STRUCT_LOCK(&structure_guard_mutex); - if (query_cache_size > 0) // Safety if cache removed - { - Query_cache_block *table_block; - if ((table_block = (Query_cache_block*) hash_search(&tables, - (byte*) key, - key_length))) - invalidate_table(table_block); - } - STRUCT_UNLOCK(&structure_guard_mutex); + Query_cache_block *table_block; + if ((table_block = (Query_cache_block*) hash_search(&tables, + (byte*) key, + key_length))) + invalidate_table(table_block); } + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_VOID_RETURN; } @@ -1483,7 +1536,12 @@ void Query_cache::destroy() } else { + /* Underlying code expects the lock. */ + STRUCT_LOCK(&structure_guard_mutex); free_cache(); + STRUCT_UNLOCK(&structure_guard_mutex); + + pthread_cond_destroy(&COND_flush_finished); pthread_mutex_destroy(&structure_guard_mutex); initialized = 0; } @@ -1499,6 +1557,8 @@ void Query_cache::init() { DBUG_ENTER("Query_cache::init"); pthread_mutex_init(&structure_guard_mutex,MY_MUTEX_INIT_FAST); + pthread_cond_init(&COND_flush_finished, NULL); + flush_in_progress= FALSE; initialized = 1; DBUG_VOID_RETURN; } @@ -1694,6 +1754,17 @@ void Query_cache::make_disabled() } +/* + free_cache() - free all resources allocated by the cache. + + SYNOPSIS + free_cache() + + DESCRIPTION + This function frees all resources allocated by the cache. You + have to call init_cache() before using the cache again. +*/ + void Query_cache::free_cache() { DBUG_ENTER("Query_cache::free_cache"); @@ -1728,17 +1799,51 @@ void Query_cache::free_cache() Free block data *****************************************************************************/ + /* - The following assumes we have a lock on the cache + flush_cache() - flush the cache. + + SYNOPSIS + flush_cache() + + DESCRIPTION + This function will flush cache contents. It assumes we have + 'structure_guard_mutex' locked. The function sets the + flush_in_progress flag and releases the lock, so other threads may + proceed skipping the cache as if it is disabled. Concurrent + flushes are performed in turn. */ void Query_cache::flush_cache() { + /* + If there is flush in progress, wait for it to finish, and then do + our flush. This is necessary because something could be added to + the cache before we acquire the lock again, and some code (like + Query_cache::free_cache()) depends on the fact that after the + flush the cache is empty. + */ + while (flush_in_progress) + pthread_cond_wait(&COND_flush_finished, &structure_guard_mutex); + + /* + Setting 'flush_in_progress' will prevent other threads from using + the cache while we are in the middle of the flush, and we release + the lock so that other threads won't block. + */ + flush_in_progress= TRUE; + STRUCT_UNLOCK(&structure_guard_mutex); + + my_hash_reset(&queries); while (queries_blocks != 0) { BLOCK_LOCK_WR(queries_blocks); - free_query(queries_blocks); + free_query_internal(queries_blocks); } + + STRUCT_LOCK(&structure_guard_mutex); + flush_in_progress= FALSE; + pthread_cond_signal(&COND_flush_finished); } /* @@ -1784,36 +1889,48 @@ my_bool Query_cache::free_old_query() DBUG_RETURN(1); // Nothing to remove } + /* - Free query from query cache. - query_block must be locked for writing. - This function will remove (and destroy) the lock for the query. + free_query_internal() - free query from query cache. + + SYNOPSIS + free_query_internal() + query_block Query_cache_block representing the query + + DESCRIPTION + This function will remove the query from a cache, and place its + memory blocks to the list of free blocks. 'query_block' must be + locked for writing, this function will release (and destroy) this + lock. + + NOTE + 'query_block' should be removed from 'queries' hash _before_ + calling this method, as the lock will be destroyed here. */ -void Query_cache::free_query(Query_cache_block *query_block) +void Query_cache::free_query_internal(Query_cache_block *query_block) { - DBUG_ENTER("Query_cache::free_query"); + DBUG_ENTER("Query_cache::free_query_internal"); DBUG_PRINT("qcache", ("free query 0x%lx %lu bytes result", (ulong) query_block, query_block->query()->length() )); queries_in_cache--; - hash_delete(&queries,(byte *) query_block); - Query_cache_query *query = query_block->query(); + Query_cache_query *query= query_block->query(); if (query->writer() != 0) { /* Tell MySQL that this query should not be cached anymore */ - query->writer()->query_cache_query = 0; + query->writer()->query_cache_query= 0; query->writer(0); } double_linked_list_exclude(query_block, &queries_blocks); - Query_cache_block_table *table=query_block->table(0); + Query_cache_block_table *table= query_block->table(0); - for (TABLE_COUNTER_TYPE i=0; i < query_block->n_tables; i++) + for (TABLE_COUNTER_TYPE i= 0; i < query_block->n_tables; i++) unlink_table(table++); - Query_cache_block *result_block = query->result(); + Query_cache_block *result_block= query->result(); /* The following is true when query destruction was called and no results @@ -1827,11 +1944,11 @@ void Query_cache::free_query(Query_cache_block *query_block) refused++; inserts--; } - Query_cache_block *block = result_block; + Query_cache_block *block= result_block; do { - Query_cache_block *current = block; - block = block->next; + Query_cache_block *current= block; + block= block->next; free_memory_block(current); } while (block != result_block); } @@ -1848,6 +1965,32 @@ void Query_cache::free_query(Query_cache_block *query_block) DBUG_VOID_RETURN; } + +/* + free_query() - free query from query cache. + + SYNOPSIS + free_query() + query_block Query_cache_block representing the query + + DESCRIPTION + This function will remove 'query_block' from 'queries' hash, and + then call free_query_internal(), which see. +*/ + +void Query_cache::free_query(Query_cache_block *query_block) +{ + DBUG_ENTER("Query_cache::free_query"); + DBUG_PRINT("qcache", ("free query 0x%lx %lu bytes result", + (ulong) query_block, + query_block->query()->length() )); + + hash_delete(&queries,(byte *) query_block); + free_query_internal(query_block); + + DBUG_VOID_RETURN; +} + /***************************************************************************** Query data creation *****************************************************************************/ @@ -2431,12 +2574,8 @@ Query_cache::allocate_block(ulong len, my_bool not_less, ulong min, if (!under_guard) { STRUCT_LOCK(&structure_guard_mutex); - /* - It is very unlikely that following condition is TRUE (it is possible - only if other thread is resizing cache), so we check it only after - guard mutex lock - */ - if (unlikely(query_cache.query_cache_size == 0)) + + if (unlikely(query_cache.query_cache_size == 0 || flush_in_progress)) { STRUCT_UNLOCK(&structure_guard_mutex); DBUG_RETURN(0); @@ -2892,11 +3031,9 @@ static TABLE_COUNTER_TYPE process_and_count_tables(TABLE_LIST *tables_used, (query without tables are not cached) */ -TABLE_COUNTER_TYPE Query_cache::is_cacheable(THD *thd, uint32 query_len, - char *query, - LEX *lex, - TABLE_LIST *tables_used, - uint8 *tables_type) +TABLE_COUNTER_TYPE +Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex, + TABLE_LIST *tables_used, uint8 *tables_type) { TABLE_COUNTER_TYPE table_count; DBUG_ENTER("Query_cache::is_cacheable"); @@ -2979,13 +3116,10 @@ my_bool Query_cache::ask_handler_allowance(THD *thd, void Query_cache::pack_cache() { DBUG_ENTER("Query_cache::pack_cache"); + STRUCT_LOCK(&structure_guard_mutex); - /* - It is very unlikely that following condition is TRUE (it is possible - only if other thread is resizing cache), so we check it only after - guard mutex lock - */ - if (unlikely(query_cache_size == 0)) + + if (unlikely(query_cache_size == 0 || flush_in_progress)) { STRUCT_UNLOCK(&structure_guard_mutex); DBUG_VOID_RETURN; @@ -3300,7 +3434,7 @@ my_bool Query_cache::join_results(ulong join_limit) DBUG_ENTER("Query_cache::join_results"); STRUCT_LOCK(&structure_guard_mutex); - if (queries_blocks != 0) + if (queries_blocks != 0 && !flush_in_progress) { DBUG_ASSERT(query_cache_size > 0); Query_cache_block *block = queries_blocks; @@ -3587,31 +3721,23 @@ void Query_cache::tables_dump() } -my_bool Query_cache::check_integrity(bool not_locked) +my_bool Query_cache::check_integrity(bool locked) { my_bool result = 0; uint i; DBUG_ENTER("check_integrity"); - if (query_cache_size == 0) + if (!locked) + STRUCT_LOCK(&structure_guard_mutex); + + if (unlikely(query_cache_size == 0 || flush_in_progress)) { + if (!locked) + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + DBUG_PRINT("qcache", ("Query Cache not initialized")); DBUG_RETURN(0); } - if (!not_locked) - { - STRUCT_LOCK(&structure_guard_mutex); - /* - It is very unlikely that following condition is TRUE (it is possible - only if other thread is resizing cache), so we check it only after - guard mutex lock - */ - if (unlikely(query_cache_size == 0)) - { - STRUCT_UNLOCK(&query_cache.structure_guard_mutex); - DBUG_RETURN(0); - } - } if (hash_check(&queries)) { @@ -3856,7 +3982,7 @@ my_bool Query_cache::check_integrity(bool not_locked) } } DBUG_ASSERT(result == 0); - if (!not_locked) + if (!locked) STRUCT_UNLOCK(&structure_guard_mutex); DBUG_RETURN(result); } diff --git a/sql/sql_cache.h b/sql/sql_cache.h index 29d314d3c44..a66067159e2 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -195,7 +195,6 @@ extern "C" byte *query_cache_table_get_key(const byte *record, uint *length, my_bool not_used); } -void query_cache_insert(NET *thd, const char *packet, ulong length); extern "C" void query_cache_invalidate_by_MyISAM_filename(const char* filename); @@ -241,6 +240,12 @@ public: ulong free_memory, queries_in_cache, hits, inserts, refused, free_memory_blocks, total_blocks, lowmem_prunes; +private: + pthread_cond_t COND_flush_finished; + bool flush_in_progress; + + void free_query_internal(Query_cache_block *point); + protected: /* The following mutex is locked when searching or changing global @@ -249,6 +254,12 @@ protected: LOCK SEQUENCE (to prevent deadlocks): 1. structure_guard_mutex 2. query block (for operation inside query (query block/results)) + + Thread doing cache flush releases the mutex once it sets + flush_in_progress flag, so other threads may bypass the cache as + if it is disabled, not waiting for reset to finish. The exception + is other threads that were going to do cache flush---they'll wait + till the end of a flush operation. */ pthread_mutex_t structure_guard_mutex; byte *cache; // cache memory @@ -358,6 +369,7 @@ protected: If query is cacheable return number tables in query (query without tables not cached) */ + static TABLE_COUNTER_TYPE is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex, TABLE_LIST *tables_used, uint8 *tables_type); @@ -410,6 +422,7 @@ protected: void destroy(); + friend void query_cache_init_query(NET *net); friend void query_cache_insert(NET *net, const char *packet, ulong length); friend void query_cache_end_of_result(THD *thd); friend void query_cache_abort(NET *net); @@ -435,6 +448,8 @@ protected: extern Query_cache query_cache; extern TYPELIB query_cache_type_typelib; +void query_cache_init_query(NET *net); +void query_cache_insert(NET *net, const char *packet, ulong length); void query_cache_end_of_result(THD *thd); void query_cache_abort(NET *net); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 5c8bd797e7c..093173ab949 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -43,6 +43,7 @@ table name */ char internal_table_name[2]= "*"; +char empty_c_string[1]= {0}; /* used for not defined db */ const char * const THD::DEFAULT_WHERE= "field list"; @@ -223,7 +224,7 @@ THD::THD() #endif client_capabilities= 0; // minimalistic client net.last_error[0]=0; // If error on boot - net.query_cache_query=0; // If error on boot + query_cache_init_query(&net); // If error on boot ull=0; system_thread= cleanup_done= abort_on_warning= no_warnings_for_error= 0; peer_port= 0; // For SHOW PROCESSLIST @@ -269,6 +270,7 @@ THD::THD() tablespace_op=FALSE; ulong tmp=sql_rnd_with_mutex(); randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::query_id); + substitute_null_with_insert_id = FALSE; thr_lock_info_init(&lock_info); /* safety: will be reset after start */ thr_lock_owner_init(&main_lock_id, &lock_info); } @@ -1881,7 +1883,7 @@ bool select_dumpvar::send_data(List<Item> &items) { if ((xx=li++)) { - xx->check(); + xx->check(0); xx->update(); } } diff --git a/sql/sql_class.h b/sql/sql_class.h index 45dc90f25b3..ed9f4b57f56 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -41,6 +41,7 @@ enum enum_check_fields { CHECK_FIELD_IGNORE, CHECK_FIELD_WARN, CHECK_FIELD_ERROR_FOR_NULL }; extern char internal_table_name[2]; +extern char empty_c_string[1]; extern const char **errmesg; #define TC_LOG_PAGE_SIZE 8192 @@ -199,7 +200,7 @@ class MYSQL_LOG: public TC_LOG IO_CACHE log_file; IO_CACHE index_file; char *name; - char time_buff[20],db[NAME_LEN+1]; + char time_buff[20],db[NAME_BYTE_LEN+1]; char log_file_name[FN_REFLEN],index_file_name[FN_REFLEN]; /* The max size before rotation (usable only if log_type == LOG_BIN: binary @@ -341,6 +342,7 @@ public: bool need_mutex); int find_next_log(LOG_INFO* linfo, bool need_mutex); int get_current_log(LOG_INFO* linfo); + int raw_get_current_log(LOG_INFO* linfo); uint next_file_id(); inline bool is_open() { return log_type != LOG_CLOSED; } inline char* get_index_fname() { return index_file_name;} @@ -1324,6 +1326,8 @@ public: bool no_errors, password, is_fatal_error; bool query_start_used, rand_used, time_zone_used; bool last_insert_id_used,insert_id_used, clear_next_insert_id; + /* for IS NULL => = last_insert_id() fix in remove_eq_conds() */ + bool substitute_null_with_insert_id; bool in_lock_tables; bool query_error, bootstrap, cleanup_done; bool tmp_table_used; @@ -1455,6 +1459,7 @@ public: { last_insert_id= id_arg; insert_id_used=1; + substitute_null_with_insert_id= TRUE; } inline ulonglong insert_id(void) { @@ -1980,11 +1985,21 @@ public: { db.str=0; } + /* + This constructor is used only for the case when we create a derived + table. A derived table has no name and doesn't belong to any database. + Later, if there was an alias specified for the table, it will be set + by add_table_to_list. + */ inline Table_ident(SELECT_LEX_UNIT *s) : sel(s) { /* We must have a table name here as this is used with add_table_to_list */ - db.str=0; table.str= internal_table_name; table.length=1; + db.str= empty_c_string; /* a subject to casedn_str */ + db.length= 0; + table.str= internal_table_name; + table.length=1; } + bool is_derived_table() const { return test(sel); } inline void change_db(char *db_name) { db.str= db_name; db.length= (uint) strlen(db_name); @@ -2001,6 +2016,7 @@ class user_var_entry ulong length; query_id_t update_query_id, used_query_id; Item_result type; + bool unsigned_flag; double val_real(my_bool *null_value); longlong val_int(my_bool *null_value); diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 902539dfdec..81508e4e9be 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -295,7 +295,6 @@ static bool write_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create) create Where to store the read options DESCRIPTION - For now, only default-character-set is read. RETURN VALUES 0 File found @@ -384,6 +383,50 @@ err1: /* + Retrieve database options by name. Load database options file or fetch from + cache. + + SYNOPSIS + load_db_opt_by_name() + db_name Database name + db_create_info Where to store the database options + + DESCRIPTION + load_db_opt_by_name() is a shortcut for load_db_opt(). + + NOTE + Although load_db_opt_by_name() (and load_db_opt()) returns status of + the operation, it is useless usually and should be ignored. The problem + is that there are 1) system databases ("mysql") and 2) virtual + databases ("information_schema"), which do not contain options file. + So, load_db_opt[_by_name]() returns FALSE for these databases, but this + is not an error. + + load_db_opt[_by_name]() clears db_create_info structure in any case, so + even on failure it contains valid data. So, common use case is just + call load_db_opt[_by_name]() without checking return value and use + db_create_info right after that. + + RETURN VALUES (read NOTE!) + FALSE Success + TRUE Failed to retrieve options +*/ + +bool load_db_opt_by_name(THD *thd, const char *db_name, + HA_CREATE_INFO *db_create_info) +{ + char db_opt_path[FN_REFLEN]; + + strxnmov(db_opt_path, sizeof (db_opt_path) - 1, mysql_data_home, "/", + db_name, "/", MY_DB_OPT_FILE, NullS); + + unpack_filename(db_opt_path, db_opt_path); + + return load_db_opt(thd, db_opt_path, db_create_info); +} + + +/* Create a database SYNOPSIS @@ -1126,8 +1169,6 @@ bool mysql_change_db(THD *thd, const char *name, bool no_access_check) { int path_length, db_length; char *db_name; - char path[FN_REFLEN]; - HA_CREATE_INFO create; bool system_db= 0; #ifndef NO_EMBEDDED_ACCESS_CHECKS ulong db_access; @@ -1196,16 +1237,14 @@ bool mysql_change_db(THD *thd, const char *name, bool no_access_check) } } #endif - (void) sprintf(path,"%s/%s", mysql_data_home, db_name); - path_length= unpack_dirname(path, path); // Convert if not UNIX - if (path_length && path[path_length-1] == FN_LIBCHAR) - path[path_length-1]= '\0'; // remove ending '\' - if (my_access(path,F_OK)) + + if (check_db_dir_existence(db_name)) { my_error(ER_BAD_DB_ERROR, MYF(0), db_name); my_free(db_name, MYF(0)); DBUG_RETURN(1); } + end: x_free(thd->db); DBUG_ASSERT(db_name == NULL || db_name[0] != '\0'); @@ -1221,8 +1260,10 @@ end: } else { - strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE); - load_db_opt(thd, path, &create); + HA_CREATE_INFO create; + + load_db_opt_by_name(thd, db_name, &create); + thd->db_charset= create.default_table_charset ? create.default_table_charset : thd->variables.collation_server; @@ -1230,3 +1271,36 @@ end: } DBUG_RETURN(0); } + + +/* + Check if there is directory for the database name. + + SYNOPSIS + check_db_dir_existence() + db_name database name + + RETURN VALUES + FALSE There is directory for the specified database name. + TRUE The directory does not exist. +*/ + +bool check_db_dir_existence(const char *db_name) +{ + char db_dir_path[FN_REFLEN]; + uint db_dir_path_len; + + strxnmov(db_dir_path, sizeof (db_dir_path) - 1, mysql_data_home, "/", + db_name, NullS); + + db_dir_path_len= unpack_dirname(db_dir_path, db_dir_path); + + /* Remove trailing '/' or '\' if exists. */ + + if (db_dir_path_len && db_dir_path[db_dir_path_len - 1] == FN_LIBCHAR) + db_dir_path[db_dir_path_len - 1]= 0; + + /* Check access. */ + + return my_access(db_dir_path, F_OK); +} diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 381d1a71e31..c95fb5d5973 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -91,6 +91,14 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, /* Handler didn't support fast delete; Delete rows one by one */ } + if (conds) + { + Item::cond_result result; + conds= remove_eq_conds(thd, conds, &result); + if (result == Item::COND_FALSE) // Impossible where + limit= 0; + } + table->used_keys.clear_all(); table->quick_keys.clear_all(); // Can't use 'only index' select=make_select(table, 0, 0, conds, 0, &error); @@ -334,6 +342,7 @@ cleanup: */ bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) { + Item *fake_conds= 0; SELECT_LEX *select_lex= &thd->lex->select_lex; DBUG_ENTER("mysql_prepare_delete"); @@ -342,7 +351,7 @@ bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) &thd->lex->select_lex.top_join_list, table_list, conds, &select_lex->leaf_tables, FALSE, - DELETE_ACL) || + DELETE_ACL, SELECT_ACL) || setup_conds(thd, table_list, select_lex->leaf_tables, conds) || setup_ftfuncs(select_lex)) DBUG_RETURN(TRUE); @@ -359,7 +368,7 @@ bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) DBUG_RETURN(TRUE); } } - select_lex->fix_prepare_information(thd, conds); + select_lex->fix_prepare_information(thd, conds, &fake_conds); DBUG_RETURN(FALSE); } @@ -405,7 +414,7 @@ bool mysql_multi_delete_prepare(THD *thd) &thd->lex->select_lex.top_join_list, lex->query_tables, &lex->select_lex.where, &lex->select_lex.leaf_tables, FALSE, - DELETE_ACL)) + DELETE_ACL, SELECT_ACL)) DBUG_RETURN(TRUE); diff --git a/sql/sql_error.cc b/sql/sql_error.cc index 19811efbb12..ebd515bd209 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -139,14 +139,8 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, } if (thd->spcont && - thd->spcont->find_handler(code, - ((int) level >= - (int) MYSQL_ERROR::WARN_LEVEL_WARN && - thd->really_abort_on_warning()) ? - MYSQL_ERROR::WARN_LEVEL_ERROR : level)) + thd->spcont->handle_error(code, level, thd)) { - if (! thd->spcont->found_handler_here()) - thd->net.report_error= 1; /* Make "select" abort correctly */ DBUG_RETURN(NULL); } query_cache_abort(&thd->net); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index e3aecbaace2..970042fca38 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -411,6 +411,15 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, table= table_list->table; context= &thd->lex->select_lex.context; + /* + These three asserts test the hypothesis that the resetting of the name + resolution context below is not necessary at all since the list of local + tables for INSERT always consists of one table. + */ + DBUG_ASSERT(!table_list->next_local); + DBUG_ASSERT(!context->table_list->next_local); + DBUG_ASSERT(!context->first_name_resolution_table->next_name_resolution_table); + /* Save the state of the current name resolution context. */ ctx_state.save_state(context, table_list); @@ -755,6 +764,7 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view) uint used_fields_buff_size= (table->s->fields + 7) / 8; uchar *used_fields_buff= (uchar*)thd->alloc(used_fields_buff_size); MY_BITMAP used_fields; + bool save_set_query_id= thd->set_query_id; DBUG_ENTER("check_key_in_view"); if (!used_fields_buff) @@ -767,15 +777,26 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view) bitmap_clear_all(&used_fields); view->contain_auto_increment= 0; + /* + we must not set query_id for fields as they're not + really used in this context + */ + thd->set_query_id= 0; /* check simplicity and prepare unique test of view */ for (trans= trans_start; trans != trans_end; trans++) { if (!trans->item->fixed && trans->item->fix_fields(thd, &trans->item)) - return TRUE; + { + thd->set_query_id= save_set_query_id; + DBUG_RETURN(TRUE); + } Item_field *field; /* simple SELECT list entry (field without expression) */ if (!(field= trans->item->filed_for_view_update())) + { + thd->set_query_id= save_set_query_id; DBUG_RETURN(TRUE); + } if (field->field->unireg_check == Field::NEXT_NUMBER) view->contain_auto_increment= 1; /* prepare unique test */ @@ -785,6 +806,7 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view) */ trans->item= field; } + thd->set_query_id= save_set_query_id; /* unique test */ for (trans= trans_start; trans != trans_end; trans++) { @@ -823,11 +845,18 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list, bool insert_into_view= (table_list->view != 0); DBUG_ENTER("mysql_prepare_insert_check_table"); + /* + first table in list is the one we'll INSERT into, requires INSERT_ACL. + all others require SELECT_ACL only. the ACL requirement below is for + new leaves only anyway (view-constituents), so check for SELECT rather + than INSERT. + */ + if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, &thd->lex->select_lex.top_join_list, table_list, where, &thd->lex->select_lex.leaf_tables, - select_insert, INSERT_ACL)) + select_insert, INSERT_ACL, SELECT_ACL)) DBUG_RETURN(TRUE); if (insert_into_view && !fields.elements) @@ -973,7 +1002,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, update_non_unique_table_error(table_list, "INSERT", duplicate); DBUG_RETURN(TRUE); } - select_lex->fix_prepare_information(thd, &fake_conds); + select_lex->fix_prepare_information(thd, &fake_conds, &fake_conds); select_lex->first_execution= 0; } if (duplic == DUP_UPDATE || duplic == DUP_REPLACE) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 7d4dca15608..d0087b14d6a 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -126,6 +126,7 @@ void lex_start(THD *thd, uchar *buf,uint length) lex->param_list.empty(); lex->view_list.empty(); lex->prepared_stmt_params.empty(); + lex->auxiliary_table_list.empty(); lex->unit.next= lex->unit.master= lex->unit.link_next= lex->unit.return_to= 0; lex->unit.prev= lex->unit.link_prev= 0; @@ -557,23 +558,20 @@ int MYSQLlex(void *arg, void *yythd) case MY_LEX_IDENT_OR_NCHAR: if (yyPeek() != '\'') - { // Found x'hex-number' + { state= MY_LEX_IDENT; break; } - yyGet(); // Skip ' - while ((c = yyGet()) && (c !='\'')) ; - length=(lex->ptr - lex->tok_start); // Length of hexnum+3 - if (c != '\'') + /* Found N'string' */ + lex->tok_start++; // Skip N + yySkip(); // Skip ' + if (!(yylval->lex_str.str = get_text(lex))) { - return(ABORT_SYM); // Illegal hex constant + state= MY_LEX_CHAR; // Read char by char + break; } - yyGet(); // get_token makes an unget - yylval->lex_str=get_token(lex,length); - yylval->lex_str.str+=2; // Skip x' - yylval->lex_str.length-=3; // Don't count x' and last ' - lex->yytoklen-=3; - return (NCHAR_STRING); + yylval->lex_str.length= lex->yytoklen; + return(NCHAR_STRING); case MY_LEX_IDENT_OR_HEX: if (yyPeek() == '\'') @@ -656,8 +654,9 @@ int MYSQLlex(void *arg, void *yythd) */ if ((yylval->lex_str.str[0]=='_') && - (lex->charset=get_charset_by_csname(yylval->lex_str.str+1, - MY_CS_PRIMARY,MYF(0)))) + (lex->underscore_charset= + get_charset_by_csname(yylval->lex_str.str + 1, + MY_CS_PRIMARY,MYF(0)))) return(UNDERSCORE_CHARSET); return(result_state); // IDENT or IDENT_QUOTED @@ -1042,6 +1041,8 @@ int MYSQLlex(void *arg, void *yythd) if (c == '.') lex->next_state=MY_LEX_IDENT_SEP; length= (uint) (lex->ptr - lex->tok_start)-1; + if (length == 0) + return(ABORT_SYM); // Names must be nonempty. if ((tokval= find_keyword(lex,length,0))) { yyUnget(); // Put back 'c' @@ -1053,6 +1054,30 @@ int MYSQLlex(void *arg, void *yythd) } } + +/* + Skip comment in the end of statement. + + SYNOPSIS + skip_rear_comments() + begin pointer to the beginning of statement + end pointer to the end of statement + + DESCRIPTION + The function is intended to trim comments at the end of the statement. + + RETURN + Pointer to the last non-comment symbol of the statement. +*/ + +uchar *skip_rear_comments(uchar *begin, uchar *end) +{ + while (begin < end && (end[-1] <= ' ' || end[-1] == '*' || + end[-1] == '/' || end[-1] == ';')) + end-= 1; + return end; +} + /* st_select_lex structures initialisations */ @@ -1499,10 +1524,10 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) */ Query_arena *arena= thd->stmt_arena; return (ref_pointer_array= - (Item **)arena->alloc(sizeof(Item*) * - (item_list.elements + - select_n_having_items + - order_group_num)* 5)) == 0; + (Item **)arena->alloc(sizeof(Item*) * (n_child_sum_items + + item_list.elements + + select_n_having_items + + order_group_num)*5)) == 0; } @@ -2133,15 +2158,25 @@ static void fix_prepare_info_in_table_list(THD *thd, TABLE_LIST *tbl) /* - fix some structures at the end of preparation + Save WHERE/HAVING/ON clauses and replace them with disposable copies SYNOPSIS st_select_lex::fix_prepare_information - thd thread handler - conds pointer on conditions which will be used for execution statement + thd thread handler + conds in/out pointer to WHERE condition to be met at execution + having_conds in/out pointer to HAVING condition to be met at execution + + DESCRIPTION + The passed WHERE and HAVING are to be saved for the future executions. + This function saves it, and returns a copy which can be thrashed during + this execution of the statement. By saving/thrashing here we mean only + AND/OR trees. + The function also calls fix_prepare_info_in_table_list that saves all + ON expressions. */ -void st_select_lex::fix_prepare_information(THD *thd, Item **conds) +void st_select_lex::fix_prepare_information(THD *thd, Item **conds, + Item **having_conds) { if (!thd->stmt_arena->is_conventional() && first_execution) { @@ -2151,6 +2186,11 @@ void st_select_lex::fix_prepare_information(THD *thd, Item **conds) prep_where= *conds; *conds= where= prep_where->copy_andor_structure(thd); } + if (*having_conds) + { + prep_having= *having_conds; + *having_conds= having= prep_having->copy_andor_structure(thd); + } fix_prepare_info_in_table_list(thd, (TABLE_LIST *)table_list.first); } } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index e5b087fc72a..fdf14c691e9 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -375,7 +375,7 @@ public: friend class st_select_lex_unit; friend bool mysql_new_select(struct st_lex *lex, bool move_down); friend bool mysql_make_view(THD *thd, File_parser *parser, - TABLE_LIST *table); + TABLE_LIST *table, uint flags); private: void fast_exclude(); }; @@ -548,6 +548,12 @@ public: bool braces; /* SELECT ... UNION (SELECT ... ) <- this braces */ /* TRUE when having fix field called in processing of this SELECT */ bool having_fix_field; + + /* Number of Item_sum-derived objects in this SELECT */ + uint n_sum_items; + /* Number of Item_sum-derived objects in children and descendant SELECTs */ + uint n_child_sum_items; + /* explicit LIMIT clause was used */ bool explicit_limit; /* @@ -640,7 +646,7 @@ public: bool test_limit(); friend void lex_start(THD *thd, uchar *buf, uint length); - st_select_lex() {} + st_select_lex() : n_sum_items(0), n_child_sum_items(0) {} void make_empty_select() { init_query(); @@ -650,7 +656,7 @@ public: void print(THD *thd, String *str); static void print_order(String *str, ORDER *order); void print_limit(THD *thd, String *str); - void fix_prepare_information(THD *thd, Item **conds); + void fix_prepare_information(THD *thd, Item **conds, Item **having_conds); /* Destroy the used execution plan (JOIN) of this subtree (this SELECT_LEX and all nested SELECT_LEXes and SELECT_LEX_UNITs). @@ -834,7 +840,7 @@ typedef struct st_lex : public Query_tables_list XID *xid; gptr yacc_yyss,yacc_yyvs; THD *thd; - CHARSET_INFO *charset; + CHARSET_INFO *charset, *underscore_charset; /* store original leaf_tables for INSERT SELECT and PS/SP */ TABLE_LIST *leaf_tables_insert; /* Position (first character index) of SELECT of CREATE VIEW statement */ @@ -978,7 +984,7 @@ typedef struct st_lex : public Query_tables_list /* view created to be run from definer (standard behaviour) */ - bool create_view_suid; + uint8 create_view_suid; /* Characterstics of trigger being created */ st_trg_chistics trg_chistics; /* @@ -1115,4 +1121,4 @@ extern void lex_free(void); extern void lex_start(THD *thd, uchar *buf,uint length); extern void lex_end(LEX *lex); extern int MYSQLlex(void *arg, void *yythd); - +extern uchar *skip_rear_comments(uchar *begin, uchar *end); diff --git a/sql/sql_list.h b/sql/sql_list.h index b2bcc4ea401..afad6d0f6ac 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -94,9 +94,9 @@ public: inline base_list() { empty(); } inline base_list(const base_list &tmp) :Sql_alloc() { - elements=tmp.elements; - first=tmp.first; - last=tmp.last; + elements= tmp.elements; + first= tmp.first; + last= elements ? tmp.last : &first; } inline base_list(bool error) { } inline bool push_back(void *info) diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 40e1e6b07aa..d5faf6ee7e9 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -147,16 +147,13 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, MYF(0)); DBUG_RETURN(TRUE); } - /* - This needs to be done before external_lock - */ - ha_enable_transaction(thd, FALSE); if (open_and_lock_tables(thd, table_list)) DBUG_RETURN(TRUE); if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, &thd->lex->select_lex.top_join_list, table_list, &unused_conds, &thd->lex->select_lex.leaf_tables, FALSE, + INSERT_ACL | UPDATE_ACL, INSERT_ACL | UPDATE_ACL)) DBUG_RETURN(-1); if (!table_list->table || // do not suport join view @@ -393,7 +390,6 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); table->next_number_field=0; } - ha_enable_transaction(thd, TRUE); if (file >= 0) my_close(file,MYF(0)); free_blobs(table); /* if pack_blob was used */ diff --git a/sql/sql_locale.cc b/sql/sql_locale.cc index 9dae55e4508..b947b9dfa98 100644 --- a/sql/sql_locale.cc +++ b/sql/sql_locale.cc @@ -52,8 +52,7 @@ static TYPELIB my_locale_typelib_day_names_ar_AE = { array_elements(my_locale_day_names_ar_AE)-1, "", my_locale_day_names_ar_AE, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ar_AE = { array_elements(my_locale_ab_day_names_ar_AE)-1, "", my_locale_ab_day_names_ar_AE, NULL }; -MY_LOCALE my_locale_ar_AE= - { "ar_AE", "Arabic - United Arab Emirates", FALSE, &my_locale_typelib_month_names_ar_AE, &my_locale_typelib_ab_month_names_ar_AE, &my_locale_typelib_day_names_ar_AE, &my_locale_typelib_ab_day_names_ar_AE }; +MY_LOCALE my_locale_ar_AE ( "ar_AE", "Arabic - United Arab Emirates", FALSE, &my_locale_typelib_month_names_ar_AE, &my_locale_typelib_ab_month_names_ar_AE, &my_locale_typelib_day_names_ar_AE, &my_locale_typelib_ab_day_names_ar_AE ); /***** LOCALE END ar_AE *****/ /***** LOCALE BEGIN ar_BH: Arabic - Bahrain *****/ @@ -73,8 +72,7 @@ static TYPELIB my_locale_typelib_day_names_ar_BH = { array_elements(my_locale_day_names_ar_BH)-1, "", my_locale_day_names_ar_BH, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ar_BH = { array_elements(my_locale_ab_day_names_ar_BH)-1, "", my_locale_ab_day_names_ar_BH, NULL }; -MY_LOCALE my_locale_ar_BH= - { "ar_BH", "Arabic - Bahrain", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH }; +MY_LOCALE my_locale_ar_BH ( "ar_BH", "Arabic - Bahrain", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); /***** LOCALE END ar_BH *****/ /***** LOCALE BEGIN ar_JO: Arabic - Jordan *****/ @@ -94,8 +92,7 @@ static TYPELIB my_locale_typelib_day_names_ar_JO = { array_elements(my_locale_day_names_ar_JO)-1, "", my_locale_day_names_ar_JO, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ar_JO = { array_elements(my_locale_ab_day_names_ar_JO)-1, "", my_locale_ab_day_names_ar_JO, NULL }; -MY_LOCALE my_locale_ar_JO= - { "ar_JO", "Arabic - Jordan", FALSE, &my_locale_typelib_month_names_ar_JO, &my_locale_typelib_ab_month_names_ar_JO, &my_locale_typelib_day_names_ar_JO, &my_locale_typelib_ab_day_names_ar_JO }; +MY_LOCALE my_locale_ar_JO ( "ar_JO", "Arabic - Jordan", FALSE, &my_locale_typelib_month_names_ar_JO, &my_locale_typelib_ab_month_names_ar_JO, &my_locale_typelib_day_names_ar_JO, &my_locale_typelib_ab_day_names_ar_JO ); /***** LOCALE END ar_JO *****/ /***** LOCALE BEGIN ar_SA: Arabic - Saudi Arabia *****/ @@ -115,8 +112,7 @@ static TYPELIB my_locale_typelib_day_names_ar_SA = { array_elements(my_locale_day_names_ar_SA)-1, "", my_locale_day_names_ar_SA, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ar_SA = { array_elements(my_locale_ab_day_names_ar_SA)-1, "", my_locale_ab_day_names_ar_SA, NULL }; -MY_LOCALE my_locale_ar_SA= - { "ar_SA", "Arabic - Saudi Arabia", FALSE, &my_locale_typelib_month_names_ar_SA, &my_locale_typelib_ab_month_names_ar_SA, &my_locale_typelib_day_names_ar_SA, &my_locale_typelib_ab_day_names_ar_SA }; +MY_LOCALE my_locale_ar_SA ( "ar_SA", "Arabic - Saudi Arabia", FALSE, &my_locale_typelib_month_names_ar_SA, &my_locale_typelib_ab_month_names_ar_SA, &my_locale_typelib_day_names_ar_SA, &my_locale_typelib_ab_day_names_ar_SA ); /***** LOCALE END ar_SA *****/ /***** LOCALE BEGIN ar_SY: Arabic - Syria *****/ @@ -136,8 +132,7 @@ static TYPELIB my_locale_typelib_day_names_ar_SY = { array_elements(my_locale_day_names_ar_SY)-1, "", my_locale_day_names_ar_SY, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ar_SY = { array_elements(my_locale_ab_day_names_ar_SY)-1, "", my_locale_ab_day_names_ar_SY, NULL }; -MY_LOCALE my_locale_ar_SY= - { "ar_SY", "Arabic - Syria", FALSE, &my_locale_typelib_month_names_ar_SY, &my_locale_typelib_ab_month_names_ar_SY, &my_locale_typelib_day_names_ar_SY, &my_locale_typelib_ab_day_names_ar_SY }; +MY_LOCALE my_locale_ar_SY ( "ar_SY", "Arabic - Syria", FALSE, &my_locale_typelib_month_names_ar_SY, &my_locale_typelib_ab_month_names_ar_SY, &my_locale_typelib_day_names_ar_SY, &my_locale_typelib_ab_day_names_ar_SY ); /***** LOCALE END ar_SY *****/ /***** LOCALE BEGIN be_BY: Belarusian - Belarus *****/ @@ -157,8 +152,7 @@ static TYPELIB my_locale_typelib_day_names_be_BY = { array_elements(my_locale_day_names_be_BY)-1, "", my_locale_day_names_be_BY, NULL }; static TYPELIB my_locale_typelib_ab_day_names_be_BY = { array_elements(my_locale_ab_day_names_be_BY)-1, "", my_locale_ab_day_names_be_BY, NULL }; -MY_LOCALE my_locale_be_BY= - { "be_BY", "Belarusian - Belarus", FALSE, &my_locale_typelib_month_names_be_BY, &my_locale_typelib_ab_month_names_be_BY, &my_locale_typelib_day_names_be_BY, &my_locale_typelib_ab_day_names_be_BY }; +MY_LOCALE my_locale_be_BY ( "be_BY", "Belarusian - Belarus", FALSE, &my_locale_typelib_month_names_be_BY, &my_locale_typelib_ab_month_names_be_BY, &my_locale_typelib_day_names_be_BY, &my_locale_typelib_ab_day_names_be_BY ); /***** LOCALE END be_BY *****/ /***** LOCALE BEGIN bg_BG: Bulgarian - Bulgaria *****/ @@ -178,8 +172,7 @@ static TYPELIB my_locale_typelib_day_names_bg_BG = { array_elements(my_locale_day_names_bg_BG)-1, "", my_locale_day_names_bg_BG, NULL }; static TYPELIB my_locale_typelib_ab_day_names_bg_BG = { array_elements(my_locale_ab_day_names_bg_BG)-1, "", my_locale_ab_day_names_bg_BG, NULL }; -MY_LOCALE my_locale_bg_BG= - { "bg_BG", "Bulgarian - Bulgaria", FALSE, &my_locale_typelib_month_names_bg_BG, &my_locale_typelib_ab_month_names_bg_BG, &my_locale_typelib_day_names_bg_BG, &my_locale_typelib_ab_day_names_bg_BG }; +MY_LOCALE my_locale_bg_BG ( "bg_BG", "Bulgarian - Bulgaria", FALSE, &my_locale_typelib_month_names_bg_BG, &my_locale_typelib_ab_month_names_bg_BG, &my_locale_typelib_day_names_bg_BG, &my_locale_typelib_ab_day_names_bg_BG ); /***** LOCALE END bg_BG *****/ /***** LOCALE BEGIN ca_ES: Catalan - Catalan *****/ @@ -199,8 +192,7 @@ static TYPELIB my_locale_typelib_day_names_ca_ES = { array_elements(my_locale_day_names_ca_ES)-1, "", my_locale_day_names_ca_ES, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ca_ES = { array_elements(my_locale_ab_day_names_ca_ES)-1, "", my_locale_ab_day_names_ca_ES, NULL }; -MY_LOCALE my_locale_ca_ES= - { "ca_ES", "Catalan - Catalan", FALSE, &my_locale_typelib_month_names_ca_ES, &my_locale_typelib_ab_month_names_ca_ES, &my_locale_typelib_day_names_ca_ES, &my_locale_typelib_ab_day_names_ca_ES }; +MY_LOCALE my_locale_ca_ES ( "ca_ES", "Catalan - Catalan", FALSE, &my_locale_typelib_month_names_ca_ES, &my_locale_typelib_ab_month_names_ca_ES, &my_locale_typelib_day_names_ca_ES, &my_locale_typelib_ab_day_names_ca_ES ); /***** LOCALE END ca_ES *****/ /***** LOCALE BEGIN cs_CZ: Czech - Czech Republic *****/ @@ -220,8 +212,7 @@ static TYPELIB my_locale_typelib_day_names_cs_CZ = { array_elements(my_locale_day_names_cs_CZ)-1, "", my_locale_day_names_cs_CZ, NULL }; static TYPELIB my_locale_typelib_ab_day_names_cs_CZ = { array_elements(my_locale_ab_day_names_cs_CZ)-1, "", my_locale_ab_day_names_cs_CZ, NULL }; -MY_LOCALE my_locale_cs_CZ= - { "cs_CZ", "Czech - Czech Republic", FALSE, &my_locale_typelib_month_names_cs_CZ, &my_locale_typelib_ab_month_names_cs_CZ, &my_locale_typelib_day_names_cs_CZ, &my_locale_typelib_ab_day_names_cs_CZ }; +MY_LOCALE my_locale_cs_CZ ( "cs_CZ", "Czech - Czech Republic", FALSE, &my_locale_typelib_month_names_cs_CZ, &my_locale_typelib_ab_month_names_cs_CZ, &my_locale_typelib_day_names_cs_CZ, &my_locale_typelib_ab_day_names_cs_CZ ); /***** LOCALE END cs_CZ *****/ /***** LOCALE BEGIN da_DK: Danish - Denmark *****/ @@ -241,8 +232,7 @@ static TYPELIB my_locale_typelib_day_names_da_DK = { array_elements(my_locale_day_names_da_DK)-1, "", my_locale_day_names_da_DK, NULL }; static TYPELIB my_locale_typelib_ab_day_names_da_DK = { array_elements(my_locale_ab_day_names_da_DK)-1, "", my_locale_ab_day_names_da_DK, NULL }; -MY_LOCALE my_locale_da_DK= - { "da_DK", "Danish - Denmark", FALSE, &my_locale_typelib_month_names_da_DK, &my_locale_typelib_ab_month_names_da_DK, &my_locale_typelib_day_names_da_DK, &my_locale_typelib_ab_day_names_da_DK }; +MY_LOCALE my_locale_da_DK ( "da_DK", "Danish - Denmark", FALSE, &my_locale_typelib_month_names_da_DK, &my_locale_typelib_ab_month_names_da_DK, &my_locale_typelib_day_names_da_DK, &my_locale_typelib_ab_day_names_da_DK ); /***** LOCALE END da_DK *****/ /***** LOCALE BEGIN de_AT: German - Austria *****/ @@ -262,8 +252,7 @@ static TYPELIB my_locale_typelib_day_names_de_AT = { array_elements(my_locale_day_names_de_AT)-1, "", my_locale_day_names_de_AT, NULL }; static TYPELIB my_locale_typelib_ab_day_names_de_AT = { array_elements(my_locale_ab_day_names_de_AT)-1, "", my_locale_ab_day_names_de_AT, NULL }; -MY_LOCALE my_locale_de_AT= - { "de_AT", "German - Austria", FALSE, &my_locale_typelib_month_names_de_AT, &my_locale_typelib_ab_month_names_de_AT, &my_locale_typelib_day_names_de_AT, &my_locale_typelib_ab_day_names_de_AT }; +MY_LOCALE my_locale_de_AT ( "de_AT", "German - Austria", FALSE, &my_locale_typelib_month_names_de_AT, &my_locale_typelib_ab_month_names_de_AT, &my_locale_typelib_day_names_de_AT, &my_locale_typelib_ab_day_names_de_AT ); /***** LOCALE END de_AT *****/ /***** LOCALE BEGIN de_DE: German - Germany *****/ @@ -283,8 +272,7 @@ static TYPELIB my_locale_typelib_day_names_de_DE = { array_elements(my_locale_day_names_de_DE)-1, "", my_locale_day_names_de_DE, NULL }; static TYPELIB my_locale_typelib_ab_day_names_de_DE = { array_elements(my_locale_ab_day_names_de_DE)-1, "", my_locale_ab_day_names_de_DE, NULL }; -MY_LOCALE my_locale_de_DE= - { "de_DE", "German - Germany", FALSE, &my_locale_typelib_month_names_de_DE, &my_locale_typelib_ab_month_names_de_DE, &my_locale_typelib_day_names_de_DE, &my_locale_typelib_ab_day_names_de_DE }; +MY_LOCALE my_locale_de_DE ( "de_DE", "German - Germany", FALSE, &my_locale_typelib_month_names_de_DE, &my_locale_typelib_ab_month_names_de_DE, &my_locale_typelib_day_names_de_DE, &my_locale_typelib_ab_day_names_de_DE ); /***** LOCALE END de_DE *****/ /***** LOCALE BEGIN en_US: English - United States *****/ @@ -304,8 +292,7 @@ static TYPELIB my_locale_typelib_day_names_en_US = { array_elements(my_locale_day_names_en_US)-1, "", my_locale_day_names_en_US, NULL }; static TYPELIB my_locale_typelib_ab_day_names_en_US = { array_elements(my_locale_ab_day_names_en_US)-1, "", my_locale_ab_day_names_en_US, NULL }; -MY_LOCALE my_locale_en_US= - { "en_US", "English - United States", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US }; +MY_LOCALE my_locale_en_US ( "en_US", "English - United States", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US ); /***** LOCALE END en_US *****/ /***** LOCALE BEGIN es_ES: Spanish - Spain *****/ @@ -325,8 +312,7 @@ static TYPELIB my_locale_typelib_day_names_es_ES = { array_elements(my_locale_day_names_es_ES)-1, "", my_locale_day_names_es_ES, NULL }; static TYPELIB my_locale_typelib_ab_day_names_es_ES = { array_elements(my_locale_ab_day_names_es_ES)-1, "", my_locale_ab_day_names_es_ES, NULL }; -MY_LOCALE my_locale_es_ES= - { "es_ES", "Spanish - Spain", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_ES ( "es_ES", "Spanish - Spain", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_ES *****/ /***** LOCALE BEGIN et_EE: Estonian - Estonia *****/ @@ -346,8 +332,7 @@ static TYPELIB my_locale_typelib_day_names_et_EE = { array_elements(my_locale_day_names_et_EE)-1, "", my_locale_day_names_et_EE, NULL }; static TYPELIB my_locale_typelib_ab_day_names_et_EE = { array_elements(my_locale_ab_day_names_et_EE)-1, "", my_locale_ab_day_names_et_EE, NULL }; -MY_LOCALE my_locale_et_EE= - { "et_EE", "Estonian - Estonia", FALSE, &my_locale_typelib_month_names_et_EE, &my_locale_typelib_ab_month_names_et_EE, &my_locale_typelib_day_names_et_EE, &my_locale_typelib_ab_day_names_et_EE }; +MY_LOCALE my_locale_et_EE ( "et_EE", "Estonian - Estonia", FALSE, &my_locale_typelib_month_names_et_EE, &my_locale_typelib_ab_month_names_et_EE, &my_locale_typelib_day_names_et_EE, &my_locale_typelib_ab_day_names_et_EE ); /***** LOCALE END et_EE *****/ /***** LOCALE BEGIN eu_ES: Basque - Basque *****/ @@ -367,8 +352,7 @@ static TYPELIB my_locale_typelib_day_names_eu_ES = { array_elements(my_locale_day_names_eu_ES)-1, "", my_locale_day_names_eu_ES, NULL }; static TYPELIB my_locale_typelib_ab_day_names_eu_ES = { array_elements(my_locale_ab_day_names_eu_ES)-1, "", my_locale_ab_day_names_eu_ES, NULL }; -MY_LOCALE my_locale_eu_ES= - { "eu_ES", "Basque - Basque", TRUE, &my_locale_typelib_month_names_eu_ES, &my_locale_typelib_ab_month_names_eu_ES, &my_locale_typelib_day_names_eu_ES, &my_locale_typelib_ab_day_names_eu_ES }; +MY_LOCALE my_locale_eu_ES ( "eu_ES", "Basque - Basque", TRUE, &my_locale_typelib_month_names_eu_ES, &my_locale_typelib_ab_month_names_eu_ES, &my_locale_typelib_day_names_eu_ES, &my_locale_typelib_ab_day_names_eu_ES ); /***** LOCALE END eu_ES *****/ /***** LOCALE BEGIN fi_FI: Finnish - Finland *****/ @@ -388,8 +372,7 @@ static TYPELIB my_locale_typelib_day_names_fi_FI = { array_elements(my_locale_day_names_fi_FI)-1, "", my_locale_day_names_fi_FI, NULL }; static TYPELIB my_locale_typelib_ab_day_names_fi_FI = { array_elements(my_locale_ab_day_names_fi_FI)-1, "", my_locale_ab_day_names_fi_FI, NULL }; -MY_LOCALE my_locale_fi_FI= - { "fi_FI", "Finnish - Finland", FALSE, &my_locale_typelib_month_names_fi_FI, &my_locale_typelib_ab_month_names_fi_FI, &my_locale_typelib_day_names_fi_FI, &my_locale_typelib_ab_day_names_fi_FI }; +MY_LOCALE my_locale_fi_FI ( "fi_FI", "Finnish - Finland", FALSE, &my_locale_typelib_month_names_fi_FI, &my_locale_typelib_ab_month_names_fi_FI, &my_locale_typelib_day_names_fi_FI, &my_locale_typelib_ab_day_names_fi_FI ); /***** LOCALE END fi_FI *****/ /***** LOCALE BEGIN fo_FO: Faroese - Faroe Islands *****/ @@ -409,8 +392,7 @@ static TYPELIB my_locale_typelib_day_names_fo_FO = { array_elements(my_locale_day_names_fo_FO)-1, "", my_locale_day_names_fo_FO, NULL }; static TYPELIB my_locale_typelib_ab_day_names_fo_FO = { array_elements(my_locale_ab_day_names_fo_FO)-1, "", my_locale_ab_day_names_fo_FO, NULL }; -MY_LOCALE my_locale_fo_FO= - { "fo_FO", "Faroese - Faroe Islands", FALSE, &my_locale_typelib_month_names_fo_FO, &my_locale_typelib_ab_month_names_fo_FO, &my_locale_typelib_day_names_fo_FO, &my_locale_typelib_ab_day_names_fo_FO }; +MY_LOCALE my_locale_fo_FO ( "fo_FO", "Faroese - Faroe Islands", FALSE, &my_locale_typelib_month_names_fo_FO, &my_locale_typelib_ab_month_names_fo_FO, &my_locale_typelib_day_names_fo_FO, &my_locale_typelib_ab_day_names_fo_FO ); /***** LOCALE END fo_FO *****/ /***** LOCALE BEGIN fr_FR: French - France *****/ @@ -430,8 +412,7 @@ static TYPELIB my_locale_typelib_day_names_fr_FR = { array_elements(my_locale_day_names_fr_FR)-1, "", my_locale_day_names_fr_FR, NULL }; static TYPELIB my_locale_typelib_ab_day_names_fr_FR = { array_elements(my_locale_ab_day_names_fr_FR)-1, "", my_locale_ab_day_names_fr_FR, NULL }; -MY_LOCALE my_locale_fr_FR= - { "fr_FR", "French - France", FALSE, &my_locale_typelib_month_names_fr_FR, &my_locale_typelib_ab_month_names_fr_FR, &my_locale_typelib_day_names_fr_FR, &my_locale_typelib_ab_day_names_fr_FR }; +MY_LOCALE my_locale_fr_FR ( "fr_FR", "French - France", FALSE, &my_locale_typelib_month_names_fr_FR, &my_locale_typelib_ab_month_names_fr_FR, &my_locale_typelib_day_names_fr_FR, &my_locale_typelib_ab_day_names_fr_FR ); /***** LOCALE END fr_FR *****/ /***** LOCALE BEGIN gl_ES: Galician - Galician *****/ @@ -451,8 +432,7 @@ static TYPELIB my_locale_typelib_day_names_gl_ES = { array_elements(my_locale_day_names_gl_ES)-1, "", my_locale_day_names_gl_ES, NULL }; static TYPELIB my_locale_typelib_ab_day_names_gl_ES = { array_elements(my_locale_ab_day_names_gl_ES)-1, "", my_locale_ab_day_names_gl_ES, NULL }; -MY_LOCALE my_locale_gl_ES= - { "gl_ES", "Galician - Galician", FALSE, &my_locale_typelib_month_names_gl_ES, &my_locale_typelib_ab_month_names_gl_ES, &my_locale_typelib_day_names_gl_ES, &my_locale_typelib_ab_day_names_gl_ES }; +MY_LOCALE my_locale_gl_ES ( "gl_ES", "Galician - Galician", FALSE, &my_locale_typelib_month_names_gl_ES, &my_locale_typelib_ab_month_names_gl_ES, &my_locale_typelib_day_names_gl_ES, &my_locale_typelib_ab_day_names_gl_ES ); /***** LOCALE END gl_ES *****/ /***** LOCALE BEGIN gu_IN: Gujarati - India *****/ @@ -472,8 +452,7 @@ static TYPELIB my_locale_typelib_day_names_gu_IN = { array_elements(my_locale_day_names_gu_IN)-1, "", my_locale_day_names_gu_IN, NULL }; static TYPELIB my_locale_typelib_ab_day_names_gu_IN = { array_elements(my_locale_ab_day_names_gu_IN)-1, "", my_locale_ab_day_names_gu_IN, NULL }; -MY_LOCALE my_locale_gu_IN= - { "gu_IN", "Gujarati - India", FALSE, &my_locale_typelib_month_names_gu_IN, &my_locale_typelib_ab_month_names_gu_IN, &my_locale_typelib_day_names_gu_IN, &my_locale_typelib_ab_day_names_gu_IN }; +MY_LOCALE my_locale_gu_IN ( "gu_IN", "Gujarati - India", FALSE, &my_locale_typelib_month_names_gu_IN, &my_locale_typelib_ab_month_names_gu_IN, &my_locale_typelib_day_names_gu_IN, &my_locale_typelib_ab_day_names_gu_IN ); /***** LOCALE END gu_IN *****/ /***** LOCALE BEGIN he_IL: Hebrew - Israel *****/ @@ -493,8 +472,7 @@ static TYPELIB my_locale_typelib_day_names_he_IL = { array_elements(my_locale_day_names_he_IL)-1, "", my_locale_day_names_he_IL, NULL }; static TYPELIB my_locale_typelib_ab_day_names_he_IL = { array_elements(my_locale_ab_day_names_he_IL)-1, "", my_locale_ab_day_names_he_IL, NULL }; -MY_LOCALE my_locale_he_IL= - { "he_IL", "Hebrew - Israel", FALSE, &my_locale_typelib_month_names_he_IL, &my_locale_typelib_ab_month_names_he_IL, &my_locale_typelib_day_names_he_IL, &my_locale_typelib_ab_day_names_he_IL }; +MY_LOCALE my_locale_he_IL ( "he_IL", "Hebrew - Israel", FALSE, &my_locale_typelib_month_names_he_IL, &my_locale_typelib_ab_month_names_he_IL, &my_locale_typelib_day_names_he_IL, &my_locale_typelib_ab_day_names_he_IL ); /***** LOCALE END he_IL *****/ /***** LOCALE BEGIN hi_IN: Hindi - India *****/ @@ -514,8 +492,7 @@ static TYPELIB my_locale_typelib_day_names_hi_IN = { array_elements(my_locale_day_names_hi_IN)-1, "", my_locale_day_names_hi_IN, NULL }; static TYPELIB my_locale_typelib_ab_day_names_hi_IN = { array_elements(my_locale_ab_day_names_hi_IN)-1, "", my_locale_ab_day_names_hi_IN, NULL }; -MY_LOCALE my_locale_hi_IN= - { "hi_IN", "Hindi - India", FALSE, &my_locale_typelib_month_names_hi_IN, &my_locale_typelib_ab_month_names_hi_IN, &my_locale_typelib_day_names_hi_IN, &my_locale_typelib_ab_day_names_hi_IN }; +MY_LOCALE my_locale_hi_IN ( "hi_IN", "Hindi - India", FALSE, &my_locale_typelib_month_names_hi_IN, &my_locale_typelib_ab_month_names_hi_IN, &my_locale_typelib_day_names_hi_IN, &my_locale_typelib_ab_day_names_hi_IN ); /***** LOCALE END hi_IN *****/ /***** LOCALE BEGIN hr_HR: Croatian - Croatia *****/ @@ -535,8 +512,7 @@ static TYPELIB my_locale_typelib_day_names_hr_HR = { array_elements(my_locale_day_names_hr_HR)-1, "", my_locale_day_names_hr_HR, NULL }; static TYPELIB my_locale_typelib_ab_day_names_hr_HR = { array_elements(my_locale_ab_day_names_hr_HR)-1, "", my_locale_ab_day_names_hr_HR, NULL }; -MY_LOCALE my_locale_hr_HR= - { "hr_HR", "Croatian - Croatia", FALSE, &my_locale_typelib_month_names_hr_HR, &my_locale_typelib_ab_month_names_hr_HR, &my_locale_typelib_day_names_hr_HR, &my_locale_typelib_ab_day_names_hr_HR }; +MY_LOCALE my_locale_hr_HR ( "hr_HR", "Croatian - Croatia", FALSE, &my_locale_typelib_month_names_hr_HR, &my_locale_typelib_ab_month_names_hr_HR, &my_locale_typelib_day_names_hr_HR, &my_locale_typelib_ab_day_names_hr_HR ); /***** LOCALE END hr_HR *****/ /***** LOCALE BEGIN hu_HU: Hungarian - Hungary *****/ @@ -556,8 +532,7 @@ static TYPELIB my_locale_typelib_day_names_hu_HU = { array_elements(my_locale_day_names_hu_HU)-1, "", my_locale_day_names_hu_HU, NULL }; static TYPELIB my_locale_typelib_ab_day_names_hu_HU = { array_elements(my_locale_ab_day_names_hu_HU)-1, "", my_locale_ab_day_names_hu_HU, NULL }; -MY_LOCALE my_locale_hu_HU= - { "hu_HU", "Hungarian - Hungary", FALSE, &my_locale_typelib_month_names_hu_HU, &my_locale_typelib_ab_month_names_hu_HU, &my_locale_typelib_day_names_hu_HU, &my_locale_typelib_ab_day_names_hu_HU }; +MY_LOCALE my_locale_hu_HU ( "hu_HU", "Hungarian - Hungary", FALSE, &my_locale_typelib_month_names_hu_HU, &my_locale_typelib_ab_month_names_hu_HU, &my_locale_typelib_day_names_hu_HU, &my_locale_typelib_ab_day_names_hu_HU ); /***** LOCALE END hu_HU *****/ /***** LOCALE BEGIN id_ID: Indonesian - Indonesia *****/ @@ -577,8 +552,7 @@ static TYPELIB my_locale_typelib_day_names_id_ID = { array_elements(my_locale_day_names_id_ID)-1, "", my_locale_day_names_id_ID, NULL }; static TYPELIB my_locale_typelib_ab_day_names_id_ID = { array_elements(my_locale_ab_day_names_id_ID)-1, "", my_locale_ab_day_names_id_ID, NULL }; -MY_LOCALE my_locale_id_ID= - { "id_ID", "Indonesian - Indonesia", TRUE, &my_locale_typelib_month_names_id_ID, &my_locale_typelib_ab_month_names_id_ID, &my_locale_typelib_day_names_id_ID, &my_locale_typelib_ab_day_names_id_ID }; +MY_LOCALE my_locale_id_ID ( "id_ID", "Indonesian - Indonesia", TRUE, &my_locale_typelib_month_names_id_ID, &my_locale_typelib_ab_month_names_id_ID, &my_locale_typelib_day_names_id_ID, &my_locale_typelib_ab_day_names_id_ID ); /***** LOCALE END id_ID *****/ /***** LOCALE BEGIN is_IS: Icelandic - Iceland *****/ @@ -598,8 +572,7 @@ static TYPELIB my_locale_typelib_day_names_is_IS = { array_elements(my_locale_day_names_is_IS)-1, "", my_locale_day_names_is_IS, NULL }; static TYPELIB my_locale_typelib_ab_day_names_is_IS = { array_elements(my_locale_ab_day_names_is_IS)-1, "", my_locale_ab_day_names_is_IS, NULL }; -MY_LOCALE my_locale_is_IS= - { "is_IS", "Icelandic - Iceland", FALSE, &my_locale_typelib_month_names_is_IS, &my_locale_typelib_ab_month_names_is_IS, &my_locale_typelib_day_names_is_IS, &my_locale_typelib_ab_day_names_is_IS }; +MY_LOCALE my_locale_is_IS ( "is_IS", "Icelandic - Iceland", FALSE, &my_locale_typelib_month_names_is_IS, &my_locale_typelib_ab_month_names_is_IS, &my_locale_typelib_day_names_is_IS, &my_locale_typelib_ab_day_names_is_IS ); /***** LOCALE END is_IS *****/ /***** LOCALE BEGIN it_CH: Italian - Switzerland *****/ @@ -619,8 +592,7 @@ static TYPELIB my_locale_typelib_day_names_it_CH = { array_elements(my_locale_day_names_it_CH)-1, "", my_locale_day_names_it_CH, NULL }; static TYPELIB my_locale_typelib_ab_day_names_it_CH = { array_elements(my_locale_ab_day_names_it_CH)-1, "", my_locale_ab_day_names_it_CH, NULL }; -MY_LOCALE my_locale_it_CH= - { "it_CH", "Italian - Switzerland", FALSE, &my_locale_typelib_month_names_it_CH, &my_locale_typelib_ab_month_names_it_CH, &my_locale_typelib_day_names_it_CH, &my_locale_typelib_ab_day_names_it_CH }; +MY_LOCALE my_locale_it_CH ( "it_CH", "Italian - Switzerland", FALSE, &my_locale_typelib_month_names_it_CH, &my_locale_typelib_ab_month_names_it_CH, &my_locale_typelib_day_names_it_CH, &my_locale_typelib_ab_day_names_it_CH ); /***** LOCALE END it_CH *****/ /***** LOCALE BEGIN ja_JP: Japanese - Japan *****/ @@ -640,8 +612,7 @@ static TYPELIB my_locale_typelib_day_names_ja_JP = { array_elements(my_locale_day_names_ja_JP)-1, "", my_locale_day_names_ja_JP, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ja_JP = { array_elements(my_locale_ab_day_names_ja_JP)-1, "", my_locale_ab_day_names_ja_JP, NULL }; -MY_LOCALE my_locale_ja_JP= - { "ja_JP", "Japanese - Japan", FALSE, &my_locale_typelib_month_names_ja_JP, &my_locale_typelib_ab_month_names_ja_JP, &my_locale_typelib_day_names_ja_JP, &my_locale_typelib_ab_day_names_ja_JP }; +MY_LOCALE my_locale_ja_JP ( "ja_JP", "Japanese - Japan", FALSE, &my_locale_typelib_month_names_ja_JP, &my_locale_typelib_ab_month_names_ja_JP, &my_locale_typelib_day_names_ja_JP, &my_locale_typelib_ab_day_names_ja_JP ); /***** LOCALE END ja_JP *****/ /***** LOCALE BEGIN ko_KR: Korean - Korea *****/ @@ -661,8 +632,7 @@ static TYPELIB my_locale_typelib_day_names_ko_KR = { array_elements(my_locale_day_names_ko_KR)-1, "", my_locale_day_names_ko_KR, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ko_KR = { array_elements(my_locale_ab_day_names_ko_KR)-1, "", my_locale_ab_day_names_ko_KR, NULL }; -MY_LOCALE my_locale_ko_KR= - { "ko_KR", "Korean - Korea", FALSE, &my_locale_typelib_month_names_ko_KR, &my_locale_typelib_ab_month_names_ko_KR, &my_locale_typelib_day_names_ko_KR, &my_locale_typelib_ab_day_names_ko_KR }; +MY_LOCALE my_locale_ko_KR ( "ko_KR", "Korean - Korea", FALSE, &my_locale_typelib_month_names_ko_KR, &my_locale_typelib_ab_month_names_ko_KR, &my_locale_typelib_day_names_ko_KR, &my_locale_typelib_ab_day_names_ko_KR ); /***** LOCALE END ko_KR *****/ /***** LOCALE BEGIN lt_LT: Lithuanian - Lithuania *****/ @@ -682,8 +652,7 @@ static TYPELIB my_locale_typelib_day_names_lt_LT = { array_elements(my_locale_day_names_lt_LT)-1, "", my_locale_day_names_lt_LT, NULL }; static TYPELIB my_locale_typelib_ab_day_names_lt_LT = { array_elements(my_locale_ab_day_names_lt_LT)-1, "", my_locale_ab_day_names_lt_LT, NULL }; -MY_LOCALE my_locale_lt_LT= - { "lt_LT", "Lithuanian - Lithuania", FALSE, &my_locale_typelib_month_names_lt_LT, &my_locale_typelib_ab_month_names_lt_LT, &my_locale_typelib_day_names_lt_LT, &my_locale_typelib_ab_day_names_lt_LT }; +MY_LOCALE my_locale_lt_LT ( "lt_LT", "Lithuanian - Lithuania", FALSE, &my_locale_typelib_month_names_lt_LT, &my_locale_typelib_ab_month_names_lt_LT, &my_locale_typelib_day_names_lt_LT, &my_locale_typelib_ab_day_names_lt_LT ); /***** LOCALE END lt_LT *****/ /***** LOCALE BEGIN lv_LV: Latvian - Latvia *****/ @@ -703,8 +672,7 @@ static TYPELIB my_locale_typelib_day_names_lv_LV = { array_elements(my_locale_day_names_lv_LV)-1, "", my_locale_day_names_lv_LV, NULL }; static TYPELIB my_locale_typelib_ab_day_names_lv_LV = { array_elements(my_locale_ab_day_names_lv_LV)-1, "", my_locale_ab_day_names_lv_LV, NULL }; -MY_LOCALE my_locale_lv_LV= - { "lv_LV", "Latvian - Latvia", FALSE, &my_locale_typelib_month_names_lv_LV, &my_locale_typelib_ab_month_names_lv_LV, &my_locale_typelib_day_names_lv_LV, &my_locale_typelib_ab_day_names_lv_LV }; +MY_LOCALE my_locale_lv_LV ( "lv_LV", "Latvian - Latvia", FALSE, &my_locale_typelib_month_names_lv_LV, &my_locale_typelib_ab_month_names_lv_LV, &my_locale_typelib_day_names_lv_LV, &my_locale_typelib_ab_day_names_lv_LV ); /***** LOCALE END lv_LV *****/ /***** LOCALE BEGIN mk_MK: Macedonian - FYROM *****/ @@ -724,8 +692,7 @@ static TYPELIB my_locale_typelib_day_names_mk_MK = { array_elements(my_locale_day_names_mk_MK)-1, "", my_locale_day_names_mk_MK, NULL }; static TYPELIB my_locale_typelib_ab_day_names_mk_MK = { array_elements(my_locale_ab_day_names_mk_MK)-1, "", my_locale_ab_day_names_mk_MK, NULL }; -MY_LOCALE my_locale_mk_MK= - { "mk_MK", "Macedonian - FYROM", FALSE, &my_locale_typelib_month_names_mk_MK, &my_locale_typelib_ab_month_names_mk_MK, &my_locale_typelib_day_names_mk_MK, &my_locale_typelib_ab_day_names_mk_MK }; +MY_LOCALE my_locale_mk_MK ( "mk_MK", "Macedonian - FYROM", FALSE, &my_locale_typelib_month_names_mk_MK, &my_locale_typelib_ab_month_names_mk_MK, &my_locale_typelib_day_names_mk_MK, &my_locale_typelib_ab_day_names_mk_MK ); /***** LOCALE END mk_MK *****/ /***** LOCALE BEGIN mn_MN: Mongolia - Mongolian *****/ @@ -745,8 +712,7 @@ static TYPELIB my_locale_typelib_day_names_mn_MN = { array_elements(my_locale_day_names_mn_MN)-1, "", my_locale_day_names_mn_MN, NULL }; static TYPELIB my_locale_typelib_ab_day_names_mn_MN = { array_elements(my_locale_ab_day_names_mn_MN)-1, "", my_locale_ab_day_names_mn_MN, NULL }; -MY_LOCALE my_locale_mn_MN= - { "mn_MN", "Mongolia - Mongolian", FALSE, &my_locale_typelib_month_names_mn_MN, &my_locale_typelib_ab_month_names_mn_MN, &my_locale_typelib_day_names_mn_MN, &my_locale_typelib_ab_day_names_mn_MN }; +MY_LOCALE my_locale_mn_MN ( "mn_MN", "Mongolia - Mongolian", FALSE, &my_locale_typelib_month_names_mn_MN, &my_locale_typelib_ab_month_names_mn_MN, &my_locale_typelib_day_names_mn_MN, &my_locale_typelib_ab_day_names_mn_MN ); /***** LOCALE END mn_MN *****/ /***** LOCALE BEGIN ms_MY: Malay - Malaysia *****/ @@ -766,8 +732,7 @@ static TYPELIB my_locale_typelib_day_names_ms_MY = { array_elements(my_locale_day_names_ms_MY)-1, "", my_locale_day_names_ms_MY, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ms_MY = { array_elements(my_locale_ab_day_names_ms_MY)-1, "", my_locale_ab_day_names_ms_MY, NULL }; -MY_LOCALE my_locale_ms_MY= - { "ms_MY", "Malay - Malaysia", TRUE, &my_locale_typelib_month_names_ms_MY, &my_locale_typelib_ab_month_names_ms_MY, &my_locale_typelib_day_names_ms_MY, &my_locale_typelib_ab_day_names_ms_MY }; +MY_LOCALE my_locale_ms_MY ( "ms_MY", "Malay - Malaysia", TRUE, &my_locale_typelib_month_names_ms_MY, &my_locale_typelib_ab_month_names_ms_MY, &my_locale_typelib_day_names_ms_MY, &my_locale_typelib_ab_day_names_ms_MY ); /***** LOCALE END ms_MY *****/ /***** LOCALE BEGIN nb_NO: Norwegian(Bokml) - Norway *****/ @@ -787,8 +752,7 @@ static TYPELIB my_locale_typelib_day_names_nb_NO = { array_elements(my_locale_day_names_nb_NO)-1, "", my_locale_day_names_nb_NO, NULL }; static TYPELIB my_locale_typelib_ab_day_names_nb_NO = { array_elements(my_locale_ab_day_names_nb_NO)-1, "", my_locale_ab_day_names_nb_NO, NULL }; -MY_LOCALE my_locale_nb_NO= - { "nb_NO", "Norwegian(Bokml) - Norway", FALSE, &my_locale_typelib_month_names_nb_NO, &my_locale_typelib_ab_month_names_nb_NO, &my_locale_typelib_day_names_nb_NO, &my_locale_typelib_ab_day_names_nb_NO }; +MY_LOCALE my_locale_nb_NO ( "nb_NO", "Norwegian(Bokml) - Norway", FALSE, &my_locale_typelib_month_names_nb_NO, &my_locale_typelib_ab_month_names_nb_NO, &my_locale_typelib_day_names_nb_NO, &my_locale_typelib_ab_day_names_nb_NO ); /***** LOCALE END nb_NO *****/ /***** LOCALE BEGIN nl_NL: Dutch - The Netherlands *****/ @@ -808,8 +772,7 @@ static TYPELIB my_locale_typelib_day_names_nl_NL = { array_elements(my_locale_day_names_nl_NL)-1, "", my_locale_day_names_nl_NL, NULL }; static TYPELIB my_locale_typelib_ab_day_names_nl_NL = { array_elements(my_locale_ab_day_names_nl_NL)-1, "", my_locale_ab_day_names_nl_NL, NULL }; -MY_LOCALE my_locale_nl_NL= - { "nl_NL", "Dutch - The Netherlands", TRUE, &my_locale_typelib_month_names_nl_NL, &my_locale_typelib_ab_month_names_nl_NL, &my_locale_typelib_day_names_nl_NL, &my_locale_typelib_ab_day_names_nl_NL }; +MY_LOCALE my_locale_nl_NL ( "nl_NL", "Dutch - The Netherlands", TRUE, &my_locale_typelib_month_names_nl_NL, &my_locale_typelib_ab_month_names_nl_NL, &my_locale_typelib_day_names_nl_NL, &my_locale_typelib_ab_day_names_nl_NL ); /***** LOCALE END nl_NL *****/ /***** LOCALE BEGIN pl_PL: Polish - Poland *****/ @@ -829,8 +792,7 @@ static TYPELIB my_locale_typelib_day_names_pl_PL = { array_elements(my_locale_day_names_pl_PL)-1, "", my_locale_day_names_pl_PL, NULL }; static TYPELIB my_locale_typelib_ab_day_names_pl_PL = { array_elements(my_locale_ab_day_names_pl_PL)-1, "", my_locale_ab_day_names_pl_PL, NULL }; -MY_LOCALE my_locale_pl_PL= - { "pl_PL", "Polish - Poland", FALSE, &my_locale_typelib_month_names_pl_PL, &my_locale_typelib_ab_month_names_pl_PL, &my_locale_typelib_day_names_pl_PL, &my_locale_typelib_ab_day_names_pl_PL }; +MY_LOCALE my_locale_pl_PL ( "pl_PL", "Polish - Poland", FALSE, &my_locale_typelib_month_names_pl_PL, &my_locale_typelib_ab_month_names_pl_PL, &my_locale_typelib_day_names_pl_PL, &my_locale_typelib_ab_day_names_pl_PL ); /***** LOCALE END pl_PL *****/ /***** LOCALE BEGIN pt_BR: Portugese - Brazil *****/ @@ -850,8 +812,7 @@ static TYPELIB my_locale_typelib_day_names_pt_BR = { array_elements(my_locale_day_names_pt_BR)-1, "", my_locale_day_names_pt_BR, NULL }; static TYPELIB my_locale_typelib_ab_day_names_pt_BR = { array_elements(my_locale_ab_day_names_pt_BR)-1, "", my_locale_ab_day_names_pt_BR, NULL }; -MY_LOCALE my_locale_pt_BR= - { "pt_BR", "Portugese - Brazil", FALSE, &my_locale_typelib_month_names_pt_BR, &my_locale_typelib_ab_month_names_pt_BR, &my_locale_typelib_day_names_pt_BR, &my_locale_typelib_ab_day_names_pt_BR }; +MY_LOCALE my_locale_pt_BR ( "pt_BR", "Portugese - Brazil", FALSE, &my_locale_typelib_month_names_pt_BR, &my_locale_typelib_ab_month_names_pt_BR, &my_locale_typelib_day_names_pt_BR, &my_locale_typelib_ab_day_names_pt_BR ); /***** LOCALE END pt_BR *****/ /***** LOCALE BEGIN pt_PT: Portugese - Portugal *****/ @@ -871,8 +832,7 @@ static TYPELIB my_locale_typelib_day_names_pt_PT = { array_elements(my_locale_day_names_pt_PT)-1, "", my_locale_day_names_pt_PT, NULL }; static TYPELIB my_locale_typelib_ab_day_names_pt_PT = { array_elements(my_locale_ab_day_names_pt_PT)-1, "", my_locale_ab_day_names_pt_PT, NULL }; -MY_LOCALE my_locale_pt_PT= - { "pt_PT", "Portugese - Portugal", FALSE, &my_locale_typelib_month_names_pt_PT, &my_locale_typelib_ab_month_names_pt_PT, &my_locale_typelib_day_names_pt_PT, &my_locale_typelib_ab_day_names_pt_PT }; +MY_LOCALE my_locale_pt_PT ( "pt_PT", "Portugese - Portugal", FALSE, &my_locale_typelib_month_names_pt_PT, &my_locale_typelib_ab_month_names_pt_PT, &my_locale_typelib_day_names_pt_PT, &my_locale_typelib_ab_day_names_pt_PT ); /***** LOCALE END pt_PT *****/ /***** LOCALE BEGIN ro_RO: Romanian - Romania *****/ @@ -892,8 +852,7 @@ static TYPELIB my_locale_typelib_day_names_ro_RO = { array_elements(my_locale_day_names_ro_RO)-1, "", my_locale_day_names_ro_RO, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ro_RO = { array_elements(my_locale_ab_day_names_ro_RO)-1, "", my_locale_ab_day_names_ro_RO, NULL }; -MY_LOCALE my_locale_ro_RO= - { "ro_RO", "Romanian - Romania", FALSE, &my_locale_typelib_month_names_ro_RO, &my_locale_typelib_ab_month_names_ro_RO, &my_locale_typelib_day_names_ro_RO, &my_locale_typelib_ab_day_names_ro_RO }; +MY_LOCALE my_locale_ro_RO ( "ro_RO", "Romanian - Romania", FALSE, &my_locale_typelib_month_names_ro_RO, &my_locale_typelib_ab_month_names_ro_RO, &my_locale_typelib_day_names_ro_RO, &my_locale_typelib_ab_day_names_ro_RO ); /***** LOCALE END ro_RO *****/ /***** LOCALE BEGIN ru_RU: Russian - Russia *****/ @@ -913,8 +872,7 @@ static TYPELIB my_locale_typelib_day_names_ru_RU = { array_elements(my_locale_day_names_ru_RU)-1, "", my_locale_day_names_ru_RU, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ru_RU = { array_elements(my_locale_ab_day_names_ru_RU)-1, "", my_locale_ab_day_names_ru_RU, NULL }; -MY_LOCALE my_locale_ru_RU= - { "ru_RU", "Russian - Russia", FALSE, &my_locale_typelib_month_names_ru_RU, &my_locale_typelib_ab_month_names_ru_RU, &my_locale_typelib_day_names_ru_RU, &my_locale_typelib_ab_day_names_ru_RU }; +MY_LOCALE my_locale_ru_RU ( "ru_RU", "Russian - Russia", FALSE, &my_locale_typelib_month_names_ru_RU, &my_locale_typelib_ab_month_names_ru_RU, &my_locale_typelib_day_names_ru_RU, &my_locale_typelib_ab_day_names_ru_RU ); /***** LOCALE END ru_RU *****/ /***** LOCALE BEGIN ru_UA: Russian - Ukraine *****/ @@ -934,8 +892,7 @@ static TYPELIB my_locale_typelib_day_names_ru_UA = { array_elements(my_locale_day_names_ru_UA)-1, "", my_locale_day_names_ru_UA, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ru_UA = { array_elements(my_locale_ab_day_names_ru_UA)-1, "", my_locale_ab_day_names_ru_UA, NULL }; -MY_LOCALE my_locale_ru_UA= - { "ru_UA", "Russian - Ukraine", FALSE, &my_locale_typelib_month_names_ru_UA, &my_locale_typelib_ab_month_names_ru_UA, &my_locale_typelib_day_names_ru_UA, &my_locale_typelib_ab_day_names_ru_UA }; +MY_LOCALE my_locale_ru_UA ( "ru_UA", "Russian - Ukraine", FALSE, &my_locale_typelib_month_names_ru_UA, &my_locale_typelib_ab_month_names_ru_UA, &my_locale_typelib_day_names_ru_UA, &my_locale_typelib_ab_day_names_ru_UA ); /***** LOCALE END ru_UA *****/ /***** LOCALE BEGIN sk_SK: Slovak - Slovakia *****/ @@ -955,8 +912,7 @@ static TYPELIB my_locale_typelib_day_names_sk_SK = { array_elements(my_locale_day_names_sk_SK)-1, "", my_locale_day_names_sk_SK, NULL }; static TYPELIB my_locale_typelib_ab_day_names_sk_SK = { array_elements(my_locale_ab_day_names_sk_SK)-1, "", my_locale_ab_day_names_sk_SK, NULL }; -MY_LOCALE my_locale_sk_SK= - { "sk_SK", "Slovak - Slovakia", FALSE, &my_locale_typelib_month_names_sk_SK, &my_locale_typelib_ab_month_names_sk_SK, &my_locale_typelib_day_names_sk_SK, &my_locale_typelib_ab_day_names_sk_SK }; +MY_LOCALE my_locale_sk_SK ( "sk_SK", "Slovak - Slovakia", FALSE, &my_locale_typelib_month_names_sk_SK, &my_locale_typelib_ab_month_names_sk_SK, &my_locale_typelib_day_names_sk_SK, &my_locale_typelib_ab_day_names_sk_SK ); /***** LOCALE END sk_SK *****/ /***** LOCALE BEGIN sl_SI: Slovenian - Slovenia *****/ @@ -976,8 +932,7 @@ static TYPELIB my_locale_typelib_day_names_sl_SI = { array_elements(my_locale_day_names_sl_SI)-1, "", my_locale_day_names_sl_SI, NULL }; static TYPELIB my_locale_typelib_ab_day_names_sl_SI = { array_elements(my_locale_ab_day_names_sl_SI)-1, "", my_locale_ab_day_names_sl_SI, NULL }; -MY_LOCALE my_locale_sl_SI= - { "sl_SI", "Slovenian - Slovenia", FALSE, &my_locale_typelib_month_names_sl_SI, &my_locale_typelib_ab_month_names_sl_SI, &my_locale_typelib_day_names_sl_SI, &my_locale_typelib_ab_day_names_sl_SI }; +MY_LOCALE my_locale_sl_SI ( "sl_SI", "Slovenian - Slovenia", FALSE, &my_locale_typelib_month_names_sl_SI, &my_locale_typelib_ab_month_names_sl_SI, &my_locale_typelib_day_names_sl_SI, &my_locale_typelib_ab_day_names_sl_SI ); /***** LOCALE END sl_SI *****/ /***** LOCALE BEGIN sq_AL: Albanian - Albania *****/ @@ -997,8 +952,7 @@ static TYPELIB my_locale_typelib_day_names_sq_AL = { array_elements(my_locale_day_names_sq_AL)-1, "", my_locale_day_names_sq_AL, NULL }; static TYPELIB my_locale_typelib_ab_day_names_sq_AL = { array_elements(my_locale_ab_day_names_sq_AL)-1, "", my_locale_ab_day_names_sq_AL, NULL }; -MY_LOCALE my_locale_sq_AL= - { "sq_AL", "Albanian - Albania", FALSE, &my_locale_typelib_month_names_sq_AL, &my_locale_typelib_ab_month_names_sq_AL, &my_locale_typelib_day_names_sq_AL, &my_locale_typelib_ab_day_names_sq_AL }; +MY_LOCALE my_locale_sq_AL ( "sq_AL", "Albanian - Albania", FALSE, &my_locale_typelib_month_names_sq_AL, &my_locale_typelib_ab_month_names_sq_AL, &my_locale_typelib_day_names_sq_AL, &my_locale_typelib_ab_day_names_sq_AL ); /***** LOCALE END sq_AL *****/ /***** LOCALE BEGIN sr_YU: Servian - Yugoslavia *****/ @@ -1018,8 +972,7 @@ static TYPELIB my_locale_typelib_day_names_sr_YU = { array_elements(my_locale_day_names_sr_YU)-1, "", my_locale_day_names_sr_YU, NULL }; static TYPELIB my_locale_typelib_ab_day_names_sr_YU = { array_elements(my_locale_ab_day_names_sr_YU)-1, "", my_locale_ab_day_names_sr_YU, NULL }; -MY_LOCALE my_locale_sr_YU= - { "sr_YU", "Servian - Yugoslavia", FALSE, &my_locale_typelib_month_names_sr_YU, &my_locale_typelib_ab_month_names_sr_YU, &my_locale_typelib_day_names_sr_YU, &my_locale_typelib_ab_day_names_sr_YU }; +MY_LOCALE my_locale_sr_YU ( "sr_YU", "Servian - Yugoslavia", FALSE, &my_locale_typelib_month_names_sr_YU, &my_locale_typelib_ab_month_names_sr_YU, &my_locale_typelib_day_names_sr_YU, &my_locale_typelib_ab_day_names_sr_YU ); /***** LOCALE END sr_YU *****/ /***** LOCALE BEGIN sv_SE: Swedish - Sweden *****/ @@ -1039,8 +992,7 @@ static TYPELIB my_locale_typelib_day_names_sv_SE = { array_elements(my_locale_day_names_sv_SE)-1, "", my_locale_day_names_sv_SE, NULL }; static TYPELIB my_locale_typelib_ab_day_names_sv_SE = { array_elements(my_locale_ab_day_names_sv_SE)-1, "", my_locale_ab_day_names_sv_SE, NULL }; -MY_LOCALE my_locale_sv_SE= - { "sv_SE", "Swedish - Sweden", FALSE, &my_locale_typelib_month_names_sv_SE, &my_locale_typelib_ab_month_names_sv_SE, &my_locale_typelib_day_names_sv_SE, &my_locale_typelib_ab_day_names_sv_SE }; +MY_LOCALE my_locale_sv_SE ( "sv_SE", "Swedish - Sweden", FALSE, &my_locale_typelib_month_names_sv_SE, &my_locale_typelib_ab_month_names_sv_SE, &my_locale_typelib_day_names_sv_SE, &my_locale_typelib_ab_day_names_sv_SE ); /***** LOCALE END sv_SE *****/ /***** LOCALE BEGIN ta_IN: Tamil - India *****/ @@ -1060,8 +1012,7 @@ static TYPELIB my_locale_typelib_day_names_ta_IN = { array_elements(my_locale_day_names_ta_IN)-1, "", my_locale_day_names_ta_IN, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ta_IN = { array_elements(my_locale_ab_day_names_ta_IN)-1, "", my_locale_ab_day_names_ta_IN, NULL }; -MY_LOCALE my_locale_ta_IN= - { "ta_IN", "Tamil - India", FALSE, &my_locale_typelib_month_names_ta_IN, &my_locale_typelib_ab_month_names_ta_IN, &my_locale_typelib_day_names_ta_IN, &my_locale_typelib_ab_day_names_ta_IN }; +MY_LOCALE my_locale_ta_IN ( "ta_IN", "Tamil - India", FALSE, &my_locale_typelib_month_names_ta_IN, &my_locale_typelib_ab_month_names_ta_IN, &my_locale_typelib_day_names_ta_IN, &my_locale_typelib_ab_day_names_ta_IN ); /***** LOCALE END ta_IN *****/ /***** LOCALE BEGIN te_IN: Telugu - India *****/ @@ -1081,8 +1032,7 @@ static TYPELIB my_locale_typelib_day_names_te_IN = { array_elements(my_locale_day_names_te_IN)-1, "", my_locale_day_names_te_IN, NULL }; static TYPELIB my_locale_typelib_ab_day_names_te_IN = { array_elements(my_locale_ab_day_names_te_IN)-1, "", my_locale_ab_day_names_te_IN, NULL }; -MY_LOCALE my_locale_te_IN= - { "te_IN", "Telugu - India", FALSE, &my_locale_typelib_month_names_te_IN, &my_locale_typelib_ab_month_names_te_IN, &my_locale_typelib_day_names_te_IN, &my_locale_typelib_ab_day_names_te_IN }; +MY_LOCALE my_locale_te_IN ( "te_IN", "Telugu - India", FALSE, &my_locale_typelib_month_names_te_IN, &my_locale_typelib_ab_month_names_te_IN, &my_locale_typelib_day_names_te_IN, &my_locale_typelib_ab_day_names_te_IN ); /***** LOCALE END te_IN *****/ /***** LOCALE BEGIN th_TH: Thai - Thailand *****/ @@ -1102,8 +1052,7 @@ static TYPELIB my_locale_typelib_day_names_th_TH = { array_elements(my_locale_day_names_th_TH)-1, "", my_locale_day_names_th_TH, NULL }; static TYPELIB my_locale_typelib_ab_day_names_th_TH = { array_elements(my_locale_ab_day_names_th_TH)-1, "", my_locale_ab_day_names_th_TH, NULL }; -MY_LOCALE my_locale_th_TH= - { "th_TH", "Thai - Thailand", FALSE, &my_locale_typelib_month_names_th_TH, &my_locale_typelib_ab_month_names_th_TH, &my_locale_typelib_day_names_th_TH, &my_locale_typelib_ab_day_names_th_TH }; +MY_LOCALE my_locale_th_TH ( "th_TH", "Thai - Thailand", FALSE, &my_locale_typelib_month_names_th_TH, &my_locale_typelib_ab_month_names_th_TH, &my_locale_typelib_day_names_th_TH, &my_locale_typelib_ab_day_names_th_TH ); /***** LOCALE END th_TH *****/ /***** LOCALE BEGIN tr_TR: Turkish - Turkey *****/ @@ -1123,8 +1072,7 @@ static TYPELIB my_locale_typelib_day_names_tr_TR = { array_elements(my_locale_day_names_tr_TR)-1, "", my_locale_day_names_tr_TR, NULL }; static TYPELIB my_locale_typelib_ab_day_names_tr_TR = { array_elements(my_locale_ab_day_names_tr_TR)-1, "", my_locale_ab_day_names_tr_TR, NULL }; -MY_LOCALE my_locale_tr_TR= - { "tr_TR", "Turkish - Turkey", FALSE, &my_locale_typelib_month_names_tr_TR, &my_locale_typelib_ab_month_names_tr_TR, &my_locale_typelib_day_names_tr_TR, &my_locale_typelib_ab_day_names_tr_TR }; +MY_LOCALE my_locale_tr_TR ( "tr_TR", "Turkish - Turkey", FALSE, &my_locale_typelib_month_names_tr_TR, &my_locale_typelib_ab_month_names_tr_TR, &my_locale_typelib_day_names_tr_TR, &my_locale_typelib_ab_day_names_tr_TR ); /***** LOCALE END tr_TR *****/ /***** LOCALE BEGIN uk_UA: Ukrainian - Ukraine *****/ @@ -1144,8 +1092,7 @@ static TYPELIB my_locale_typelib_day_names_uk_UA = { array_elements(my_locale_day_names_uk_UA)-1, "", my_locale_day_names_uk_UA, NULL }; static TYPELIB my_locale_typelib_ab_day_names_uk_UA = { array_elements(my_locale_ab_day_names_uk_UA)-1, "", my_locale_ab_day_names_uk_UA, NULL }; -MY_LOCALE my_locale_uk_UA= - { "uk_UA", "Ukrainian - Ukraine", FALSE, &my_locale_typelib_month_names_uk_UA, &my_locale_typelib_ab_month_names_uk_UA, &my_locale_typelib_day_names_uk_UA, &my_locale_typelib_ab_day_names_uk_UA }; +MY_LOCALE my_locale_uk_UA ( "uk_UA", "Ukrainian - Ukraine", FALSE, &my_locale_typelib_month_names_uk_UA, &my_locale_typelib_ab_month_names_uk_UA, &my_locale_typelib_day_names_uk_UA, &my_locale_typelib_ab_day_names_uk_UA ); /***** LOCALE END uk_UA *****/ /***** LOCALE BEGIN ur_PK: Urdu - Pakistan *****/ @@ -1165,8 +1112,7 @@ static TYPELIB my_locale_typelib_day_names_ur_PK = { array_elements(my_locale_day_names_ur_PK)-1, "", my_locale_day_names_ur_PK, NULL }; static TYPELIB my_locale_typelib_ab_day_names_ur_PK = { array_elements(my_locale_ab_day_names_ur_PK)-1, "", my_locale_ab_day_names_ur_PK, NULL }; -MY_LOCALE my_locale_ur_PK= - { "ur_PK", "Urdu - Pakistan", FALSE, &my_locale_typelib_month_names_ur_PK, &my_locale_typelib_ab_month_names_ur_PK, &my_locale_typelib_day_names_ur_PK, &my_locale_typelib_ab_day_names_ur_PK }; +MY_LOCALE my_locale_ur_PK ( "ur_PK", "Urdu - Pakistan", FALSE, &my_locale_typelib_month_names_ur_PK, &my_locale_typelib_ab_month_names_ur_PK, &my_locale_typelib_day_names_ur_PK, &my_locale_typelib_ab_day_names_ur_PK ); /***** LOCALE END ur_PK *****/ /***** LOCALE BEGIN vi_VN: Vietnamese - Vietnam *****/ @@ -1186,8 +1132,7 @@ static TYPELIB my_locale_typelib_day_names_vi_VN = { array_elements(my_locale_day_names_vi_VN)-1, "", my_locale_day_names_vi_VN, NULL }; static TYPELIB my_locale_typelib_ab_day_names_vi_VN = { array_elements(my_locale_ab_day_names_vi_VN)-1, "", my_locale_ab_day_names_vi_VN, NULL }; -MY_LOCALE my_locale_vi_VN= - { "vi_VN", "Vietnamese - Vietnam", FALSE, &my_locale_typelib_month_names_vi_VN, &my_locale_typelib_ab_month_names_vi_VN, &my_locale_typelib_day_names_vi_VN, &my_locale_typelib_ab_day_names_vi_VN }; +MY_LOCALE my_locale_vi_VN ( "vi_VN", "Vietnamese - Vietnam", FALSE, &my_locale_typelib_month_names_vi_VN, &my_locale_typelib_ab_month_names_vi_VN, &my_locale_typelib_day_names_vi_VN, &my_locale_typelib_ab_day_names_vi_VN ); /***** LOCALE END vi_VN *****/ /***** LOCALE BEGIN zh_CN: Chinese - Peoples Republic of China *****/ @@ -1207,8 +1152,7 @@ static TYPELIB my_locale_typelib_day_names_zh_CN = { array_elements(my_locale_day_names_zh_CN)-1, "", my_locale_day_names_zh_CN, NULL }; static TYPELIB my_locale_typelib_ab_day_names_zh_CN = { array_elements(my_locale_ab_day_names_zh_CN)-1, "", my_locale_ab_day_names_zh_CN, NULL }; -MY_LOCALE my_locale_zh_CN= - { "zh_CN", "Chinese - Peoples Republic of China", FALSE, &my_locale_typelib_month_names_zh_CN, &my_locale_typelib_ab_month_names_zh_CN, &my_locale_typelib_day_names_zh_CN, &my_locale_typelib_ab_day_names_zh_CN }; +MY_LOCALE my_locale_zh_CN ( "zh_CN", "Chinese - Peoples Republic of China", FALSE, &my_locale_typelib_month_names_zh_CN, &my_locale_typelib_ab_month_names_zh_CN, &my_locale_typelib_day_names_zh_CN, &my_locale_typelib_ab_day_names_zh_CN ); /***** LOCALE END zh_CN *****/ /***** LOCALE BEGIN zh_TW: Chinese - Taiwan *****/ @@ -1228,268 +1172,215 @@ static TYPELIB my_locale_typelib_day_names_zh_TW = { array_elements(my_locale_day_names_zh_TW)-1, "", my_locale_day_names_zh_TW, NULL }; static TYPELIB my_locale_typelib_ab_day_names_zh_TW = { array_elements(my_locale_ab_day_names_zh_TW)-1, "", my_locale_ab_day_names_zh_TW, NULL }; -MY_LOCALE my_locale_zh_TW= - { "zh_TW", "Chinese - Taiwan", FALSE, &my_locale_typelib_month_names_zh_TW, &my_locale_typelib_ab_month_names_zh_TW, &my_locale_typelib_day_names_zh_TW, &my_locale_typelib_ab_day_names_zh_TW }; +MY_LOCALE my_locale_zh_TW ( "zh_TW", "Chinese - Taiwan", FALSE, &my_locale_typelib_month_names_zh_TW, &my_locale_typelib_ab_month_names_zh_TW, &my_locale_typelib_day_names_zh_TW, &my_locale_typelib_ab_day_names_zh_TW ); /***** LOCALE END zh_TW *****/ /***** LOCALE BEGIN ar_DZ: Arabic - Algeria *****/ -MY_LOCALE my_locale_ar_DZ= - { "ar_DZ", "Arabic - Algeria", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH }; +MY_LOCALE my_locale_ar_DZ ( "ar_DZ", "Arabic - Algeria", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); /***** LOCALE END ar_DZ *****/ /***** LOCALE BEGIN ar_EG: Arabic - Egypt *****/ -MY_LOCALE my_locale_ar_EG= - { "ar_EG", "Arabic - Egypt", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH }; +MY_LOCALE my_locale_ar_EG ( "ar_EG", "Arabic - Egypt", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); /***** LOCALE END ar_EG *****/ /***** LOCALE BEGIN ar_IN: Arabic - Iran *****/ -MY_LOCALE my_locale_ar_IN= - { "ar_IN", "Arabic - Iran", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH }; +MY_LOCALE my_locale_ar_IN ( "ar_IN", "Arabic - Iran", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); /***** LOCALE END ar_IN *****/ /***** LOCALE BEGIN ar_IQ: Arabic - Iraq *****/ -MY_LOCALE my_locale_ar_IQ= - { "ar_IQ", "Arabic - Iraq", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH }; +MY_LOCALE my_locale_ar_IQ ( "ar_IQ", "Arabic - Iraq", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); /***** LOCALE END ar_IQ *****/ /***** LOCALE BEGIN ar_KW: Arabic - Kuwait *****/ -MY_LOCALE my_locale_ar_KW= - { "ar_KW", "Arabic - Kuwait", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH }; +MY_LOCALE my_locale_ar_KW ( "ar_KW", "Arabic - Kuwait", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); /***** LOCALE END ar_KW *****/ /***** LOCALE BEGIN ar_LB: Arabic - Lebanon *****/ -MY_LOCALE my_locale_ar_LB= - { "ar_LB", "Arabic - Lebanon", FALSE, &my_locale_typelib_month_names_ar_JO, &my_locale_typelib_ab_month_names_ar_JO, &my_locale_typelib_day_names_ar_JO, &my_locale_typelib_ab_day_names_ar_JO }; +MY_LOCALE my_locale_ar_LB ( "ar_LB", "Arabic - Lebanon", FALSE, &my_locale_typelib_month_names_ar_JO, &my_locale_typelib_ab_month_names_ar_JO, &my_locale_typelib_day_names_ar_JO, &my_locale_typelib_ab_day_names_ar_JO ); /***** LOCALE END ar_LB *****/ /***** LOCALE BEGIN ar_LY: Arabic - Libya *****/ -MY_LOCALE my_locale_ar_LY= - { "ar_LY", "Arabic - Libya", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH }; +MY_LOCALE my_locale_ar_LY ( "ar_LY", "Arabic - Libya", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); /***** LOCALE END ar_LY *****/ /***** LOCALE BEGIN ar_MA: Arabic - Morocco *****/ -MY_LOCALE my_locale_ar_MA= - { "ar_MA", "Arabic - Morocco", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH }; +MY_LOCALE my_locale_ar_MA ( "ar_MA", "Arabic - Morocco", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); /***** LOCALE END ar_MA *****/ /***** LOCALE BEGIN ar_OM: Arabic - Oman *****/ -MY_LOCALE my_locale_ar_OM= - { "ar_OM", "Arabic - Oman", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH }; +MY_LOCALE my_locale_ar_OM ( "ar_OM", "Arabic - Oman", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); /***** LOCALE END ar_OM *****/ /***** LOCALE BEGIN ar_QA: Arabic - Qatar *****/ -MY_LOCALE my_locale_ar_QA= - { "ar_QA", "Arabic - Qatar", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH }; +MY_LOCALE my_locale_ar_QA ( "ar_QA", "Arabic - Qatar", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); /***** LOCALE END ar_QA *****/ /***** LOCALE BEGIN ar_SD: Arabic - Sudan *****/ -MY_LOCALE my_locale_ar_SD= - { "ar_SD", "Arabic - Sudan", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH }; +MY_LOCALE my_locale_ar_SD ( "ar_SD", "Arabic - Sudan", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); /***** LOCALE END ar_SD *****/ /***** LOCALE BEGIN ar_TN: Arabic - Tunisia *****/ -MY_LOCALE my_locale_ar_TN= - { "ar_TN", "Arabic - Tunisia", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH }; +MY_LOCALE my_locale_ar_TN ( "ar_TN", "Arabic - Tunisia", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); /***** LOCALE END ar_TN *****/ /***** LOCALE BEGIN ar_YE: Arabic - Yemen *****/ -MY_LOCALE my_locale_ar_YE= - { "ar_YE", "Arabic - Yemen", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH }; +MY_LOCALE my_locale_ar_YE ( "ar_YE", "Arabic - Yemen", FALSE, &my_locale_typelib_month_names_ar_BH, &my_locale_typelib_ab_month_names_ar_BH, &my_locale_typelib_day_names_ar_BH, &my_locale_typelib_ab_day_names_ar_BH ); /***** LOCALE END ar_YE *****/ /***** LOCALE BEGIN de_BE: German - Belgium *****/ -MY_LOCALE my_locale_de_BE= - { "de_BE", "German - Belgium", FALSE, &my_locale_typelib_month_names_de_DE, &my_locale_typelib_ab_month_names_de_DE, &my_locale_typelib_day_names_de_DE, &my_locale_typelib_ab_day_names_de_DE }; +MY_LOCALE my_locale_de_BE ( "de_BE", "German - Belgium", FALSE, &my_locale_typelib_month_names_de_DE, &my_locale_typelib_ab_month_names_de_DE, &my_locale_typelib_day_names_de_DE, &my_locale_typelib_ab_day_names_de_DE ); /***** LOCALE END de_BE *****/ /***** LOCALE BEGIN de_CH: German - Switzerland *****/ -MY_LOCALE my_locale_de_CH= - { "de_CH", "German - Switzerland", FALSE, &my_locale_typelib_month_names_de_DE, &my_locale_typelib_ab_month_names_de_DE, &my_locale_typelib_day_names_de_DE, &my_locale_typelib_ab_day_names_de_DE }; +MY_LOCALE my_locale_de_CH ( "de_CH", "German - Switzerland", FALSE, &my_locale_typelib_month_names_de_DE, &my_locale_typelib_ab_month_names_de_DE, &my_locale_typelib_day_names_de_DE, &my_locale_typelib_ab_day_names_de_DE ); /***** LOCALE END de_CH *****/ /***** LOCALE BEGIN de_LU: German - Luxembourg *****/ -MY_LOCALE my_locale_de_LU= - { "de_LU", "German - Luxembourg", FALSE, &my_locale_typelib_month_names_de_DE, &my_locale_typelib_ab_month_names_de_DE, &my_locale_typelib_day_names_de_DE, &my_locale_typelib_ab_day_names_de_DE }; +MY_LOCALE my_locale_de_LU ( "de_LU", "German - Luxembourg", FALSE, &my_locale_typelib_month_names_de_DE, &my_locale_typelib_ab_month_names_de_DE, &my_locale_typelib_day_names_de_DE, &my_locale_typelib_ab_day_names_de_DE ); /***** LOCALE END de_LU *****/ /***** LOCALE BEGIN en_AU: English - Australia *****/ -MY_LOCALE my_locale_en_AU= - { "en_AU", "English - Australia", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US }; +MY_LOCALE my_locale_en_AU ( "en_AU", "English - Australia", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US ); /***** LOCALE END en_AU *****/ /***** LOCALE BEGIN en_CA: English - Canada *****/ -MY_LOCALE my_locale_en_CA= - { "en_CA", "English - Canada", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US }; +MY_LOCALE my_locale_en_CA ( "en_CA", "English - Canada", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US ); /***** LOCALE END en_CA *****/ /***** LOCALE BEGIN en_GB: English - United Kingdom *****/ -MY_LOCALE my_locale_en_GB= - { "en_GB", "English - United Kingdom", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US }; +MY_LOCALE my_locale_en_GB ( "en_GB", "English - United Kingdom", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US ); /***** LOCALE END en_GB *****/ /***** LOCALE BEGIN en_IN: English - India *****/ -MY_LOCALE my_locale_en_IN= - { "en_IN", "English - India", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US }; +MY_LOCALE my_locale_en_IN ( "en_IN", "English - India", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US ); /***** LOCALE END en_IN *****/ /***** LOCALE BEGIN en_NZ: English - New Zealand *****/ -MY_LOCALE my_locale_en_NZ= - { "en_NZ", "English - New Zealand", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US }; +MY_LOCALE my_locale_en_NZ ( "en_NZ", "English - New Zealand", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US ); /***** LOCALE END en_NZ *****/ /***** LOCALE BEGIN en_PH: English - Philippines *****/ -MY_LOCALE my_locale_en_PH= - { "en_PH", "English - Philippines", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US }; +MY_LOCALE my_locale_en_PH ( "en_PH", "English - Philippines", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US ); /***** LOCALE END en_PH *****/ /***** LOCALE BEGIN en_ZA: English - South Africa *****/ -MY_LOCALE my_locale_en_ZA= - { "en_ZA", "English - South Africa", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US }; +MY_LOCALE my_locale_en_ZA ( "en_ZA", "English - South Africa", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US ); /***** LOCALE END en_ZA *****/ /***** LOCALE BEGIN en_ZW: English - Zimbabwe *****/ -MY_LOCALE my_locale_en_ZW= - { "en_ZW", "English - Zimbabwe", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US }; +MY_LOCALE my_locale_en_ZW ( "en_ZW", "English - Zimbabwe", TRUE, &my_locale_typelib_month_names_en_US, &my_locale_typelib_ab_month_names_en_US, &my_locale_typelib_day_names_en_US, &my_locale_typelib_ab_day_names_en_US ); /***** LOCALE END en_ZW *****/ /***** LOCALE BEGIN es_AR: Spanish - Argentina *****/ -MY_LOCALE my_locale_es_AR= - { "es_AR", "Spanish - Argentina", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_AR ( "es_AR", "Spanish - Argentina", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_AR *****/ /***** LOCALE BEGIN es_BO: Spanish - Bolivia *****/ -MY_LOCALE my_locale_es_BO= - { "es_BO", "Spanish - Bolivia", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_BO ( "es_BO", "Spanish - Bolivia", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_BO *****/ /***** LOCALE BEGIN es_CL: Spanish - Chile *****/ -MY_LOCALE my_locale_es_CL= - { "es_CL", "Spanish - Chile", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_CL ( "es_CL", "Spanish - Chile", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_CL *****/ /***** LOCALE BEGIN es_CO: Spanish - Columbia *****/ -MY_LOCALE my_locale_es_CO= - { "es_CO", "Spanish - Columbia", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_CO ( "es_CO", "Spanish - Columbia", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_CO *****/ /***** LOCALE BEGIN es_CR: Spanish - Costa Rica *****/ -MY_LOCALE my_locale_es_CR= - { "es_CR", "Spanish - Costa Rica", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_CR ( "es_CR", "Spanish - Costa Rica", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_CR *****/ /***** LOCALE BEGIN es_DO: Spanish - Dominican Republic *****/ -MY_LOCALE my_locale_es_DO= - { "es_DO", "Spanish - Dominican Republic", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_DO ( "es_DO", "Spanish - Dominican Republic", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_DO *****/ /***** LOCALE BEGIN es_EC: Spanish - Ecuador *****/ -MY_LOCALE my_locale_es_EC= - { "es_EC", "Spanish - Ecuador", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_EC ( "es_EC", "Spanish - Ecuador", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_EC *****/ /***** LOCALE BEGIN es_GT: Spanish - Guatemala *****/ -MY_LOCALE my_locale_es_GT= - { "es_GT", "Spanish - Guatemala", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_GT ( "es_GT", "Spanish - Guatemala", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_GT *****/ /***** LOCALE BEGIN es_HN: Spanish - Honduras *****/ -MY_LOCALE my_locale_es_HN= - { "es_HN", "Spanish - Honduras", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_HN ( "es_HN", "Spanish - Honduras", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_HN *****/ /***** LOCALE BEGIN es_MX: Spanish - Mexico *****/ -MY_LOCALE my_locale_es_MX= - { "es_MX", "Spanish - Mexico", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_MX ( "es_MX", "Spanish - Mexico", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_MX *****/ /***** LOCALE BEGIN es_NI: Spanish - Nicaragua *****/ -MY_LOCALE my_locale_es_NI= - { "es_NI", "Spanish - Nicaragua", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_NI ( "es_NI", "Spanish - Nicaragua", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_NI *****/ /***** LOCALE BEGIN es_PA: Spanish - Panama *****/ -MY_LOCALE my_locale_es_PA= - { "es_PA", "Spanish - Panama", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_PA ( "es_PA", "Spanish - Panama", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_PA *****/ /***** LOCALE BEGIN es_PE: Spanish - Peru *****/ -MY_LOCALE my_locale_es_PE= - { "es_PE", "Spanish - Peru", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_PE ( "es_PE", "Spanish - Peru", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_PE *****/ /***** LOCALE BEGIN es_PR: Spanish - Puerto Rico *****/ -MY_LOCALE my_locale_es_PR= - { "es_PR", "Spanish - Puerto Rico", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_PR ( "es_PR", "Spanish - Puerto Rico", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_PR *****/ /***** LOCALE BEGIN es_PY: Spanish - Paraguay *****/ -MY_LOCALE my_locale_es_PY= - { "es_PY", "Spanish - Paraguay", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_PY ( "es_PY", "Spanish - Paraguay", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_PY *****/ /***** LOCALE BEGIN es_SV: Spanish - El Salvador *****/ -MY_LOCALE my_locale_es_SV= - { "es_SV", "Spanish - El Salvador", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_SV ( "es_SV", "Spanish - El Salvador", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_SV *****/ /***** LOCALE BEGIN es_US: Spanish - United States *****/ -MY_LOCALE my_locale_es_US= - { "es_US", "Spanish - United States", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_US ( "es_US", "Spanish - United States", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_US *****/ /***** LOCALE BEGIN es_UY: Spanish - Uruguay *****/ -MY_LOCALE my_locale_es_UY= - { "es_UY", "Spanish - Uruguay", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_UY ( "es_UY", "Spanish - Uruguay", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_UY *****/ /***** LOCALE BEGIN es_VE: Spanish - Venezuela *****/ -MY_LOCALE my_locale_es_VE= - { "es_VE", "Spanish - Venezuela", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES }; +MY_LOCALE my_locale_es_VE ( "es_VE", "Spanish - Venezuela", FALSE, &my_locale_typelib_month_names_es_ES, &my_locale_typelib_ab_month_names_es_ES, &my_locale_typelib_day_names_es_ES, &my_locale_typelib_ab_day_names_es_ES ); /***** LOCALE END es_VE *****/ /***** LOCALE BEGIN fr_BE: French - Belgium *****/ -MY_LOCALE my_locale_fr_BE= - { "fr_BE", "French - Belgium", FALSE, &my_locale_typelib_month_names_fr_FR, &my_locale_typelib_ab_month_names_fr_FR, &my_locale_typelib_day_names_fr_FR, &my_locale_typelib_ab_day_names_fr_FR }; +MY_LOCALE my_locale_fr_BE ( "fr_BE", "French - Belgium", FALSE, &my_locale_typelib_month_names_fr_FR, &my_locale_typelib_ab_month_names_fr_FR, &my_locale_typelib_day_names_fr_FR, &my_locale_typelib_ab_day_names_fr_FR ); /***** LOCALE END fr_BE *****/ /***** LOCALE BEGIN fr_CA: French - Canada *****/ -MY_LOCALE my_locale_fr_CA= - { "fr_CA", "French - Canada", FALSE, &my_locale_typelib_month_names_fr_FR, &my_locale_typelib_ab_month_names_fr_FR, &my_locale_typelib_day_names_fr_FR, &my_locale_typelib_ab_day_names_fr_FR }; +MY_LOCALE my_locale_fr_CA ( "fr_CA", "French - Canada", FALSE, &my_locale_typelib_month_names_fr_FR, &my_locale_typelib_ab_month_names_fr_FR, &my_locale_typelib_day_names_fr_FR, &my_locale_typelib_ab_day_names_fr_FR ); /***** LOCALE END fr_CA *****/ /***** LOCALE BEGIN fr_CH: French - Switzerland *****/ -MY_LOCALE my_locale_fr_CH= - { "fr_CH", "French - Switzerland", FALSE, &my_locale_typelib_month_names_fr_FR, &my_locale_typelib_ab_month_names_fr_FR, &my_locale_typelib_day_names_fr_FR, &my_locale_typelib_ab_day_names_fr_FR }; +MY_LOCALE my_locale_fr_CH ( "fr_CH", "French - Switzerland", FALSE, &my_locale_typelib_month_names_fr_FR, &my_locale_typelib_ab_month_names_fr_FR, &my_locale_typelib_day_names_fr_FR, &my_locale_typelib_ab_day_names_fr_FR ); /***** LOCALE END fr_CH *****/ /***** LOCALE BEGIN fr_LU: French - Luxembourg *****/ -MY_LOCALE my_locale_fr_LU= - { "fr_LU", "French - Luxembourg", FALSE, &my_locale_typelib_month_names_fr_FR, &my_locale_typelib_ab_month_names_fr_FR, &my_locale_typelib_day_names_fr_FR, &my_locale_typelib_ab_day_names_fr_FR }; +MY_LOCALE my_locale_fr_LU ( "fr_LU", "French - Luxembourg", FALSE, &my_locale_typelib_month_names_fr_FR, &my_locale_typelib_ab_month_names_fr_FR, &my_locale_typelib_day_names_fr_FR, &my_locale_typelib_ab_day_names_fr_FR ); /***** LOCALE END fr_LU *****/ /***** LOCALE BEGIN it_IT: Italian - Italy *****/ -MY_LOCALE my_locale_it_IT= - { "it_IT", "Italian - Italy", FALSE, &my_locale_typelib_month_names_it_CH, &my_locale_typelib_ab_month_names_it_CH, &my_locale_typelib_day_names_it_CH, &my_locale_typelib_ab_day_names_it_CH }; +MY_LOCALE my_locale_it_IT ( "it_IT", "Italian - Italy", FALSE, &my_locale_typelib_month_names_it_CH, &my_locale_typelib_ab_month_names_it_CH, &my_locale_typelib_day_names_it_CH, &my_locale_typelib_ab_day_names_it_CH ); /***** LOCALE END it_IT *****/ /***** LOCALE BEGIN nl_BE: Dutch - Belgium *****/ -MY_LOCALE my_locale_nl_BE= - { "nl_BE", "Dutch - Belgium", TRUE, &my_locale_typelib_month_names_nl_NL, &my_locale_typelib_ab_month_names_nl_NL, &my_locale_typelib_day_names_nl_NL, &my_locale_typelib_ab_day_names_nl_NL }; +MY_LOCALE my_locale_nl_BE ( "nl_BE", "Dutch - Belgium", TRUE, &my_locale_typelib_month_names_nl_NL, &my_locale_typelib_ab_month_names_nl_NL, &my_locale_typelib_day_names_nl_NL, &my_locale_typelib_ab_day_names_nl_NL ); /***** LOCALE END nl_BE *****/ /***** LOCALE BEGIN no_NO: Norwegian - Norway *****/ -MY_LOCALE my_locale_no_NO= - { "no_NO", "Norwegian - Norway", FALSE, &my_locale_typelib_month_names_nb_NO, &my_locale_typelib_ab_month_names_nb_NO, &my_locale_typelib_day_names_nb_NO, &my_locale_typelib_ab_day_names_nb_NO }; +MY_LOCALE my_locale_no_NO ( "no_NO", "Norwegian - Norway", FALSE, &my_locale_typelib_month_names_nb_NO, &my_locale_typelib_ab_month_names_nb_NO, &my_locale_typelib_day_names_nb_NO, &my_locale_typelib_ab_day_names_nb_NO ); /***** LOCALE END no_NO *****/ /***** LOCALE BEGIN sv_FI: Swedish - Finland *****/ -MY_LOCALE my_locale_sv_FI= - { "sv_FI", "Swedish - Finland", FALSE, &my_locale_typelib_month_names_sv_SE, &my_locale_typelib_ab_month_names_sv_SE, &my_locale_typelib_day_names_sv_SE, &my_locale_typelib_ab_day_names_sv_SE }; +MY_LOCALE my_locale_sv_FI ( "sv_FI", "Swedish - Finland", FALSE, &my_locale_typelib_month_names_sv_SE, &my_locale_typelib_ab_month_names_sv_SE, &my_locale_typelib_day_names_sv_SE, &my_locale_typelib_ab_day_names_sv_SE ); /***** LOCALE END sv_FI *****/ /***** LOCALE BEGIN zh_HK: Chinese - Hong Kong SAR *****/ -MY_LOCALE my_locale_zh_HK= - { "zh_HK", "Chinese - Hong Kong SAR", FALSE, &my_locale_typelib_month_names_zh_CN, &my_locale_typelib_ab_month_names_zh_CN, &my_locale_typelib_day_names_zh_CN, &my_locale_typelib_ab_day_names_zh_CN }; +MY_LOCALE my_locale_zh_HK ( "zh_HK", "Chinese - Hong Kong SAR", FALSE, &my_locale_typelib_month_names_zh_CN, &my_locale_typelib_ab_month_names_zh_CN, &my_locale_typelib_day_names_zh_CN, &my_locale_typelib_ab_day_names_zh_CN ); /***** LOCALE END zh_HK *****/ MY_LOCALE *my_locales[]= diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 6dbd6623264..42862084d62 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -69,8 +69,8 @@ extern "C" int gethostname(char *name, int namelen); static void time_out_user_resource_limits(THD *thd, USER_CONN *uc); #ifndef NO_EMBEDDED_ACCESS_CHECKS static int check_for_max_user_connections(THD *thd, USER_CONN *uc); -#endif static void decrease_user_connections(USER_CONN *uc); +#endif /* NO_EMBEDDED_ACCESS_CHECKS */ static bool check_db_used(THD *thd,TABLE_LIST *tables); static bool check_multi_update_lock(THD *thd); static void remove_escape(char *name); @@ -204,6 +204,7 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables) return 0; } +#ifndef NO_EMBEDDED_ACCESS_CHECKS static HASH hash_user_connections; static int get_or_create_user_conn(THD *thd, const char *user, @@ -255,6 +256,7 @@ end: return return_val; } +#endif /* !NO_EMBEDDED_ACCESS_CHECKS */ /* @@ -303,8 +305,6 @@ int check_user(THD *thd, enum enum_server_command command, { /* Send the error to the client */ net_send_error(thd); - if (thd->user_connect) - decrease_user_connections(thd->user_connect); DBUG_RETURN(-1); } } @@ -495,10 +495,12 @@ extern "C" void free_user(struct user_conn *uc) void init_max_user_conn(void) { +#ifndef NO_EMBEDDED_ACCESS_CHECKS (void) hash_init(&hash_user_connections,system_charset_info,max_connections, 0,0, (hash_get_key) get_key_conn, (hash_free_key) free_user, 0); +#endif } @@ -561,7 +563,6 @@ static int check_for_max_user_connections(THD *thd, USER_CONN *uc) (void) pthread_mutex_unlock(&LOCK_user_conn); DBUG_RETURN(error); } -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ /* Decrease user connection count @@ -595,13 +596,18 @@ static void decrease_user_connections(USER_CONN *uc) DBUG_VOID_RETURN; } +#endif /* NO_EMBEDDED_ACCESS_CHECKS */ + void free_max_user_conn(void) { +#ifndef NO_EMBEDDED_ACCESS_CHECKS hash_free(&hash_user_connections); +#endif /* NO_EMBEDDED_ACCESS_CHECKS */ } + /* Mark all commands that somehow changes a table This is used to check number of updates / hour @@ -995,8 +1001,8 @@ static int check_connection(THD *thd) char *passwd= strend(user)+1; uint user_len= passwd - user - 1; char *db= passwd; - char db_buff[NAME_LEN+1]; // buffer to store db in utf8 - char user_buff[USERNAME_LENGTH+1]; // buffer to store user in utf8 + char db_buff[NAME_BYTE_LEN + 1]; // buffer to store db in utf8 + char user_buff[USERNAME_BYTE_LENGTH + 1]; // buffer to store user in utf8 uint dummy_errors; /* @@ -1656,7 +1662,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, password. New clients send the size (1 byte) + string (not null terminated, so also '\0' for empty string). */ - char db_buff[NAME_LEN+1]; // buffer to store db in utf8 + char db_buff[NAME_BYTE_LEN+1]; // buffer to store db in utf8 char *db= passwd; uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? *passwd++ : strlen(passwd); @@ -1706,9 +1712,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } else { +#ifndef NO_EMBEDDED_ACCESS_CHECKS /* we've authenticated new user */ if (save_user_connect) decrease_user_connections(save_user_connect); +#endif /* NO_EMBEDDED_ACCESS_CHECKS */ x_free((gptr) save_db); x_free((gptr) save_security_ctx.user); } @@ -3336,8 +3344,6 @@ end_with_restore_list: DBUG_ASSERT(first_table == all_tables && first_table != 0); if ((res= insert_precheck(thd, all_tables))) break; - /* Skip first table, which is the table we are inserting in */ - select_lex->context.table_list= first_table->next_local; if (!thd->locked_tables && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) @@ -4405,9 +4411,6 @@ end_with_restore_list: } else { -#ifndef NO_EMBEDDED_ACCESS_CHECKS - Security_context *save_ctx; -#endif ha_rows select_limit; /* bits that should be cleared in thd->server_status */ uint bits_to_be_cleared= 0; @@ -4449,21 +4452,11 @@ end_with_restore_list: #ifndef NO_EMBEDDED_ACCESS_CHECKS if (check_routine_access(thd, EXECUTE_ACL, - sp->m_db.str, sp->m_name.str, TRUE, 0) || - sp_change_security_context(thd, sp, &save_ctx)) + sp->m_db.str, sp->m_name.str, TRUE, FALSE)) { thd->net.no_send_ok= nsok; goto error; } - if (save_ctx && - check_routine_access(thd, EXECUTE_ACL, - sp->m_db.str, sp->m_name.str, TRUE, 0)) - { - thd->net.no_send_ok= nsok; - sp_restore_security_context(thd, save_ctx); - goto error; - } - #endif select_limit= thd->variables.select_limit; thd->variables.select_limit= HA_POS_ERROR; @@ -4487,9 +4480,6 @@ end_with_restore_list: thd->total_warn_count= 0; thd->variables.select_limit= select_limit; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - sp_restore_security_context(thd, save_ctx); -#endif thd->net.no_send_ok= nsok; thd->server_status&= ~bits_to_be_cleared; @@ -5107,10 +5097,21 @@ bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) return 1; /* Check rights on tables of subselects and implictly opened tables */ - TABLE_LIST *subselects_tables; + TABLE_LIST *subselects_tables, *view= all_tables->view ? all_tables : 0; if ((subselects_tables= all_tables->next_global)) { - if ((check_table_access(thd, SELECT_ACL, subselects_tables, 0))) + /* + Access rights asked for the first table of a view should be the same + as for the view + */ + if (view && subselects_tables->belong_to_view == view) + { + if (check_single_table_access (thd, privilege, subselects_tables)) + return 1; + subselects_tables= subselects_tables->next_global; + } + if (subselects_tables && + (check_table_access(thd, SELECT_ACL, subselects_tables, 0))) return 1; } return 0; @@ -5784,7 +5785,6 @@ void mysql_init_multi_delete(LEX *lex) lex->query_tables_last= &lex->query_tables; } - /* When you modify mysql_parse(), you may need to mofify mysql_test_parse_for_slave() in this same file. @@ -5793,6 +5793,9 @@ void mysql_init_multi_delete(LEX *lex) void mysql_parse(THD *thd, char *inBuf, uint length) { DBUG_ENTER("mysql_parse"); + + DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on();); + mysql_init_query(thd, (uchar*) inBuf, length); if (query_cache_send_result_to_client(thd, inBuf, length) <= 0) { @@ -6082,6 +6085,7 @@ bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc) table_options A set of the following bits: TL_OPTION_UPDATING Table will be updated TL_OPTION_FORCE_INDEX Force usage of index + TL_OPTION_ALIAS an alias in multi table DELETE lock_type How table should be locked use_index List of indexed used in USE INDEX ignore_index List of indexed used in IGNORE INDEX @@ -6110,8 +6114,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, if (!table) DBUG_RETURN(0); // End of memory alias_str= alias ? alias->str : table->table.str; - if (check_table_name(table->table.str,table->table.length) || - table->db.str && check_db_name(table->db.str)) + if (!test(table_options & TL_OPTION_ALIAS) && + check_table_name(table->table.str, table->table.length)) { my_error(ER_WRONG_TABLE_NAME, MYF(0), table->table.str); DBUG_RETURN(0); @@ -6132,6 +6136,11 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, DBUG_RETURN(0); /* purecov: inspected */ if (table->db.str) { + if (table->is_derived_table() == FALSE && check_db_name(table->db.str)) + { + my_error(ER_WRONG_DB_NAME, MYF(0), table->db.str); + DBUG_RETURN(0); + } ptr->db= table->db.str; ptr->db_length= table->db.length; } @@ -6683,11 +6692,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, select_errors=0; /* Write if more errors */ bool tmp_write_to_binlog= 1; - if (thd && thd->in_sub_stmt) - { - my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "FLUSH"); - return 1; - } + DBUG_ASSERT(!thd || !thd->in_sub_stmt); #ifndef NO_EMBEDDED_ACCESS_CHECKS if (options & REFRESH_GRANT) @@ -7005,7 +7010,7 @@ Item * all_any_subquery_creator(Item *left_expr, return new Item_func_not(new Item_in_subselect(left_expr, select_lex)); Item_allany_subselect *it= - new Item_allany_subselect(left_expr, (*cmp)(all), select_lex, all); + new Item_allany_subselect(left_expr, cmp, select_lex, all); if (all) return it->upper_item= new Item_func_not_all(it); /* ALL */ @@ -7535,16 +7540,35 @@ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name) LEX_USER *get_current_user(THD *thd, LEX_USER *user) { - LEX_USER *curr_user; if (!user->user.str) // current_user - { - if (!(curr_user= (LEX_USER*) thd->alloc(sizeof(LEX_USER)))) - { - my_error(ER_OUTOFMEMORY, MYF(0), sizeof(LEX_USER)); - return 0; - } - get_default_definer(thd, curr_user); - return curr_user; - } + return create_default_definer(thd); + return user; } + + +/* + Check that length of a string does not exceed some limit. + + SYNOPSIS + check_string_length() + cs string charset + str string to be checked + err_msg error message to be displayed if the string is too long + max_length max length + + RETURN + FALSE the passed string is not longer than max_length + TRUE the passed string is longer than max_length +*/ + +bool check_string_length(CHARSET_INFO *cs, LEX_STRING *str, + const char *err_msg, uint max_length) +{ + if (cs->cset->charpos(cs, str->str, str->str + str->length, + max_length) >= str->length) + return FALSE; + + my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_length); + return TRUE; +} diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 103a0b9e54e..32f0ca6859d 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1877,7 +1877,8 @@ void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length) thd->stmt_map.erase(stmt); } else - mysql_log.write(thd, COM_STMT_PREPARE, "[%lu] %s", stmt->id, packet); + mysql_log.write(thd, COM_STMT_PREPARE, "[%lu] %.*b", stmt->id, + stmt->query_length, stmt->query); /* check_prepared_statemnt sends the metadata packet in case of success */ DBUG_VOID_RETURN; @@ -2128,28 +2129,21 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) they have their own table list). */ for (TABLE_LIST *tables= lex->query_tables; - tables; - tables= tables->next_global) + tables; + tables= tables->next_global) { - /* - Reset old pointers to TABLEs: they are not valid since the tables - were closed in the end of previous prepare or execute call. - */ - tables->table= 0; - /* Reset is_schema_table_processed value(needed for I_S tables */ - tables->is_schema_table_processed= FALSE; - - TABLE_LIST *embedded; /* The table at the current level of nesting. */ - TABLE_LIST *embedding= tables; /* The parent nested table reference. */ - do - { - embedded= embedding; - if (embedded->prep_on_expr) - embedded->on_expr= embedded->prep_on_expr->copy_andor_structure(thd); - embedding= embedded->embedding; - } - while (embedding && - embedding->nested_join->join_list.head() == embedded); + tables->reinit_before_use(thd); + } + /* + Cleanup of the special case of DELETE t1, t2 FROM t1, t2, t3 ... + (multi-delete). We do a full clean up, although at the moment all we + need to clean in the tables of MULTI-DELETE list is 'table' member. + */ + for (TABLE_LIST *tables= (TABLE_LIST*) lex->auxiliary_table_list.first; + tables; + tables= tables->next_global) + { + tables->reinit_before_use(thd); } lex->current_select= &lex->select_lex; @@ -2164,7 +2158,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) } lex->allow_sum_func= 0; lex->in_sum_func= NULL; - DBUG_VOID_RETURN; + DBUG_VOID_RETURN; } @@ -2259,7 +2253,8 @@ void mysql_stmt_execute(THD *thd, char *packet_arg, uint packet_length) if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(), WAIT_PRIOR); if (error == 0) - mysql_log.write(thd, COM_STMT_EXECUTE, "[%lu] %s", stmt->id, thd->query); + mysql_log.write(thd, COM_STMT_EXECUTE, "[%lu] %.*b", stmt->id, + thd->query_length, thd->query); DBUG_VOID_RETURN; diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index ccda69522c7..e1933d42f9e 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1494,10 +1494,14 @@ bool show_binlogs(THD* thd) if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); + + pthread_mutex_lock(mysql_bin_log.get_log_lock()); mysql_bin_log.lock_index(); index_file=mysql_bin_log.get_index_file(); - - mysql_bin_log.get_current_log(&cur); + + mysql_bin_log.raw_get_current_log(&cur); // dont take mutex + pthread_mutex_unlock(mysql_bin_log.get_log_lock()); // lockdep, OK + cur_dir_len= dirname_length(cur.log_file_name); reinit_io_cache(index_file, READ_CACHE, (my_off_t) 0, 0, 0); diff --git a/sql/sql_repl.h b/sql/sql_repl.h index 9eb6456ee20..6b1e5e6b447 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -22,7 +22,7 @@ typedef struct st_slave_info uint32 server_id; uint32 rpl_recovery_rank, master_id; char host[HOSTNAME_LENGTH+1]; - char user[USERNAME_LENGTH+1]; + char user[USERNAME_BYTE_LENGTH+1]; char password[MAX_PASSWORD_LENGTH+1]; uint16 port; THD* thd; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 400342854a6..ac4b404ce8e 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -344,7 +344,7 @@ JOIN::prepare(Item ***rref_pointer_array, setup_tables_and_check_access(thd, &select_lex->context, join_list, tables_list, &conds, &select_lex->leaf_tables, FALSE, - SELECT_ACL)) || + SELECT_ACL, SELECT_ACL)) || setup_wild(thd, tables_list, fields_list, &all_fields, wild_num) || select_lex->setup_ref_array(thd, og_num) || setup_fields(thd, (*rref_pointer_array), fields_list, 1, @@ -382,12 +382,14 @@ JOIN::prepare(Item ***rref_pointer_array, if ((res= subselect->select_transformer(this)) != Item_subselect::RES_OK) { - select_lex->fix_prepare_information(thd, &conds); + select_lex->fix_prepare_information(thd, &conds, &having); DBUG_RETURN((res == Item_subselect::RES_ERROR)); } } } + select_lex->fix_prepare_information(thd, &conds, &having); + if (having && having->with_sum_func) having->split_sum_func2(thd, ref_pointer_array, all_fields, &having, TRUE); @@ -499,7 +501,6 @@ JOIN::prepare(Item ***rref_pointer_array, if (alloc_func_list()) goto err; - select_lex->fix_prepare_information(thd, &conds); DBUG_RETURN(0); // All OK err: @@ -618,7 +619,6 @@ JOIN::optimize() build_bitmap_for_nested_joins(join_list, 0); sel->prep_where= conds ? conds->copy_andor_structure(thd) : 0; - sel->prep_having= having ? having->copy_andor_structure(thd) : 0; if (arena) thd->restore_active_arena(arena, &backup); @@ -679,6 +679,25 @@ JOIN::optimize() DBUG_PRINT("info",("Select tables optimized away")); zero_result_cause= "Select tables optimized away"; tables_list= 0; // All tables resolved + /* + Extract all table-independent conditions and replace the WHERE + clause with them. All other conditions were computed by opt_sum_query + and the MIN/MAX/COUNT function(s) have been replaced by constants, + so there is no need to compute the whole WHERE clause again. + Notice that make_cond_for_table() will always succeed to remove all + computed conditions, because opt_sum_query() is applicable only to + conjunctions. + Preserve conditions for EXPLAIN. + */ + if (conds && !(thd->lex->describe & DESCRIBE_EXTENDED)) + { + COND *table_independent_conds= + make_cond_for_table(conds, PSEUDO_TABLE_BITS, 0); + DBUG_EXECUTE("where", + print_where(table_independent_conds, + "where after opt_sum_query()");); + conds= table_independent_conds; + } } } if (!tables_list) @@ -793,6 +812,40 @@ JOIN::optimize() if (!order && org_order) skip_sort_order= 1; } + /* + Check if we can optimize away GROUP BY/DISTINCT. + We can do that if there are no aggregate functions and the + fields in DISTINCT clause (if present) and/or columns in GROUP BY + (if present) contain direct references to all key parts of + an unique index (in whatever order). + Note that the unique keys for DISTINCT and GROUP BY should not + be the same (as long as they are unique). + + The FROM clause must contain a single non-constant table. + */ + if (tables - const_tables == 1 && (group_list || select_distinct) && + !tmp_table_param.sum_func_count && + (!join_tab[const_tables].select || + !join_tab[const_tables].select->quick || + join_tab[const_tables].select->quick->get_type() != + QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)) + { + if (group_list && + list_contains_unique_index(join_tab[const_tables].table, + find_field_in_order_list, + (void *) group_list)) + { + group_list= 0; + group= 0; + } + if (select_distinct && + list_contains_unique_index(join_tab[const_tables].table, + find_field_in_item_list, + (void *) &fields_list)) + { + select_distinct= 0; + } + } if (group_list || tmp_table_param.sum_func_count) { if (! hidden_group_fields && rollup.state == ROLLUP::STATE_NONE) @@ -862,40 +915,6 @@ JOIN::optimize() if (old_group_list && !group_list) select_distinct= 0; } - /* - Check if we can optimize away GROUP BY/DISTINCT. - We can do that if there are no aggregate functions and the - fields in DISTINCT clause (if present) and/or columns in GROUP BY - (if present) contain direct references to all key parts of - an unique index (in whatever order). - Note that the unique keys for DISTINCT and GROUP BY should not - be the same (as long as they are unique). - - The FROM clause must contain a single non-constant table. - */ - if (tables - const_tables == 1 && (group_list || select_distinct) && - !tmp_table_param.sum_func_count && - (!join_tab[const_tables].select || - !join_tab[const_tables].select->quick || - join_tab[const_tables].select->quick->get_type() != - QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)) - { - if (group_list && - list_contains_unique_index(join_tab[const_tables].table, - find_field_in_order_list, - (void *) group_list)) - { - group_list= 0; - group= 0; - } - if (select_distinct && - list_contains_unique_index(join_tab[const_tables].table, - find_field_in_item_list, - (void *) &fields_list)) - { - select_distinct= 0; - } - } if (!group_list && group) { order=0; // The output has only one row @@ -1064,6 +1083,23 @@ JOIN::optimize() { need_tmp=1; simple_order=simple_group=0; // Force tmp table without sort } + if (order) + { + /* + Force using of tmp table if sorting by a SP or UDF function due to + their expensive and probably non-deterministic nature. + */ + for (ORDER *tmp_order= order; tmp_order ; tmp_order=tmp_order->next) + { + Item *item= *tmp_order->item; + if (item->walk(&Item::is_expensive_processor,(byte*)0)) + { + /* Force tmp table without sort */ + need_tmp=1; simple_order=simple_group=0; + break; + } + } + } } tmp_having= having; @@ -1209,6 +1245,11 @@ int JOIN::reinit() { DBUG_ENTER("JOIN::reinit"); + + unit->offset_limit_cnt= (ha_rows)(select_lex->offset_limit ? + select_lex->offset_limit->val_uint() : + ULL(0)); + first_record= 0; if (exec_tmp_table1) @@ -1283,7 +1324,7 @@ JOIN::exec() } (void) result->prepare2(); // Currently, this cannot fail. - if (!tables_list) + if (!tables_list && (tables || !select_lex->with_sum_func)) { // Only test of functions if (select_options & SELECT_DESCRIBE) select_describe(this, FALSE, FALSE, FALSE, @@ -1323,7 +1364,12 @@ JOIN::exec() thd->examined_row_count= 0; DBUG_VOID_RETURN; } - thd->limit_found_rows= thd->examined_row_count= 0; + /* + don't reset the found rows count if there're no tables + as FOUND_ROWS() may be called. + */ + if (tables) + thd->limit_found_rows= thd->examined_row_count= 0; if (zero_result_cause) { @@ -1362,7 +1408,8 @@ JOIN::exec() having= tmp_having; select_describe(this, need_tmp, order != 0 && !skip_sort_order, - select_distinct); + select_distinct, + !tables ? "No tables used" : NullS); DBUG_VOID_RETURN; } @@ -2164,6 +2211,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds, int ref_changed; do { + more_const_tables_found: ref_changed = 0; found_ref=0; @@ -2175,6 +2223,30 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds, for (JOIN_TAB **pos=stat_vector+const_count ; (s= *pos) ; pos++) { table=s->table; + + /* + If equi-join condition by a key is null rejecting and after a + substitution of a const table the key value happens to be null + then we can state that there are no matches for this equi-join. + */ + if ((keyuse= s->keyuse) && *s->on_expr_ref) + { + while (keyuse->table == table) + { + if (!(keyuse->val->used_tables() & ~join->const_table_map) && + keyuse->val->is_null() && keyuse->null_rejecting) + { + s->type= JT_CONST; + mark_as_null_row(table); + found_const_table_map|= table->map; + join->const_table_map|= table->map; + set_position(join,const_count++,s,(KEYUSE*) 0); + goto more_const_tables_found; + } + keyuse++; + } + } + if (s->dependent) // If dependent on some table { // All dep. must be constants @@ -2225,34 +2297,38 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds, } while (keyuse->table == table && keyuse->key == key); if (eq_part.is_prefix(table->key_info[key].key_parts) && - ((table->key_info[key].flags & (HA_NOSAME | HA_END_SPACE_KEY)) == - HA_NOSAME) && !table->fulltext_searched && !table->pos_in_table_list->embedding) { - if (const_ref == eq_part) - { // Found everything for ref. - int tmp; - ref_changed = 1; - s->type= JT_CONST; - join->const_table_map|=table->map; - set_position(join,const_count++,s,start_keyuse); - if (create_ref_for_key(join, s, start_keyuse, - found_const_table_map)) - DBUG_RETURN(1); - if ((tmp=join_read_const_table(s, - join->positions+const_count-1))) - { - if (tmp > 0) - DBUG_RETURN(1); // Fatal error + if ((table->key_info[key].flags & (HA_NOSAME | HA_END_SPACE_KEY)) + == HA_NOSAME) + { + if (const_ref == eq_part) + { // Found everything for ref. + int tmp; + ref_changed = 1; + s->type= JT_CONST; + join->const_table_map|=table->map; + set_position(join,const_count++,s,start_keyuse); + if (create_ref_for_key(join, s, start_keyuse, + found_const_table_map)) + DBUG_RETURN(1); + if ((tmp=join_read_const_table(s, + join->positions+const_count-1))) + { + if (tmp > 0) + DBUG_RETURN(1); // Fatal error + } + else + found_const_table_map|= table->map; + break; } else - found_const_table_map|= table->map; - break; + found_ref|= refs; // Table is const if all refs are const } - else - found_ref|= refs; // Table is const if all refs are const - } + else if (const_ref == eq_part) + s->const_keys.set_bit(key); + } } } } @@ -2465,8 +2541,11 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, /* field = expression OR field IS NULL */ old->level= and_level; old->optimize= KEY_OPTIMIZE_REF_OR_NULL; - /* Remember the NOT NULL value */ - if (old->val->is_null()) + /* + Remember the NOT NULL value unless the value does not depend + on other tables. + */ + if (!old->val->used_tables() && old->val->is_null()) old->val= new_fields->val; /* The referred expression can be NULL: */ old->null_rejecting= 0; @@ -2652,7 +2731,8 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, We use null_rejecting in add_not_null_conds() to add 'othertbl.field IS NOT NULL' to tab->select_cond. */ - (*key_fields)->null_rejecting= ((cond->functype() == Item_func::EQ_FUNC) && + (*key_fields)->null_rejecting= ((cond->functype() == Item_func::EQ_FUNC || + cond->functype() == Item_func::MULT_EQUAL_FUNC) && ((*value)->type() == Item::FIELD_ITEM) && ((Item_field*)*value)->field->maybe_null()); (*key_fields)++; @@ -2752,11 +2832,12 @@ add_key_fields(KEY_FIELD **key_fields,uint *and_level, break; case Item_func::OPTIMIZE_KEY: { + Item **values; // BETWEEN, IN, NE if (cond_func->key_item()->real_item()->type() == Item::FIELD_ITEM && !(cond_func->used_tables() & OUTER_REF_TABLE_BIT)) { - Item **values= cond_func->arguments()+1; + values= cond_func->arguments()+1; if (cond_func->functype() == Item_func::NE_FUNC && cond_func->arguments()[1]->real_item()->type() == Item::FIELD_ITEM && !(cond_func->arguments()[0]->used_tables() & OUTER_REF_TABLE_BIT)) @@ -2769,6 +2850,22 @@ add_key_fields(KEY_FIELD **key_fields,uint *and_level, cond_func->argument_count()-1, usable_tables); } + if (cond_func->functype() == Item_func::BETWEEN) + { + values= cond_func->arguments(); + for (uint i= 1 ; i < cond_func->argument_count() ; i++) + { + Item_field *field_item; + if (cond_func->arguments()[i]->real_item()->type() == Item::FIELD_ITEM + && + !(cond_func->arguments()[i]->used_tables() & OUTER_REF_TABLE_BIT)) + { + field_item= (Item_field *) (cond_func->arguments()[i]->real_item()); + add_key_equal_fields(key_fields, *and_level, cond_func, + field_item, 0, values, 1, usable_tables); + } + } + } break; } case Item_func::OPTIMIZE_OP: @@ -3400,7 +3497,7 @@ best_access_path(JOIN *join, keyuse->used_tables)); if (tmp < best_prev_record_reads) { - best_part_found_ref= keyuse->used_tables; + best_part_found_ref= keyuse->used_tables & ~join->const_table_map; best_prev_record_reads= tmp; } if (rec > keyuse->ref_table_rows) @@ -4126,14 +4223,14 @@ greedy_search(JOIN *join, double read_time= 0.0; uint idx= join->const_tables; // index into 'join->best_ref' uint best_idx; - uint rem_size; // cardinality of remaining_tables + uint size_remain; // cardinality of remaining_tables POSITION best_pos; JOIN_TAB *best_table; // the next plan node to be added to the curr QEP DBUG_ENTER("greedy_search"); /* number of tables that remain to be optimized */ - rem_size= my_count_bits(remaining_tables); + size_remain= my_count_bits(remaining_tables); do { /* Find the extension of the current QEP with the lowest cost */ @@ -4141,7 +4238,7 @@ greedy_search(JOIN *join, best_extension_by_limited_search(join, remaining_tables, idx, record_count, read_time, search_depth, prune_level); - if (rem_size <= search_depth) + if (size_remain <= search_depth) { /* 'join->best_positions' contains a complete optimal extension of the @@ -4177,7 +4274,7 @@ greedy_search(JOIN *join, read_time+= join->positions[idx].read_time; remaining_tables&= ~(best_table->table->map); - --rem_size; + --size_remain; ++idx; DBUG_EXECUTE("opt", print_plan(join, join->tables, @@ -5482,6 +5579,7 @@ make_join_readinfo(JOIN *join, uint options) uint i; bool statistics= test(!(join->select_options & SELECT_DESCRIBE)); + bool ordered_set= 0; DBUG_ENTER("make_join_readinfo"); for (i=join->const_tables ; i < join->tables ; i++) @@ -5491,6 +5589,22 @@ make_join_readinfo(JOIN *join, uint options) tab->read_record.table= table; tab->read_record.file=table->file; tab->next_select=sub_select; /* normal select */ + + /* + Determine if the set is already ordered for ORDER BY, so it can + disable join cache because it will change the ordering of the results. + Code handles sort table that is at any location (not only first after + the const tables) despite the fact that it's currently prohibited. + */ + if (!ordered_set && + (table == join->sort_by_table && + (!join->order || join->skip_sort_order || + test_if_skip_sort_order(tab, join->order, join->select_limit, + 1)) + ) || + (join->sort_by_table == (TABLE *) 1 && i != join->const_tables)) + ordered_set= 1; + switch (tab->type) { case JT_SYSTEM: // Only happens with left join table->status=STATUS_NO_RECORD; @@ -5561,10 +5675,11 @@ make_join_readinfo(JOIN *join, uint options) case JT_ALL: /* If previous table use cache + If the incoming data set is already sorted don't use cache. */ table->status=STATUS_NO_RECORD; if (i != join->const_tables && !(options & SELECT_NO_JOIN_CACHE) && - tab->use_quick != 2 && !tab->first_inner) + tab->use_quick != 2 && !tab->first_inner && !ordered_set) { if ((options & SELECT_DESCRIBE) || !join_init_cache(join->thd,join->join_tab+join->const_tables, @@ -5914,7 +6029,8 @@ eq_ref_table(JOIN *join, ORDER *start_order, JOIN_TAB *tab) if (tab->cached_eq_ref_table) // If cached return tab->eq_ref_table; tab->cached_eq_ref_table=1; - if (tab->type == JT_CONST) // We can skip const tables + /* We can skip const tables only if not an outer table */ + if (tab->type == JT_CONST && !tab->first_inner) return (tab->eq_ref_table=1); /* purecov: inspected */ if (tab->type != JT_EQ_REF || tab->table->maybe_null) return (tab->eq_ref_table=0); // We must use this @@ -6151,10 +6267,16 @@ return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables, DBUG_RETURN(0); } - +/* + used only in JOIN::clear +*/ static void clear_tables(JOIN *join) { - for (uint i=0 ; i < join->tables ; i++) + /* + must clear only the non-const tables, as const tables + are not re-calculated. + */ + for (uint i=join->const_tables ; i < join->tables ; i++) mark_as_null_row(join->table[i]); // All fields are NULL } @@ -6235,29 +6357,30 @@ finish: /* - Check whether an item is a simple equality predicate and if so - create/find a multiple equality for this predicate + Check whether an equality can be used to build multiple equalities SYNOPSIS - check_equality() - item item to check - cond_equal multiple equalities that must hold together with the predicate + check_simple_equality() + left_item left term of the quality to be checked + right_item right term of the equality to be checked + item equality item if the equality originates from a condition + predicate, 0 if the equality is the result of row elimination + cond_equal multiple equalities that must hold together with the equality DESCRIPTION - This function first checks whether an item is a simple equality i.e. - the one that equates a field with another field or a constant - (item=constant_item or item=field_item). - If this is the case the function looks a for a multiple equality + This function first checks whether the equality (left_item=right_item) + is a simple equality i.e. the one that equates a field with another field + or a constant (field=field_item or field=const_item). + If this is the case the function looks for a multiple equality in the lists referenced directly or indirectly by cond_equal inferring the given simple equality. If it doesn't find any, it builds a multiple equality that covers the predicate, i.e. the predicate can be inferred - from it. + from this multiple equality. The built multiple equality could be obtained in such a way: create a binary multiple equality equivalent to the predicate, then merge it, if possible, with one of old multiple equalities. This guarantees that the set of multiple equalities covering equality - predicates will - be minimal. + predicates will be minimal. EXAMPLE For the where condition @@ -6275,7 +6398,7 @@ finish: and will transform *cond_equal into (ptr(CE),[Item_equal(f,e)]). NOTES - Now only fields that have the same type defintions (verified by + Now only fields that have the same type definitions (verified by the Field::eq_def method) are placed to the same multiple equalities. Because of this some equality predicates are not eliminated and can be used in the constant propagation procedure. @@ -6308,176 +6431,289 @@ finish: copying would be much more complicated. RETURN - TRUE - if the predicate is a simple equality predicate - FALSE - otherwise + TRUE if the predicate is a simple equality predicate to be used + for building multiple equalities + FALSE otherwise */ -static bool check_equality(Item *item, COND_EQUAL *cond_equal) +static bool check_simple_equality(Item *left_item, Item *right_item, + Item *item, COND_EQUAL *cond_equal) { - if (item->type() == Item::FUNC_ITEM && - ((Item_func*) item)->functype() == Item_func::EQ_FUNC) + if (left_item->type() == Item::REF_ITEM && + ((Item_ref*)left_item)->ref_type() == Item_ref::VIEW_REF) { - Item *left_item= ((Item_func*) item)->arguments()[0]; - Item *right_item= ((Item_func*) item)->arguments()[1]; + if (((Item_ref*)left_item)->depended_from) + return FALSE; + left_item= left_item->real_item(); + } + if (right_item->type() == Item::REF_ITEM && + ((Item_ref*)right_item)->ref_type() == Item_ref::VIEW_REF) + { + if (((Item_ref*)right_item)->depended_from) + return FALSE; + right_item= right_item->real_item(); + } + if (left_item->type() == Item::FIELD_ITEM && + right_item->type() == Item::FIELD_ITEM && + !((Item_field*)left_item)->depended_from && + !((Item_field*)right_item)->depended_from) + { + /* The predicate the form field1=field2 is processed */ - if (left_item->type() == Item::REF_ITEM && - ((Item_ref*)left_item)->ref_type() == Item_ref::VIEW_REF) + Field *left_field= ((Item_field*) left_item)->field; + Field *right_field= ((Item_field*) right_item)->field; + + if (!left_field->eq_def(right_field)) + return FALSE; + + /* Search for multiple equalities containing field1 and/or field2 */ + bool left_copyfl, right_copyfl; + Item_equal *left_item_equal= + find_item_equal(cond_equal, left_field, &left_copyfl); + Item_equal *right_item_equal= + find_item_equal(cond_equal, right_field, &right_copyfl); + + /* As (NULL=NULL) != TRUE we can't just remove the predicate f=f */ + if (left_field->eq(right_field)) /* f = f */ + return (!(left_field->maybe_null() && !left_item_equal)); + + if (left_item_equal && left_item_equal == right_item_equal) { - if (((Item_ref*)left_item)->depended_from) - return FALSE; - left_item= left_item->real_item(); + /* + The equality predicate is inference of one of the existing + multiple equalities, i.e the condition is already covered + by upper level equalities + */ + return TRUE; } - if (right_item->type() == Item::REF_ITEM && - ((Item_ref*)right_item)->ref_type() == Item_ref::VIEW_REF) + + /* Copy the found multiple equalities at the current level if needed */ + if (left_copyfl) { - if (((Item_ref*)right_item)->depended_from) - return FALSE; - right_item= right_item->real_item(); + /* left_item_equal of an upper level contains left_item */ + left_item_equal= new Item_equal(left_item_equal); + cond_equal->current_level.push_back(left_item_equal); } - if (left_item->type() == Item::FIELD_ITEM && - right_item->type() == Item::FIELD_ITEM && - !((Item_field*)left_item)->depended_from && - !((Item_field*)right_item)->depended_from) + if (right_copyfl) { - /* The predicate the form field1=field2 is processed */ + /* right_item_equal of an upper level contains right_item */ + right_item_equal= new Item_equal(right_item_equal); + cond_equal->current_level.push_back(right_item_equal); + } - Field *left_field= ((Item_field*) left_item)->field; - Field *right_field= ((Item_field*) right_item)->field; + if (left_item_equal) + { + /* left item was found in the current or one of the upper levels */ + if (! right_item_equal) + left_item_equal->add((Item_field *) right_item); + else + { + /* Merge two multiple equalities forming a new one */ + left_item_equal->merge(right_item_equal); + /* Remove the merged multiple equality from the list */ + List_iterator<Item_equal> li(cond_equal->current_level); + while ((li++) != right_item_equal); + li.remove(); + } + } + else + { + /* left item was not found neither the current nor in upper levels */ + if (right_item_equal) + right_item_equal->add((Item_field *) left_item); + else + { + /* None of the fields was found in multiple equalities */ + Item_equal *item= new Item_equal((Item_field *) left_item, + (Item_field *) right_item); + cond_equal->current_level.push_back(item); + } + } + return TRUE; + } - if (!left_field->eq_def(right_field)) - return FALSE; + { + /* The predicate of the form field=const/const=field is processed */ + Item *const_item= 0; + Item_field *field_item= 0; + if (left_item->type() == Item::FIELD_ITEM && + !((Item_field*)left_item)->depended_from && + right_item->const_item()) + { + field_item= (Item_field*) left_item; + const_item= right_item; + } + else if (right_item->type() == Item::FIELD_ITEM && + !((Item_field*)right_item)->depended_from && + left_item->const_item()) + { + field_item= (Item_field*) right_item; + const_item= left_item; + } - if (left_field->eq(right_field)) /* f = f */ - return TRUE; - - /* Search for multiple equalities containing field1 and/or field2 */ - bool left_copyfl, right_copyfl; - Item_equal *left_item_equal= - find_item_equal(cond_equal, left_field, &left_copyfl); - Item_equal *right_item_equal= - find_item_equal(cond_equal, right_field, &right_copyfl); + if (const_item && + field_item->result_type() == const_item->result_type()) + { + bool copyfl; - if (left_item_equal && left_item_equal == right_item_equal) + if (field_item->result_type() == STRING_RESULT) { - /* - The equality predicate is inference of one of the existing - multiple equalities, i.e the condition is already covered - by upper level equalities - */ - return TRUE; + CHARSET_INFO *cs= ((Field_str*) field_item->field)->charset(); + if (!item) + { + Item_func_eq *eq_item; + if ((eq_item= new Item_func_eq(left_item, right_item))) + return FALSE; + eq_item->set_cmp_func(); + eq_item->quick_fix_field(); + item= eq_item; + } + if ((cs != ((Item_func *) item)->compare_collation()) || + !cs->coll->propagate(cs, 0, 0)) + return FALSE; } - - /* Copy the found multiple equalities at the current level if needed */ - if (left_copyfl) + + Item_equal *item_equal = find_item_equal(cond_equal, + field_item->field, ©fl); + if (copyfl) { - /* left_item_equal of an upper level contains left_item */ - left_item_equal= new Item_equal(left_item_equal); - cond_equal->current_level.push_back(left_item_equal); + item_equal= new Item_equal(item_equal); + cond_equal->current_level.push_back(item_equal); } - if (right_copyfl) + if (item_equal) { - /* right_item_equal of an upper level contains right_item */ - right_item_equal= new Item_equal(right_item_equal); - cond_equal->current_level.push_back(right_item_equal); - } - - if (left_item_equal) - { - /* left item was found in the current or one of the upper levels */ - if (! right_item_equal) - left_item_equal->add((Item_field *) right_item); - else - { - /* Merge two multiple equalities forming a new one */ - left_item_equal->merge(right_item_equal); - /* Remove the merged multiple equality from the list */ - List_iterator<Item_equal> li(cond_equal->current_level); - while ((li++) != right_item_equal); - li.remove(); - } + /* + The flag cond_false will be set to 1 after this, if item_equal + already contains a constant and its value is not equal to + the value of const_item. + */ + item_equal->add(const_item); } else - { - /* left item was not found neither the current nor in upper levels */ - if (right_item_equal) - right_item_equal->add((Item_field *) left_item); - else - { - /* None of the fields was found in multiple equalities */ - Item_equal *item= new Item_equal((Item_field *) left_item, - (Item_field *) right_item); - cond_equal->current_level.push_back(item); - } + { + item_equal= new Item_equal(const_item, field_item); + cond_equal->current_level.push_back(item_equal); } return TRUE; } + } + return FALSE; +} - { - /* The predicate of the form field=const/const=field is processed */ - Item *const_item= 0; - Item_field *field_item= 0; - if (left_item->type() == Item::FIELD_ITEM && - !((Item_field*)left_item)->depended_from && - right_item->const_item()) - { - field_item= (Item_field*) left_item; - const_item= right_item; - } - else if (right_item->type() == Item::FIELD_ITEM && - !((Item_field*)right_item)->depended_from && - left_item->const_item()) - { - field_item= (Item_field*) right_item; - const_item= left_item; - } - if (const_item && - field_item->result_type() == const_item->result_type()) - { - bool copyfl; - if (field_item->result_type() == STRING_RESULT) - { - CHARSET_INFO *cs= ((Field_str*) field_item->field)->charset(); - if ((cs != ((Item_cond *) item)->compare_collation()) || - !cs->coll->propagate(cs, 0, 0)) - return FALSE; - } +/* + Convert row equalities into a conjunction of regular equalities - Item_equal *item_equal = find_item_equal(cond_equal, - field_item->field, ©fl); - if (copyfl) - { - item_equal= new Item_equal(item_equal); - cond_equal->current_level.push_back(item_equal); - } - if (item_equal) - { - /* - The flag cond_false will be set to 1 after this, if item_equal - already contains a constant and its value is not equal to - the value of const_item. - */ - item_equal->add(const_item); - } - else - { - item_equal= new Item_equal(const_item, field_item); - cond_equal->current_level.push_back(item_equal); - } - return TRUE; - } + SYNOPSIS + check_row_equality() + left_row left term of the row equality to be processed + right_row right term of the row equality to be processed + cond_equal multiple equalities that must hold together with the predicate + eq_list results of conversions of row equalities that are not simple + enough to form multiple equalities + + DESCRIPTION + The function converts a row equality of the form (E1,...,En)=(E'1,...,E'n) + into a list of equalities E1=E'1,...,En=E'n. For each of these equalities + Ei=E'i the function checks whether it is a simple equality or a row equality. + If it is a simple equality it is used to expand multiple equalities of + cond_equal. If it is a row equality it converted to a sequence of equalities + between row elements. If Ei=E'i is neither a simple equality nor a row + equality the item for this predicate is added to eq_list. + + RETURN + TRUE if conversion has succeeded (no fatal error) + FALSE otherwise +*/ + +static bool check_row_equality(Item *left_row, Item_row *right_row, + COND_EQUAL *cond_equal, List<Item>* eq_list) +{ + uint n= left_row->cols(); + for (uint i= 0 ; i < n; i++) + { + bool is_converted; + Item *left_item= left_row->el(i); + Item *right_item= right_row->el(i); + if (left_item->type() == Item::ROW_ITEM && + right_item->type() == Item::ROW_ITEM) + is_converted= check_row_equality((Item_row *) left_item, + (Item_row *) right_item, + cond_equal, eq_list); + else + is_converted= check_simple_equality(left_item, right_item, 0, cond_equal); + + if (!is_converted) + { + Item_func_eq *eq_item; + if (!(eq_item= new Item_func_eq(left_item, right_item))) + return FALSE; + eq_item->set_cmp_func(); + eq_item->quick_fix_field(); + eq_list->push_back(eq_item); } } + return TRUE; +} + + +/* + Eliminate row equalities and form multiple equalities predicates + + SYNOPSIS + check_equality() + item predicate to process + cond_equal multiple equalities that must hold together with the predicate + eq_list results of conversions of row equalities that are not simple + enough to form multiple equalities + + DESCRIPTION + This function checks whether the item is a simple equality + i.e. the one that equates a field with another field or a constant + (field=field_item or field=constant_item), or, a row equality. + For a simple equality the function looks for a multiple equality + in the lists referenced directly or indirectly by cond_equal inferring + the given simple equality. If it doesn't find any, it builds/expands + multiple equality that covers the predicate. + Row equalities are eliminated substituted for conjunctive regular equalities + which are treated in the same way as original equality predicates. + + RETURN + TRUE if re-writing rules have been applied + FALSE otherwise, i.e. + if the predicate is not an equality, + or, if the equality is neither a simple one nor a row equality, + or, if the procedure fails by a fatal error. +*/ + +static bool check_equality(Item *item, COND_EQUAL *cond_equal, + List<Item> *eq_list) +{ + if (item->type() == Item::FUNC_ITEM && + ((Item_func*) item)->functype() == Item_func::EQ_FUNC) + { + Item *left_item= ((Item_func*) item)->arguments()[0]; + Item *right_item= ((Item_func*) item)->arguments()[1]; + + if (left_item->type() == Item::ROW_ITEM && + right_item->type() == Item::ROW_ITEM) + return check_row_equality((Item_row *) left_item, + (Item_row *) right_item, + cond_equal, eq_list); + else + return check_simple_equality(left_item, right_item, item, cond_equal); + } return FALSE; } + /* Replace all equality predicates in a condition by multiple equality items SYNOPSIS build_equal_items_for_cond() - cond condition(expression) where to make replacement - inherited path to all inherited multiple equality items + cond condition(expression) where to make replacement + inherited path to all inherited multiple equality items DESCRIPTION At each 'and' level the function detects items for equality predicates @@ -6491,7 +6727,9 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal) The function also traverses the cond tree and and for each field reference sets a pointer to the multiple equality item containing the field, if there is any. If this multiple equality equates fields to a constant the - function replace the field reference by the constant. + function replaces the field reference by the constant in the cases + when the field is not of a string type or when the field reference is + just an argument of a comparison predicate. The function also determines the maximum number of members in equality lists of each Item_cond_and object assigning it to cond_equal->max_members of this object and updating accordingly @@ -6542,10 +6780,12 @@ static COND *build_equal_items_for_cond(COND *cond, Item_equal *item_equal; uint members; COND_EQUAL cond_equal; + COND *new_cond; cond_equal.upper_levels= inherited; if (cond->type() == Item::COND_ITEM) { + List<Item> eq_list; bool and_level= ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC; List<Item> *args= ((Item_cond*) cond)->argument_list(); @@ -6568,7 +6808,7 @@ static COND *build_equal_items_for_cond(COND *cond, structure here because it's restored before each re-execution of any prepared statement/stored procedure. */ - if (check_equality(item, &cond_equal)) + if (check_equality(item, &cond_equal, &eq_list)) li.remove(); } @@ -6615,10 +6855,14 @@ static COND *build_equal_items_for_cond(COND *cond, } } if (and_level) + { + args->concat(&eq_list); args->concat((List<Item> *)&cond_equal.current_level); + } } else if (cond->type() == Item::FUNC_ITEM) { + List<Item> eq_list; /* If an equality predicate forms the whole and level, we call it standalone equality and it's processed here. @@ -6629,19 +6873,57 @@ static COND *build_equal_items_for_cond(COND *cond, for WHERE a=b AND c=d AND (b=c OR d=5) b=c is replaced by =(a,b,c,d). */ - if (check_equality(cond, &cond_equal) && - (item_equal= cond_equal.current_level.pop())) + if (check_equality(cond, &cond_equal, &eq_list)) { - item_equal->fix_length_and_dec(); - item_equal->update_used_tables(); - return item_equal; + int n= cond_equal.current_level.elements + eq_list.elements; + if (n == 0) + return new Item_int((longlong) 1,1); + else if (n == 1) + { + if ((item_equal= cond_equal.current_level.pop())) + { + item_equal->fix_length_and_dec(); + item_equal->update_used_tables(); + return item_equal; + } + else + return eq_list.pop(); + } + else + { + /* + Here a new AND level must be created. It can happen only + when a row equality is processed as a standalone predicate. + */ + Item_cond_and *and_cond= new Item_cond_and(eq_list); + and_cond->quick_fix_field(); + List<Item> *args= and_cond->argument_list(); + List_iterator_fast<Item_equal> it(cond_equal.current_level); + while ((item_equal= it++)) + { + item_equal->fix_length_and_dec(); + item_equal->update_used_tables(); + members= item_equal->members(); + if (cond_equal.max_members < members) + cond_equal.max_members= members; + } + and_cond->cond_equal= cond_equal; + args->concat((List<Item> *)&cond_equal.current_level); + + return and_cond; + } } /* For each field reference in cond, not from equal item predicates, set a pointer to the multiple equality it belongs to (if there is any) + as soon the field is not of a string type or the field reference is + an argument of a comparison predicate. */ - cond= cond->transform(&Item::equal_fields_propagator, - (byte *) inherited); + byte *is_subst_valid= (byte *) 1; + cond= cond->compile(&Item::subst_argument_checker, + &is_subst_valid, + &Item::equal_fields_propagator, + (byte *) inherited); cond->update_used_tables(); } return cond; @@ -6921,7 +7203,7 @@ static Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, /* Substitute every field reference in a condition by the best equal field - and eliminate all multiplle equality predicates + and eliminate all multiple equality predicates SYNOPSIS substitute_for_best_equal_field() @@ -7087,11 +7369,14 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list, Item_func::Functype functype= func->functype(); if (right_item->eq(field,0) && left_item != value && + right_item->cmp_context == field->cmp_context && (left_item->result_type() != STRING_RESULT || value->result_type() != STRING_RESULT || left_item->collation.collation == value->collation.collation)) { Item *tmp=value->new_item(); + tmp->collation.set(right_item->collation); + if (tmp) { thd->change_item_tree(args + 1, tmp); @@ -7108,11 +7393,14 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list, } } else if (left_item->eq(field,0) && right_item != value && + left_item->cmp_context == field->cmp_context && (right_item->result_type() != STRING_RESULT || value->result_type() != STRING_RESULT || right_item->collation.collation == value->collation.collation)) { Item *tmp=value->new_item(); + tmp->collation.set(left_item->collation); + if (tmp) { thd->change_item_tree(args, tmp); @@ -7854,7 +8142,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) Field *field=((Item_field*) args[0])->field; if (field->flags & AUTO_INCREMENT_FLAG && !field->table->maybe_null && (thd->options & OPTION_AUTO_IS_NULL) && - thd->insert_id()) + thd->insert_id() && thd->substitute_null_with_insert_id) { #ifdef HAVE_QUERY_CACHE query_cache_abort(&thd->net); @@ -7873,7 +8161,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) */ cond->fix_fields(thd, &cond); } - thd->insert_id(0); // Clear for next request + thd->substitute_null_with_insert_id= FALSE; // Clear for next request } /* fix to replace 'NULL' dates with '0' (shreeve@uci.edu) */ else if (((field->type() == FIELD_TYPE_DATE) || @@ -8011,7 +8299,12 @@ Field* create_tmp_field_from_field(THD *thd, Field* org_field, { Field *new_field; - if (convert_blob_length && (org_field->flags & BLOB_FLAG)) + /* + Make sure that the blob fits into a Field_varstring which has + 2-byte lenght. + */ + if (convert_blob_length && convert_blob_length < UINT_MAX16 && + (org_field->flags & BLOB_FLAG)) new_field= new Field_varstring(convert_blob_length, org_field->maybe_null(), org_field->field_name, table, @@ -8074,8 +8367,13 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, item->name, table, item->decimals); break; case INT_RESULT: - new_field=new Field_longlong(item->max_length, maybe_null, - item->name, table, item->unsigned_flag); + /* Select an integer type with the minimal fit precision */ + if (item->max_length > 11) + new_field=new Field_longlong(item->max_length, maybe_null, + item->name, table, item->unsigned_flag); + else + new_field=new Field_long(item->max_length, maybe_null, + item->name, table, item->unsigned_flag); break; case STRING_RESULT: DBUG_ASSERT(item->collation.collation); @@ -8088,8 +8386,13 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, if ((type= item->field_type()) == MYSQL_TYPE_DATETIME || type == MYSQL_TYPE_TIME || type == MYSQL_TYPE_DATE) new_field= item->tmp_table_field_from_field_type(table); + /* + Make sure that the blob fits into a Field_varstring which has + 2-byte lenght. + */ else if (item->max_length/item->collation.collation->mbmaxlen > 255 && - convert_blob_length) + item->max_length/item->collation.collation->mbmaxlen < UINT_MAX16 + && convert_blob_length) new_field= new Field_varstring(convert_blob_length, maybe_null, item->name, table, item->collation.collation); @@ -8414,13 +8717,15 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, param->group_length : 0, NullS)) { - bitmap_clear_bit(&temp_pool, temp_pool_slot); + if (temp_pool_slot != MY_BIT_NONE) + bitmap_clear_bit(&temp_pool, temp_pool_slot); DBUG_RETURN(NULL); /* purecov: inspected */ } /* Copy_field belongs to TMP_TABLE_PARAM, allocate it in THD mem_root */ if (!(param->copy_field= copy= new (thd->mem_root) Copy_field[field_count])) { - bitmap_clear_bit(&temp_pool, temp_pool_slot); + if (temp_pool_slot != MY_BIT_NONE) + bitmap_clear_bit(&temp_pool, temp_pool_slot); free_root(&own_root, MYF(0)); /* purecov: inspected */ DBUG_RETURN(NULL); /* purecov: inspected */ } @@ -8809,10 +9114,11 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, keyinfo->rec_per_key=0; keyinfo->algorithm= HA_KEY_ALG_UNDEF; keyinfo->name= (char*) "group_key"; - for (; group ; group=group->next,key_part_info++) + ORDER *cur_group= group; + for (; cur_group ; cur_group= cur_group->next, key_part_info++) { - Field *field=(*group->item)->get_tmp_table_field(); - bool maybe_null=(*group->item)->maybe_null; + Field *field=(*cur_group->item)->get_tmp_table_field(); + bool maybe_null=(*cur_group->item)->maybe_null; key_part_info->null_bit=0; key_part_info->field= field; key_part_info->offset= field->offset(); @@ -8825,8 +9131,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, 0 : FIELDFLAG_BINARY; if (!using_unique_constraint) { - group->buff=(char*) group_buff; - if (!(group->field= field->new_key_field(thd->mem_root,table, + cur_group->buff=(char*) group_buff; + if (!(cur_group->field= field->new_key_field(thd->mem_root,table, (char*) group_buff + test(maybe_null), field->null_ptr, @@ -8844,21 +9150,16 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, key_part_info->null_bit=field->null_bit; key_part_info->null_offset= (uint) (field->null_ptr - (uchar*) table->record[0]); - group->buff++; // Pointer to field data + cur_group->buff++; // Pointer to field data group_buff++; // Skipp null flag } /* In GROUP BY 'a' and 'a ' are equal for VARCHAR fields */ key_part_info->key_part_flag|= HA_END_SPACE_ARE_EQUAL; - group_buff+= group->field->pack_length(); + group_buff+= cur_group->field->pack_length(); } keyinfo->key_length+= key_part_info->length; } } - else - { - set_if_smaller(table->s->max_rows, rows_limit); - param->end_write_records= rows_limit; - } if (distinct && field_count != param->hidden_field_count) { @@ -8873,8 +9174,6 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, null_pack_length-=hidden_null_pack_length; keyinfo->key_parts= ((field_count-param->hidden_field_count)+ test(null_pack_length)); - set_if_smaller(table->s->max_rows, rows_limit); - param->end_write_records= rows_limit; table->distinct= 1; table->s->keys= 1; if (blob_count) @@ -8926,6 +9225,20 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, 0 : FIELDFLAG_BINARY; } } + + /* + Push the LIMIT clause to the temporary table creation, so that we + materialize only up to 'rows_limit' records instead of all result records. + This optimization is not applicable when there is GROUP BY or there is + no GROUP BY, but there are aggregate functions, because both must be + computed for all result rows. + */ + if (!group && !thd->lex->current_select->with_sum_func) + { + set_if_smaller(table->s->max_rows, rows_limit); + param->end_write_records= rows_limit; + } + if (thd->is_fatal_error) // If end of memory goto err; /* purecov: inspected */ table->s->db_record_offset= 1; @@ -8944,7 +9257,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, err: thd->mem_root= mem_root_save; free_tmp_table(thd,table); /* purecov: inspected */ - bitmap_clear_bit(&temp_pool, temp_pool_slot); + if (temp_pool_slot != MY_BIT_NONE) + bitmap_clear_bit(&temp_pool, temp_pool_slot); DBUG_RETURN(NULL); /* purecov: inspected */ } @@ -9232,7 +9546,8 @@ free_tmp_table(THD *thd, TABLE *entry) (*ptr)->free(); free_io_cache(entry); - bitmap_clear_bit(&temp_pool, entry->temp_pool_slot); + if (entry->temp_pool_slot != MY_BIT_NONE) + bitmap_clear_bit(&temp_pool, entry->temp_pool_slot); free_root(&own_root, MYF(0)); /* the table is allocated in its own root */ thd->proc_info=save_proc_info; @@ -9438,9 +9753,13 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) table->file->ha_index_init(0); } /* Set up select_end */ - join->join_tab[join->tables-1].next_select= setup_end_select_func(join); + Next_select_func end_select= setup_end_select_func(join); + if (join->tables) + { + join->join_tab[join->tables-1].next_select= end_select; - join_tab=join->join_tab+join->const_tables; + join_tab=join->join_tab+join->const_tables; + } join->send_records=0; if (join->tables == join->const_tables) { @@ -9450,7 +9769,6 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) */ if (!join->conds || join->conds->val_int()) { - Next_select_func end_select= join->join_tab[join->tables-1].next_select; error= (*end_select)(join,join_tab,0); if (error == NESTED_LOOP_OK || error == NESTED_LOOP_QUERY_LIMIT) error= (*end_select)(join,join_tab,1); @@ -9464,6 +9782,8 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) } else { + DBUG_ASSERT(join->tables); + DBUG_ASSERT(join_tab); error= sub_select(join,join_tab,0); if (error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS) error= sub_select(join,join_tab,1); @@ -10608,8 +10928,13 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), { if (!join->first_record) { + List_iterator_fast<Item> it(*join->fields); + Item *item; /* No matching rows for group function */ join->clear(); + + while ((item= it++)) + item->no_rows_in_result(); } if (join->having && join->having->val_int() == 0) error= -1; // Didn't satisfy having @@ -11410,6 +11735,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, We must not try to use disabled keys. */ usable_keys= table->s->keys_in_use; + /* we must not consider keys that are disabled by IGNORE INDEX */ + usable_keys.intersect(table->keys_in_use_for_query); for (ORDER *tmp_order=order; tmp_order ; tmp_order=tmp_order->next) { @@ -12773,7 +13100,7 @@ count_field_types(TMP_TABLE_PARAM *param, List<Item> &fields, { if (! field->const_item()) { - Item_sum *sum_item=(Item_sum*) field; + Item_sum *sum_item=(Item_sum*) field->real_item(); if (!sum_item->quick_group) param->quick_group=0; // UDF SUM function param->sum_func_count++; @@ -13033,10 +13360,13 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, param->copy_funcs.empty(); for (i= 0; (pos= li++); i++) { - if (pos->real_item()->type() == Item::FIELD_ITEM) + Field *field; + char *tmp; + Item *real_pos= pos->real_item(); + if (real_pos->type() == Item::FIELD_ITEM) { Item_field *item; - pos= pos->real_item(); + pos= real_pos; if (!(item= new Item_field(thd, ((Item_field*) pos)))) goto err; pos= item; @@ -13062,25 +13392,34 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, set up save buffer and change result_field to point at saved value */ - Field *field= item->field; + field= item->field; item->result_field=field->new_field(thd->mem_root,field->table, 1); - char *tmp=(char*) sql_alloc(field->pack_length()+1); + /* + We need to allocate one extra byte for null handling and + another extra byte to not get warnings from purify in + Field_string::val_int + */ + tmp= (char*) sql_alloc(field->pack_length()+2); if (!tmp) goto err; - if (copy) - { - copy->set(tmp, item->result_field); - item->result_field->move_field(copy->to_ptr,copy->to_null_ptr,1); - copy++; - } + if (copy) + { + copy->set(tmp, item->result_field); + item->result_field->move_field(copy->to_ptr,copy->to_null_ptr,1); +#ifdef HAVE_purify + copy->to_ptr[copy->from_length]= 0; +#endif + copy++; + } } } - else if ((pos->type() == Item::FUNC_ITEM || - pos->type() == Item::SUBSELECT_ITEM || - pos->type() == Item::CACHE_ITEM || - pos->type() == Item::COND_ITEM) && - !pos->with_sum_func) + else if ((real_pos->type() == Item::FUNC_ITEM || + real_pos->type() == Item::SUBSELECT_ITEM || + real_pos->type() == Item::CACHE_ITEM || + real_pos->type() == Item::COND_ITEM) && + !real_pos->with_sum_func) { // Save for send fields + pos= real_pos; /* TODO: In most cases this result will be sent to the user. This should be changed to use copy_int or copy_real depending @@ -13271,7 +13610,9 @@ change_to_use_tmp_fields(THD *thd, Item **ref_pointer_array, { Field *field; - if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) + if ((item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) || + (item->type() == Item::FUNC_ITEM && + ((Item_func*)item)->functype() == Item_func::SUSERVAR_FUNC)) item_field= item; else { @@ -14072,9 +14413,12 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, item_list.push_back(new Item_string(table_name_buffer, len, cs)); } else - item_list.push_back(new Item_string(table->alias, - strlen(table->alias), + { + TABLE_LIST *tab=table->pos_in_table_list; + item_list.push_back(new Item_string(tab->alias, + strlen(tab->alias), cs)); + } /* type */ item_list.push_back(new Item_string(join_type_str[tab->type], strlen(join_type_str[tab->type]), @@ -14261,8 +14605,8 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result) // drop UNCACHEABLE_EXPLAIN, because it is for internal usage only uint8 uncacheable= (sl->uncacheable & ~UNCACHEABLE_EXPLAIN); sl->type= (((&thd->lex->select_lex)==sl)? - ((thd->lex->all_selects_list != sl) ? - primary_key_name : "SIMPLE"): + (sl->first_inner_unit() || sl->next_select() ? + "PRIMARY" : "SIMPLE"): ((sl == first)? ((sl->linkage == DERIVED_TABLE_TYPE) ? "DERIVED": diff --git a/sql/sql_show.cc b/sql/sql_show.cc index cabb04c5f16..eb78f4fbdae 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -250,9 +250,35 @@ bool mysqld_show_column_types(THD *thd) } -int -mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, - const char *wild, bool dir) +/* + find_files() - find files in a given directory. + + SYNOPSIS + find_files() + thd thread handler + files put found files in this list + db database name to set in TABLE_LIST structure + path path to database + wild filter for found files + dir read databases in path if TRUE, read .frm files in + database otherwise + + RETURN + FIND_FILES_OK success + FIND_FILES_OOM out of memory error + FIND_FILES_DIR no such directory, or directory can't be read +*/ + +enum find_files_result { + FIND_FILES_OK, + FIND_FILES_OOM, + FIND_FILES_DIR +}; + +static +find_files_result +find_files(THD *thd, List<char> *files, const char *db, + const char *path, const char *wild, bool dir) { uint i; char *ext; @@ -262,7 +288,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, uint col_access=thd->col_access; #endif TABLE_LIST table_list; - DBUG_ENTER("mysql_find_files"); + DBUG_ENTER("find_files"); if (wild && !wild[0]) wild=0; @@ -275,7 +301,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), db); else my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), path, my_errno); - DBUG_RETURN(-1); + DBUG_RETURN(FIND_FILES_DIR); } for (i=0 ; i < (uint) dirp->number_off_files ; i++) @@ -337,7 +363,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, if (files->push_back(thd->strdup(file->name))) { my_dirend(dirp); - DBUG_RETURN(-1); + DBUG_RETURN(FIND_FILES_OOM); } } DBUG_PRINT("info",("found: %d files", files->elements)); @@ -345,7 +371,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, VOID(ha_find_files(thd,db,path,wild,dir,files)); - DBUG_RETURN(0); + DBUG_RETURN(FIND_FILES_OK); } @@ -439,13 +465,11 @@ bool mysqld_show_create_db(THD *thd, char *dbname, { Security_context *sctx= thd->security_ctx; int length; - char path[FN_REFLEN]; char buff[2048]; String buffer(buff, sizeof(buff), system_charset_info); #ifndef NO_EMBEDDED_ACCESS_CHECKS uint db_access; #endif - bool found_libchar; HA_CREATE_INFO create; uint create_options = create_info ? create_info->options : 0; Protocol *protocol=thd->protocol; @@ -480,23 +504,13 @@ bool mysqld_show_create_db(THD *thd, char *dbname, } else { - (void) sprintf(path,"%s/%s",mysql_data_home, dbname); - length=unpack_dirname(path,path); // Convert if not unix - found_libchar= 0; - if (length && path[length-1] == FN_LIBCHAR) - { - found_libchar= 1; - path[length-1]=0; // remove ending '\' - } - if (access(path,F_OK)) + if (check_db_dir_existence(dbname)) { my_error(ER_BAD_DB_ERROR, MYF(0), dbname); DBUG_RETURN(TRUE); } - if (found_libchar) - path[length-1]= FN_LIBCHAR; - strmov(path+length, MY_DB_OPT_FILE); - load_db_opt(thd, path, &create); + + load_db_opt_by_name(thd, dbname, &create); } List<Item> field_list; field_list.push_back(new Item_empty_string("Database",NAME_LEN)); @@ -2000,8 +2014,8 @@ enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table) wild string otherwise it's db name; RETURN - 1 error - 0 success + zero success + non-zero error */ int make_db_list(THD *thd, List<char> *files, @@ -2027,8 +2041,8 @@ int make_db_list(THD *thd, List<char> *files, if (files->push_back(thd->strdup(information_schema_name.str))) return 1; } - return mysql_find_files(thd, files, NullS, mysql_data_home, - idx_field_vals->db_value, 1); + return (find_files(thd, files, NullS, mysql_data_home, + idx_field_vals->db_value, 1) != FIND_FILES_OK); } /* @@ -2055,7 +2069,8 @@ int make_db_list(THD *thd, List<char> *files, if (files->push_back(thd->strdup(information_schema_name.str))) return 1; *with_i_schema= 1; - return mysql_find_files(thd, files, NullS, mysql_data_home, NullS, 1); + return (find_files(thd, files, NullS, + mysql_data_home, NullS, 1) != FIND_FILES_OK); } @@ -2116,12 +2131,6 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) LINT_INIT(end); LINT_INIT(len); - /* - Let us set fake sql_command so views won't try to merge - themselves into main statement. - */ - lex->sql_command= SQLCOM_SHOW_FIELDS; - lex->reset_n_backup_query_tables_list(&query_tables_list_backup); /* @@ -2144,8 +2153,16 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) I_S tables will be done. */ thd->temporary_tables= open_tables_state_backup.temporary_tables; + /* + Let us set fake sql_command so views won't try to merge + themselves into main statement. If we don't do this, + SELECT * from information_schema.xxxx will cause problems. + SQLCOM_SHOW_FIELDS is used because it satisfies 'only_view_structure()' + */ + lex->sql_command= SQLCOM_SHOW_FIELDS; res= open_normal_and_derived_tables(thd, show_table_list, MYSQL_LOCK_IGNORE_FLUSH); + lex->sql_command= save_sql_command; /* get_all_tables() returns 1 on failure and 0 on success thus return only these and not the result code of ::process_table() @@ -2204,9 +2221,28 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) strxmov(path, mysql_data_home, "/", base_name, NullS); end= path + (len= unpack_dirname(path,path)); len= FN_LEN - len; - if (mysql_find_files(thd, &files, base_name, - path, idx_field_vals.table_value, 0)) - goto err; + find_files_result res= find_files(thd, &files, base_name, + path, idx_field_vals.table_value, 0); + if (res != FIND_FILES_OK) + { + /* + Downgrade errors about problems with database directory to + warnings if this is not a 'SHOW' command. Another thread + may have dropped database, and we may still have a name + for that directory. + */ + if (res == FIND_FILES_DIR && lex->orig_sql_command == SQLCOM_END) + { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + thd->net.last_errno, thd->net.last_error); + thd->clear_error(); + continue; + } + else + { + goto err; + } + } if (lower_case_table_names) orig_base_name= thd->strdup(base_name); } @@ -2267,8 +2303,10 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) TABLE_LIST *show_table_list= (TABLE_LIST*) sel.table_list.first; lex->all_selects_list= &sel; lex->derived_tables= 0; + lex->sql_command= SQLCOM_SHOW_FIELDS; res= open_normal_and_derived_tables(thd, show_table_list, MYSQL_LOCK_IGNORE_FLUSH); + lex->sql_command= save_sql_command; /* We should use show_table_list->alias instead of show_table_list->table_name because table_name @@ -2319,8 +2357,11 @@ bool store_schema_shemata(THD* thd, TABLE *table, const char *db_name, int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond) { - char path[FN_REFLEN]; - bool found_libchar; + /* + TODO: fill_schema_shemata() is called when new client is connected. + Returning error status in this case leads to client hangup. + */ + INDEX_FIELD_VALUES idx_field_vals; List<char> files; char *file_name; @@ -2352,20 +2393,9 @@ int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond) (grant_option && !check_grant_db(thd, file_name))) #endif { - strxmov(path, mysql_data_home, "/", file_name, NullS); - length=unpack_dirname(path,path); // Convert if not unix - found_libchar= 0; - if (length && path[length-1] == FN_LIBCHAR) - { - found_libchar= 1; - path[length-1]=0; // remove ending '\' - } + load_db_opt_by_name(thd, file_name, &create); - if (found_libchar) - path[length-1]= FN_LIBCHAR; - strmov(path+length, MY_DB_OPT_FILE); - load_db_opt(thd, path, &create); - if (store_schema_shemata(thd, table, file_name, + if (store_schema_shemata(thd, table, file_name, create.default_table_charset)) DBUG_RETURN(1); } @@ -2683,9 +2713,7 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, table->field[5]->store("",0, cs); table->field[5]->set_notnull(); } - pos=(byte*) ((flags & NOT_NULL_FLAG) && - field->type() != FIELD_TYPE_TIMESTAMP ? - "NO" : "YES"); + pos=(byte*) ((flags & NOT_NULL_FLAG) ? "NO" : "YES"); table->field[6]->store((const char*) pos, strlen((const char*) pos), cs); is_blob= (field->type() == FIELD_TYPE_BLOB); @@ -3110,31 +3138,18 @@ static int get_schema_views_record(THD *thd, struct st_table_list *tables, if (tables->view) { Security_context *sctx= thd->security_ctx; - ulong grant= SHOW_VIEW_ACL; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - char *save_table_name= tables->table_name; - if (!my_strcasecmp(system_charset_info, tables->definer.user.str, - sctx->priv_user) && - !my_strcasecmp(system_charset_info, tables->definer.host.str, - sctx->priv_host)) - grant= SHOW_VIEW_ACL; - else + if (!tables->allowed_show) { - tables->table_name= tables->view_name.str; - if (check_access(thd, SHOW_VIEW_ACL , base_name, - &tables->grant.privilege, 0, 1, - test(tables->schema_table))) - grant= get_table_grant(thd, tables); - else - grant= tables->grant.privilege; + if (!my_strcasecmp(system_charset_info, tables->definer.user.str, + sctx->priv_user) && + !my_strcasecmp(system_charset_info, tables->definer.host.str, + sctx->priv_host)) + tables->allowed_show= TRUE; } - tables->table_name= save_table_name; -#endif - restore_record(table, s->default_values); table->field[1]->store(tables->view_db.str, tables->view_db.length, cs); table->field[2]->store(tables->view_name.str, tables->view_name.length, cs); - if (grant & SHOW_VIEW_ACL) + if (tables->allowed_show) { char buff[2048]; String qwe_str(buff, sizeof(buff), cs); @@ -3963,13 +3978,19 @@ bool get_schema_tables_result(JOIN *join) table_list->table->file->delete_all_rows(); free_io_cache(table_list->table); filesort_free_buffers(table_list->table); + table_list->table->null_row= 0; } else table_list->table->file->records= 0; if (table_list->schema_table->fill_table(thd, table_list, tab->select_cond)) + { result= 1; + join->error= 1; + table_list->is_schema_table_processed= TRUE; + break; + } table_list->is_schema_table_processed= TRUE; } } diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 79228be8a76..7aaca809113 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -248,6 +248,10 @@ bool String::copy(const char *str,uint32 arg_length, CHARSET_INFO *cs) 0 No conversion needed 1 Either character set conversion or adding leading zeros (e.g. for UCS-2) must be done + + NOTE + to_cs may be NULL for "no conversion" if the system variable + character_set_results is NULL. */ bool String::needs_conversion(uint32 arg_length, @@ -256,7 +260,8 @@ bool String::needs_conversion(uint32 arg_length, uint32 *offset) { *offset= 0; - if ((to_cs == &my_charset_bin) || + if (!to_cs || + (to_cs == &my_charset_bin) || (to_cs == from_cs) || my_charset_same(from_cs, to_cs) || ((from_cs == &my_charset_bin) && diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 4772d64ad0a..a340c5ffc98 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -715,6 +715,40 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, DBUG_RETURN(-1); } + /* + Convert the default value from client character + set into the column character set if necessary. + */ + if (sql_field->def && + save_cs != sql_field->def->collation.collation && + (sql_field->sql_type == FIELD_TYPE_VAR_STRING || + sql_field->sql_type == FIELD_TYPE_STRING || + sql_field->sql_type == FIELD_TYPE_SET || + sql_field->sql_type == FIELD_TYPE_ENUM)) + { + Query_arena backup_arena; + bool need_to_change_arena= !thd->stmt_arena->is_conventional(); + if (need_to_change_arena) + { + /* Asser that we don't do that at every PS execute */ + DBUG_ASSERT(thd->stmt_arena->is_first_stmt_execute() || + thd->stmt_arena->is_first_sp_execute()); + thd->set_n_backup_active_arena(thd->stmt_arena, &backup_arena); + } + + sql_field->def= sql_field->def->safe_charset_converter(save_cs); + + if (need_to_change_arena) + thd->restore_active_arena(thd->stmt_arena, &backup_arena); + + if (sql_field->def == NULL) + { + /* Could not convert */ + my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); + DBUG_RETURN(-1); + } + } + if (sql_field->sql_type == FIELD_TYPE_SET || sql_field->sql_type == FIELD_TYPE_ENUM) { @@ -776,35 +810,6 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, sql_field->interval_list.empty(); // Don't need interval_list anymore } - /* - Convert the default value from client character - set into the column character set if necessary. - */ - if (sql_field->def && cs != sql_field->def->collation.collation) - { - Query_arena backup_arena; - bool need_to_change_arena= !thd->stmt_arena->is_conventional(); - if (need_to_change_arena) - { - /* Asser that we don't do that at every PS execute */ - DBUG_ASSERT(thd->stmt_arena->is_first_stmt_execute() || - thd->stmt_arena->is_first_sp_execute()); - thd->set_n_backup_active_arena(thd->stmt_arena, &backup_arena); - } - - sql_field->def= sql_field->def->safe_charset_converter(cs); - - if (need_to_change_arena) - thd->restore_active_arena(thd->stmt_arena, &backup_arena); - - if (sql_field->def == NULL) - { - /* Could not convert */ - my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); - DBUG_RETURN(-1); - } - } - if (sql_field->sql_type == FIELD_TYPE_SET) { uint32 field_length; @@ -1631,10 +1636,9 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name, if (!create_info->default_table_charset) { HA_CREATE_INFO db_info; - char path[FN_REFLEN]; - /* Abuse build_table_path() to build the path to the db.opt file */ - build_table_path(path, sizeof(path), db, MY_DB_OPT_FILE, ""); - load_db_opt(thd, path, &db_info); + + load_db_opt_by_name(thd, db, &db_info); + create_info->default_table_charset= db_info.default_table_charset; } diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 66a16f16d8c..6bb50d602c3 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -158,11 +158,13 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) { TABLE *table; bool result= TRUE; - LEX_STRING definer_user; - LEX_STRING definer_host; + String stmt_query; DBUG_ENTER("mysql_create_or_drop_trigger"); + /* Charset of the buffer for statement must be system one. */ + stmt_query.set_charset(system_charset_info); + /* QQ: This function could be merged in mysql_alter_table() function But do we want this ? @@ -264,8 +266,8 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) } result= (create ? - table->triggers->create_trigger(thd, tables, &definer_user, &definer_host): - table->triggers->drop_trigger(thd, tables)); + table->triggers->create_trigger(thd, tables, &stmt_query): + table->triggers->drop_trigger(thd, tables, &stmt_query)); end: VOID(pthread_mutex_unlock(&LOCK_open)); @@ -277,29 +279,9 @@ end: { thd->clear_error(); - String log_query(thd->query, thd->query_length, system_charset_info); - - if (create) - { - log_query.set((char *) 0, 0, system_charset_info); /* reset log_query */ - - log_query.append(STRING_WITH_LEN("CREATE ")); - - if (definer_user.str && definer_host.str) - { - /* - Append definer-clause if the trigger is SUID (a usual trigger in - new MySQL versions). - */ - - append_definer(thd, &log_query, &definer_user, &definer_host); - } - - log_query.append(thd->lex->stmt_definition_begin); - } - /* Such a statement can always go directly to binlog, no trans cache. */ - Query_log_event qinfo(thd, log_query.ptr(), log_query.length(), 0, FALSE); + Query_log_event qinfo(thd, stmt_query.ptr(), stmt_query.length(), 0, + FALSE); mysql_bin_log.write(&qinfo); } @@ -319,22 +301,8 @@ end: LEX) tables - table list containing one open table for which the trigger is created. - definer_user - [out] after a call it points to 0-terminated string or - contains the NULL-string: - - 0-terminated is returned if the trigger is SUID. The - string contains user name part of the actual trigger - definer. - - NULL-string is returned if the trigger is non-SUID. - Anyway, the caller is responsible to provide memory for - storing LEX_STRING object. - definer_host - [out] after a call it points to 0-terminated string or - contains the NULL-string: - - 0-terminated string is returned if the trigger is - SUID. The string contains host name part of the - actual trigger definer. - - NULL-string is returned if the trigger is non-SUID. - Anyway, the caller is responsible to provide memory for - storing LEX_STRING object. + stmt_query - [OUT] after successful return, this string contains + well-formed statement for creation this trigger. NOTE - Assumes that trigger name is fully qualified. @@ -349,8 +317,7 @@ end: True - error */ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, - LEX_STRING *definer_user, - LEX_STRING *definer_host) + String *stmt_query) { LEX *lex= thd->lex; TABLE *table= tables->table; @@ -358,6 +325,8 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, trigname_path[FN_REFLEN]; LEX_STRING dir, file, trigname_file; LEX_STRING *trg_def, *name; + LEX_STRING definer_user; + LEX_STRING definer_host; ulonglong *trg_sql_mode; char trg_definer_holder[USER_HOST_BUFF_SIZE]; LEX_STRING *trg_definer; @@ -505,8 +474,6 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, definers_list.push_back(trg_definer, &table->mem_root)) goto err_with_cleanup; - trg_def->str= thd->query; - trg_def->length= thd->query_length; *trg_sql_mode= thd->variables.sql_mode; #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -526,27 +493,54 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, { /* SUID trigger. */ - *definer_user= lex->definer->user; - *definer_host= lex->definer->host; + definer_user= lex->definer->user; + definer_host= lex->definer->host; trg_definer->str= trg_definer_holder; - trg_definer->length= strxmov(trg_definer->str, definer_user->str, "@", - definer_host->str, NullS) - trg_definer->str; + trg_definer->length= strxmov(trg_definer->str, definer_user.str, "@", + definer_host.str, NullS) - trg_definer->str; } else { /* non-SUID trigger. */ - definer_user->str= 0; - definer_user->length= 0; + definer_user.str= 0; + definer_user.length= 0; - definer_host->str= 0; - definer_host->length= 0; + definer_host.str= 0; + definer_host.length= 0; trg_definer->str= (char*) ""; trg_definer->length= 0; } + /* + Create well-formed trigger definition query. Original query is not + appropriated, because definer-clause can be not truncated. + */ + + stmt_query->append(STRING_WITH_LEN("CREATE ")); + + if (trg_definer) + { + /* + Append definer-clause if the trigger is SUID (a usual trigger in + new MySQL versions). + */ + + append_definer(thd, stmt_query, &definer_user, &definer_host); + } + + stmt_query->append(thd->lex->stmt_definition_begin, + (char *) thd->lex->sphead->m_body_begin - + thd->lex->stmt_definition_begin + + thd->lex->sphead->m_body.length); + + trg_def->str= stmt_query->c_ptr(); + trg_def->length= stmt_query->length(); + + /* Create trigger definition file. */ + if (!sql_create_definition_file(&dir, &file, &triggers_file_type, (gptr)this, triggers_file_parameters, 0)) return 0; @@ -644,15 +638,19 @@ static bool save_trigger_file(Table_triggers_list *triggers, const char *db, SYNOPSIS drop_trigger() - thd - current thread context (including trigger definition in LEX) - tables - table list containing one open table for which trigger is - dropped. + thd - current thread context + (including trigger definition in LEX) + tables - table list containing one open table for which trigger + is dropped. + stmt_query - [OUT] after successful return, this string contains + well-formed statement for creation this trigger. RETURN VALUE False - success True - error */ -bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables) +bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables, + String *stmt_query) { LEX *lex= thd->lex; LEX_STRING *name; @@ -662,6 +660,8 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables) List_iterator<LEX_STRING> it_definer(definers_list); char path[FN_REFLEN]; + stmt_query->append(thd->query, thd->query_length); + while ((name= it_name++)) { it_def++; @@ -1503,40 +1503,11 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event, old_field= table->field; } -#ifndef NO_EMBEDDED_ACCESS_CHECKS - Security_context *save_ctx; - - if (sp_change_security_context(thd, sp_trigger, &save_ctx)) - return TRUE; - - /* - NOTE: TRIGGER_ACL should be used below. - */ - - if (check_global_access(thd, SUPER_ACL)) - { - sp_restore_security_context(thd, save_ctx); - return TRUE; - } - - /* - Fetch information about table-level privileges to GRANT_INFO structure for - subject table. Check of privileges that will use it and information about - column-level privileges will happen in Item_trigger_field::fix_fields(). - */ - - fill_effective_table_privileges(thd, - &subject_table_grants[event][time_type], - table->s->db, table->s->table_name); -#endif // NO_EMBEDDED_ACCESS_CHECKS - thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER); - err_status= sp_trigger->execute_function(thd, 0, 0, 0); + err_status= sp_trigger->execute_trigger + (thd, table->s->db, table->s->table_name, + &subject_table_grants[event][time_type]); thd->restore_sub_statement_state(&statement_state); - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - sp_restore_security_context(thd, save_ctx); -#endif // NO_EMBEDDED_ACCESS_CHECKS } return err_status; diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index e736c3e0e1a..521905a2c56 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -92,10 +92,8 @@ public: } ~Table_triggers_list(); - bool create_trigger(THD *thd, TABLE_LIST *table, - LEX_STRING *definer_user, - LEX_STRING *definer_host); - bool drop_trigger(THD *thd, TABLE_LIST *table); + bool create_trigger(THD *thd, TABLE_LIST *table, String *stmt_query); + bool drop_trigger(THD *thd, TABLE_LIST *table, String *stmt_query); bool process_triggers(THD *thd, trg_event_type event, trg_action_time_type time_type, bool old_row_is_record1); diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 8f98bab5c04..163801e2f1e 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -237,7 +237,7 @@ void udf_init() } } if (error > 0) - sql_print_error(ER(ER_GET_ERRNO), my_errno); + sql_print_error("Got unknown error: %d", my_errno); end_read_record(&read_record_info); new_thd->version--; // Force close to free memory diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 9a207845893..a5a767b6ebc 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -606,6 +606,7 @@ err: bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, Item **conds, uint order_num, ORDER *order) { + Item *fake_conds= 0; TABLE *table= table_list->table; TABLE_LIST tables; List<Item> all_fields; @@ -627,7 +628,7 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, &select_lex->top_join_list, table_list, conds, &select_lex->leaf_tables, - FALSE, UPDATE_ACL) || + FALSE, UPDATE_ACL, SELECT_ACL) || setup_conds(thd, table_list, select_lex->leaf_tables, conds) || select_lex->setup_ref_array(thd, order_num) || setup_order(thd, select_lex->ref_pointer_array, @@ -645,7 +646,7 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, DBUG_RETURN(TRUE); } } - select_lex->fix_prepare_information(thd, conds); + select_lex->fix_prepare_information(thd, conds, &fake_conds); DBUG_RETURN(FALSE); } @@ -722,7 +723,7 @@ reopen_tables: &lex->select_lex.top_join_list, table_list, &lex->select_lex.where, &lex->select_lex.leaf_tables, FALSE, - UPDATE_ACL)) + UPDATE_ACL, SELECT_ACL)) DBUG_RETURN(TRUE); if (setup_fields_with_no_wrap(thd, 0, *fields, 1, 0, 0)) diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 1561ade78af..4e2b48d9faf 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -155,6 +155,56 @@ err: DBUG_RETURN(TRUE); } +/* + Fill defined view parts + + SYNOPSIS + fill_defined_view_parts() + thd current thread. + view view to operate on + + DESCRIPTION + This function will initialize the parts of the view + definition that are not specified in ALTER VIEW + to their values from CREATE VIEW. + The view must be opened to get its definition. + We use a copy of the view when opening because we want + to preserve the original view instance. + + RETURN VALUE + TRUE can't open table + FALSE success +*/ +static bool +fill_defined_view_parts (THD *thd, TABLE_LIST *view) +{ + LEX *lex= thd->lex; + bool not_used; + TABLE_LIST decoy; + + memcpy (&decoy, view, sizeof (TABLE_LIST)); + if (!open_table(thd, &decoy, thd->mem_root, ¬_used, OPEN_VIEW_NO_PARSE) && + !decoy.view) + { + /* It's a table */ + return TRUE; + } + + if (!lex->definer) + { + view->definer.host= decoy.definer.host; + view->definer.user= decoy.definer.user; + lex->definer= &view->definer; + } + if (lex->create_view_algorithm == VIEW_ALGORITHM_UNDEFINED) + lex->create_view_algorithm= decoy.algorithm; + if (lex->create_view_suid == VIEW_SUID_DEFAULT) + lex->create_view_suid= decoy.view_suid ? + VIEW_SUID_DEFINER : VIEW_SUID_INVOKER; + + return FALSE; +} + /* Creating/altering VIEW procedure @@ -207,7 +257,15 @@ bool mysql_create_view(THD *thd, } if (mode != VIEW_CREATE_NEW) + { + if (mode == VIEW_ALTER && + fill_defined_view_parts(thd, view)) + { + res= TRUE; + goto err; + } sp_cache_invalidate(); + } if (!lex->definer) { @@ -671,8 +729,10 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, view->query.str= (char*)str.ptr(); view->query.length= str.length()-1; // we do not need last \0 view->source.str= thd->query + thd->lex->create_view_select_start; - view->source.length= (thd->query_length - - thd->lex->create_view_select_start); + view->source.length= (char *)skip_rear_comments((uchar *)view->source.str, + (uchar *)thd->query + + thd->query_length) - + view->source.str; view->file_version= 1; view->calc_md5(md5); view->md5.str= md5; @@ -759,13 +819,14 @@ loop_out: thd Thread handler parser parser object table TABLE_LIST structure for filling - + flags flags RETURN 0 ok 1 error */ -bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table) +bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, + uint flags) { SELECT_LEX *end, *view_select; LEX *old_lex, *lex; @@ -856,6 +917,10 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table) table->db, table->table_name); get_default_definer(thd, &table->definer); } + if (flags & OPEN_VIEW_NO_PARSE) + { + DBUG_RETURN(FALSE); + } /* Save VIEW parameters, which will be wiped out by derived table @@ -934,7 +999,8 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table) } } else if (!table->prelocking_placeholder && - old_lex->sql_command == SQLCOM_SHOW_CREATE) + old_lex->sql_command == SQLCOM_SHOW_CREATE && + !table->belong_to_view) { if (check_table_access(thd, SHOW_VIEW_ACL, table, 0)) goto err; @@ -994,6 +1060,31 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table) table->next_global= view_tables; } + bool view_is_mergeable= (table->algorithm != VIEW_ALGORITHM_TMPTABLE && + lex->can_be_merged()); + TABLE_LIST *view_main_select_tables; + if (view_is_mergeable) + { + /* + Currently 'view_main_select_tables' differs from 'view_tables' + only then view has CONVERT_TZ() function in its select list. + This may change in future, for example if we enable merging of + views with subqueries in select list. + */ + view_main_select_tables= + (TABLE_LIST*)lex->select_lex.table_list.first; + + /* + Let us set proper lock type for tables of the view's main + select since we may want to perform update or insert on + view. This won't work for view containing union. But this is + ok since we don't allow insert and update on such views + anyway. + */ + for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local) + tbl->lock_type= table->lock_type; + } + /* If we are opening this view as part of implicit LOCK TABLES, then this view serves as simple placeholder and we should not continue @@ -1048,43 +1139,26 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table) - VIEW SELECT allow merging - VIEW used in subquery or command support MERGE algorithm */ - if (table->algorithm != VIEW_ALGORITHM_TMPTABLE && - lex->can_be_merged() && + if (view_is_mergeable && (table->select_lex->master_unit() != &old_lex->unit || old_lex->can_use_merged()) && !old_lex->can_not_use_merged()) { - List_iterator_fast<TABLE_LIST> ti(view_select->top_join_list); - /* - Currently 'view_main_select_tables' differs from 'view_tables' - only then view has CONVERT_TZ() function in its select list. - This may change in future, for example if we enable merging - of views with subqueries in select list. - */ - TABLE_LIST *view_main_select_tables= - (TABLE_LIST*)lex->select_lex.table_list.first; /* lex should contain at least one table */ DBUG_ASSERT(view_main_select_tables != 0); + List_iterator_fast<TABLE_LIST> ti(view_select->top_join_list); + table->effective_algorithm= VIEW_ALGORITHM_MERGE; DBUG_PRINT("info", ("algorithm: MERGE")); table->updatable= (table->updatable_view != 0); table->effective_with_check= old_lex->get_effective_with_check(table); table->merge_underlying_list= view_main_select_tables; - /* - Let us set proper lock type for tables of the view's main select - since we may want to perform update or insert on view. This won't - work for view containing union. But this is ok since we don't - allow insert and update on such views anyway. - Also we fill correct wanted privileges. - */ - for (tbl= table->merge_underlying_list; tbl; tbl= tbl->next_local) - { - tbl->lock_type= table->lock_type; + /* Fill correct wanted privileges. */ + for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local) tbl->grant.want_privilege= top_view->grant.orig_want_privilege; - } /* prepare view context */ lex->select_lex.context.resolve_in_table_list_only(view_main_select_tables); @@ -1226,8 +1300,11 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) DBUG_ENTER("mysql_drop_view"); char path[FN_REFLEN]; TABLE_LIST *view; - bool type= 0; + frm_type_enum type; db_type not_used; + String non_existant_views; + char *wrong_object_db= NULL, *wrong_object_name= NULL; + bool error= FALSE; for (view= views; view; view= view->next_local) { @@ -1235,8 +1312,9 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) view->table_name, reg_ext, NullS); (void) unpack_filename(path, path); VOID(pthread_mutex_lock(&LOCK_open)); - if (access(path, F_OK) || - (type= (mysql_frm_type(thd, path, ¬_used) != FRMTYPE_VIEW))) + type= FRMTYPE_ERROR; + if (access(path, F_OK) || + FRMTYPE_VIEW != (type= mysql_frm_type(thd, path, ¬_used))) { char name[FN_REFLEN]; my_snprintf(name, sizeof(name), "%s.%s", view->db, view->table_name); @@ -1248,25 +1326,46 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) VOID(pthread_mutex_unlock(&LOCK_open)); continue; } - if (type) - my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->table_name, "VIEW"); + if (type == FRMTYPE_TABLE) + { + if (!wrong_object_name) + { + wrong_object_db= view->db; + wrong_object_name= view->table_name; + } + } else - my_error(ER_BAD_TABLE_ERROR, MYF(0), name); - goto err; + { + if (non_existant_views.length()) + non_existant_views.append(','); + non_existant_views.append(String(view->table_name,system_charset_info)); + } + VOID(pthread_mutex_unlock(&LOCK_open)); + continue; } if (my_delete(path, MYF(MY_WME))) - goto err; + error= TRUE; query_cache_invalidate3(thd, view, 0); sp_cache_invalidate(); VOID(pthread_mutex_unlock(&LOCK_open)); } + if (error) + { + DBUG_RETURN(TRUE); + } + if (wrong_object_name) + { + my_error(ER_WRONG_OBJECT, MYF(0), wrong_object_db, wrong_object_name, + "VIEW"); + DBUG_RETURN(TRUE); + } + if (non_existant_views.length()) + { + my_error(ER_BAD_TABLE_ERROR, MYF(0), non_existant_views.c_ptr()); + DBUG_RETURN(TRUE); + } send_ok(thd); DBUG_RETURN(FALSE); - -err: - VOID(pthread_mutex_unlock(&LOCK_open)); - DBUG_RETURN(TRUE); - } diff --git a/sql/sql_view.h b/sql/sql_view.h index cd61d7e9e71..b6c27a60eeb 100644 --- a/sql/sql_view.h +++ b/sql/sql_view.h @@ -19,7 +19,8 @@ bool mysql_create_view(THD *thd, enum_view_create_mode mode); -bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table); +bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, + uint flags); bool mysql_drop_view(THD *thd, TABLE_LIST *view, enum_drop_mode drop_mode); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index deac9cb5c40..e39deadbf12 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -68,6 +68,34 @@ inline Item *is_truth_value(Item *A, bool v1, bool v2) new Item_int((char *) (v1 ? "FALSE" : "TRUE"),!v1, 1)); } +#ifndef DBUG_OFF +#define YYDEBUG 1 +#else +#define YYDEBUG 0 +#endif + +#ifndef DBUG_OFF +void turn_parser_debug_on() +{ + /* + MYSQLdebug is in sql/sql_yacc.cc, in bison generated code. + Turning this option on is **VERY** verbose, and should be + used when investigating a syntax error problem only. + + The syntax to run with bison traces is as follows : + - Starting a server manually : + mysqld --debug="d,parser_debug" ... + - Running a test : + mysql-test-run.pl --mysqld="--debug=d,parser_debug" ... + + The result will be in the process stderr (var/log/master.err) + */ + + extern int yydebug; + yydebug= 1; +} +#endif + %} %union { int num; @@ -725,8 +753,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); predicate bit_expr bit_term bit_factor value_expr term factor table_wild simple_expr udf_expr expr_or_default set_expr_or_default interval_expr - param_marker singlerow_subselect singlerow_subselect_init - exists_subselect exists_subselect_init geometry_function + param_marker geometry_function signed_literal now_or_signed_literal opt_escape sp_opt_default simple_ident_nospvar simple_ident_q @@ -791,7 +818,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <variable> internal_variable_name -%type <select_lex> in_subselect in_subselect_init +%type <select_lex> subselect subselect_init get_select_lex %type <boolfunc2creator> comp_op @@ -1256,6 +1283,17 @@ create_function_tail: RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING_sys { LEX *lex=Lex; + if (lex->definer != NULL) + { + /* + DEFINER is a concept meaningful when interpreting SQL code. + UDF functions are compiled. + Using DEFINER with UDF has therefore no semantic, + and is considered a parsing error. + */ + my_error(ER_WRONG_USAGE, MYF(0), "SONAME", "DEFINER"); + YYABORT; + } lex->sql_command = SQLCOM_CREATE_FUNCTION; lex->udf.name = lex->spname->m_name; lex->udf.returns=(Item_result) $2; @@ -1285,6 +1323,7 @@ create_function_tail: sp= new sp_head(); sp->reset_thd_mem_root(YYTHD); sp->init(lex); + sp->init_sp_name(YYTHD, lex->spname); sp->m_type= TYPE_ENUM_FUNCTION; lex->sphead= sp; @@ -1339,7 +1378,7 @@ create_function_tail: YYABORT; lex->sql_command= SQLCOM_CREATE_SPFUNCTION; - sp->init_strings(YYTHD, lex, lex->spname); + sp->init_strings(YYTHD, lex); if (!(sp->m_flags & sp_head::HAS_RETURN)) { my_error(ER_SP_NORETURN, MYF(0), sp->m_qname.str); @@ -1911,9 +1950,12 @@ sp_proc_stmt: sp_instr_stmt *i=new sp_instr_stmt(sp->instructions(), lex->spcont, lex); - /* Extract the query statement from the tokenizer: - The end is either lex->tok_end or tok->ptr. */ - if (lex->ptr - lex->tok_end > 1) + /* + Extract the query statement from the tokenizer. The + end is either lex->ptr, if there was no lookahead, + lex->tok_end otherwise. + */ + if (yychar == YYEMPTY) i->m_query.length= lex->ptr - sp->m_tmp_query; else i->m_query.length= lex->tok_end - sp->m_tmp_query; @@ -3089,7 +3131,7 @@ opt_bin_mod: | BINARY { Lex->type|= BINCMP_FLAG; }; opt_bin_charset: - /* empty */ { } + /* empty */ { Lex->charset= NULL; } | ASCII_SYM { Lex->charset=&my_charset_latin1; } | UNICODE_SYM { @@ -3899,12 +3941,14 @@ select_paren: yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } - if (sel->linkage == UNION_TYPE && - !sel->master_unit()->first_select()->braces) - { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; - } + if (sel->linkage == UNION_TYPE && + !sel->master_unit()->first_select()->braces && + sel->master_unit()->first_select()->linkage == + UNION_TYPE) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } /* select in braces, can't contain global parameters */ if (sel->master_unit()->fake_select_lex) sel->master_unit()->global_parameters= @@ -4060,8 +4104,8 @@ select_item: YYABORT; if ($4.str) { - $2->set_name($4.str, $4.length, system_charset_info); $2->is_autogenerated_name= FALSE; + $2->set_name($4.str, $4.length, system_charset_info); } else if (!$2->name) { char *str = $1; @@ -4162,37 +4206,37 @@ bool_pri: | bool_pri EQUAL_SYM predicate { $$= new Item_func_equal($1,$3); } | bool_pri comp_op predicate %prec EQ { $$= (*$2)(0)->create($1,$3); } - | bool_pri comp_op all_or_any in_subselect %prec EQ - { $$= all_any_subquery_creator($1, $2, $3, $4); } + | bool_pri comp_op all_or_any '(' subselect ')' %prec EQ + { $$= all_any_subquery_creator($1, $2, $3, $5); } | predicate ; predicate: - bit_expr IN_SYM '(' expr_list ')' + bit_expr IN_SYM '(' subselect ')' + { $$= new Item_in_subselect($1, $4); } + | bit_expr not IN_SYM '(' subselect ')' + { $$= negate_expression(YYTHD, new Item_in_subselect($1, $5)); } + | bit_expr IN_SYM '(' expr ')' + { + $$= new Item_func_eq($1, $4); + } + | bit_expr IN_SYM '(' expr ',' expr_list ')' { - if ($4->elements == 1) - $$= new Item_func_eq($1, $4->head()); - else - { - $4->push_front($1); - $$= new Item_func_in(*$4); - } + $6->push_front($4); + $6->push_front($1); + $$= new Item_func_in(*$6); } - | bit_expr not IN_SYM '(' expr_list ')' + | bit_expr not IN_SYM '(' expr ')' { - if ($5->elements == 1) - $$= new Item_func_ne($1, $5->head()); - else - { - $5->push_front($1); - Item_func_in *item = new Item_func_in(*$5); + $$= new Item_func_ne($1, $5); + } + | bit_expr not IN_SYM '(' expr ',' expr_list ')' + { + $7->push_front($5); + $7->push_front($1); + Item_func_in *item = new Item_func_in(*$7); item->negate(); $$= item; - } } - | bit_expr IN_SYM in_subselect - { $$= new Item_in_subselect($1, $3); } - | bit_expr not IN_SYM in_subselect - { $$= negate_expression(YYTHD, new Item_in_subselect($1, $4)); } | bit_expr BETWEEN_SYM bit_expr AND_SYM predicate { $$= new Item_func_between($1,$3,$5); } | bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate @@ -4314,6 +4358,10 @@ simple_expr: | '-' simple_expr %prec NEG { $$= new Item_func_neg($2); } | '~' simple_expr %prec NEG { $$= new Item_func_bit_neg($2); } | not2 simple_expr %prec NEG { $$= negate_expression(YYTHD, $2); } + | '(' subselect ')' + { + $$= new Item_singlerow_subselect($2); + } | '(' expr ')' { $$= $2; } | '(' expr ',' expr_list ')' { @@ -4325,8 +4373,10 @@ simple_expr: $5->push_front($3); $$= new Item_row(*$5); } - | EXISTS exists_subselect { $$= $2; } - | singlerow_subselect { $$= $1; } + | EXISTS '(' subselect ')' + { + $$= new Item_exists_subselect($3); + } | '{' ident expr '}' { $$= $3; } | MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')' { $2->push_front($5); @@ -4344,6 +4394,8 @@ simple_expr: lex->length ? atoi(lex->length) : -1, lex->dec ? atoi(lex->dec) : 0, lex->charset); + if (!$$) + YYABORT; } | CASE_SYM opt_expr WHEN_SYM when_list opt_else END { $$= new Item_func_case(* $4, $2, $5 ); } @@ -4353,6 +4405,8 @@ simple_expr: Lex->length ? atoi(Lex->length) : -1, Lex->dec ? atoi(Lex->dec) : 0, Lex->charset); + if (!$$) + YYABORT; } | CONVERT_SYM '(' expr USING charset_name ')' { $$= new Item_func_conv_charset($3,$5); } @@ -4936,8 +4990,8 @@ udf_expr: { if ($4.str) { - $2->set_name($4.str, $4.length, system_charset_info); $2->is_autogenerated_name= FALSE; + $2->set_name($4.str, $4.length, system_charset_info); } else $2->set_name($1, (uint) ($3 - $1), YYTHD->charset()); @@ -6300,14 +6354,17 @@ table_wild_one: ident opt_wild opt_table_alias { if (!Select->add_table_to_list(YYTHD, new Table_ident($1), $3, - TL_OPTION_UPDATING, Lex->lock_option)) + TL_OPTION_UPDATING | + TL_OPTION_ALIAS, Lex->lock_option)) YYABORT; } | ident '.' ident opt_wild opt_table_alias { if (!Select->add_table_to_list(YYTHD, new Table_ident(YYTHD, $1, $3, 0), - $5, TL_OPTION_UPDATING, + $5, + TL_OPTION_UPDATING | + TL_OPTION_ALIAS, Lex->lock_option)) YYABORT; } @@ -6740,17 +6797,8 @@ flush: FLUSH_SYM opt_no_write_to_binlog { LEX *lex=Lex; - if (lex->sphead && lex->sphead->m_type != TYPE_ENUM_PROCEDURE) - { - /* - Note that both FLUSH TABLES and FLUSH PRIVILEGES will break - execution in prelocked mode. So it is better to disable - FLUSH in stored functions and triggers completely. - */ - my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "FLUSH"); - YYABORT; - } - lex->sql_command= SQLCOM_FLUSH; lex->type=0; + lex->sql_command= SQLCOM_FLUSH; + lex->type= 0; lex->no_write_to_binlog= $2; } flush_options @@ -7031,7 +7079,7 @@ text_literal: | NCHAR_STRING { $$= new Item_string($1.str,$1.length,national_charset_info); } | UNDERSCORE_CHARSET TEXT_STRING - { $$ = new Item_string($2.str,$2.length,Lex->charset); } + { $$ = new Item_string($2.str,$2.length,Lex->underscore_charset); } | text_literal TEXT_STRING_literal { ((Item_string*) $1)->append($2.str,$2.length); } ; @@ -7109,7 +7157,7 @@ literal: (String*) 0; $$= new Item_string(str ? str->ptr() : "", str ? str->length() : 0, - Lex->charset); + Lex->underscore_charset); } | UNDERSCORE_CHARSET BIN_NUM { @@ -7468,6 +7516,10 @@ user: $$->user = $1; $$->host.str= (char *) "%"; $$->host.length= 1; + + if (check_string_length(system_charset_info, &$$->user, + ER(ER_USERNAME), USERNAME_LENGTH)) + YYABORT; } | ident_or_text '@' ident_or_text { @@ -7475,6 +7527,12 @@ user: if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) YYABORT; $$->user = $1; $$->host=$3; + + if (check_string_length(system_charset_info, &$$->user, + ER(ER_USERNAME), USERNAME_LENGTH) || + check_string_length(&my_charset_latin1, &$$->host, + ER(ER_HOSTNAME), HOSTNAME_LENGTH)) + YYABORT; } | CURRENT_USER optional_braces { @@ -7526,6 +7584,7 @@ keyword: | TRUNCATE_SYM {} | UNICODE_SYM {} | XA_SYM {} + | UPGRADE_SYM {} ; /* @@ -7822,7 +7881,12 @@ option_type_value: lex))) YYABORT; - if (lex->ptr - lex->tok_end > 1) + /* + Extract the query statement from the tokenizer. The + end is either lex->ptr, if there was no lookahead, + lex->tok_end otherwise. + */ + if (yychar == YYEMPTY) qbuff.length= lex->ptr - sp->m_tmp_query; else qbuff.length= lex->tok_end - sp->m_tmp_query; @@ -8843,49 +8907,38 @@ union_option: | ALL { $$=0; } ; -singlerow_subselect: - subselect_start singlerow_subselect_init - subselect_end - { - $$= $2; - }; - -singlerow_subselect_init: - select_init2 - { - $$= new Item_singlerow_subselect(Lex->current_select-> - master_unit()->first_select()); - }; - -exists_subselect: - subselect_start exists_subselect_init - subselect_end - { - $$= $2; - }; - -exists_subselect_init: - select_init2 - { - $$= new Item_exists_subselect(Lex->current_select->master_unit()-> - first_select()); - }; - -in_subselect: - subselect_start in_subselect_init - subselect_end - { - $$= $2; - }; +subselect: + SELECT_SYM subselect_start subselect_init subselect_end + { + $$= $3; + } + | '(' subselect_start subselect ')' + { + LEX *lex= Lex; + THD *thd= YYTHD; + /* + note that a local variable can't be used for + $3 as it's used in local variable construction + and some compilers can't guarnatee the order + in which the local variables are initialized. + */ + List_iterator<Item> it($3->item_list); + Item *item; + /* + we must fill the items list for the "derived table". + */ + while ((item= it++)) + add_item_to_list(thd, item); + } + union_clause subselect_end { $$= $3; }; -in_subselect_init: +subselect_init: select_init2 { $$= Lex->current_select->master_unit()->first_select(); }; subselect_start: - '(' SELECT_SYM { LEX *lex=Lex; if (lex->sql_command == (int)SQLCOM_HA_READ || @@ -8894,17 +8947,25 @@ subselect_start: yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } + /* + we are making a "derived table" for the parenthesis + as we need to have a lex level to fit the union + after the parenthesis, e.g. + (SELECT .. ) UNION ... becomes + SELECT * FROM ((SELECT ...) UNION ...) + */ if (mysql_new_select(Lex, 1)) YYABORT; }; subselect_end: - ')' { LEX *lex=Lex; lex->pop_context(); + SELECT_LEX *child= lex->current_select; lex->current_select = lex->current_select->return_after_parsing(); lex->nest_level--; + lex->current_select->n_child_sum_items += child->n_sum_items; }; /************************************************************************** @@ -8947,15 +9008,9 @@ definer: */ YYTHD->lex->definer= 0; } - | DEFINER_SYM EQ CURRENT_USER optional_braces + | DEFINER_SYM EQ user { - if (! (YYTHD->lex->definer= create_default_definer(YYTHD))) - YYABORT; - } - | DEFINER_SYM EQ ident_or_text '@' ident_or_text - { - if (!(YYTHD->lex->definer= create_definer(YYTHD, &$3, &$5))) - YYABORT; + YYTHD->lex->definer= get_current_user(YYTHD, $3); } ; @@ -8997,11 +9052,11 @@ view_algorithm_opt: view_suid: /* empty */ - { Lex->create_view_suid= TRUE; } + { Lex->create_view_suid= VIEW_SUID_DEFAULT; } | SQL_SYM SECURITY_SYM DEFINER_SYM - { Lex->create_view_suid= TRUE; } + { Lex->create_view_suid= VIEW_SUID_DEFINER; } | SQL_SYM SECURITY_SYM INVOKER_SYM - { Lex->create_view_suid= FALSE; } + { Lex->create_view_suid= VIEW_SUID_INVOKER; } ; view_tail: @@ -9092,6 +9147,7 @@ trigger_tail: YYABORT; sp->reset_thd_mem_root(YYTHD); sp->init(lex); + sp->init_sp_name(YYTHD, $3); lex->stmt_definition_begin= $2; lex->ident.str= $7; @@ -9120,7 +9176,7 @@ trigger_tail: sp_head *sp= lex->sphead; lex->sql_command= SQLCOM_CREATE_TRIGGER; - sp->init_strings(YYTHD, lex, $3); + sp->init_strings(YYTHD, lex); /* Restore flag if it was cleared above */ if (sp->m_old_cmq) YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; @@ -9168,13 +9224,14 @@ sp_tail: my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE"); YYABORT; } - + lex->stmt_definition_begin= $2; - + /* Order is important here: new - reset - init */ sp= new sp_head(); sp->reset_thd_mem_root(YYTHD); sp->init(lex); + sp->init_sp_name(YYTHD, $3); sp->m_type= TYPE_ENUM_PROCEDURE; lex->sphead= sp; @@ -9212,7 +9269,7 @@ sp_tail: LEX *lex= Lex; sp_head *sp= lex->sphead; - sp->init_strings(YYTHD, lex, $3); + sp->init_strings(YYTHD, lex); lex->sql_command= SQLCOM_CREATE_PROCEDURE; /* Restore flag if it was cleared above */ if (sp->m_old_cmq) diff --git a/sql/stacktrace.c b/sql/stacktrace.c index 838f547dc02..43f35c452f7 100644 --- a/sql/stacktrace.c +++ b/sql/stacktrace.c @@ -21,6 +21,7 @@ #ifdef HAVE_STACKTRACE #include <unistd.h> +#include <strings.h> #define PTR_SANE(p) ((p) && (char*)(p) >= heap_start && (char*)(p) <= heap_end) @@ -44,7 +45,29 @@ void safe_print_str(const char* name, const char* val, int max_len) } #ifdef TARGET_OS_LINUX -#define SIGRETURN_FRAME_COUNT 2 + +#ifdef __i386__ +#define SIGRETURN_FRAME_OFFSET 17 +#endif + +#ifdef __x86_64__ +#define SIGRETURN_FRAME_OFFSET 23 +#endif + +static my_bool is_nptl; + +/* Check if we are using NPTL or LinuxThreads on Linux */ +void check_thread_lib(void) +{ + char buf[5]; + +#ifdef _CS_GNU_LIBPTHREAD_VERSION + confstr(_CS_GNU_LIBPTHREAD_VERSION, buf, sizeof(buf)); + is_nptl = !strncasecmp(buf, "NPTL", sizeof(buf)); +#else + is_nptl = 0; +#endif +} #if defined(__alpha__) && defined(__GNUC__) /* @@ -90,7 +113,7 @@ inline uint32* find_prev_pc(uint32* pc, uchar** fp) void print_stacktrace(gptr stack_bottom, ulong thread_stack) { uchar** fp; - uint frame_count = 0; + uint frame_count = 0, sigreturn_frame_count; #if defined(__alpha__) && defined(__GNUC__) uint32* pc; #endif @@ -100,28 +123,27 @@ void print_stacktrace(gptr stack_bottom, ulong thread_stack) Attempting backtrace. You can use the following information to find out\n\ where mysqld died. If you see no messages after this, something went\n\ terribly wrong...\n"); -#ifdef __i386__ +#ifdef __i386__ __asm __volatile__ ("movl %%ebp,%0" :"=r"(fp) :"r"(fp)); - if (!fp) - { - fprintf(stderr, "frame pointer (ebp) is NULL, did you compile with\n\ --fomit-frame-pointer? Aborting backtrace!\n"); - return; - } +#endif +#ifdef __x86_64__ + __asm __volatile__ ("movq %%rbp,%0" + :"=r"(fp) + :"r"(fp)); #endif #if defined(__alpha__) && defined(__GNUC__) __asm __volatile__ ("mov $30,%0" :"=r"(fp) :"r"(fp)); +#endif if (!fp) { - fprintf(stderr, "frame pointer (fp) is NULL, did you compile with\n\ + fprintf(stderr, "frame pointer is NULL, did you compile with\n\ -fomit-frame-pointer? Aborting backtrace!\n"); return; } -#endif /* __alpha__ */ if (!stack_bottom || (gptr) stack_bottom > (gptr) &fp) { @@ -151,13 +173,16 @@ terribly wrong...\n"); :"r"(pc)); #endif /* __alpha__ */ + /* We are 1 frame above signal frame with NPTL and 2 frames above with LT */ + sigreturn_frame_count = is_nptl ? 1 : 2; + while (fp < (uchar**) stack_bottom) { -#ifdef __i386__ +#if defined(__i386__) || defined(__x86_64__) uchar** new_fp = (uchar**)*fp; - fprintf(stderr, "%p\n", frame_count == SIGRETURN_FRAME_COUNT ? - *(fp+17) : *(fp+1)); -#endif /* __386__ */ + fprintf(stderr, "%p\n", frame_count == sigreturn_frame_count ? + *(fp + SIGRETURN_FRAME_OFFSET) : *(fp + 1)); +#endif /* defined(__386__) || defined(__x86_64__) */ #if defined(__alpha__) && defined(__GNUC__) uchar** new_fp = find_prev_fp(pc, fp); diff --git a/sql/stacktrace.h b/sql/stacktrace.h index d5d1e05ef0e..527d10d70a2 100644 --- a/sql/stacktrace.h +++ b/sql/stacktrace.h @@ -19,16 +19,20 @@ extern "C" { #endif #ifdef TARGET_OS_LINUX -#if defined(HAVE_STACKTRACE) || (defined (__i386__) || (defined(__alpha__) && defined(__GNUC__))) +#if defined(HAVE_STACKTRACE) || (defined (__x86_64__) || defined (__i386__) || (defined(__alpha__) && defined(__GNUC__))) #undef HAVE_STACKTRACE #define HAVE_STACKTRACE extern char* __bss_start; extern char* heap_start; -#define init_stacktrace() { heap_start = (char*) &__bss_start; } +#define init_stacktrace() do { \ + heap_start = (char*) &__bss_start; \ + check_thread_lib(); \ + } while(0); void print_stacktrace(gptr stack_bottom, ulong thread_stack); void safe_print_str(const char* name, const char* val, int max_len); +void check_thread_lib(void); #endif /* (defined (__i386__) || (defined(__alpha__) && defined(__GNUC__))) */ #endif /* TARGET_OS_LINUX */ diff --git a/sql/table.cc b/sql/table.cc index 728b98b2d35..054736401ff 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -121,6 +121,8 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, // caller can't process new .frm goto err; } + if (prgflag & OPEN_VIEW_NO_PARSE) + goto err; share->blob_ptr_size= sizeof(char*); outparam->db_stat= db_stat; @@ -1590,7 +1592,7 @@ char *get_field(MEM_ROOT *mem, Field *field) bool check_db_name(char *name) { - char *start=name; + uint name_length= 0; // name length in symbols /* Used to catch empty names and names with end space */ bool last_char_is_space= TRUE; @@ -1607,6 +1609,7 @@ bool check_db_name(char *name) name+system_charset_info->mbmaxlen); if (len) { + name_length++; name += len; continue; } @@ -1614,12 +1617,13 @@ bool check_db_name(char *name) #else last_char_is_space= *name==' '; #endif + name_length++; if (*name == '/' || *name == '\\' || *name == FN_LIBCHAR || *name == FN_EXTCHAR) return 1; name++; } - return last_char_is_space || (uint) (name - start) > NAME_LEN; + return (last_char_is_space || name_length > NAME_LEN); } @@ -2985,6 +2989,36 @@ Field_iterator_table_ref::get_natural_column_ref() return nj_col; } +/* + Cleanup this table for re-execution. + + SYNOPSIS + st_table_list::reinit_before_use() +*/ + +void st_table_list::reinit_before_use(THD *thd) +{ + /* + Reset old pointers to TABLEs: they are not valid since the tables + were closed in the end of previous prepare or execute call. + */ + table= 0; + /* Reset is_schema_table_processed value(needed for I_S tables */ + is_schema_table_processed= FALSE; + + TABLE_LIST *embedded; /* The table at the current level of nesting. */ + TABLE_LIST *embedding= this; /* The parent nested table reference. */ + do + { + embedded= embedding; + if (embedded->prep_on_expr) + embedded->on_expr= embedded->prep_on_expr->copy_andor_structure(thd); + embedding= embedded->embedding; + } + while (embedding && + embedding->nested_join->join_list.head() == embedded); +} + /***************************************************************************** ** Instansiate templates diff --git a/sql/table.h b/sql/table.h index d23d58e964f..5136ac2c4db 100644 --- a/sql/table.h +++ b/sql/table.h @@ -360,6 +360,10 @@ typedef struct st_schema_table #define VIEW_ALGORITHM_TMPTABLE 1 #define VIEW_ALGORITHM_MERGE 2 +#define VIEW_SUID_INVOKER 0 +#define VIEW_SUID_DEFINER 1 +#define VIEW_SUID_DEFAULT 2 + /* view WITH CHECK OPTION parameter options */ #define VIEW_CHECK_NONE 0 #define VIEW_CHECK_LOCAL 1 @@ -569,6 +573,7 @@ typedef struct st_table_list tables. Unlike 'next_local', this in this list views are *not* leaves. Created in setup_tables() -> make_leaves_list(). */ + bool allowed_show; st_table_list *next_leaf; Item *where; /* VIEW WHERE clause condition */ Item *check_option; /* WITH CHECK OPTION condition */ @@ -668,10 +673,19 @@ typedef struct st_table_list Security_context *find_view_security_context(THD *thd); bool prepare_view_securety_context(THD *thd); #endif + /* + Cleanup for re-execution in a prepared statement or a stored + procedure. + */ + void reinit_before_use(THD *thd); private: bool prep_check_option(THD *thd, uint8 check_opt_type); bool prep_where(THD *thd, Item **conds, bool no_where_clause); + /* + Cleanup for re-execution in a prepared statement or a stored + procedure. + */ } TABLE_LIST; class Item; diff --git a/sql/udf_example.cc b/sql/udf_example.c index 6ad066eacc2..a80fce81278 100644 --- a/sql/udf_example.cc +++ b/sql/udf_example.c @@ -127,7 +127,7 @@ typedef long long longlong; #else #include <my_global.h> #include <my_sys.h> -#include <m_string.h> // To get strmov() +#include <m_string.h> /* To get strmov() */ #endif #include <mysql.h> #include <ctype.h> @@ -138,7 +138,6 @@ static pthread_mutex_t LOCK_hostname; /* These must be right or mysqld will not find the symbol! */ -extern "C" { my_bool metaphon_init(UDF_INIT *initid, UDF_ARGS *args, char *message); void metaphon_deinit(UDF_INIT *initid); char *metaphon(UDF_INIT *initid, UDF_ARGS *args, char *result, @@ -159,7 +158,6 @@ void avgcost_reset( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error void avgcost_clear( UDF_INIT* initid, char* is_null, char *error ); void avgcost_add( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ); double avgcost( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ); -} /************************************************************************* @@ -221,7 +219,7 @@ my_bool metaphon_init(UDF_INIT *initid, UDF_ARGS *args, char *message) ****************************************************************************/ -void metaphon_deinit(UDF_INIT *initid) +void metaphon_deinit(UDF_INIT *initid __attribute__((unused))) { } @@ -267,23 +265,25 @@ static char codes[26] = { #define NOGHTOF(x) (codes[(x) - 'A'] & 16) /* BDH */ -char *metaphon(UDF_INIT *initid, UDF_ARGS *args, char *result, - unsigned long *length, char *is_null, char *error) +char *metaphon(UDF_INIT *initid __attribute__((unused)), + UDF_ARGS *args, char *result, unsigned long *length, + char *is_null, char *error __attribute__((unused))) { const char *word=args->args[0]; - if (!word) // Null argument + const char *w_end; + char *org_result; + char *n, *n_start, *n_end; /* pointers to string */ + char *metaph_end; /* pointers to end of metaph */ + char ntrans[32]; /* word with uppercase letters */ + int KSflag; /* state flag for X to KS */ + + if (!word) /* Null argument */ { *is_null=1; return 0; } - const char *w_end=word+args->lengths[0]; - char *org_result=result; - - char *n, *n_start, *n_end; /* pointers to string */ - char *metaph, *metaph_end; /* pointers to metaph */ - char ntrans[32]; /* word with uppercase letters */ - char newm[8]; /* new metaph for comparison */ - int KSflag; /* state flag for X to KS */ + w_end=word+args->lengths[0]; + org_result=result; /*-------------------------------------------------------- * Copy word to internal buffer, dropping non-alphabetic @@ -519,6 +519,8 @@ char *metaphon(UDF_INIT *initid, UDF_ARGS *args, char *result, my_bool myfunc_double_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { + uint i; + if (!args->arg_count) { strcpy(message,"myfunc_double must have at least one argument"); @@ -528,27 +530,28 @@ my_bool myfunc_double_init(UDF_INIT *initid, UDF_ARGS *args, char *message) ** As this function wants to have everything as strings, force all arguments ** to strings. */ - for (uint i=0 ; i < args->arg_count; i++) + for (i=0 ; i < args->arg_count; i++) args->arg_type[i]=STRING_RESULT; - initid->maybe_null=1; // The result may be null - initid->decimals=2; // We want 2 decimals in the result - initid->max_length=6; // 3 digits + . + 2 decimals + initid->maybe_null=1; /* The result may be null */ + initid->decimals=2; /* We want 2 decimals in the result */ + initid->max_length=6; /* 3 digits + . + 2 decimals */ return 0; } -double myfunc_double(UDF_INIT *initid, UDF_ARGS *args, char *is_null, - char *error) +double myfunc_double(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, + char *is_null, char *error __attribute__((unused))) { unsigned long val = 0; unsigned long v = 0; + uint i, j; - for (uint i = 0; i < args->arg_count; i++) + for (i = 0; i < args->arg_count; i++) { if (args->args[i] == NULL) continue; val += args->lengths[i]; - for (uint j=args->lengths[i] ; j-- > 0 ;) + for (j=args->lengths[i] ; j-- > 0 ;) v += args->args[i][j]; } if (val) @@ -575,22 +578,25 @@ double myfunc_double(UDF_INIT *initid, UDF_ARGS *args, char *is_null, /* This function returns the sum of all arguments */ -longlong myfunc_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null, - char *error) +longlong myfunc_int(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, + char *is_null __attribute__((unused)), + char *error __attribute__((unused))) { longlong val = 0; - for (uint i = 0; i < args->arg_count; i++) + uint i; + + for (i = 0; i < args->arg_count; i++) { if (args->args[i] == NULL) continue; switch (args->arg_type[i]) { - case STRING_RESULT: // Add string lengths + case STRING_RESULT: /* Add string lengths */ val += args->lengths[i]; break; - case INT_RESULT: // Add numbers + case INT_RESULT: /* Add numbers */ val += *((longlong*) args->args[i]); break; - case REAL_RESULT: // Add numers as longlong + case REAL_RESULT: /* Add numers as longlong */ val += (longlong) *((double*) args->args[i]); break; default: @@ -604,7 +610,9 @@ longlong myfunc_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null, At least one of _init/_deinit is needed unless the server is started with --allow_suspicious_udfs. */ -my_bool myfunc_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +my_bool myfunc_int_init(UDF_INIT *initid __attribute__((unused)), + UDF_ARGS *args __attribute__((unused)), + char *message __attribute__((unused))) { return 0; } @@ -622,7 +630,7 @@ my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message) return 1; } if (args->arg_count) - args->arg_type[0]= INT_RESULT; // Force argument to int + args->arg_type[0]= INT_RESULT; /* Force argument to int */ if (!(initid->ptr=(char*) malloc(sizeof(longlong)))) { @@ -646,8 +654,9 @@ void sequence_deinit(UDF_INIT *initid) free(initid->ptr); } -longlong sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null, - char *error) +longlong sequence(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, + char *is_null __attribute__((unused)), + char *error __attribute__((unused))) { ulonglong val=0; if (args->arg_count) @@ -670,7 +679,6 @@ longlong sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null, #include <arpa/inet.h> #include <netdb.h> -extern "C" { my_bool lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message); void lookup_deinit(UDF_INIT *initid); char *lookup(UDF_INIT *initid, UDF_ARGS *args, char *result, @@ -679,7 +687,6 @@ my_bool reverse_lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message); void reverse_lookup_deinit(UDF_INIT *initid); char *reverse_lookup(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *null_value, char *error); -} /**************************************************************************** @@ -705,20 +712,26 @@ my_bool lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message) return 0; } -void lookup_deinit(UDF_INIT *initid) +void lookup_deinit(UDF_INIT *initid __attribute__((unused))) { #if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST) (void) pthread_mutex_destroy(&LOCK_hostname); #endif } -char *lookup(UDF_INIT *initid, UDF_ARGS *args, char *result, - unsigned long *res_length, char *null_value, char *error) +char *lookup(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, + char *result, unsigned long *res_length, char *null_value, + char *error __attribute__((unused))) { uint length; + char name_buff[256]; + struct hostent *hostent; +#if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST) int tmp_errno; - char name_buff[256],hostname_buff[2048]; - struct hostent tmp_hostent,*hostent; + char hostname_buff[2048]; + struct hostent tmp_hostent; +#endif + struct in_addr in; if (!args->args[0] || !(length=args->lengths[0])) { @@ -746,7 +759,6 @@ char *lookup(UDF_INIT *initid, UDF_ARGS *args, char *result, } VOID(pthread_mutex_unlock(&LOCK_hostname)); #endif - struct in_addr in; memcpy_fixed((char*) &in,(char*) *hostent->h_addr_list, sizeof(in.s_addr)); *res_length= (ulong) (strmov(result, inet_ntoa(in)) - result); return result; @@ -780,18 +792,24 @@ my_bool reverse_lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message) return 0; } -void reverse_lookup_deinit(UDF_INIT *initid) +void reverse_lookup_deinit(UDF_INIT *initid __attribute__((unused))) { #if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST) (void) pthread_mutex_destroy(&LOCK_hostname); #endif } -char *reverse_lookup(UDF_INIT *initid, UDF_ARGS *args, char *result, - unsigned long *res_length, char *null_value, char *error) +char *reverse_lookup(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, + char *result, unsigned long *res_length, + char *null_value, char *error __attribute__((unused))) { +#if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST) char name_buff[256]; struct hostent tmp_hostent; + int tmp_errno; +#endif + struct hostent *hp; + unsigned long taddr; uint length; if (args->arg_count == 4) @@ -808,8 +826,8 @@ char *reverse_lookup(UDF_INIT *initid, UDF_ARGS *args, char *result, (int) *((longlong*) args->args[3])); } else - { // string argument - if (!args->args[0]) // Return NULL for NULL values + { /* string argument */ + if (!args->args[0]) /* Return NULL for NULL values */ { *null_value=1; return 0; @@ -821,15 +839,13 @@ char *reverse_lookup(UDF_INIT *initid, UDF_ARGS *args, char *result, result[length]=0; } - unsigned long taddr = inet_addr(result); + taddr = inet_addr(result); if (taddr == (unsigned long) -1L) { *null_value=1; return 0; } - struct hostent *hp; #if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST) - int tmp_errno; if (!(hp=gethostbyaddr_r((char*) &taddr,sizeof(taddr), AF_INET, &tmp_hostent, name_buff,sizeof(name_buff), &tmp_errno))) @@ -902,11 +918,15 @@ avgcost_init( UDF_INIT* initid, UDF_ARGS* args, char* message ) /*args->arg_type[0] = REAL_RESULT; args->arg_type[1] = REAL_RESULT;*/ - initid->maybe_null = 0; // The result may be null - initid->decimals = 4; // We want 4 decimals in the result - initid->max_length = 20; // 6 digits + . + 10 decimals + initid->maybe_null = 0; /* The result may be null */ + initid->decimals = 4; /* We want 4 decimals in the result */ + initid->max_length = 20; /* 6 digits + . + 10 decimals */ - data = new struct avgcost_data; + if (!(data = (struct avgcost_data*) malloc(sizeof(struct avgcost_data)))) + { + strmov(message,"Couldn't allocate memory"); + return 1; + } data->totalquantity = 0; data->totalprice = 0.0; @@ -918,7 +938,7 @@ avgcost_init( UDF_INIT* initid, UDF_ARGS* args, char* message ) void avgcost_deinit( UDF_INIT* initid ) { - delete initid->ptr; + free(initid->ptr); } @@ -933,7 +953,8 @@ avgcost_reset(UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* message) /* This is needed to get things to work in MySQL 4.1.1 and above */ void -avgcost_clear(UDF_INIT* initid, char* is_null, char* message) +avgcost_clear(UDF_INIT* initid, char* is_null __attribute__((unused)), + char* message __attribute__((unused))) { struct avgcost_data* data = (struct avgcost_data*)initid->ptr; data->totalprice= 0.0; @@ -943,7 +964,9 @@ avgcost_clear(UDF_INIT* initid, char* is_null, char* message) void -avgcost_add(UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* message) +avgcost_add(UDF_INIT* initid, UDF_ARGS* args, + char* is_null __attribute__((unused)), + char* message __attribute__((unused))) { if (args->args[0] && args->args[1]) { @@ -963,7 +986,7 @@ avgcost_add(UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* message) if ( ((quantity < 0) && (newquantity < 0)) || ((quantity > 0) && (newquantity > 0)) ) { - data->totalprice = price * double(newquantity); + data->totalprice = price * (double)newquantity; } /* ** sub q if totalq > 0 @@ -971,15 +994,15 @@ avgcost_add(UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* message) */ else { - price = data->totalprice / double(data->totalquantity); - data->totalprice = price * double(newquantity); + price = data->totalprice / (double)data->totalquantity; + data->totalprice = price * (double)newquantity; } data->totalquantity = newquantity; } else { data->totalquantity += quantity; - data->totalprice += price * double(quantity); + data->totalprice += price * (double)quantity; } if (data->totalquantity == 0) @@ -989,7 +1012,8 @@ avgcost_add(UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* message) double -avgcost( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* error ) +avgcost( UDF_INIT* initid, UDF_ARGS* args __attribute__((unused)), + char* is_null, char* error __attribute__((unused))) { struct avgcost_data* data = (struct avgcost_data*)initid->ptr; if (!data->count || !data->totalquantity) @@ -999,16 +1023,14 @@ avgcost( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* error ) } *is_null = 0; - return data->totalprice/double(data->totalquantity); + return data->totalprice/(double)data->totalquantity; } -extern "C" { my_bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args, char *message); char *myfunc_argument_name(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *null_value, char *error); -} my_bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args, char *message) @@ -1024,16 +1046,17 @@ my_bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args, return 0; } -char *myfunc_argument_name(UDF_INIT *initid, UDF_ARGS *args, char *result, - unsigned long *length, char *null_value, - char *error) +char *myfunc_argument_name(UDF_INIT *initid __attribute__((unused)), + UDF_ARGS *args, char *result, + unsigned long *length, char *null_value, + char *error __attribute__((unused))) { if (!args->attributes[0]) { null_value= 0; return 0; } - (*length)--; // space for ending \0 (for debugging purposes) + (*length)--; /* space for ending \0 (for debugging purposes) */ if (*length > args->attribute_lengths[0]) *length= args->attribute_lengths[0]; memcpy(result, args->attributes[0], *length); diff --git a/sql/unireg.h b/sql/unireg.h index b932a2f320c..dfebde01338 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -147,7 +147,8 @@ #define READ_SCREENS 1024 /* Read screens, info and helpfile */ #define DELAYED_OPEN 4096 /* Open table later */ #define NO_ERR_ON_NEW_FRM 8192 /* stop error sending on new format */ - +#define OPEN_VIEW_NO_PARSE 16384 /* Open frm only if it's a view, + but do not parse view itself */ #define SC_INFO_LENGTH 4 /* Form format constant */ #define TE_INFO_LENGTH 3 #define MTYP_NOEMPTY_BIT 128 |