diff options
author | monty@mysql.com <> | 2005-11-05 01:32:55 +0200 |
---|---|---|
committer | monty@mysql.com <> | 2005-11-05 01:32:55 +0200 |
commit | a6f5375cb0cb40055f52e92d20ca04233ce70386 (patch) | |
tree | 4a7b533b6da471261f5c13c46e4e9f256ec63a0c /sql | |
parent | 3b74bb1b03a32616818f50f59cc44644036ab711 (diff) | |
parent | 303f6b4a7a44511aeb33ccbd9ff43ea2d454aa4c (diff) | |
download | mariadb-git-a6f5375cb0cb40055f52e92d20ca04233ce70386.tar.gz |
Merge mysql.com:/home/my/mysql-5.0
into mysql.com:/home/my/mysql-5.1
Diffstat (limited to 'sql')
84 files changed, 3643 insertions, 1522 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am index ada462f5e09..98d4d6b3d3a 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -22,7 +22,8 @@ MYSQLBASEdir= $(prefix) INCLUDES = @ZLIB_INCLUDES@ \ @bdb_includes@ @innodb_includes@ @ndbcluster_includes@ \ -I$(top_builddir)/include -I$(top_srcdir)/include \ - -I$(top_srcdir)/regex -I$(srcdir) $(openssl_includes) + -I$(top_srcdir)/regex -I$(srcdir) $(yassl_includes) \ + $(openssl_includes) WRAPLIBS= @WRAPLIBS@ SUBDIRS = share libexec_PROGRAMS = mysqld @@ -42,7 +43,8 @@ mysqld_LDADD = @MYSQLD_EXTRA_LDFLAGS@ \ @bdb_libs@ @innodb_libs@ @pstack_libs@ \ @innodb_system_libs@ \ @ndbcluster_libs@ @ndbcluster_system_libs@ \ - $(LDADD) $(CXXLDFLAGS) $(WRAPLIBS) @LIBDL@ @openssl_libs@ + $(LDADD) $(CXXLDFLAGS) $(WRAPLIBS) @LIBDL@ \ + @yassl_libs@ @openssl_libs@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ item_strfunc.h item_timefunc.h item_uniq.h \ item_create.h item_subselect.h item_row.h \ diff --git a/sql/field.cc b/sql/field.cc index d334dc1ab53..ef4d9429bda 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1630,6 +1630,7 @@ bool Field::needs_quotes(void) case FIELD_TYPE_MEDIUM_BLOB : case FIELD_TYPE_LONG_BLOB : case FIELD_TYPE_GEOMETRY : + case FIELD_TYPE_BIT: DBUG_RETURN(1); case FIELD_TYPE_DECIMAL : @@ -5883,7 +5884,8 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) memcpy(ptr,from,copy_length); if (copy_length < field_length) // Append spaces if shorter field_charset->cset->fill(field_charset,ptr+copy_length, - field_length-copy_length,' '); + field_length-copy_length, + field_charset->pad_char); if ((copy_length < length) && table->in_use->count_cuted_fields) { // Check if we loosed some info @@ -6426,6 +6428,17 @@ int Field_varstring::key_cmp(const byte *a,const byte *b) void Field_varstring::sort_string(char *to,uint length) { uint tot_length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr); + + if (field_charset == &my_charset_bin) + { + /* Store length last in high-byte order to sort longer strings first */ + if (length_bytes == 1) + to[length-1]= tot_length; + else + mi_int2store(to+length-2, tot_length); + length-= length_bytes; + } + tot_length= my_strnxfrm(field_charset, (uchar*) to, length, (uchar*) ptr + length_bytes, @@ -7073,8 +7086,7 @@ void Field_blob::get_key_image(char *buff, uint length, imagetype type) return; } get_ptr(&blob); - gobj= Geometry::create_from_wkb(&buffer, - blob + SRID_SIZE, blob_length - SRID_SIZE); + gobj= Geometry::construct(&buffer, blob, blob_length); if (gobj->get_mbr(&mbr, &dummy)) bzero(buff, SIZEOF_STORED_DOUBLE*4); else @@ -7136,6 +7148,13 @@ int Field_blob::key_cmp(const byte *a,const byte *b) } +uint32 Field_blob::sort_length() const +{ + return (uint32) (current_thd->variables.max_sort_length + + (field_charset == &my_charset_bin ? 0 : packlength)); +} + + void Field_blob::sort_string(char *to,uint length) { char *blob; @@ -7145,6 +7164,31 @@ void Field_blob::sort_string(char *to,uint length) bzero(to,length); else { + if (field_charset == &my_charset_bin) + { + char *pos; + + /* + Store length of blob last in blob to shorter blobs before longer blobs + */ + length-= packlength; + pos= to+length; + + switch (packlength) { + case 1: + *pos= (char) blob_length; + break; + case 2: + mi_int2store(pos, blob_length); + break; + case 3: + mi_int3store(pos, blob_length); + break; + case 4: + mi_int4store(pos, blob_length); + break; + } + } memcpy_fixed(&blob,ptr+packlength,sizeof(char*)); blob_length=my_strnxfrm(field_charset, @@ -7371,8 +7415,7 @@ void Field_geom::get_key_image(char *buff, uint length, imagetype type) return; } get_ptr(&blob); - gobj= Geometry::create_from_wkb(&buffer, - blob + SRID_SIZE, blob_length - SRID_SIZE); + gobj= Geometry::construct(&buffer, blob, blob_length); if (gobj->get_mbr(&mbr, &dummy)) bzero(buff, SIZEOF_STORED_DOUBLE*4); else @@ -7453,7 +7496,7 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs) uint32 wkb_type; if (length < SRID_SIZE + WKB_HEADER_SIZE + SIZEOF_STORED_DOUBLE*2) goto err; - wkb_type= uint4korr(from + WKB_HEADER_SIZE); + wkb_type= uint4korr(from + SRID_SIZE + 1); if (wkb_type < (uint32) Geometry::wkb_point || wkb_type > (uint32) Geometry::wkb_end) goto err; @@ -8226,8 +8269,20 @@ void Field_bit_as_char::sql_type(String &res) const Handling of field and create_field *****************************************************************************/ +/* + Convert create_field::length from number of characters to number of bytes + + SYNOPSIS + create_field::create_length_to_internal_length() + + DESCRIPTION + Convert create_field::length from number of characters to number of bytes, + save original value in chars_length. +*/ + void create_field::create_length_to_internal_length(void) { + chars_length= length; switch (sql_type) { case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: diff --git a/sql/field.h b/sql/field.h index 3833f764787..77d00b671f6 100644 --- a/sql/field.h +++ b/sql/field.h @@ -137,6 +137,7 @@ public: virtual bool eq_def(Field *field); virtual uint32 pack_length() const { return (uint32) field_length; } virtual uint32 pack_length_in_rec() const { return pack_length(); } + virtual uint32 sort_length() const { return pack_length(); } virtual void reset(void) { bzero(ptr,pack_length()); } virtual void reset_fields() {} virtual void set_default() @@ -1065,6 +1066,11 @@ public: void reset(void) { bzero(ptr,field_length+length_bytes); } uint32 pack_length() const { return (uint32) field_length+length_bytes; } uint32 key_length() const { return (uint32) field_length; } + uint32 sort_length() const + { + return (uint32) field_length + (field_charset == &my_charset_bin ? + length_bytes : 0); + } int store(const char *to,uint length,CHARSET_INFO *charset); int store(longlong nr, bool unsigned_val); int store(double nr) { return Field_str::store(nr); } /* QQ: To be deleted */ @@ -1144,6 +1150,7 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return (uint32) (packlength+table->s->blob_ptr_size); } + uint32 sort_length() const; inline uint32 max_data_length() const { return (uint32) (((ulonglong) 1 << (packlength*8)) -1); @@ -1390,7 +1397,15 @@ public: LEX_STRING comment; // Comment for field Item *def; // Default value enum enum_field_types sql_type; + /* + At various stages in execution this can be length of field in bytes or + max number of characters. + */ ulong length; + /* + The value of 'length' before a call to create_length_to_internal_length + */ + uint32 chars_length; uint decimals, flags, pack_length, key_length; Field::utype unireg_check; TYPELIB *interval; // Which interval to use diff --git a/sql/filesort.cc b/sql/filesort.cc index ad784c729fa..42d25dbbaee 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -50,7 +50,8 @@ static int merge_index(SORTPARAM *param,uchar *sort_buffer, IO_CACHE *outfile); static bool save_index(SORTPARAM *param,uchar **sort_keys, uint count, FILESORT_INFO *table_sort); -static uint sortlength(SORT_FIELD *sortorder, uint s_length, +static uint suffix_length(ulong string_length); +static uint sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset); static SORT_ADDON_FIELD *get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength); @@ -123,7 +124,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, sort_keys= (uchar **) NULL; error= 1; bzero((char*) ¶m,sizeof(param)); - param.sort_length= sortlength(sortorder, s_length, &multi_byte_charset); + param.sort_length= sortlength(thd, sortorder, s_length, &multi_byte_charset); param.ref_length= table->file->ref_length; param.addon_field= 0; param.addon_length= 0; @@ -585,6 +586,28 @@ err: } /* write_keys */ +/* + Store length as suffix in high-byte-first order +*/ + +static inline void store_length(uchar *to, uint length, uint pack_length) +{ + switch (pack_length) { + case 1: + *to= (uchar) length; + break; + case 2: + mi_int2store(to, length); + break; + case 3: + mi_int3store(to, length); + default: + mi_int4store(to, length); + break; + } +} + + /* makes a sort-key from record */ static void make_sortkey(register SORTPARAM *param, @@ -623,9 +646,11 @@ static void make_sortkey(register SORTPARAM *param, maybe_null= item->maybe_null; switch (sort_field->result_type) { case STRING_RESULT: - { + { CHARSET_INFO *cs=item->collation.collation; char fill_char= ((cs->state & MY_CS_BINSORT) ? (char) 0 : ' '); + int diff; + uint sort_field_length; if (maybe_null) *to++=1; @@ -644,24 +669,32 @@ static void make_sortkey(register SORTPARAM *param, } break; } - length=res->length(); - int diff=(int) (sort_field->length-length); + length= res->length(); + sort_field_length= sort_field->length - sort_field->suffix_length; + diff=(int) (sort_field_length - length); if (diff < 0) { diff=0; /* purecov: inspected */ - length=sort_field->length; + length= sort_field_length; } + if (sort_field->suffix_length) + { + /* Store length last in result_string */ + store_length(to + sort_field_length, length, + sort_field->suffix_length); + } if (sort_field->need_strxnfrm) { char *from=(char*) res->ptr(); + uint tmp_length; if ((unsigned char *)from == to) { set_if_smaller(length,sort_field->length); memcpy(param->tmp_buffer,from,length); from=param->tmp_buffer; } - uint tmp_length=my_strnxfrm(cs,to,sort_field->length, - (unsigned char *) from, length); + tmp_length= my_strnxfrm(cs,to,sort_field->length, + (unsigned char *) from, length); DBUG_ASSERT(tmp_length == sort_field->length); } else @@ -670,7 +703,7 @@ static void make_sortkey(register SORTPARAM *param, cs->cset->fill(cs, (char *)to+length,diff,fill_char); } break; - } + } case INT_RESULT: { longlong value= item->val_int_result(); @@ -1170,11 +1203,25 @@ static int merge_index(SORTPARAM *param, uchar *sort_buffer, } /* merge_index */ +static uint suffix_length(ulong string_length) +{ + if (string_length < 256) + return 1; + if (string_length < 256L*256L) + return 2; + if (string_length < 256L*256L*256L) + return 3; + return 4; // Can't sort longer than 4G +} + + + /* Calculate length of sort key SYNOPSIS sortlength() + thd Thread handler sortorder Order of items to sort uint s_length Number of items to sort multi_byte_charset (out) @@ -1190,10 +1237,10 @@ static int merge_index(SORTPARAM *param, uchar *sort_buffer, */ static uint -sortlength(SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset) +sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length, + bool *multi_byte_charset) { reg2 uint length; - THD *thd= current_thd; CHARSET_INFO *cs; *multi_byte_charset= 0; @@ -1201,19 +1248,17 @@ sortlength(SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset) for (; s_length-- ; sortorder++) { sortorder->need_strxnfrm= 0; + sortorder->suffix_length= 0; if (sortorder->field) { - if (sortorder->field->type() == FIELD_TYPE_BLOB) - sortorder->length= thd->variables.max_sort_length; - else + cs= sortorder->field->sort_charset(); + sortorder->length= sortorder->field->sort_length(); + + if (use_strnxfrm((cs=sortorder->field->sort_charset()))) { - sortorder->length=sortorder->field->pack_length(); - if (use_strnxfrm((cs=sortorder->field->sort_charset()))) - { - sortorder->need_strxnfrm= 1; - *multi_byte_charset= 1; - sortorder->length= cs->coll->strnxfrmlen(cs, sortorder->length); - } + sortorder->need_strxnfrm= 1; + *multi_byte_charset= 1; + sortorder->length= cs->coll->strnxfrmlen(cs, sortorder->length); } if (sortorder->field->maybe_null()) length++; // Place for NULL marker @@ -1229,6 +1274,12 @@ sortlength(SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset) sortorder->need_strxnfrm= 1; *multi_byte_charset= 1; } + else if (cs == &my_charset_bin) + { + /* Store length last to be able to sort blob/varbinary */ + sortorder->suffix_length= suffix_length(sortorder->length); + sortorder->length+= sortorder->suffix_length; + } break; case INT_RESULT: #if SIZEOF_LONG_LONG > 4 diff --git a/sql/ha_archive.cc b/sql/ha_archive.cc index 59c56e80cc3..c4801de5fb2 100644 --- a/sql/ha_archive.cc +++ b/sql/ha_archive.cc @@ -453,7 +453,6 @@ int ha_archive::free_share(ARCHIVE_SHARE *share) */ static const char *ha_archive_exts[] = { ARZ, - ARN, ARM, NullS }; @@ -1053,4 +1052,81 @@ int ha_archive::delete_all_rows() DBUG_ENTER("ha_archive::delete_all_rows"); DBUG_RETURN(0); } + +/* + We just return state if asked. +*/ +bool ha_archive::is_crashed() const +{ + return share->crashed; +} + +/* + Simple scan of the tables to make sure everything is ok. +*/ + +int ha_archive::check(THD* thd, HA_CHECK_OPT* check_opt) +{ + int rc= 0; + byte *buf; + const char *old_proc_info=thd->proc_info; + ha_rows count= share->rows_recorded; + DBUG_ENTER("ha_archive::check"); + + thd->proc_info= "Checking table"; + /* Flush any waiting data */ + gzflush(share->archive_write, Z_SYNC_FLUSH); + + /* + First we create a buffer that we can use for reading rows, and can pass + to get_row(). + */ + if (!(buf= (byte*) my_malloc(table->s->reclength, MYF(MY_WME)))) + rc= HA_ERR_OUT_OF_MEM; + + /* + Now we will rewind the archive file so that we are positioned at the + start of the file. + */ + if (!rc) + read_data_header(archive); + + if (!rc) + while (!(rc= get_row(archive, buf))) + count--; + + my_free((char*)buf, MYF(0)); + + thd->proc_info= old_proc_info; + + if ((rc && rc != HA_ERR_END_OF_FILE) || count) + { + share->crashed= FALSE; + DBUG_RETURN(HA_ADMIN_CORRUPT); + } + else + { + DBUG_RETURN(HA_ADMIN_OK); + } +} + +/* + Check and repair the table if needed. +*/ +bool ha_archive::check_and_repair(THD *thd) +{ + HA_CHECK_OPT check_opt; + DBUG_ENTER("ha_archive::check_and_repair"); + + check_opt.init(); + + if (check(thd, &check_opt) == HA_ADMIN_CORRUPT) + { + DBUG_RETURN(repair(thd, &check_opt)); + } + else + { + DBUG_RETURN(HA_ADMIN_OK); + } +} #endif /* HAVE_ARCHIVE_DB */ diff --git a/sql/ha_archive.h b/sql/ha_archive.h index 849b5b5bd6c..56a4b9d1e27 100644 --- a/sql/ha_archive.h +++ b/sql/ha_archive.h @@ -68,7 +68,7 @@ public: ulong table_flags() const { return (HA_REC_NOT_IN_SEQ | HA_NOT_EXACT_COUNT | HA_NO_AUTO_INCREMENT | - HA_FILE_BASED | HA_CAN_INSERT_DELAYED); + HA_FILE_BASED | HA_CAN_INSERT_DELAYED | HA_CAN_GEOMETRY); } ulong index_flags(uint idx, uint part, bool all_parts) const { @@ -103,6 +103,9 @@ public: } THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type); + bool is_crashed() const; + int check(THD* thd, HA_CHECK_OPT* check_opt); + bool check_and_repair(THD *thd); }; bool archive_db_init(void); diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc index 3312b60e6c4..fa8cda44866 100644 --- a/sql/ha_berkeley.cc +++ b/sql/ha_berkeley.cc @@ -394,6 +394,7 @@ ha_berkeley::ha_berkeley(TABLE *table_arg) int_table_flags(HA_REC_NOT_IN_SEQ | HA_FAST_KEY_READ | HA_NULL_IN_KEY | HA_CAN_INDEX_BLOBS | HA_NOT_EXACT_COUNT | HA_PRIMARY_KEY_IN_READ_INDEX | HA_FILE_BASED | + HA_CAN_GEOMETRY | HA_AUTO_PART_KEY | HA_TABLE_SCAN_ON_INDEX), changed_rows(0), last_dup_key((uint) -1), version(0), using_ignore(0) {} diff --git a/sql/ha_berkeley.h b/sql/ha_berkeley.h index 95ff64a082a..b3ce72d374b 100644 --- a/sql/ha_berkeley.h +++ b/sql/ha_berkeley.h @@ -94,6 +94,9 @@ class ha_berkeley: public handler uint max_supported_keys() const { return MAX_KEY-1; } uint extra_rec_buf_length() const { return BDB_HIDDEN_PRIMARY_KEY_LENGTH; } ha_rows estimate_rows_upper_bound(); + uint max_supported_key_length() const { return UINT_MAX32; } + uint max_supported_key_part_length() const { return UINT_MAX32; } + const key_map *keys_to_use_for_scanning() { return &key_map_full; } bool has_transactions() { return 1;} diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc index 66c2431844d..703646f8bf3 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -480,6 +480,7 @@ static int check_foreign_data_source( String query(query_buffer, sizeof(query_buffer), &my_charset_bin); MYSQL *mysql; DBUG_ENTER("ha_federated::check_foreign_data_source"); + /* Zero the length, otherwise the string will have misc chars */ query.length(0); @@ -564,6 +565,7 @@ static int parse_url_error(FEDERATED_SHARE *share, TABLE *table, int error_num) char buf[FEDERATED_QUERY_BUFFER_SIZE]; int buf_len; DBUG_ENTER("ha_federated parse_url_error"); + if (share->scheme) { DBUG_PRINT("info", @@ -572,11 +574,9 @@ static int parse_url_error(FEDERATED_SHARE *share, TABLE *table, int error_num) my_free((gptr) share->scheme, MYF(0)); share->scheme= 0; } - buf_len= (table->s->connect_string.length > (FEDERATED_QUERY_BUFFER_SIZE - 1)) - ? FEDERATED_QUERY_BUFFER_SIZE - 1 : table->s->connect_string.length; - - strnmov(buf, table->s->connect_string.str, buf_len); - buf[buf_len]= '\0'; + buf_len= min(table->s->connect_string.length, + FEDERATED_QUERY_BUFFER_SIZE-1); + strmake(buf, table->s->connect_string.str, buf_len); my_error(error_num, MYF(0), buf); DBUG_RETURN(error_num); } @@ -767,12 +767,9 @@ uint ha_federated::convert_row_to_internal_format(byte *record, MYSQL_ROW row) { ulong *lengths; Field **field; - DBUG_ENTER("ha_federated::convert_row_to_internal_format"); - // num_fields= mysql_num_fields(stored_result); lengths= mysql_fetch_lengths(stored_result); - memset(record, 0, table->s->null_bytes); for (field= table->field; *field; field++) @@ -824,13 +821,8 @@ static bool emit_key_part_element(String *to, KEY_PART_INFO *part, *buf++= '0'; *buf++= 'x'; - for (; len; ptr++,len--) - { - uint tmp= (uint)(uchar) *ptr; - *buf++= _dig_vec_upper[tmp >> 4]; - *buf++= _dig_vec_upper[tmp & 15]; - } - if (to->append(buff, (uint)(buf - buff))) + buf= octet2hex(buf, (char*) ptr, len); + if (to->append((char*) buff, (uint)(buf - buff))) DBUG_RETURN(1); } else if (part->key_part_flag & HA_BLOB_PART) @@ -1127,8 +1119,8 @@ bool ha_federated::create_where_from_key(String *to, char tmpbuff[FEDERATED_QUERY_BUFFER_SIZE]; String tmp(tmpbuff, sizeof(tmpbuff), system_charset_info); const key_range *ranges[2]= { start_key, end_key }; - DBUG_ENTER("ha_federated::create_where_from_key"); + tmp.length(0); if (start_key == NULL && end_key == NULL) DBUG_RETURN(1); @@ -1177,6 +1169,7 @@ bool ha_federated::create_where_from_key(String *to, switch(ranges[i]->flag) { case(HA_READ_KEY_EXACT): + DBUG_PRINT("info", ("federated HA_READ_KEY_EXACT %d", i)); if (store_length >= length || !needs_quotes || key_part->type == HA_KEYTYPE_BIT || @@ -1211,6 +1204,7 @@ bool ha_federated::create_where_from_key(String *to, } break; case(HA_READ_AFTER_KEY): + DBUG_PRINT("info", ("federated HA_READ_AFTER_KEY %d", i)); if (store_length >= length) /* end key */ { if (emit_key_part_name(&tmp, key_part)) @@ -1235,6 +1229,7 @@ bool ha_federated::create_where_from_key(String *to, break; } case(HA_READ_KEY_OR_NEXT): + DBUG_PRINT("info", ("federated HA_READ_KEY_OR_NEXT %d", i)); if (emit_key_part_name(&tmp, key_part) || tmp.append(FEDERATED_GE) || emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr, @@ -1242,6 +1237,7 @@ bool ha_federated::create_where_from_key(String *to, DBUG_RETURN(1); break; case(HA_READ_BEFORE_KEY): + DBUG_PRINT("info", ("federated HA_READ_BEFORE_KEY %d", i)); if (store_length >= length) { if (emit_key_part_name(&tmp, key_part) || @@ -1252,6 +1248,7 @@ bool ha_federated::create_where_from_key(String *to, break; } case(HA_READ_KEY_OR_PREV): + DBUG_PRINT("info", ("federated HA_READ_KEY_OR_PREV %d", i)); if (emit_key_part_name(&tmp, key_part) || tmp.append(FEDERATED_LE) || emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr, @@ -1334,7 +1331,6 @@ static FEDERATED_SHARE *get_share(const char *table_name, TABLE *table) query.append(FEDERATED_FROM); query.append(FEDERATED_BTICK); - if (!(share= (FEDERATED_SHARE *) my_multi_malloc(MYF(MY_WME), &share, sizeof(*share), @@ -1389,8 +1385,8 @@ error: static int free_share(FEDERATED_SHARE *share) { DBUG_ENTER("free_share"); - pthread_mutex_lock(&federated_mutex); + pthread_mutex_lock(&federated_mutex); if (!--share->use_count) { hash_delete(&federated_open_tables, (byte*) share); @@ -1578,7 +1574,6 @@ int ha_federated::write_row(byte *buf) values_string.length(0); insert_string.length(0); insert_field_value_string.length(0); - DBUG_ENTER("ha_federated::write_row"); DBUG_PRINT("info", ("table charset name %s csname %s", @@ -1683,7 +1678,6 @@ int ha_federated::optimize(THD* thd, HA_CHECK_OPT* check_opt) { char query_buffer[STRING_BUFFER_USUAL_SIZE]; String query(query_buffer, sizeof(query_buffer), &my_charset_bin); - DBUG_ENTER("ha_federated::optimize"); query.length(0); @@ -1707,7 +1701,6 @@ int ha_federated::repair(THD* thd, HA_CHECK_OPT* check_opt) { char query_buffer[STRING_BUFFER_USUAL_SIZE]; String query(query_buffer, sizeof(query_buffer), &my_charset_bin); - DBUG_ENTER("ha_federated::repair"); query.length(0); @@ -1753,14 +1746,16 @@ int ha_federated::repair(THD* thd, HA_CHECK_OPT* check_opt) int ha_federated::update_row(const byte *old_data, byte *new_data) { /* - This used to control how the query was built. If there was a primary key, - the query would be built such that there was a where clause with only - that column as the condition. This is flawed, because if we have a multi-part - primary key, it would only use the first part! We don't need to do this anyway, - because read_range_first will retrieve the correct record, which is what is used - to build the WHERE clause. We can however use this to append a LIMIT to the end - if there is NOT a primary key. Why do this? Because we only are updating one - record, and LIMIT enforces this. + This used to control how the query was built. If there was a + primary key, the query would be built such that there was a where + clause with only that column as the condition. This is flawed, + because if we have a multi-part primary key, it would only use the + first part! We don't need to do this anyway, because + read_range_first will retrieve the correct record, which is what + is used to build the WHERE clause. We can however use this to + append a LIMIT to the end if there is NOT a primary key. Why do + this? Because we only are updating one record, and LIMIT enforces + this. */ bool has_a_primary_key= (table->s->primary_key == 0 ? TRUE : FALSE); /* @@ -1787,7 +1782,6 @@ int ha_federated::update_row(const byte *old_data, byte *new_data) String where_string(where_buffer, sizeof(where_buffer), &my_charset_bin); - DBUG_ENTER("ha_federated::update_row"); /* set string lengths to 0 to avoid misc chars in string @@ -1982,12 +1976,10 @@ int ha_federated::index_read_idx(byte *buf, uint index, const byte *key, sizeof(sql_query_buffer), &my_charset_bin); key_range range; + DBUG_ENTER("ha_federated::index_read_idx"); index_string.length(0); sql_query.length(0); - - DBUG_ENTER("ha_federated::index_read_idx"); - statistic_increment(table->in_use->status_var.ha_read_key_count, &LOCK_status); @@ -2076,8 +2068,8 @@ int ha_federated::read_range_first(const key_range *start_key, String sql_query(sql_query_buffer, sizeof(sql_query_buffer), &my_charset_bin); - DBUG_ENTER("ha_federated::read_range_first"); + if (start_key == NULL && end_key == NULL) DBUG_RETURN(0); @@ -2392,7 +2384,6 @@ void ha_federated::info(uint flag) MYSQL_RES *result= 0; MYSQL_ROW row; String status_query_string(status_buf, sizeof(status_buf), &my_charset_bin); - DBUG_ENTER("ha_federated::info"); error_code= ER_QUERY_ON_FOREIGN_DATA_SOURCE; @@ -2483,10 +2474,10 @@ error: int ha_federated::delete_all_rows() { - DBUG_ENTER("ha_federated::delete_all_rows"); - char query_buffer[FEDERATED_QUERY_BUFFER_SIZE]; String query(query_buffer, sizeof(query_buffer), &my_charset_bin); + DBUG_ENTER("ha_federated::delete_all_rows"); + query.length(0); query.set_charset(system_charset_info); @@ -2498,11 +2489,12 @@ int ha_federated::delete_all_rows() /* TRUNCATE won't return anything in mysql_affected_rows */ - deleted+= records; if (mysql_real_query(mysql, query.ptr(), query.length())) { DBUG_RETURN(stash_remote_error()); } + deleted+= records; + records= 0; DBUG_RETURN(0); } @@ -2581,32 +2573,14 @@ THR_LOCK_DATA **ha_federated::store_lock(THD *thd, int ha_federated::create(const char *name, TABLE *table_arg, HA_CREATE_INFO *create_info) { - int retval= 0; - /* - only a temporary share, to test the url - */ - FEDERATED_SHARE tmp_share; + int retval; + FEDERATED_SHARE tmp_share; // Only a temporary share, to test the url DBUG_ENTER("ha_federated::create"); - if ((retval= parse_url(&tmp_share, table_arg, 1))) - goto error; - - if ((retval= check_foreign_data_source(&tmp_share, 1))) - goto error; - - if (tmp_share.scheme) - { - my_free((gptr) tmp_share.scheme, MYF(0)); - tmp_share.scheme= 0; - } - DBUG_RETURN(retval); + if (!(retval= parse_url(&tmp_share, table_arg, 1))) + retval= check_foreign_data_source(&tmp_share, 1); -error: - if (tmp_share.scheme) - { - my_free((gptr) tmp_share.scheme, MYF(0)); - tmp_share.scheme= 0; - } + my_free((gptr) tmp_share.scheme, MYF(MY_ALLOW_ZERO_PTR)); DBUG_RETURN(retval); } diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 12f8b984971..1fdc36c84fe 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -812,6 +812,7 @@ ha_innobase::ha_innobase(TABLE *table_arg) HA_CAN_SQL_HANDLER | HA_NOT_EXACT_COUNT | HA_PRIMARY_KEY_IN_READ_INDEX | + HA_CAN_GEOMETRY | HA_TABLE_SCAN_ON_INDEX), last_dup_key((uint) -1), start_of_scan(0), @@ -2757,6 +2758,7 @@ get_innobase_type_from_mysql_type( return(DATA_DOUBLE); case FIELD_TYPE_DECIMAL: return(DATA_DECIMAL); + case FIELD_TYPE_GEOMETRY: case FIELD_TYPE_TINY_BLOB: case FIELD_TYPE_MEDIUM_BLOB: case FIELD_TYPE_BLOB: @@ -2872,6 +2874,9 @@ ha_innobase::store_key_val_for_row( ulint lenlen; ulint len; byte* data; + ulint key_len; + CHARSET_INFO* cs; + int error=0; if (is_null) { buff += key_part->length + 2; @@ -2890,8 +2895,20 @@ ha_innobase::store_key_val_for_row( /* In a column prefix index, we may need to truncate the stored value: */ - if (len > key_part->length) { - len = key_part->length; + cs = key_part->field->charset(); + + if (cs->mbmaxlen > 1 && key_part->length > 0) { + key_len = (ulint) cs->cset->well_formed_len(cs, + (const char *) data, + (const char *) data + key_part->length, + key_part->length / cs->mbmaxlen, + &error); + } else { + key_len = key_part->length; + } + + if (len > key_len) { + len = key_len; } /* The length in a key value is always stored in 2 @@ -2915,6 +2932,11 @@ ha_innobase::store_key_val_for_row( || mysql_type == FIELD_TYPE_BLOB || mysql_type == FIELD_TYPE_LONG_BLOB) { + CHARSET_INFO* cs; + ulint key_len; + ulint len; + int error=0; + ut_a(key_part->key_part_flag & HA_PART_KEY_SEG); if (is_null) { @@ -2935,8 +2957,21 @@ ha_innobase::store_key_val_for_row( indexes, and we may need to truncate the data to be stored in the key value: */ - if (blob_len > key_part->length) { - blob_len = key_part->length; + cs = key_part->field->charset(); + + if (cs->mbmaxlen > 1 && key_part->length > 0) { + key_len = (ulint) cs->cset->well_formed_len(cs, + (const char *) blob_data, + (const char *) blob_data + + key_part->length, + key_part->length / cs->mbmaxlen, + &error); + } else { + key_len = key_part->length; + } + + if (blob_len > key_len) { + blob_len = key_len; } /* MySQL reserves 2 bytes for the length and the @@ -2958,15 +2993,40 @@ ha_innobase::store_key_val_for_row( value we store may be also in a column prefix index. */ + CHARSET_INFO* cs; + ulint len; + const mysql_byte* src_start; + int error=0; + if (is_null) { buff += key_part->length; continue; } - memcpy(buff, record + key_part->offset, - key_part->length); - buff += key_part->length; + cs = key_part->field->charset(); + src_start = record + key_part->offset; + + if (key_part->length > 0 && cs->mbmaxlen > 1) { + len = (ulint) cs->cset->well_formed_len(cs, + src_start, + src_start + key_part->length, + key_part->length / cs->mbmaxlen, + &error); + } else { + len = key_part->length; + } + + memcpy(buff, src_start, len); + buff+=len; + + /* Pad the unused space with spaces */ + + if (len < key_part->length) { + len = key_part->length - len; + memset(buff, ' ', len); + buff+=len; + } } } @@ -6755,6 +6815,7 @@ ha_innobase::store_lock( || thd->lex->sql_command == SQLCOM_CALL) && !thd->tablespace_op && thd->lex->sql_command != SQLCOM_TRUNCATE + && thd->lex->sql_command != SQLCOM_OPTIMIZE && thd->lex->sql_command != SQLCOM_CREATE_TABLE) { lock_type = TL_WRITE_ALLOW_WRITE; @@ -7044,8 +7105,7 @@ ha_innobase::cmp_ref( (const char*)ref1, len1, (const char*)ref2, len2); } else { - result = field->key_cmp((const char*)ref1, - (const char*)ref2); + result = field->key_cmp(ref1, ref2); } if (result) { diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 689dbf2fc24..03ef362c0d4 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -40,7 +40,7 @@ TYPELIB myisam_recover_typelib= {array_elements(myisam_recover_names)-1,"", myisam_recover_names, NULL}; const char *myisam_stats_method_names[] = {"nulls_unequal", "nulls_equal", - NullS}; + "nulls_ignored", NullS}; TYPELIB myisam_stats_method_typelib= { array_elements(myisam_stats_method_names) - 1, "", myisam_stats_method_names, NULL}; @@ -985,11 +985,16 @@ int ha_myisam::enable_indexes(uint mode) { sql_print_warning("Warning: Enabling keys got errno %d, retrying", my_errno); - thd->clear_error(); + /* Repairing by sort failed. Now try standard repair method. */ param.testflag&= ~(T_REP_BY_SORT | T_QUICK); error= (repair(thd,param,0) != HA_ADMIN_OK); - if (!error && thd->net.report_error) - error= HA_ERR_CRASHED; + /* + If the standard repair succeeded, clear all error messages which + might have been set by the first repair. They can still be seen + with SHOW WARNINGS then. + */ + if (! error) + thd->clear_error(); } info(HA_STATUS_CONST); thd->proc_info=save_proc_info; diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 50d9f5b701a..2340cc006f6 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -599,9 +599,9 @@ static bool ndb_supported_type(enum_field_types type) case MYSQL_TYPE_ENUM: case MYSQL_TYPE_SET: case MYSQL_TYPE_BIT: + case MYSQL_TYPE_GEOMETRY: return TRUE; case MYSQL_TYPE_NULL: - case MYSQL_TYPE_GEOMETRY: break; } return FALSE; @@ -3216,7 +3216,7 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) if (!thd_ndb->lock_count++) { PRINT_OPTION_FLAGS(thd); - if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN | OPTION_TABLE_LOCK))) + if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) { // Autocommit transaction DBUG_ASSERT(!thd_ndb->stmt); @@ -3388,11 +3388,11 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) } /* - When using LOCK TABLE's external_lock is only called when the actual - TABLE LOCK is done. - Under LOCK TABLES, each used tables will force a call to start_stmt. - Ndb doesn't currently support table locks, and will do ordinary - startTransaction for each transaction/statement. + Start a transaction for running a statement if one is not + already running in a transaction. This will be the case in + a BEGIN; COMMIT; block + When using LOCK TABLE's external_lock will start a transaction + since ndb does not currently does not support table locking */ int ha_ndbcluster::start_stmt(THD *thd, thr_lock_type lock_type) @@ -3402,17 +3402,10 @@ int ha_ndbcluster::start_stmt(THD *thd, thr_lock_type lock_type) PRINT_OPTION_FLAGS(thd); Thd_ndb *thd_ndb= get_thd_ndb(thd); - NdbTransaction *trans= thd_ndb->stmt; + NdbTransaction *trans= (thd_ndb->stmt)?thd_ndb->stmt:thd_ndb->all; if (!trans){ Ndb *ndb= thd_ndb->ndb; DBUG_PRINT("trans",("Starting transaction stmt")); - -#if 0 - NdbTransaction *tablock_trans= thd_ndb->all; - DBUG_PRINT("info", ("tablock_trans: %x", (UintPtr)tablock_trans)); - DBUG_ASSERT(tablock_trans); -// trans= ndb->hupp(tablock_trans); -#endif trans= ndb->startTransaction(); if (trans == NULL) ERR_RETURN(ndb->getNdbError()); @@ -3709,6 +3702,7 @@ static int create_ndb_column(NDBCOL &col, col.setStripeSize(0); break; //mysql_type_blob: + case MYSQL_TYPE_GEOMETRY: case MYSQL_TYPE_BLOB: if ((field->flags & BINARY_FLAG) && cs == &my_charset_bin) col.setType(NDBCOL::Blob); @@ -3774,7 +3768,6 @@ static int create_ndb_column(NDBCOL &col, break; } case MYSQL_TYPE_NULL: - case MYSQL_TYPE_GEOMETRY: goto mysql_type_unsupported; mysql_type_unsupported: default: @@ -3806,7 +3799,8 @@ int ha_ndbcluster::create(const char *name, uint pack_length, length, i, pk_length= 0; const void *data, *pack_data; char name2[FN_HEADLEN]; - bool create_from_engine= (info->table_options & HA_CREATE_FROM_ENGINE); + bool create_from_engine= test(info->table_options & + HA_OPTION_CREATE_FROM_ENGINE); DBUG_ENTER("ha_ndbcluster::create"); DBUG_PRINT("enter", ("name: %s", name)); @@ -3876,6 +3870,7 @@ int ha_ndbcluster::create(const char *name, * 5 - from extra words added by tup/dict?? */ switch (form->field[i]->real_type()) { + case MYSQL_TYPE_GEOMETRY: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: @@ -4140,7 +4135,12 @@ ulonglong ha_ndbcluster::get_auto_increment() --retries && ndb->getNdbError().status == NdbError::TemporaryError); if (auto_value == NDB_FAILED_AUTO_INCREMENT) - ERR_RETURN(ndb->getNdbError()); + { + const NdbError err= ndb->getNdbError(); + sql_print_error("Error %lu in ::get_auto_increment(): %s", + (ulong) err.code, err.message); + DBUG_RETURN(~(ulonglong) 0); + } DBUG_RETURN((longlong)auto_value); } @@ -4161,6 +4161,7 @@ ha_ndbcluster::ha_ndbcluster(TABLE *table_arg): HA_AUTO_PART_KEY | HA_NO_PREFIX_CHAR_KEYS | HA_NEED_READ_RANGE_BUFFER | + HA_CAN_GEOMETRY | HA_CAN_BIT_FIELD), m_share(0), m_part_info(NULL), @@ -6008,7 +6009,6 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused))) { THD *thd; /* needs to be first for thread_stack */ Ndb* ndb; - int error= 0; struct timespec abstime; my_thread_init(); @@ -6037,9 +6037,9 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused))) { pthread_mutex_lock(&LOCK_ndb_util_thread); - error= pthread_cond_timedwait(&COND_ndb_util_thread, - &LOCK_ndb_util_thread, - &abstime); + pthread_cond_timedwait(&COND_ndb_util_thread, + &LOCK_ndb_util_thread, + &abstime); pthread_mutex_unlock(&LOCK_ndb_util_thread); DBUG_PRINT("ndb_util_thread", ("Started, ndb_cache_check_time: %d", diff --git a/sql/handler.cc b/sql/handler.cc index 5b07c9e9c24..1ec4ddf17b7 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -186,15 +186,18 @@ enum db_type ha_resolve_by_name(const char *name, uint namelen) THD *thd= current_thd; show_table_alias_st *table_alias; handlerton **types; - const char *ptr= name; - if (thd && !my_strcasecmp(&my_charset_latin1, ptr, "DEFAULT")) + if (thd && !my_strnncoll(&my_charset_latin1, + (const uchar *)name, namelen, + (const uchar *)"DEFAULT", 7)) return (enum db_type) thd->variables.table_type; retest: for (types= sys_table_types; *types; types++) { - if (!my_strcasecmp(&my_charset_latin1, ptr, (*types)->name)) + if (!my_strnncoll(&my_charset_latin1, + (const uchar *)name, namelen, + (const uchar *)(*types)->name, strlen((*types)->name))) return (enum db_type) (*types)->db_type; } @@ -203,15 +206,21 @@ retest: */ for (table_alias= sys_table_aliases; table_alias->type; table_alias++) { - if (!my_strcasecmp(&my_charset_latin1, ptr, table_alias->alias)) + if (!my_strnncoll(&my_charset_latin1, + (const uchar *)name, namelen, + (const uchar *)table_alias->alias, + strlen(table_alias->alias))) { - ptr= table_alias->type; + name= table_alias->type; + namelen= strlen(name); goto retest; } } return DB_TYPE_UNKNOWN; } + + const char *ha_get_storage_engine(enum db_type db_type) { handlerton **types; @@ -220,25 +229,19 @@ const char *ha_get_storage_engine(enum db_type db_type) if (db_type == (*types)->db_type) return (*types)->name; } - - return "none"; + return "*NONE*"; } + bool ha_check_storage_engine_flag(enum db_type db_type, uint32 flag) { handlerton **types; for (types= sys_table_types; *types; types++) { if (db_type == (*types)->db_type) - { - if ((*types)->flags & flag) - return TRUE; - else - return FALSE; - } + return test((*types)->flags & flag); } - - return FALSE; + return FALSE; // No matching engine } @@ -299,26 +302,26 @@ enum db_type ha_checktype(THD *thd, enum db_type database_type, } /* ha_checktype */ -handler *get_new_handler(TABLE *table, enum db_type db_type) +handler *get_new_handler(TABLE *table, MEM_ROOT *alloc, enum db_type db_type) { handler *file; switch (db_type) { #ifndef NO_HASH case DB_TYPE_HASH: - file= new ha_hash(table); + file= new (alloc) ha_hash(table); break; #endif case DB_TYPE_MRG_ISAM: - file= new ha_myisammrg(table); + file= new (alloc) ha_myisammrg(table); break; #ifdef HAVE_BERKELEY_DB case DB_TYPE_BERKELEY_DB: - file= new ha_berkeley(table); + file= new (alloc) ha_berkeley(table); break; #endif #ifdef HAVE_INNOBASE_DB case DB_TYPE_INNODB: - file= new ha_innobase(table); + file= new (alloc) ha_innobase(table); break; #endif #ifdef HAVE_EXAMPLE_DB @@ -329,51 +332,51 @@ handler *get_new_handler(TABLE *table, enum db_type db_type) #ifdef HAVE_PARTITION_DB case DB_TYPE_PARTITION_DB: { - file= new ha_partition(table); + file= new (alloc) ha_partition(table); break; } #endif #ifdef HAVE_ARCHIVE_DB case DB_TYPE_ARCHIVE_DB: - file= new ha_archive(table); + file= new (alloc) ha_archive(table); break; #endif #ifdef HAVE_BLACKHOLE_DB case DB_TYPE_BLACKHOLE_DB: - file= new ha_blackhole(table); + file= new (alloc) ha_blackhole(table); break; #endif #ifdef HAVE_FEDERATED_DB case DB_TYPE_FEDERATED_DB: - file= new ha_federated(table); + file= new (alloc) ha_federated(table); break; #endif #ifdef HAVE_CSV_DB case DB_TYPE_CSV_DB: - file= new ha_tina(table); + file= new (alloc) ha_tina(table); break; #endif #ifdef HAVE_NDBCLUSTER_DB case DB_TYPE_NDBCLUSTER: - file= new ha_ndbcluster(table); + file= new (alloc) ha_ndbcluster(table); break; #endif case DB_TYPE_HEAP: - file= new ha_heap(table); + file= new (alloc) ha_heap(table); break; default: // should never happen { enum db_type def=(enum db_type) current_thd->variables.table_type; /* Try first with 'default table type' */ if (db_type != def) - return get_new_handler(table, def); + return get_new_handler(table, alloc, def); } /* Fall back to MyISAM */ case DB_TYPE_MYISAM: - file= new ha_myisam(table); + file= new (alloc) ha_myisam(table); break; case DB_TYPE_MRG_MYISAM: - file= new ha_myisammrg(table); + file= new (alloc) ha_myisammrg(table); break; } if (file) @@ -383,8 +386,6 @@ handler *get_new_handler(TABLE *table, enum db_type db_type) delete file; file=0; } - } - return file; } @@ -912,18 +913,25 @@ int ha_autocommit_or_rollback(THD *thd, int error) DBUG_RETURN(error); } + int ha_commit_or_rollback_by_xid(XID *xid, bool commit) { handlerton **types; int res= 1; for (types= sys_table_types; *types; types++) + { if ((*types)->state == SHOW_OPTION_YES && (*types)->recover) - res= res && - (*(commit ? (*types)->commit_by_xid : (*types)->rollback_by_xid))(xid); + { + if ((*(commit ? (*types)->commit_by_xid : + (*types)->rollback_by_xid))(xid)); + res= 0; + } + } return res; } + #ifndef DBUG_OFF /* this does not need to be multi-byte safe or anything */ static char* xid_to_str(char *buf, XID *xid) @@ -1352,7 +1360,7 @@ int ha_delete_table(THD *thd, enum db_type table_type, const char *path, /* DB_TYPE_UNKNOWN is used in ALTER TABLE when renaming only .frm files */ if (table_type == DB_TYPE_UNKNOWN || - ! (file=get_new_handler(&dummy_table, table_type))) + ! (file=get_new_handler(&dummy_table, thd->mem_root, table_type))) DBUG_RETURN(ENOENT); if (lower_case_table_names == 2 && !(file->table_flags() & HA_FILE_BASED)) @@ -2173,7 +2181,7 @@ int ha_create_table_from_engine(THD* thd, DBUG_RETURN(3); update_create_info_from_table(&create_info, &table); - create_info.table_options|= HA_CREATE_FROM_ENGINE; + create_info.table_options|= HA_OPTION_CREATE_FROM_ENGINE; if (lower_case_table_names == 2 && !(table.file->table_flags() & HA_FILE_BASED)) @@ -2626,6 +2634,7 @@ int handler::index_read_idx(byte * buf, uint index, const byte * key, TYPELIB *ha_known_exts(void) { + MEM_ROOT *mem_root= current_thd->mem_root; if (!known_extensions.type_names || mysys_usage_id != known_extensions_id) { handlerton **types; @@ -2640,7 +2649,8 @@ TYPELIB *ha_known_exts(void) { if ((*types)->state == SHOW_OPTION_YES) { - handler *file= get_new_handler(0,(enum db_type) (*types)->db_type); + handler *file= get_new_handler(0, mem_root, + (enum db_type) (*types)->db_type); for (ext= file->bas_ext(); *ext; ext++) { while ((old_ext= it++)) diff --git a/sql/handler.h b/sql/handler.h index 2347c03ae2d..8d749c5fc33 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -664,6 +664,7 @@ typedef struct st_ha_create_information uint options; /* OR of HA_CREATE_ options */ uint raid_type,raid_chunks; uint merge_insert_method; + uint extra_size; /* length of extra data segment */ bool table_existed; /* 1 in create if table existed */ bool frm_only; /* 1 if no ha_create_table() */ bool varchar; /* 1 if table has a VARCHAR */ @@ -1389,7 +1390,7 @@ extern ulong total_ha, total_ha_2pc; /* lookups */ enum db_type ha_resolve_by_name(const char *name, uint namelen); const char *ha_get_storage_engine(enum db_type db_type); -handler *get_new_handler(TABLE *table, enum db_type db_type); +handler *get_new_handler(TABLE *table, MEM_ROOT *alloc, enum db_type db_type); enum db_type ha_checktype(THD *thd, enum db_type database_type, bool no_substitute, bool report_error); bool ha_check_storage_engine_flag(enum db_type db_type, uint32 flag); diff --git a/sql/item.cc b/sql/item.cc index 5caac05f6ca..9a64b02f80b 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -696,6 +696,32 @@ Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs) } +Item *Item_param::safe_charset_converter(CHARSET_INFO *tocs) +{ + if (const_item()) + { + Item_string *conv; + uint cnv_errors; + char buf[MAX_FIELD_WIDTH]; + String tmp(buf, sizeof(buf), &my_charset_bin); + String cstr, *ostr= val_str(&tmp); + /* + As safe_charset_converter is not executed for + a parameter bound to NULL, ostr should never be 0. + */ + cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &cnv_errors); + if (cnv_errors || !(conv= new Item_string(cstr.ptr(), cstr.length(), + cstr.charset(), + collation.derivation))) + return NULL; + conv->str_value.copy(); + conv->str_value.mark_as_const(); + return conv; + } + return NULL; +} + + Item *Item_static_string_func::safe_charset_converter(CHARSET_INFO *tocs) { Item_string *conv; @@ -729,7 +755,8 @@ bool Item_string::eq(const Item *item, bool binary_cmp) const { if (binary_cmp) return !stringcmp(&str_value, &item->str_value); - return !sortcmp(&str_value, &item->str_value, collation.collation); + return (collation.collation == item->collation.collation && + !sortcmp(&str_value, &item->str_value, collation.collation)); } return 0; } @@ -817,9 +844,12 @@ String *Item_splocal::val_str(String *sp) { DBUG_ASSERT(fixed); Item *it= this_item(); - String *ret= it->val_str(sp); + String *res= it->val_str(sp); null_value= it->null_value; + if (!res) + return NULL; + /* This way we mark returned value of val_str as const, so that various functions (e.g. CONCAT) won't try to @@ -836,12 +866,11 @@ String *Item_splocal::val_str(String *sp) Item_param class contain some more details on the topic. */ - if (!ret) - return NULL; - - str_value_ptr.set(ret->ptr(), ret->length(), - ret->charset()); - return &str_value_ptr; + if (res != &str_value) + str_value.set(res->ptr(), res->length(), res->charset()); + else + res->mark_as_const(); + return &str_value; } @@ -858,17 +887,13 @@ my_decimal *Item_splocal::val_decimal(my_decimal *decimal_value) bool Item_splocal::is_null() { Item *it= this_item(); - bool ret= it->is_null(); - null_value= it->null_value; - return ret; + return it->is_null(); } Item * Item_splocal::this_item() { - THD *thd= current_thd; - return thd->spcont->get_item(m_offset); } @@ -882,25 +907,23 @@ Item_splocal::this_item_addr(THD *thd, Item **addr) Item * Item_splocal::this_const_item() const { - THD *thd= current_thd; - return thd->spcont->get_item(m_offset); } Item::Type Item_splocal::type() const { - THD *thd= current_thd; - - if (thd->spcont) + if (thd && thd->spcont) return thd->spcont->get_item(m_offset)->type(); return NULL_ITEM; // Anything but SUBSELECT_ITEM } -bool Item_splocal::fix_fields(THD *, Item **) +bool Item_splocal::fix_fields(THD *thd_arg, Item **ref) { - Item *it= this_item(); + Item *it; + thd= thd_arg; // Must be set before this_item() + it= this_item(); DBUG_ASSERT(it->fixed); max_length= it->max_length; decimals= it->decimals; @@ -928,6 +951,7 @@ void Item_splocal::print(String *str) /***************************************************************************** Item_name_const methods *****************************************************************************/ + double Item_name_const::val_real() { DBUG_ASSERT(fixed); @@ -966,9 +990,7 @@ my_decimal *Item_name_const::val_decimal(my_decimal *decimal_value) bool Item_name_const::is_null() { - bool ret= value_item->is_null(); - null_value= value_item->null_value; - return ret; + return value_item->is_null(); } Item::Type Item_name_const::type() const @@ -977,7 +999,7 @@ Item::Type Item_name_const::type() const } -bool Item_name_const::fix_fields(THD *thd, Item **) +bool Item_name_const::fix_fields(THD *thd, Item **ref) { char buf[128]; String *item_name; @@ -1356,7 +1378,7 @@ bool agg_item_charsets(DTCollation &coll, const char *fname, been created in prepare. In this case register the change for rollback. */ - if (arena) + if (arena && arena->is_conventional()) *arg= conv; else thd->change_item_tree(arg, conv); @@ -2783,7 +2805,7 @@ int Item_copy_string::save_in_field(Field *field, bool no_conversions) */ /* ARGSUSED */ -bool Item::fix_fields(THD *thd, Item ** ref) +bool Item::fix_fields(THD *thd, Item **ref) { // We do not check fields which are fixed during construction @@ -3213,11 +3235,26 @@ bool Item_field::fix_fields(THD *thd, Item **reference) context->last_name_resolution_table, reference, IGNORE_EXCEPT_NON_UNIQUE, - !any_privileges && - context->check_privileges, + !any_privileges, TRUE)) == not_found_field) { + + /* Look up in current select's item_list to find aliased fields */ + if (thd->lex->current_select->is_item_list_lookup) + { + uint counter; + bool not_used; + Item** res= find_item_in_list(this, thd->lex->current_select->item_list, + &counter, REPORT_EXCEPT_NOT_FOUND, + ¬_used); + if (res != (Item **)not_found_item && (*res)->type() == Item::FIELD_ITEM) + { + set_field((*((Item_field**)res))->field); + return 0; + } + } + /* If there are outer contexts (outer selects, but current select is not derived table or view) try to resolve this reference in the @@ -3260,9 +3297,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) last_name_resolution_table, reference, IGNORE_EXCEPT_NON_UNIQUE, - outer_context-> - check_privileges, - TRUE)) != + TRUE, TRUE)) != not_found_field) { if (from_field) @@ -3337,7 +3372,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) context->last_name_resolution_table, reference, REPORT_ALL_ERRORS, !any_privileges && - context->check_privileges, TRUE); + TRUE, TRUE); } goto error; } @@ -3412,7 +3447,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) We can leave expression substituted from view for next PS/SP rexecution (i.e. do not register this substitution for reverting on cleupup() (register_item_tree_changing())), because this subtree will be - fix_field'ed during setup_tables()->setup_ancestor() (i.e. before + fix_field'ed during setup_tables()->setup_underlying() (i.e. before all other expressions of query, and references on tables which do not present in query will not make problems. @@ -4512,8 +4547,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) last_name_resolution_table, reference, IGNORE_EXCEPT_NON_UNIQUE, - outer_context->check_privileges, - TRUE); + TRUE, TRUE); if (! from_field) goto error; if (from_field == view_ref_found) @@ -4638,7 +4672,7 @@ void Item_ref::cleanup() void Item_ref::print(String *str) { - if (ref && *ref) + if (ref) (*ref)->print(str); else Item_ident::print(str); @@ -4770,8 +4804,7 @@ String *Item_ref::val_str(String* tmp) bool Item_ref::is_null() { DBUG_ASSERT(fixed); - (void) (*ref)->val_int_result(); - return (*ref)->null_value; + return (*ref)->is_null(); } @@ -4825,7 +4858,7 @@ void Item_ref::make_field(Send_field *field) void Item_ref_null_helper::print(String *str) { str->append("<ref_null_helper>(", 18); - if (ref && *ref) + if (ref) (*ref)->print(str); else str->append('?'); @@ -5053,8 +5086,17 @@ bool Item_insert_value::eq(const Item *item, bool binary_cmp) const bool Item_insert_value::fix_fields(THD *thd, Item **items) { DBUG_ASSERT(fixed == 0); - if (!arg->fixed && arg->fix_fields(thd, &arg)) - return TRUE; + /* We should only check that arg is in first table */ + if (!arg->fixed) + { + bool res; + st_table_list *orig_next_table= context->last_name_resolution_table; + context->last_name_resolution_table= context->first_name_resolution_table; + res= arg->fix_fields(thd, &arg); + context->last_name_resolution_table= orig_next_table; + if (res) + return TRUE; + } if (arg->type() == REF_ITEM) { @@ -5066,6 +5108,7 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items) arg= ref->ref[0]; } Item_field *field_arg= (Item_field *)arg; + if (field_arg->field->table->insert_values) { Field *def_field= (Field*) sql_alloc(field_arg->field->size_of()); @@ -5126,7 +5169,7 @@ void Item_trigger_field::setup_field(THD *thd, TABLE *table) set field_idx properly. */ (void)find_field_in_table(thd, table, field_name, (uint) strlen(field_name), - 0, 0, &field_idx); + 0, 0, &field_idx, 0); thd->set_query_id= save_set_query_id; triggers= table->triggers; } @@ -5241,32 +5284,21 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) } case ROW_RESULT: { + Item_row *item_row= (Item_row*) item; + Item_row *comp_item_row= (Item_row*) comp_item; + uint col; new_item= 0; /* If item and comp_item are both Item_rows and have same number of cols - then process items in Item_row one by one. If Item_row contain nulls - substitute it by Item_null. Otherwise just return. + then process items in Item_row one by one. + We can't ignore NULL values here as this item may be used with <=>, in + which case NULL's are significant. */ - if (item->result_type() == comp_item->result_type() && - ((Item_row*)item)->cols() == ((Item_row*)comp_item)->cols()) - { - Item_row *item_row= (Item_row*)item,*comp_item_row= (Item_row*)comp_item; - if (item_row->null_inside()) - new_item= (Item*) new Item_null(name); - else - { - int i= item_row->cols() - 1; - for (; i >= 0; i--) - { - if (item_row->maybe_null && item_row->el(i)->is_null()) - { - new_item= (Item*) new Item_null(name); - break; - } - resolve_const_item(thd, item_row->addr(i), comp_item_row->el(i)); - } - } - } + DBUG_ASSERT(item->result_type() == comp_item->result_type()); + DBUG_ASSERT(item_row->cols() == comp_item_row->cols()); + col= item_row->cols(); + while (col-- > 0) + resolve_const_item(thd, item_row->addr(col), comp_item_row->el(col)); break; } case REAL_RESULT: @@ -5721,6 +5753,8 @@ enum_field_types Item_type_holder::get_real_type(Item *item) bool Item_type_holder::join_types(THD *thd, Item *item) { + uint max_length_orig= max_length; + uint decimals_orig= decimals; DBUG_ENTER("Item_type_holder::join_types"); DBUG_PRINT("info:", ("was type %d len %d, dec %d name %s", fld_type, max_length, decimals, @@ -5747,7 +5781,10 @@ bool Item_type_holder::join_types(THD *thd, Item *item) } else max_length= max(max_length, display_length(item)); - if (Field::result_merge_type(fld_type) == STRING_RESULT) + + switch (Field::result_merge_type(fld_type)) + { + case STRING_RESULT: { const char *old_cs, *old_derivation; old_cs= collation.collation->name; @@ -5761,7 +5798,23 @@ bool Item_type_holder::join_types(THD *thd, Item *item) "UNION"); DBUG_RETURN(TRUE); } + break; + } + case REAL_RESULT: + { + if (decimals != NOT_FIXED_DEC) + { + int delta1= max_length_orig - decimals_orig; + int delta2= item->max_length - item->decimals; + max_length= min(max(delta1, delta2) + decimals, + (fld_type == MYSQL_TYPE_FLOAT) ? FLT_DIG+6 : DBL_DIG+7); + } + else + max_length= (fld_type == MYSQL_TYPE_FLOAT) ? FLT_DIG+6 : DBL_DIG+7; + break; } + default:; + }; maybe_null|= item->maybe_null; get_full_info(item); diff --git a/sql/item.h b/sql/item.h index a8f013f60d4..8bc659c3060 100644 --- a/sql/item.h +++ b/sql/item.h @@ -294,15 +294,15 @@ struct Name_resolution_context: Sql_alloc bool resolve_in_select_list; /* - When FALSE we do not check columns right of resolving items, used to - prevent rights check on underlying tables of view + Security context of this name resolution context. It's used for views + and is non-zero only if the view is defined with SQL SECURITY DEFINER. */ - bool check_privileges; + Security_context *security_ctx; Name_resolution_context() :outer_context(0), table_list(0), select_lex(0), error_processor_data(0), - check_privileges(TRUE) + security_ctx(0) {} void init() @@ -526,6 +526,7 @@ public: double val_real_from_decimal(); virtual Field *get_tmp_table_field() { return 0; } + /* This is also used to create fields in CREATE ... SELECT: */ virtual Field *tmp_table_field(TABLE *t_arg) { return 0; } virtual const char *full_name() const { return name ? name : "???"; } @@ -721,13 +722,7 @@ class Item_splocal : public Item public: LEX_STRING m_name; - - /* - Buffer, pointing to the string value of the item. We need it to - protect internal buffer from changes. See comment to analogous - member in Item_param for more details. - */ - String str_value_ptr; + THD *thd; /* Position of this reference to SP variable in the statement (the @@ -739,10 +734,10 @@ public: Value of 0 means that this object doesn't corresponding to reference to SP variable in query text. */ - int pos_in_query; + uint pos_in_query; - Item_splocal(LEX_STRING name, uint offset, int pos_in_q=0) - : m_offset(offset), m_name(name), pos_in_query(pos_in_q) + Item_splocal(LEX_STRING name, uint offset, uint pos_in_q=0) + : m_offset(offset), m_name(name), thd(0), pos_in_query(pos_in_q) { maybe_null= TRUE; } @@ -1200,6 +1195,7 @@ public: constant, assert otherwise. This method is called only if basic_const_item returned TRUE. */ + Item *safe_charset_converter(CHARSET_INFO *tocs); Item *new_item(); /* Implement by-value equality evaluation if parameter value @@ -1337,6 +1333,14 @@ public: longlong val_int() { DBUG_ASSERT(fixed == 1); + if (value <= (double) LONGLONG_MIN) + { + return LONGLONG_MIN; + } + else if (value >= (double) (ulonglong) LONGLONG_MAX) + { + return LONGLONG_MAX; + } return (longlong) (value+(value > 0 ? 0.5 : -0.5)); } String *val_str(String*); @@ -1622,7 +1626,7 @@ public: } Item *real_item() { - return (ref && *ref) ? (*ref)->real_item() : this; + return ref ? (*ref)->real_item() : this; } bool walk(Item_processor processor, byte *arg) { return (*ref)->walk(processor, arg); } @@ -1746,6 +1750,7 @@ public: return ref->save_in_field(field, no_conversions); } Item *new_item(); + virtual Item *real_item() { return ref; } }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 138ebaaf9a5..06cb83a7101 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -186,13 +186,18 @@ static bool convert_constant_item(THD *thd, Field *field, Item **item) { if ((*item)->const_item()) { + /* For comparison purposes allow invalid dates like 2000-01-32 */ + ulong orig_sql_mode= field->table->in_use->variables.sql_mode; + field->table->in_use->variables.sql_mode|= MODE_INVALID_DATES; if (!(*item)->save_in_field(field, 1) && !((*item)->null_value)) { Item *tmp=new Item_int_with_ref(field->val_int(), *item); + field->table->in_use->variables.sql_mode= orig_sql_mode; if (tmp) thd->change_item_tree(item, tmp); return 1; // Item was replaced } + field->table->in_use->variables.sql_mode= orig_sql_mode; } return 0; } @@ -2958,6 +2963,15 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) String *escape_str= escape_item->val_str(&tmp_value1); if (escape_str) { + if (escape_used_in_parsing && ( + (((thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) && + escape_str->numchars() != 1) || + escape_str->numchars() > 1))) + { + my_error(ER_WRONG_ARGUMENTS,MYF(0),"ESCAPE"); + return TRUE; + } + if (use_mb(cmp.cmp_collation.collation)) { CHARSET_INFO *cs= escape_str->charset(); diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index aa50593abf4..bfd32223d4c 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -985,13 +985,16 @@ class Item_func_like :public Item_bool_func2 enum { alphabet_size = 256 }; Item *escape_item; + + bool escape_used_in_parsing; public: int escape; - Item_func_like(Item *a,Item *b, Item *escape_arg) + Item_func_like(Item *a,Item *b, Item *escape_arg, bool escape_used) :Item_bool_func2(a,b), canDoTurboBM(FALSE), pattern(0), pattern_len(0), - bmGs(0), bmBc(0), escape_item(escape_arg) {} + bmGs(0), bmBc(0), escape_item(escape_arg), + escape_used_in_parsing(escape_used) {} longlong val_int(); enum Functype functype() const { return LIKE_FUNC; } optimize_type select_optimize() const; diff --git a/sql/item_func.cc b/sql/item_func.cc index f07460f2a05..f467981540b 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -32,6 +32,11 @@ #include "sp_rcontext.h" #include "sp.h" +#ifdef NO_EMBEDDED_ACCESS_CHECKS +#define sp_restore_security_context(A,B) while (0) {} +#endif + + bool check_reserved_words(LEX_STRING *name) { if (!my_strcasecmp(system_charset_info, name->str, "GLOBAL") || @@ -967,8 +972,8 @@ my_decimal *Item_func_plus::decimal_op(my_decimal *decimal_value) return 0; val2= args[1]->val_decimal(&value2); if (!(null_value= (args[1]->null_value || - my_decimal_add(E_DEC_FATAL_ERROR, decimal_value, val1, - val2) > 1))) + (my_decimal_add(E_DEC_FATAL_ERROR, decimal_value, val1, + val2) > 3)))) return decimal_value; return 0; } @@ -1040,8 +1045,8 @@ my_decimal *Item_func_minus::decimal_op(my_decimal *decimal_value) return 0; val2= args[1]->val_decimal(&value2); if (!(null_value= (args[1]->null_value || - my_decimal_sub(E_DEC_FATAL_ERROR, decimal_value, val1, - val2) > 1))) + (my_decimal_sub(E_DEC_FATAL_ERROR, decimal_value, val1, + val2) > 3)))) return decimal_value; return 0; } @@ -1078,8 +1083,8 @@ my_decimal *Item_func_mul::decimal_op(my_decimal *decimal_value) return 0; val2= args[1]->val_decimal(&value2); if (!(null_value= (args[1]->null_value || - my_decimal_mul(E_DEC_FATAL_ERROR, decimal_value, val1, - val2) > 1))) + (my_decimal_mul(E_DEC_FATAL_ERROR, decimal_value, val1, + val2) > 3)))) return decimal_value; return 0; } @@ -1119,6 +1124,7 @@ my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value) { my_decimal value1, *val1; my_decimal value2, *val2; + int err; val1= args[0]->val_decimal(&value1); if ((null_value= args[0]->null_value)) @@ -1126,17 +1132,15 @@ my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value) val2= args[1]->val_decimal(&value2); if ((null_value= args[1]->null_value)) return 0; - switch (my_decimal_div(E_DEC_FATAL_ERROR & ~E_DEC_DIV_ZERO, decimal_value, - val1, val2, prec_increment)) { - case E_DEC_TRUNCATED: - case E_DEC_OK: - return decimal_value; - case E_DEC_DIV_ZERO: - signal_divide_by_null(); - default: - null_value= 1; // Safety + if ((err= my_decimal_div(E_DEC_FATAL_ERROR & ~E_DEC_DIV_ZERO, decimal_value, + val1, val2, prec_increment)) > 3) + { + if (err == E_DEC_DIV_ZERO) + signal_divide_by_null(); + null_value= 1; return 0; } + return decimal_value; } @@ -1381,8 +1385,13 @@ double Item_func_ln::val_real() { DBUG_ASSERT(fixed == 1); double value= args[0]->val_real(); - if ((null_value=(args[0]->null_value || value <= 0.0))) + if ((null_value= args[0]->null_value)) + return 0.0; + if (value <= 0.0) + { + signal_divide_by_null(); return 0.0; + } return log(value); } @@ -1395,13 +1404,23 @@ double Item_func_log::val_real() { DBUG_ASSERT(fixed == 1); double value= args[0]->val_real(); - if ((null_value=(args[0]->null_value || value <= 0.0))) + if ((null_value= args[0]->null_value)) return 0.0; + if (value <= 0.0) + { + signal_divide_by_null(); + return 0.0; + } if (arg_count == 2) { double value2= args[1]->val_real(); - if ((null_value=(args[1]->null_value || value2 <= 0.0 || value == 1.0))) + if ((null_value= args[1]->null_value)) + return 0.0; + if (value2 <= 0.0 || value == 1.0) + { + signal_divide_by_null(); return 0.0; + } return log(value2) / log(value); } return log(value); @@ -1411,8 +1430,14 @@ double Item_func_log2::val_real() { DBUG_ASSERT(fixed == 1); double value= args[0]->val_real(); - if ((null_value=(args[0]->null_value || value <= 0.0))) + + if ((null_value=args[0]->null_value)) + return 0.0; + if (value <= 0.0) + { + signal_divide_by_null(); return 0.0; + } return log(value) / M_LN2; } @@ -1420,8 +1445,13 @@ double Item_func_log10::val_real() { DBUG_ASSERT(fixed == 1); double value= args[0]->val_real(); - if ((null_value=(args[0]->null_value || value <= 0.0))) - return 0.0; /* purecov: inspected */ + if ((null_value= args[0]->null_value)) + return 0.0; + if (value <= 0.0) + { + signal_divide_by_null(); + return 0.0; + } return log10(value); } @@ -2015,7 +2045,6 @@ String *Item_func_min_max::val_str(String *str) { String *res; LINT_INIT(res); - null_value= 0; for (uint i=0; i < arg_count ; i++) { if (i == 0) @@ -2030,14 +2059,11 @@ String *Item_func_min_max::val_str(String *str) if ((cmp_sign < 0 ? cmp : -cmp) < 0) res=res2; } - else - res= 0; } if ((null_value= args[i]->null_value)) - break; + return 0; } - if (res) // If !NULL - res->set_charset(collation.collation); + res->set_charset(collation.collation); return res; } case ROW_RESULT: @@ -2054,7 +2080,6 @@ double Item_func_min_max::val_real() { DBUG_ASSERT(fixed == 1); double value=0.0; - null_value= 0; for (uint i=0; i < arg_count ; i++) { if (i == 0) @@ -2076,7 +2101,6 @@ longlong Item_func_min_max::val_int() { DBUG_ASSERT(fixed == 1); longlong value=0; - null_value= 0; for (uint i=0; i < arg_count ; i++) { if (i == 0) @@ -2097,21 +2121,21 @@ longlong Item_func_min_max::val_int() my_decimal *Item_func_min_max::val_decimal(my_decimal *dec) { DBUG_ASSERT(fixed == 1); - my_decimal tmp_buf, *tmp, *res= NULL; - null_value= 0; + my_decimal tmp_buf, *tmp, *res; + LINT_INIT(res); + for (uint i=0; i < arg_count ; i++) { if (i == 0) res= args[i]->val_decimal(dec); else { - tmp= args[i]->val_decimal(&tmp_buf); - if (args[i]->null_value) - res= 0; - else if ((my_decimal_cmp(tmp, res) * cmp_sign) < 0) + tmp= args[i]->val_decimal(&tmp_buf); // Zero if NULL + if (tmp && (my_decimal_cmp(tmp, res) * cmp_sign) < 0) { if (tmp == &tmp_buf) { + /* Move value out of tmp_buf as this will be reused on next loop */ my_decimal2decimal(tmp, dec); res= dec; } @@ -2120,7 +2144,10 @@ my_decimal *Item_func_min_max::val_decimal(my_decimal *dec) } } if ((null_value= args[i]->null_value)) + { + res= 0; break; + } } return res; } @@ -2349,7 +2376,7 @@ void Item_func_find_in_set::fix_length_and_dec() } } } - agg_arg_collations_for_comparison(cmp_collation, args, 2); + agg_arg_charsets(cmp_collation, args, 2, MY_COLL_CMP_CONV); } static const char separator=','; @@ -3031,9 +3058,13 @@ void debug_sync_point(const char* lock_name, uint lock_timeout) thd->mysys_var->current_cond= &ull->cond; set_timespec(abstime,lock_timeout); - while (!thd->killed && - pthread_cond_timedwait(&ull->cond, &LOCK_user_locks, - &abstime) != ETIMEDOUT && ull->locked) ; + while (ull->locked && !thd->killed) + { + int error= pthread_cond_timedwait(&ull->cond, &LOCK_user_locks, &abstime); + if (error == ETIMEDOUT || error == ETIME) + break; + } + if (ull->locked) { if (!--ull->count) @@ -3077,7 +3108,7 @@ longlong Item_func_get_lock::val_int() struct timespec abstime; THD *thd=current_thd; User_level_lock *ull; - int error=0; + int error; /* In slave thread no need to get locks, everything is serialized. Anyway @@ -3133,22 +3164,29 @@ longlong Item_func_get_lock::val_int() thd->mysys_var->current_cond= &ull->cond; set_timespec(abstime,timeout); - while (!thd->killed && - (error=pthread_cond_timedwait(&ull->cond,&LOCK_user_locks,&abstime)) - != ETIMEDOUT && error != EINVAL && ull->locked) ; - if (thd->killed) - error=EINTR; // Return NULL + error= 0; + while (ull->locked && !thd->killed) + { + error= pthread_cond_timedwait(&ull->cond,&LOCK_user_locks,&abstime); + if (error == ETIMEDOUT || error == ETIME) + break; + error= 0; + } + if (ull->locked) { if (!--ull->count) + { + DBUG_ASSERT(0); delete ull; // Should never happen - if (error != ETIMEDOUT) + } + if (!error) // Killed (thd->killed != 0) { error=1; null_value=1; // Return NULL } } - else + else // We got the lock { ull->locked=1; ull->thread=thd->real_id; @@ -3270,6 +3308,7 @@ void Item_func_benchmark::print(String *str) str->append(')'); } + /* This function is just used to create tests with time gaps */ longlong Item_func_sleep::val_int() @@ -3290,10 +3329,14 @@ longlong Item_func_sleep::val_int() thd->mysys_var->current_mutex= &LOCK_user_locks; thd->mysys_var->current_cond= &cond; - while (!thd->killed && - (error= pthread_cond_timedwait(&cond, &LOCK_user_locks, - &abstime)) != ETIMEDOUT && - error != EINVAL) ; + error= 0; + while (!thd->killed) + { + error= pthread_cond_timedwait(&cond, &LOCK_user_locks, &abstime); + if (error == ETIMEDOUT || error == ETIME) + break; + error= 0; + } pthread_mutex_lock(&thd->mysys_var->mutex); thd->mysys_var->current_mutex= 0; @@ -3303,7 +3346,7 @@ longlong Item_func_sleep::val_int() pthread_mutex_unlock(&LOCK_user_locks); pthread_cond_destroy(&cond); - return (error == ETIMEDOUT) ? 0 : 1; + return test(!error); // Return 1 killed } @@ -4485,7 +4528,6 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, LEX_STRING component) { sys_var *var; - char buff[MAX_SYS_VAR_LENGTH*2+4+8], *pos; LEX_STRING *base_name, *component_name; if (component.str == 0 && @@ -4689,6 +4731,7 @@ Item_func_sp::execute(Field **flp) if (execute(&it)) { null_value= 1; + context->process_error(current_thd); return 1; } if (!(f= *flp)) @@ -4711,9 +4754,16 @@ Item_func_sp::execute(Item **itp) THD *thd= current_thd; int res= -1; Sub_statement_state statement_state; - Security_context *save_ctx; + Security_context *save_security_ctx= thd->security_ctx, *save_ctx_func; - if (find_and_check_access(thd, EXECUTE_ACL, &save_ctx)) +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (context->security_ctx) + { + /* Set view definer security context */ + thd->security_ctx= context->security_ctx; + } +#endif + if (find_and_check_access(thd, EXECUTE_ACL, &save_ctx_func)) goto error; /* @@ -4731,12 +4781,13 @@ Item_func_sp::execute(Item **itp) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_FAILED_ROUTINE_BREAK_BINLOG, ER(ER_FAILED_ROUTINE_BREAK_BINLOG)); - #ifndef NO_EMBEDDED_ACCESS_CHECKS - sp_restore_security_context(thd, save_ctx); -#endif - + sp_restore_security_context(thd, save_ctx_func); error: + thd->security_ctx= save_security_ctx; +#else +error: +#endif DBUG_RETURN(res); } @@ -4849,11 +4900,12 @@ Item_func_sp::tmp_table_field(TABLE *t_arg) find_and_check_access() thd thread handler want_access requested access - backup backup of security context or 0 + 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. @@ -4868,12 +4920,11 @@ Item_func_sp::tmp_table_field(TABLE *t_arg) bool Item_func_sp::find_and_check_access(THD *thd, ulong want_access, - Security_context **backup) + Security_context **save) { - bool res; - Security_context *local_save, - **save= (backup ? backup : &local_save); - res= TRUE; + bool res= TRUE; + + *save= 0; // Safety if error if (! m_sp && ! (m_sp= sp_find_function(thd, m_name, TRUE))) { my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str); @@ -4883,26 +4934,31 @@ Item_func_sp::find_and_check_access(THD *thd, ulong want_access, #ifndef NO_EMBEDDED_ACCESS_CHECKS if (check_routine_access(thd, want_access, 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)) { - goto error_check_ctx; + sp_restore_security_context(thd, *save); + *save= 0; // Safety + goto error; } - res= FALSE; -error_check_ctx: - if (*save && (res || !backup)) - sp_restore_security_context(thd, local_save); -error: -#else - res= 0; -error: #endif + res= FALSE; // no error + +error: return res; } @@ -4912,7 +4968,23 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) bool res; DBUG_ASSERT(fixed == 0); res= Item_func::fix_fields(thd, ref); - if (!res && find_and_check_access(thd, EXECUTE_ACL, NULL)) - res= 1; + if (!res && thd->lex->view_prepare_mode) + { + /* + 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 + */ + Security_context *save_ctx; + if (!(res= find_and_check_access(thd, EXECUTE_ACL, &save_ctx))) + sp_restore_security_context(thd, save_ctx); + } return res; } diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 4a6ceb4bf7d..2b92e72e728 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -81,8 +81,7 @@ String *Item_func_geometry_from_wkb::val_str(String *str) str->q_append(srid); if ((null_value= (args[0]->null_value || - !Geometry::create_from_wkb(&buffer, wkb->ptr(), wkb->length()) || - str->append(*wkb)))) + !Geometry::create_from_wkb(&buffer, wkb->ptr(), wkb->length(), str)))) return 0; return str; } @@ -99,8 +98,7 @@ String *Item_func_as_wkt::val_str(String *str) if ((null_value= (args[0]->null_value || - !(geom= Geometry::create_from_wkb(&buffer, swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE))))) + !(geom= Geometry::construct(&buffer, swkb->ptr(), swkb->length()))))) return 0; str->length(0); @@ -126,8 +124,7 @@ String *Item_func_as_wkb::val_str(String *str) if ((null_value= (args[0]->null_value || - !(Geometry::create_from_wkb(&buffer, swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE))))) + !(Geometry::construct(&buffer, swkb->ptr(), swkb->length()))))) return 0; str->copy(swkb->ptr() + SRID_SIZE, swkb->length() - SRID_SIZE, @@ -145,8 +142,7 @@ String *Item_func_geometry_type::val_str(String *str) if ((null_value= (args[0]->null_value || - !(geom= Geometry::create_from_wkb(&buffer, swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE))))) + !(geom= Geometry::construct(&buffer, swkb->ptr(), swkb->length()))))) return 0; /* String will not move */ str->copy(geom->get_class_info()->m_name.str, @@ -167,8 +163,7 @@ String *Item_func_envelope::val_str(String *str) if ((null_value= args[0]->null_value || - !(geom= Geometry::create_from_wkb(&buffer, swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE)))) + !(geom= Geometry::construct(&buffer, swkb->ptr(), swkb->length())))) return 0; srid= uint4korr(swkb->ptr()); @@ -191,8 +186,7 @@ String *Item_func_centroid::val_str(String *str) uint32 srid; if ((null_value= args[0]->null_value || - !(geom= Geometry::create_from_wkb(&buffer, swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE)))) + !(geom= Geometry::construct(&buffer, swkb->ptr(), swkb->length())))) return 0; str->set_charset(&my_charset_bin); @@ -221,8 +215,7 @@ String *Item_func_spatial_decomp::val_str(String *str) if ((null_value= (args[0]->null_value || - !(geom= Geometry::create_from_wkb(&buffer, swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE))))) + !(geom= Geometry::construct(&buffer, swkb->ptr(), swkb->length()))))) return 0; srid= uint4korr(swkb->ptr()); @@ -270,8 +263,7 @@ String *Item_func_spatial_decomp_n::val_str(String *str) if ((null_value= (args[0]->null_value || args[1]->null_value || - !(geom= Geometry::create_from_wkb(&buffer, swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE))))) + !(geom= Geometry::construct(&buffer, swkb->ptr(), swkb->length()))))) return 0; str->set_charset(&my_charset_bin); @@ -478,10 +470,8 @@ longlong Item_func_spatial_rel::val_int() if ((null_value= (args[0]->null_value || args[1]->null_value || - !(g1= Geometry::create_from_wkb(&buffer1, res1->ptr() + SRID_SIZE, - res1->length() - SRID_SIZE)) || - !(g2= Geometry::create_from_wkb(&buffer2, res2->ptr() + SRID_SIZE, - res2->length() - SRID_SIZE)) || + !(g1= Geometry::construct(&buffer1, res1->ptr(), res1->length())) || + !(g2= Geometry::construct(&buffer2, res2->ptr(), res2->length())) || g1->get_mbr(&mbr1, &dummy) || g2->get_mbr(&mbr2, &dummy)))) return 0; @@ -546,8 +536,7 @@ longlong Item_func_isclosed::val_int() null_value= (!swkb || args[0]->null_value || !(geom= - Geometry::create_from_wkb(&buffer, swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE)) || + Geometry::construct(&buffer, swkb->ptr(), swkb->length())) || geom->is_closed(&isclosed)); return (longlong) isclosed; @@ -569,9 +558,7 @@ longlong Item_func_dimension::val_int() null_value= (!swkb || args[0]->null_value || - !(geom= Geometry::create_from_wkb(&buffer, - swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE)) || + !(geom= Geometry::construct(&buffer, swkb->ptr(), swkb->length())) || geom->dimension(&dim, &dummy)); return (longlong) dim; } @@ -586,9 +573,8 @@ longlong Item_func_numinteriorring::val_int() Geometry *geom; null_value= (!swkb || - !(geom= Geometry::create_from_wkb(&buffer, - swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE)) || + !(geom= Geometry::construct(&buffer, + swkb->ptr(), swkb->length())) || geom->num_interior_ring(&num)); return (longlong) num; } @@ -603,9 +589,8 @@ longlong Item_func_numgeometries::val_int() Geometry *geom; null_value= (!swkb || - !(geom= Geometry::create_from_wkb(&buffer, - swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE)) || + !(geom= Geometry::construct(&buffer, + swkb->ptr(), swkb->length())) || geom->num_geometries(&num)); return (longlong) num; } @@ -621,9 +606,8 @@ longlong Item_func_numpoints::val_int() null_value= (!swkb || args[0]->null_value || - !(geom= Geometry::create_from_wkb(&buffer, - swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE)) || + !(geom= Geometry::construct(&buffer, + swkb->ptr(), swkb->length())) || geom->num_points(&num)); return (longlong) num; } @@ -638,9 +622,8 @@ double Item_func_x::val_real() Geometry *geom; null_value= (!swkb || - !(geom= Geometry::create_from_wkb(&buffer, - swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE)) || + !(geom= Geometry::construct(&buffer, + swkb->ptr(), swkb->length())) || geom->get_x(&res)); return res; } @@ -655,9 +638,8 @@ double Item_func_y::val_real() Geometry *geom; null_value= (!swkb || - !(geom= Geometry::create_from_wkb(&buffer, - swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE)) || + !(geom= Geometry::construct(&buffer, + swkb->ptr(), swkb->length())) || geom->get_y(&res)); return res; } @@ -673,9 +655,8 @@ double Item_func_area::val_real() const char *dummy; null_value= (!swkb || - !(geom= Geometry::create_from_wkb(&buffer, - swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE)) || + !(geom= Geometry::construct(&buffer, + swkb->ptr(), swkb->length())) || geom->area(&res, &dummy)); return res; } @@ -689,9 +670,8 @@ double Item_func_glength::val_real() Geometry *geom; null_value= (!swkb || - !(geom= Geometry::create_from_wkb(&buffer, - swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE)) || + !(geom= Geometry::construct(&buffer, + swkb->ptr(), swkb->length())) || geom->length(&res)); return res; } @@ -703,9 +683,8 @@ longlong Item_func_srid::val_int() Geometry_buffer buffer; null_value= (!swkb || - !Geometry::create_from_wkb(&buffer, - swkb->ptr() + SRID_SIZE, - swkb->length() - SRID_SIZE)); + !Geometry::construct(&buffer, + swkb->ptr(), swkb->length())); if (null_value) return 0; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 018afac3812..1e8fe2e695f 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1504,7 +1504,13 @@ String *Item_func_encrypt::val_str(String *str) salt_ptr= salt_str->c_ptr(); } pthread_mutex_lock(&LOCK_crypt); - char *tmp=crypt(res->c_ptr(),salt_ptr); + char *tmp= crypt(res->c_ptr(),salt_ptr); + if (!tmp) + { + pthread_mutex_unlock(&LOCK_crypt); + null_value= 1; + return 0; + } str->set(tmp,(uint) strlen(tmp),res->charset()); str->copy(); pthread_mutex_unlock(&LOCK_crypt); @@ -1964,21 +1970,16 @@ String *Item_func_char::val_str(String *str) int32 num=(int32) args[i]->val_int(); if (!args[i]->null_value) { -#ifdef USE_MB - if (use_mb(collation.collation)) - { - if (num&0xFF000000L) { - str->append((char)(num>>24)); - goto b2; - } else if (num&0xFF0000L) { -b2: str->append((char)(num>>16)); - goto b1; - } else if (num&0xFF00L) { -b1: str->append((char)(num>>8)); - } + if (num&0xFF000000L) { + str->append((char)(num>>24)); + goto b2; + } else if (num&0xFF0000L) { + b2: str->append((char)(num>>16)); + goto b1; + } else if (num&0xFF00L) { + b1: str->append((char)(num>>8)); } -#endif - str->append((char)num); + str->append((char) num); } } str->set_charset(collation.collation); @@ -1997,7 +1998,7 @@ b1: str->append((char)(num>>8)); enum MYSQL_ERROR::enum_warning_level level; uint diff= str->length() - wlen; set_if_smaller(diff, 3); - octet2hex(hexbuf, (const uchar*) str->ptr() + wlen, diff); + octet2hex(hexbuf, str->ptr() + wlen, diff); if (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)) { @@ -2436,6 +2437,7 @@ String *Item_func_collation::val_str(String *str) String *Item_func_hex::val_str(String *str) { + String *res; DBUG_ASSERT(fixed == 1); if (args[0]->result_type() != STRING_RESULT) { @@ -2464,24 +2466,16 @@ String *Item_func_hex::val_str(String *str) } /* Convert given string to a hex string, character by character */ - String *res= args[0]->val_str(str); - const char *from, *end; - char *to; - if (!res || tmp_value.alloc(res->length()*2)) + res= args[0]->val_str(str); + if (!res || tmp_value.alloc(res->length()*2+1)) { null_value=1; return 0; } null_value=0; tmp_value.length(res->length()*2); - for (from=res->ptr(), end=from+res->length(), to= (char*) tmp_value.ptr(); - from < end ; - from++, to+=2) - { - uint tmp=(uint) (uchar) *from; - to[0]=_dig_vec_upper[tmp >> 4]; - to[1]=_dig_vec_upper[tmp & 15]; - } + + octet2hex((char*) tmp_value.ptr(), res->ptr(), res->length()); return &tmp_value; } diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index fa098849a43..5889821293d 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -480,12 +480,15 @@ public: class Item_func_char :public Item_str_func { public: - Item_func_char(List<Item> &list) :Item_str_func(list) {} + Item_func_char(List<Item> &list) :Item_str_func(list) + { collation.set(&my_charset_bin); } + Item_func_char(List<Item> &list, CHARSET_INFO *cs) :Item_str_func(list) + { collation.set(cs); } String *val_str(String *); void fix_length_and_dec() { - collation.set(default_charset()); - maybe_null=0; max_length=arg_count; + maybe_null=0; + max_length=arg_count * collation.collation->mbmaxlen; } const char *func_name() const { return "char"; } }; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 0e30bbd8a96..a32b0d1dbbf 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1413,6 +1413,12 @@ void subselect_union_engine::cleanup() } +bool subselect_union_engine::is_executed() const +{ + return unit->executed; +} + + void subselect_uniquesubquery_engine::cleanup() { DBUG_ENTER("subselect_uniquesubquery_engine::cleanup"); diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 5b22930ae1f..f1c99f74498 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -110,6 +110,12 @@ public: return eng == 0; } /* + True if this subquery has been already evaluated. Implemented only for + single select and union subqueries only. + */ + bool is_evaluated() const; + + /* Used by max/min subquery to initialize value presence registration mechanism. Engine call this method before rexecution query. */ @@ -317,6 +323,7 @@ public: virtual void print(String *str)= 0; virtual bool change_result(Item_subselect *si, select_subselect *result)= 0; virtual bool no_tables()= 0; + virtual bool is_executed() const { return FALSE; } }; @@ -342,6 +349,7 @@ public: void print (String *str); bool change_result(Item_subselect *si, select_subselect *result); bool no_tables(); + bool is_executed() const { return executed; } }; @@ -363,6 +371,7 @@ public: void print (String *str); bool change_result(Item_subselect *si, select_subselect *result); bool no_tables(); + bool is_executed() const; }; @@ -411,3 +420,10 @@ public: int exec(); void print (String *str); }; + + +inline bool Item_subselect::is_evaluated() const +{ + return engine->is_executed(); +} + diff --git a/sql/item_sum.cc b/sql/item_sum.cc index b56d99cf245..b2eaf39d624 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -3173,6 +3173,10 @@ void Item_func_group_concat::print(String *str) if (i) str->append(','); (*order[i]->item)->print(str); + if (order[i]->asc) + str->append(" ASC"); + else + str->append(" DESC"); } } str->append(" separator \'", 12); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 7f94c19647e..2b0314bb287 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1610,6 +1610,7 @@ void Item_func_date_format::fix_length_and_dec() if (args[1]->type() == STRING_ITEM) { // Optimize the normal case fixed_length=1; + /* The result is a binary string (no reason to use collation->mbmaxlen This is becasue make_date_time() only returns binary strings @@ -1627,6 +1628,30 @@ void Item_func_date_format::fix_length_and_dec() } +bool Item_func_date_format::eq(const Item *item, bool binary_cmp) const +{ + Item_func_date_format *item_func; + if (item->type() != FUNC_ITEM) + return 0; + if (func_name() != ((Item_func*) item)->func_name()) + return 0; + if (this == item) + return 1; + item_func= (Item_func_date_format*) item; + if (!args[0]->eq(item_func->args[0], binary_cmp)) + return 0; + /* + We must compare format string case sensitive. + This needed because format modifiers with different case, + for example %m and %M, have different meaning. + */ + if (!args[1]->eq(item_func->args[1], 1)) + return 0; + return 1; +} + + + uint Item_func_date_format::format_length(const String *format) { uint size=0; @@ -2479,6 +2504,7 @@ void Item_func_add_time::fix_length_and_dec() enum_field_types arg0_field_type; decimals=0; max_length=MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; + maybe_null= 1; /* The field type for the result of an Item_func_add_time function is defined @@ -2723,16 +2749,16 @@ longlong Item_func_timestamp_diff::val_int() int_type == INTERVAL_QUARTER || int_type == INTERVAL_MONTH) { - uint year; - uint year_beg, year_end, month_beg, month_end; - uint diff_days= (uint) (seconds/86400L); - uint diff_years= 0; + uint year_beg, year_end, month_beg, month_end, day_beg, day_end; + uint years= 0; if (neg == -1) { year_beg= ltime2.year; year_end= ltime1.year; month_beg= ltime2.month; month_end= ltime1.month; + day_beg= ltime2.day; + day_end= ltime1.day; } else { @@ -2740,53 +2766,32 @@ longlong Item_func_timestamp_diff::val_int() year_end= ltime2.year; month_beg= ltime1.month; month_end= ltime2.month; - } - /* calc years*/ - for (year= year_beg;year < year_end; year++) - { - uint days=calc_days_in_year(year); - if (days > diff_days) - break; - diff_days-= days; - diff_years++; + day_beg= ltime1.day; + day_end= ltime2.day; } - /* calc months; Current year is in the 'year' variable */ - month_beg--; /* Change months to be 0-11 for easier calculation */ - month_end--; + /* calc years */ + years= year_end - year_beg; + if (month_end < month_beg || (month_end == month_beg && day_end < day_beg)) + years-= 1; - months= 12*diff_years; - while (month_beg != month_end) - { - uint m_days= (uint) days_in_month[month_beg]; - if (month_beg == 1) - { - /* This is only calculated once so there is no reason to cache it*/ - uint leap= (uint) ((year & 3) == 0 && (year%100 || - (year%400 == 0 && year))); - m_days+= leap; - } - if (m_days > diff_days) - break; - diff_days-= m_days; - months++; - if (month_beg++ == 11) /* if we wrap to next year */ - { - month_beg= 0; - year++; - } - } - if (neg == -1) - months= -months; + /* calc months */ + months= 12*years; + if (month_end < month_beg || (month_end == month_beg && day_end < day_beg)) + months+= 12 - (month_beg - month_end); + else + months+= (month_end - month_beg); + if (day_end < day_beg) + months-= 1; } switch (int_type) { case INTERVAL_YEAR: - return months/12; + return months/12*neg; case INTERVAL_QUARTER: - return months/3; + return months/3*neg; case INTERVAL_MONTH: - return months; + return months*neg; case INTERVAL_WEEK: return seconds/86400L/7L*neg; case INTERVAL_DAY: diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 8e15acbc1fc..b2352e728c5 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -537,6 +537,7 @@ public: { return is_time_format ? "time_format" : "date_format"; } void fix_length_and_dec(); uint format_length(const String *format); + bool eq(const Item *item, bool binary_cmp) const; }; @@ -844,6 +845,7 @@ public: { decimals=0; max_length=MAX_TIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; + maybe_null= 1; } Field *tmp_table_field(TABLE *t_arg) { diff --git a/sql/log.cc b/sql/log.cc index 0ab3e6f94ed..3341b0265eb 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -357,9 +357,10 @@ static int find_uniq_filename(char *name) MYSQL_LOG::MYSQL_LOG() :bytes_written(0), last_time(0), query_start(0), name(0), - file_id(1), open_count(1), log_type(LOG_CLOSED), write_error(0), inited(0), - need_start_event(1), prepared_xids(0), description_event_for_exec(0), - description_event_for_queue(0) + prepared_xids(0), log_type(LOG_CLOSED), file_id(1), open_count(1), + readers_count(0), reset_pending(FALSE), write_error(FALSE), inited(FALSE), + need_start_event(TRUE), + description_event_for_exec(0), description_event_for_queue(0) { /* We don't want to initialize LOCK_Log here as such initialization depends on @@ -385,7 +386,9 @@ void MYSQL_LOG::cleanup() delete description_event_for_exec; (void) pthread_mutex_destroy(&LOCK_log); (void) pthread_mutex_destroy(&LOCK_index); + (void) pthread_mutex_destroy(&LOCK_readers); (void) pthread_cond_destroy(&update_cond); + (void) pthread_cond_destroy(&reset_cond); } DBUG_VOID_RETURN; } @@ -430,7 +433,9 @@ void MYSQL_LOG::init_pthread_objects() inited= 1; (void) pthread_mutex_init(&LOCK_log,MY_MUTEX_INIT_SLOW); (void) pthread_mutex_init(&LOCK_index, MY_MUTEX_INIT_SLOW); + (void) pthread_mutex_init(&LOCK_readers, MY_MUTEX_INIT_SLOW); (void) pthread_cond_init(&update_cond, 0); + (void) pthread_cond_init(&reset_cond, 0); } const char *MYSQL_LOG::generate_name(const char *log_name, @@ -933,6 +938,13 @@ bool MYSQL_LOG::reset_logs(THD* thd) */ pthread_mutex_lock(&LOCK_log); pthread_mutex_lock(&LOCK_index); + + /* + we need one more lock to block attempts to open a log while + we are waiting untill all log files will be closed + */ + pthread_mutex_lock(&LOCK_readers); + /* The following mutex is needed to ensure that no threads call 'delete thd' as we would then risk missing a 'rollback' from this @@ -955,6 +967,19 @@ bool MYSQL_LOG::reset_logs(THD* thd) goto err; } + reset_pending= TRUE; + /* + send update signal just in case so that all reader threads waiting + for log update will leave wait condition + */ + signal_update(); + /* + if there are active readers wait until all of them will + release opened files + */ + while (readers_count) + pthread_cond_wait(&reset_cond, &LOCK_log); + for (;;) { my_delete(linfo.log_file_name, MYF(MY_WME)); @@ -973,7 +998,10 @@ bool MYSQL_LOG::reset_logs(THD* thd) my_free((gptr) save_name, MYF(0)); err: + reset_pending= FALSE; + (void) pthread_mutex_unlock(&LOCK_thread_count); + pthread_mutex_unlock(&LOCK_readers); pthread_mutex_unlock(&LOCK_index); pthread_mutex_unlock(&LOCK_log); DBUG_RETURN(error); @@ -1354,7 +1382,8 @@ void MYSQL_LOG::new_file(bool need_lock) to change base names at some point. */ THD *thd = current_thd; /* may be 0 if we are reacting to SIGHUP */ - Rotate_log_event r(thd,new_name+dirname_length(new_name)); + Rotate_log_event r(thd,new_name+dirname_length(new_name), + 0, LOG_EVENT_OFFSET, 0); r.write(&log_file); bytes_written += r.data_written; } @@ -1433,7 +1462,7 @@ bool MYSQL_LOG::appendv(const char* buf, uint len,...) DBUG_ASSERT(log_file.type == SEQ_READ_APPEND); - pthread_mutex_lock(&LOCK_log); + safe_mutex_assert_owner(&LOCK_log); do { if (my_b_append(&log_file,(byte*) buf,len)) @@ -1448,7 +1477,6 @@ bool MYSQL_LOG::appendv(const char* buf, uint len,...) new_file(0); err: - pthread_mutex_unlock(&LOCK_log); if (!error) signal_update(); DBUG_RETURN(error); @@ -2047,6 +2075,13 @@ void MYSQL_LOG::wait_for_update(THD* thd, bool is_slave) { const char *old_msg; DBUG_ENTER("wait_for_update"); + + if (reset_pending) + { + pthread_mutex_unlock(&LOCK_log); + DBUG_VOID_RETURN; + } + old_msg= thd->enter_cond(&update_cond, &LOCK_log, is_slave ? "Has read all relay log; waiting for the slave I/O " @@ -2297,6 +2332,32 @@ void MYSQL_LOG::signal_update() DBUG_VOID_RETURN; } +void MYSQL_LOG::readers_addref() +{ + /* + There is no necessity for reference counting on *nix, since it allows to + delete opened files, however it is more clean way to wait + untill all files will be closed on *nix as well. + */ + DBUG_ENTER("MYSQL_LOG::reader_addref"); + pthread_mutex_lock(&LOCK_log); + pthread_mutex_lock(&LOCK_readers); + readers_count++; + pthread_mutex_unlock(&LOCK_readers); + pthread_mutex_unlock(&LOCK_log); + DBUG_VOID_RETURN; +} + +void MYSQL_LOG::readers_release() +{ + DBUG_ENTER("MYSQL_LOG::reader_release"); + pthread_mutex_lock(&LOCK_log); + readers_count--; + if (!readers_count) + pthread_cond_broadcast(&reset_cond); + pthread_mutex_unlock(&LOCK_log); + DBUG_VOID_RETURN; +} #ifdef __NT__ void print_buffer_to_nt_eventlog(enum loglevel level, char *buff, diff --git a/sql/log_event.cc b/sql/log_event.cc index 7ee505939f0..081b498f01e 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -122,8 +122,9 @@ static char *pretty_print_str(char *packet, char *str, int len) static inline char* slave_load_file_stem(char*buf, uint file_id, int event_server_id) { - fn_format(buf,"SQL_LOAD-",slave_load_tmpdir, "", - MY_UNPACK_FILENAME | MY_UNIX_PATH); + fn_format(buf,"SQL_LOAD-",slave_load_tmpdir, "", MY_UNPACK_FILENAME); + to_unix_path(buf); + buf = strend(buf); buf = int10_to_str(::server_id, buf, 10); *buf++ = '-'; @@ -212,24 +213,18 @@ static inline int read_str(char **buf, char *buf_end, char **str, /* Transforms a string into "" or its expression in 0x... form. */ + char *str_to_hex(char *to, const char *from, uint len) { - char *p= to; if (len) { - p= strmov(p, "0x"); - for (uint i= 0; i < len; i++, p+= 2) - { - /* val[i] is char. Casting to uchar helps greatly if val[i] < 0 */ - uint tmp= (uint) (uchar) from[i]; - p[0]= _dig_vec_upper[tmp >> 4]; - p[1]= _dig_vec_upper[tmp & 15]; - } - *p= 0; + *to++= '0'; + *to++= 'x'; + to= octet2hex(to, from, len); } else - p= strmov(p, "\"\""); - return p; // pointer to end 0 of 'to' + to= strmov(to, "\"\""); + return to; // pointer to end 0 of 'to' } /* @@ -702,7 +697,6 @@ failed my_b_read")); */ DBUG_RETURN(0); } - uint data_len = uint4korr(head + EVENT_LEN_OFFSET); char *buf= 0; const char *error= 0; @@ -882,15 +876,76 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len, Log_event::print_header() */ -void Log_event::print_header(FILE* file) +void Log_event::print_header(FILE* file, PRINT_EVENT_INFO* print_event_info) { char llbuff[22]; + my_off_t hexdump_from= print_event_info->hexdump_from; + fputc('#', file); print_timestamp(file); fprintf(file, " server id %d end_log_pos %s ", server_id, - llstr(log_pos,llbuff)); + llstr(log_pos,llbuff)); + + /* mysqlbinlog --hexdump */ + if (print_event_info->hexdump_from) + { + fprintf(file, "\n"); + uchar *ptr= (uchar*)temp_buf; + my_off_t size= + uint4korr(ptr + EVENT_LEN_OFFSET) - LOG_EVENT_MINIMAL_HEADER_LEN; + my_off_t i; + + /* Header len * 4 >= header len * (2 chars + space + extra space) */ + char *h, hex_string[LOG_EVENT_MINIMAL_HEADER_LEN*4]= {0}; + char *c, char_string[16+1]= {0}; + + /* Pretty-print event common header if header is exactly 19 bytes */ + if (print_event_info->common_header_len == LOG_EVENT_MINIMAL_HEADER_LEN) + { + fprintf(file, "# Position Timestamp Type Master ID " + "Size Master Pos Flags \n"); + fprintf(file, "# %8.8lx %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x\n", + hexdump_from, ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], + ptr[5], ptr[6], ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], + ptr[12], ptr[13], ptr[14], ptr[15], ptr[16], ptr[17], ptr[18]); + ptr += LOG_EVENT_MINIMAL_HEADER_LEN; + hexdump_from += LOG_EVENT_MINIMAL_HEADER_LEN; + } + + /* Rest of event (without common header) */ + for (i= 0, c= char_string, h=hex_string; + i < size; + i++, ptr++) + { + my_snprintf(h, 4, "%02x ", *ptr); + h += 3; + + *c++= my_isalnum(&my_charset_bin, *ptr) ? *ptr : '.'; + + if (i % 16 == 15) + { + fprintf(file, "# %8.8lx %-48.48s |%16s|\n", + hexdump_from + (i & 0xfffffff0), hex_string, char_string); + hex_string[0]= 0; + char_string[0]= 0; + c= char_string; + h= hex_string; + } + else if (i % 8 == 7) *h++ = ' '; + } + *c= '\0'; + + /* Non-full last line */ + if (hex_string[0]) { + printf("# %8.8lx %-48.48s |%s|\n# ", + hexdump_from + (i & 0xfffffff0), hex_string, char_string); + } + } } + /* Log_event::print_timestamp() */ @@ -1166,7 +1221,7 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, But it's likely that we don't want to use 32 bits for 3 bits; in the future we will probably want to reclaim the 29 bits. So we need the &. */ - flags2= thd_arg->options & OPTIONS_WRITTEN_TO_BIN_LOG; + flags2= (uint32) (thd_arg->options & OPTIONS_WRITTEN_TO_BIN_LOG); DBUG_ASSERT(thd->variables.character_set_client->number < 256*256); DBUG_ASSERT(thd->variables.collation_connection->number < 256*256); DBUG_ASSERT(thd->variables.collation_server->number < 256*256); @@ -1373,25 +1428,25 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, */ #ifdef MYSQL_CLIENT -void Query_log_event::print_query_header(FILE* file, bool short_form, - LAST_EVENT_INFO* last_event_info) +void Query_log_event::print_query_header(FILE* file, + PRINT_EVENT_INFO* print_event_info) { // TODO: print the catalog ?? char buff[40],*end; // Enough for SET TIMESTAMP bool different_db= 1; uint32 tmp; - if (!short_form) + if (!print_event_info->short_form) { - print_header(file); + print_header(file, print_event_info); fprintf(file, "\t%s\tthread_id=%lu\texec_time=%lu\terror_code=%d\n", get_type_str(), (ulong) thread_id, (ulong) exec_time, error_code); } if (!(flags & LOG_EVENT_SUPPRESS_USE_F) && db) { - if (different_db= memcmp(last_event_info->db, db, db_len + 1)) - memcpy(last_event_info->db, db, db_len + 1); + if (different_db= memcmp(print_event_info->db, db, db_len + 1)) + memcpy(print_event_info->db, db, db_len + 1); if (db[0] && different_db) fprintf(file, "use %s;\n", db); } @@ -1411,12 +1466,12 @@ void Query_log_event::print_query_header(FILE* file, bool short_form, if (likely(flags2_inited)) /* likely as this will mainly read 5.0 logs */ { /* tmp is a bitmask of bits which have changed. */ - if (likely(last_event_info->flags2_inited)) + if (likely(print_event_info->flags2_inited)) /* All bits which have changed */ - tmp= (last_event_info->flags2) ^ flags2; + tmp= (print_event_info->flags2) ^ flags2; else /* that's the first Query event we read */ { - last_event_info->flags2_inited= 1; + print_event_info->flags2_inited= 1; tmp= ~((uint32)0); /* all bits have changed */ } @@ -1431,7 +1486,7 @@ void Query_log_event::print_query_header(FILE* file, bool short_form, print_set_option(file, tmp, OPTION_RELAXED_UNIQUE_CHECKS, ~flags2, "@@session.unique_checks", &need_comma); fprintf(file,";\n"); - last_event_info->flags2= flags2; + print_event_info->flags2= flags2; } } @@ -1450,37 +1505,37 @@ void Query_log_event::print_query_header(FILE* file, bool short_form, if (likely(sql_mode_inited)) { - if (unlikely(!last_event_info->sql_mode_inited)) /* first Query event */ + if (unlikely(!print_event_info->sql_mode_inited)) /* first Query event */ { - last_event_info->sql_mode_inited= 1; + print_event_info->sql_mode_inited= 1; /* force a difference to force write */ - last_event_info->sql_mode= ~sql_mode; + print_event_info->sql_mode= ~sql_mode; } - if (unlikely(last_event_info->sql_mode != sql_mode)) + if (unlikely(print_event_info->sql_mode != sql_mode)) { fprintf(file,"SET @@session.sql_mode=%lu;\n",(ulong)sql_mode); - last_event_info->sql_mode= sql_mode; + print_event_info->sql_mode= sql_mode; } } - if (last_event_info->auto_increment_increment != auto_increment_increment || - last_event_info->auto_increment_offset != auto_increment_offset) + if (print_event_info->auto_increment_increment != auto_increment_increment || + print_event_info->auto_increment_offset != auto_increment_offset) { fprintf(file,"SET @@session.auto_increment_increment=%lu, @@session.auto_increment_offset=%lu;\n", auto_increment_increment,auto_increment_offset); - last_event_info->auto_increment_increment= auto_increment_increment; - last_event_info->auto_increment_offset= auto_increment_offset; + print_event_info->auto_increment_increment= auto_increment_increment; + print_event_info->auto_increment_offset= auto_increment_offset; } /* TODO: print the catalog when we feature SET CATALOG */ if (likely(charset_inited)) { - if (unlikely(!last_event_info->charset_inited)) /* first Query event */ + if (unlikely(!print_event_info->charset_inited)) /* first Query event */ { - last_event_info->charset_inited= 1; - last_event_info->charset[0]= ~charset[0]; // force a difference to force write + print_event_info->charset_inited= 1; + print_event_info->charset[0]= ~charset[0]; // force a difference to force write } - if (unlikely(bcmp(last_event_info->charset, charset, 6))) + if (unlikely(bcmp(print_event_info->charset, charset, 6))) { fprintf(file,"SET " "@@session.character_set_client=%d," @@ -1490,24 +1545,23 @@ void Query_log_event::print_query_header(FILE* file, bool short_form, uint2korr(charset), uint2korr(charset+2), uint2korr(charset+4)); - memcpy(last_event_info->charset, charset, 6); + memcpy(print_event_info->charset, charset, 6); } } if (time_zone_len) { - if (bcmp(last_event_info->time_zone_str, time_zone_str, time_zone_len+1)) + if (bcmp(print_event_info->time_zone_str, time_zone_str, time_zone_len+1)) { fprintf(file,"SET @@session.time_zone='%s';\n", time_zone_str); - memcpy(last_event_info->time_zone_str, time_zone_str, time_zone_len+1); + memcpy(print_event_info->time_zone_str, time_zone_str, time_zone_len+1); } } } -void Query_log_event::print(FILE* file, bool short_form, - LAST_EVENT_INFO* last_event_info) +void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { - print_query_header(file, short_form, last_event_info); + print_query_header(file, print_event_info); my_fwrite(file, (byte*) query, q_len, MYF(MY_NABP | MY_WME)); fputs(";\n", file); } @@ -1805,11 +1859,11 @@ void Start_log_event_v3::pack_info(Protocol *protocol) */ #ifdef MYSQL_CLIENT -void Start_log_event_v3::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info) +void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { - if (!short_form) + if (!print_event_info->short_form) { - print_header(file); + print_header(file, print_event_info); fprintf(file, "\tStart: binlog v %d, server v %s created ", binlog_version, server_version); print_timestamp(file); @@ -2533,19 +2587,19 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len, */ #ifdef MYSQL_CLIENT -void Load_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info) +void Load_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { - print(file, short_form, last_event_info, 0); + print(file, print_event_info, 0); } -void Load_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info, +void Load_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info, bool commented) { DBUG_ENTER("Load_log_event::print"); - if (!short_form) + if (!print_event_info->short_form) { - print_header(file); + print_header(file, print_event_info); fprintf(file, "\tQuery\tthread_id=%ld\texec_time=%ld\n", thread_id, exec_time); } @@ -2559,9 +2613,9 @@ void Load_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_ev But if commented, the "use" is going to be commented so we should not update the last_db. */ - if ((different_db= memcmp(last_event_info->db, db, db_len + 1)) && + if ((different_db= memcmp(print_event_info->db, db, db_len + 1)) && !commented) - memcpy(last_event_info->db, db, db_len + 1); + memcpy(print_event_info->db, db, db_len + 1); } if (db && db[0] && different_db) @@ -2950,13 +3004,13 @@ void Rotate_log_event::pack_info(Protocol *protocol) */ #ifdef MYSQL_CLIENT -void Rotate_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info) +void Rotate_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { char buf[22]; - if (short_form) + if (print_event_info->short_form) return; - print_header(file); + print_header(file, print_event_info); fprintf(file, "\tRotate to "); if (new_log_ident) my_fwrite(file, (byte*) new_log_ident, (uint)ident_len, @@ -2968,13 +3022,38 @@ void Rotate_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_ #endif /* MYSQL_CLIENT */ + /* - Rotate_log_event::Rotate_log_event() + Rotate_log_event::Rotate_log_event() (2 constructors) */ + +#ifndef MYSQL_CLIENT +Rotate_log_event::Rotate_log_event(THD* thd_arg, + const char* new_log_ident_arg, + uint ident_len_arg, ulonglong pos_arg, + uint flags_arg) + :Log_event(), new_log_ident(new_log_ident_arg), + pos(pos_arg),ident_len(ident_len_arg ? ident_len_arg : + (uint) strlen(new_log_ident_arg)), flags(flags_arg) +{ +#ifndef DBUG_OFF + char buff[22]; + DBUG_ENTER("Rotate_log_event::Rotate_log_event(THD*,...)"); + DBUG_PRINT("enter",("new_log_ident %s pos %s flags %lu", new_log_ident_arg, + llstr(pos_arg, buff), flags)); +#endif + if (flags & DUP_NAME) + new_log_ident= my_strdup_with_length((const byte*) new_log_ident_arg, + ident_len, MYF(MY_WME)); + DBUG_VOID_RETURN; +} +#endif + + Rotate_log_event::Rotate_log_event(const char* buf, uint event_len, const Format_description_log_event* description_event) - :Log_event(buf, description_event) ,new_log_ident(NULL),alloced(0) + :Log_event(buf, description_event) ,new_log_ident(0), flags(DUP_NAME) { DBUG_ENTER("Rotate_log_event::Rotate_log_event(char*,...)"); // The caller will ensure that event_len is what we have at EVENT_LEN_OFFSET @@ -2989,12 +3068,9 @@ Rotate_log_event::Rotate_log_event(const char* buf, uint event_len, (header_size+post_header_len)); ident_offset = post_header_len; set_if_smaller(ident_len,FN_REFLEN-1); - if (!(new_log_ident= my_strdup_with_length((byte*) buf + - ident_offset, - (uint) ident_len, - MYF(MY_WME)))) - DBUG_VOID_RETURN; - alloced = 1; + new_log_ident= my_strdup_with_length((byte*) buf + ident_offset, + (uint) ident_len, + MYF(MY_WME)); DBUG_VOID_RETURN; } @@ -3152,16 +3228,15 @@ bool Intvar_log_event::write(IO_CACHE* file) */ #ifdef MYSQL_CLIENT -void Intvar_log_event::print(FILE* file, bool short_form, - LAST_EVENT_INFO* last_event_info) +void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { char llbuff[22]; const char *msg; LINT_INIT(msg); - if (!short_form) + if (!print_event_info->short_form) { - print_header(file); + print_header(file, print_event_info); fprintf(file, "\tIntvar\n"); } @@ -3242,12 +3317,12 @@ bool Rand_log_event::write(IO_CACHE* file) #ifdef MYSQL_CLIENT -void Rand_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info) +void Rand_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { char llbuff[22],llbuff2[22]; - if (!short_form) + if (!print_event_info->short_form) { - print_header(file); + print_header(file, print_event_info); fprintf(file, "\tRand\n"); } fprintf(file, "SET @@RAND_SEED1=%s, @@RAND_SEED2=%s;\n", @@ -3312,14 +3387,14 @@ bool Xid_log_event::write(IO_CACHE* file) #ifdef MYSQL_CLIENT -void Xid_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info) +void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { - if (!short_form) + if (!print_event_info->short_form) { char buf[64]; longlong10_to_str(xid, buf, 10); - print_header(file); + print_header(file, print_event_info); fprintf(file, "\tXid = %s\n", buf); fflush(file); } @@ -3510,11 +3585,11 @@ bool User_var_log_event::write(IO_CACHE* file) */ #ifdef MYSQL_CLIENT -void User_var_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info) +void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { - if (!short_form) + if (!print_event_info->short_form) { - print_header(file); + print_header(file, print_event_info); fprintf(file, "\tUser_var\n"); } @@ -3685,11 +3760,11 @@ int User_var_log_event::exec_event(struct st_relay_log_info* rli) #ifdef HAVE_REPLICATION #ifdef MYSQL_CLIENT -void Unknown_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info) +void Unknown_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { - if (short_form) + if (print_event_info->short_form) return; - print_header(file); + print_header(file, print_event_info); fputc('\n', file); fprintf(file, "# %s", "Unknown event\n"); } @@ -3756,12 +3831,12 @@ Slave_log_event::~Slave_log_event() #ifdef MYSQL_CLIENT -void Slave_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info) +void Slave_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { char llbuff[22]; - if (short_form) + if (print_event_info->short_form) return; - print_header(file); + print_header(file, print_event_info); fputc('\n', file); fprintf(file, "\ Slave: master_host: '%s' master_port: %d master_log: '%s' master_pos: %s\n", @@ -3841,12 +3916,12 @@ int Slave_log_event::exec_event(struct st_relay_log_info* rli) */ #ifdef MYSQL_CLIENT -void Stop_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info) +void Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { - if (short_form) + if (print_event_info->short_form) return; - print_header(file); + print_header(file, print_event_info); fprintf(file, "\tStop\n"); fflush(file); } @@ -4020,19 +4095,20 @@ Create_file_log_event::Create_file_log_event(const char* buf, uint len, */ #ifdef MYSQL_CLIENT -void Create_file_log_event::print(FILE* file, bool short_form, - LAST_EVENT_INFO* last_event_info, bool enable_local) +void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info, + bool enable_local) { - if (short_form) + if (print_event_info->short_form) { if (enable_local && check_fname_outside_temp_buf()) - Load_log_event::print(file, 1, last_event_info); + Load_log_event::print(file, print_event_info); return; } if (enable_local) { - Load_log_event::print(file, short_form, last_event_info, !check_fname_outside_temp_buf()); + Load_log_event::print(file, print_event_info, + !check_fname_outside_temp_buf()); /* That one is for "file_id: etc" below: in mysqlbinlog we want the #, in SHOW BINLOG EVENTS we don't. @@ -4044,10 +4120,9 @@ void Create_file_log_event::print(FILE* file, bool short_form, } -void Create_file_log_event::print(FILE* file, bool short_form, - LAST_EVENT_INFO* last_event_info) +void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { - print(file,short_form,last_event_info,0); + print(file, print_event_info, 0); } #endif /* MYSQL_CLIENT */ @@ -4207,12 +4282,12 @@ bool Append_block_log_event::write(IO_CACHE* file) */ #ifdef MYSQL_CLIENT -void Append_block_log_event::print(FILE* file, bool short_form, - LAST_EVENT_INFO* last_event_info) +void Append_block_log_event::print(FILE* file, + PRINT_EVENT_INFO* print_event_info) { - if (short_form) + if (print_event_info->short_form) return; - print_header(file); + print_header(file, print_event_info); fputc('\n', file); fprintf(file, "#%s: file_id: %d block_len: %d\n", get_type_str(), file_id, block_len); @@ -4351,12 +4426,12 @@ bool Delete_file_log_event::write(IO_CACHE* file) */ #ifdef MYSQL_CLIENT -void Delete_file_log_event::print(FILE* file, bool short_form, - LAST_EVENT_INFO* last_event_info) +void Delete_file_log_event::print(FILE* file, + PRINT_EVENT_INFO* print_event_info) { - if (short_form) + if (print_event_info->short_form) return; - print_header(file); + print_header(file, print_event_info); fputc('\n', file); fprintf(file, "#Delete_file: file_id=%u\n", file_id); } @@ -4447,12 +4522,12 @@ bool Execute_load_log_event::write(IO_CACHE* file) */ #ifdef MYSQL_CLIENT -void Execute_load_log_event::print(FILE* file, bool short_form, - LAST_EVENT_INFO* last_event_info) +void Execute_load_log_event::print(FILE* file, + PRINT_EVENT_INFO* print_event_info) { - if (short_form) + if (print_event_info->short_form) return; - print_header(file); + print_header(file, print_event_info); fputc('\n', file); fprintf(file, "#Exec_load: file_id=%d\n", file_id); @@ -4659,18 +4734,18 @@ Execute_load_query_log_event::write_post_header_for_derived(IO_CACHE* file) #ifdef MYSQL_CLIENT -void Execute_load_query_log_event::print(FILE* file, bool short_form, - LAST_EVENT_INFO* last_event_info) +void Execute_load_query_log_event::print(FILE* file, + PRINT_EVENT_INFO* print_event_info) { - print(file, short_form, last_event_info, 0); + print(file, print_event_info, 0); } -void Execute_load_query_log_event::print(FILE* file, bool short_form, - LAST_EVENT_INFO* last_event_info, +void Execute_load_query_log_event::print(FILE* file, + PRINT_EVENT_INFO* print_event_info, const char *local_fname) { - print_query_header(file, short_form, last_event_info); + print_query_header(file, print_event_info); if (local_fname) { @@ -4691,7 +4766,7 @@ void Execute_load_query_log_event::print(FILE* file, bool short_form, fprintf(file, ";\n"); } - if (!short_form) + if (!print_event_info->short_form) fprintf(file, "# file_id: %d \n", file_id); } #endif diff --git a/sql/log_event.h b/sql/log_event.h index 29580589a34..7783a97f03f 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -403,24 +403,34 @@ enum Log_event_type Every time you update this enum (when you add a type), you have to fix Format_description_log_event::Format_description_log_event(). */ - UNKNOWN_EVENT= 0, START_EVENT_V3, QUERY_EVENT, STOP_EVENT, ROTATE_EVENT, - INTVAR_EVENT, LOAD_EVENT, SLAVE_EVENT, CREATE_FILE_EVENT, - APPEND_BLOCK_EVENT, EXEC_LOAD_EVENT, DELETE_FILE_EVENT, + UNKNOWN_EVENT= 0, + START_EVENT_V3= 1, + QUERY_EVENT= 2, + STOP_EVENT= 3, + ROTATE_EVENT= 4, + INTVAR_EVENT= 5, + LOAD_EVENT= 6, + SLAVE_EVENT= 7, + CREATE_FILE_EVENT= 8, + APPEND_BLOCK_EVENT= 9, + EXEC_LOAD_EVENT= 10, + DELETE_FILE_EVENT= 11, /* NEW_LOAD_EVENT is like LOAD_EVENT except that it has a longer sql_ex, allowing multibyte TERMINATED BY etc; both types share the same class (Load_log_event) */ - NEW_LOAD_EVENT, - RAND_EVENT, USER_VAR_EVENT, - FORMAT_DESCRIPTION_EVENT, - XID_EVENT, - BEGIN_LOAD_QUERY_EVENT, - EXECUTE_LOAD_QUERY_EVENT, + NEW_LOAD_EVENT= 12, + RAND_EVENT= 13, + USER_VAR_EVENT= 14, + FORMAT_DESCRIPTION_EVENT= 15, + XID_EVENT= 16, + BEGIN_LOAD_QUERY_EVENT= 17, + EXECUTE_LOAD_QUERY_EVENT= 18, /* - add new events here - right above this comment! - existing events should never change their numbers + Add new events here - right above this comment! + Existing events (except ENUM_END_EVENT) should never change their numbers */ ENUM_END_EVENT /* end marker */ @@ -451,12 +461,23 @@ struct st_relay_log_info; #ifdef MYSQL_CLIENT /* - A structure for mysqlbinlog to remember the last db, flags2, sql_mode etc; it - is passed to events' print() methods, so that they print only the necessary - USE and SET commands. + A structure for mysqlbinlog to know how to print events + + This structure is passed to the event's print() methods, + + There are two types of settings stored here: + 1. Last db, flags2, sql_mode etc comes from the last printed event. + They are stored so that only the necessary USE and SET commands + are printed. + 2. Other information on how to print the events, e.g. short_form, + hexdump_from. These are not dependent on the last event. */ -typedef struct st_last_event_info +typedef struct st_print_event_info { + /* + Settings for database, sql_mode etc that comes from the last event + that was printed. + */ // TODO: have the last catalog here ?? char db[FN_REFLEN+1]; // TODO: make this a LEX_STRING when thd->db is bool flags2_inited; @@ -467,12 +488,12 @@ typedef struct st_last_event_info bool charset_inited; char charset[6]; // 3 variables, each of them storable in 2 bytes char time_zone_str[MAX_TIME_ZONE_NAME_LENGTH]; - st_last_event_info() + st_print_event_info() :flags2_inited(0), sql_mode_inited(0), auto_increment_increment(1),auto_increment_offset(1), charset_inited(0) { /* - Currently we only use static LAST_EVENT_INFO objects, so zeroed at + Currently we only use static PRINT_EVENT_INFO objects, so zeroed at program's startup, but these explicit bzero() is for the day someone creates dynamic instances. */ @@ -480,7 +501,13 @@ typedef struct st_last_event_info bzero(charset, sizeof(charset)); bzero(time_zone_str, sizeof(time_zone_str)); } -} LAST_EVENT_INFO; + + /* Settings on how to print the events */ + bool short_form; + my_off_t hexdump_from; + uint8 common_header_len; + +} PRINT_EVENT_INFO; #endif @@ -589,9 +616,9 @@ public: static Log_event* read_log_event(IO_CACHE* file, const Format_description_log_event *description_event); /* print*() functions are used by mysqlbinlog */ - virtual void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0) = 0; + virtual void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0) = 0; void print_timestamp(FILE* file, time_t *ts = 0); - void print_header(FILE* file); + void print_header(FILE* file, PRINT_EVENT_INFO* print_event_info= 0); #endif static void *operator new(size_t size) @@ -751,8 +778,8 @@ public: uint32 q_len_arg); #endif /* HAVE_REPLICATION */ #else - void print_query_header(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); - void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); + void print_query_header(FILE* file, PRINT_EVENT_INFO* print_event_info= 0); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0); #endif Query_log_event(const char* buf, uint event_len, @@ -806,7 +833,7 @@ public: void pack_info(Protocol* protocol); int exec_event(struct st_relay_log_info* rli); #else - void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0); #endif Slave_log_event(const char* buf, uint event_len); @@ -894,8 +921,8 @@ public: bool use_rli_only_for_errors); #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info = 0); - void print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info, bool commented); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info = 0); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info, bool commented); #endif /* @@ -984,7 +1011,7 @@ public: #endif /* HAVE_REPLICATION */ #else Start_log_event_v3() {} - void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0); #endif Start_log_event_v3(const char* buf, @@ -1079,7 +1106,7 @@ public: int exec_event(struct st_relay_log_info* rli); #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0); #endif Intvar_log_event(const char* buf, const Format_description_log_event* description_event); @@ -1120,7 +1147,7 @@ class Rand_log_event: public Log_event int exec_event(struct st_relay_log_info* rli); #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0); #endif Rand_log_event(const char* buf, const Format_description_log_event* description_event); @@ -1157,7 +1184,7 @@ class Xid_log_event: public Log_event int exec_event(struct st_relay_log_info* rli); #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0); #endif Xid_log_event(const char* buf, const Format_description_log_event* description_event); @@ -1199,7 +1226,7 @@ public: void pack_info(Protocol* protocol); int exec_event(struct st_relay_log_info* rli); #else - void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0); #endif User_var_log_event(const char* buf, const Format_description_log_event* description_event); @@ -1225,7 +1252,7 @@ public: {} int exec_event(struct st_relay_log_info* rli); #else - void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0); #endif Stop_log_event(const char* buf, const Format_description_log_event* description_event): @@ -1247,32 +1274,31 @@ public: class Rotate_log_event: public Log_event { public: + enum { + DUP_NAME= 2 // if constructor should dup the string argument + }; const char* new_log_ident; ulonglong pos; uint ident_len; - bool alloced; + uint flags; #ifndef MYSQL_CLIENT Rotate_log_event(THD* thd_arg, const char* new_log_ident_arg, - uint ident_len_arg = 0, - ulonglong pos_arg = LOG_EVENT_OFFSET) - :Log_event(), new_log_ident(new_log_ident_arg), - pos(pos_arg),ident_len(ident_len_arg ? ident_len_arg : - (uint) strlen(new_log_ident_arg)), alloced(0) - {} + uint ident_len_arg, + ulonglong pos_arg, uint flags); #ifdef HAVE_REPLICATION void pack_info(Protocol* protocol); int exec_event(struct st_relay_log_info* rli); #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0); #endif Rotate_log_event(const char* buf, uint event_len, const Format_description_log_event* description_event); ~Rotate_log_event() { - if (alloced) - my_free((gptr) new_log_ident, MYF(0)); + if (flags & DUP_NAME) + my_free((gptr) new_log_ident, MYF(MY_ALLOW_ZERO_PTR)); } Log_event_type get_type_code() { return ROTATE_EVENT;} int get_data_size() { return ident_len + ROTATE_HEADER_LEN;} @@ -1317,8 +1343,8 @@ public: int exec_event(struct st_relay_log_info* rli); #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); - void print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info, bool enable_local); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info, bool enable_local); #endif Create_file_log_event(const char* buf, uint event_len, @@ -1385,7 +1411,7 @@ public: virtual int get_create_or_append() const; #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0); #endif Append_block_log_event(const char* buf, uint event_len, @@ -1420,8 +1446,8 @@ public: int exec_event(struct st_relay_log_info* rli); #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); - void print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info, bool enable_local); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info, bool enable_local); #endif Delete_file_log_event(const char* buf, uint event_len, @@ -1456,7 +1482,7 @@ public: int exec_event(struct st_relay_log_info* rli); #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0); #endif Execute_load_log_event(const char* buf, uint event_len, @@ -1541,11 +1567,10 @@ public: int exec_event(struct st_relay_log_info* rli); #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, bool short_form = 0, - LAST_EVENT_INFO* last_event_info= 0); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0); /* Prints the query as LOAD DATA LOCAL and with rewritten filename */ - void print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info, - const char *local_fname); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info, + const char *local_fname); #endif Execute_load_query_log_event(const char* buf, uint event_len, const Format_description_log_event *description_event); @@ -1574,7 +1599,7 @@ public: Log_event(buf, description_event) {} ~Unknown_log_event() {} - void print(FILE* file, bool short_form= 0, LAST_EVENT_INFO* last_event_info= 0); + void print(FILE* file, PRINT_EVENT_INFO* print_event_info= 0); Log_event_type get_type_code() { return UNKNOWN_EVENT;} bool is_valid() const { return 1; } }; diff --git a/sql/my_decimal.cc b/sql/my_decimal.cc index f188d27ff78..1bd16940b47 100644 --- a/sql/my_decimal.cc +++ b/sql/my_decimal.cc @@ -185,7 +185,7 @@ int str2my_decimal(uint mask, const char *from, uint length, } } } - check_result(mask, err); + check_result_and_overflow(mask, err, decimal_value); return err; } diff --git a/sql/my_decimal.h b/sql/my_decimal.h index b65e6aedaa2..b02abacf0a3 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -126,6 +126,19 @@ inline int decimal_operation_results(int result) } #endif /*MYSQL_CLIENT*/ +inline +void max_my_decimal(my_decimal *to, int precision, int frac) +{ + DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION)&& + (frac <= DECIMAL_MAX_SCALE)); + max_decimal(precision, frac, (decimal_t*) to); +} + +inline void max_internal_decimal(my_decimal *to) +{ + max_my_decimal(to, DECIMAL_MAX_PRECISION, 0); +} + inline int check_result(uint mask, int result) { if (result & mask) @@ -133,6 +146,18 @@ inline int check_result(uint mask, int result) return result; } +inline int check_result_and_overflow(uint mask, int result, my_decimal *val) +{ + if (check_result(mask, result) & E_DEC_OVERFLOW) + { + bool sign= val->sign(); + val->fix_buffer_pointer(); + max_internal_decimal(val); + val->sign(sign); + } + return result; +} + inline uint my_decimal_length_to_precision(uint length, uint scale, bool unsigned_flag) { @@ -256,7 +281,8 @@ int my_decimal2double(uint mask, const my_decimal *d, double *result) inline int str2my_decimal(uint mask, const char *str, my_decimal *d, char **end) { - return check_result(mask, string2decimal(str, (decimal_t*) d, end)); + return check_result_and_overflow(mask, string2decimal(str,(decimal_t*)d,end), + d); } @@ -274,7 +300,7 @@ int string2my_decimal(uint mask, const String *str, my_decimal *d) inline int double2my_decimal(uint mask, double val, my_decimal *d) { - return check_result(mask, double2decimal(val, (decimal_t*) d)); + return check_result_and_overflow(mask, double2decimal(val, (decimal_t*)d), d); } @@ -303,7 +329,9 @@ inline int my_decimal_add(uint mask, my_decimal *res, const my_decimal *a, const my_decimal *b) { - return check_result(mask, decimal_add((decimal_t*) a, (decimal_t*) b, res)); + return check_result_and_overflow(mask, + decimal_add((decimal_t*)a,(decimal_t*)b,res), + res); } @@ -311,7 +339,9 @@ inline int my_decimal_sub(uint mask, my_decimal *res, const my_decimal *a, const my_decimal *b) { - return check_result(mask, decimal_sub((decimal_t*) a, (decimal_t*) b, res)); + return check_result_and_overflow(mask, + decimal_sub((decimal_t*)a,(decimal_t*)b,res), + res); } @@ -319,7 +349,9 @@ inline int my_decimal_mul(uint mask, my_decimal *res, const my_decimal *a, const my_decimal *b) { - return check_result(mask, decimal_mul((decimal_t*) a, (decimal_t*) b, res)); + return check_result_and_overflow(mask, + decimal_mul((decimal_t*)a,(decimal_t*)b,res), + res); } @@ -327,8 +359,10 @@ inline int my_decimal_div(uint mask, my_decimal *res, const my_decimal *a, const my_decimal *b, int div_scale_inc) { - return check_result(mask, decimal_div((decimal_t*) a, (decimal_t*) b, res, - div_scale_inc)); + return check_result_and_overflow(mask, + decimal_div((decimal_t*)a,(decimal_t*)b,res, + div_scale_inc), + res); } @@ -336,7 +370,9 @@ inline int my_decimal_mod(uint mask, my_decimal *res, const my_decimal *a, const my_decimal *b) { - return check_result(mask, decimal_mod((decimal_t*) a, (decimal_t*) b, res)); + return check_result_and_overflow(mask, + decimal_mod((decimal_t*)a,(decimal_t*)b,res), + res); } @@ -347,13 +383,5 @@ int my_decimal_cmp(const my_decimal *a, const my_decimal *b) return decimal_cmp((decimal_t*) a, (decimal_t*) b); } -inline -void max_my_decimal(my_decimal *to, int precision, int frac) -{ - DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION)&& - (frac <= DECIMAL_MAX_SCALE)); - max_decimal(precision, frac, (decimal_t*) to); -} - #endif /*my_decimal_h*/ diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 0110cb70892..a967e2ee7fe 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -702,11 +702,11 @@ int mysql_explain_select(THD *thd, SELECT_LEX *sl, char const *type, select_result *result); bool mysql_union(THD *thd, LEX *lex, select_result *result, SELECT_LEX_UNIT *unit, ulong setup_tables_done_option); -int mysql_handle_derived(LEX *lex, int (*processor)(THD *thd, - LEX *lex, - TABLE_LIST *table)); -int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *t); -int mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *t); +bool mysql_handle_derived(LEX *lex, bool (*processor)(THD *thd, + LEX *lex, + TABLE_LIST *table)); +bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *t); +bool mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *t); Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, Item ***copy_func, Field **from_field, bool group, bool modify_item, @@ -780,7 +780,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create); TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update); TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem, bool *refresh, uint flags); -TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table); +bool reopen_name_locked_table(THD* thd, TABLE_LIST* table); TABLE *find_locked_table(THD *thd, const char *db,const char *table_name); bool reopen_table(TABLE *table,bool locked); bool reopen_tables(THD *thd,bool get_locks,bool in_refresh); @@ -816,8 +816,8 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, Field * find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, bool check_grants, bool allow_rowid, - uint *cached_field_index_ptr); - + uint *cached_field_index_ptr, + Security_context *sctx); Field * find_field_in_table_sef(TABLE *table, const char *name); @@ -1323,7 +1323,6 @@ int openfrm(THD *thd, const char *name,const char *alias,uint filestat, int readfrm(const char *name, const void** data, uint* length); int writefrm(const char* name, const void* data, uint len); int closefrm(TABLE *table); -db_type get_table_type(THD *thd, const char *name); int read_string(File file, gptr *to, uint length); void free_blobs(TABLE *table); int set_zone(int nr,int min_zone,int max_zone); @@ -1362,6 +1361,8 @@ void change_byte(byte *,uint,char,char); void init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form, SQL_SELECT *select, int use_record_cache, bool print_errors); +void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, + bool print_error, uint idx); void end_read_record(READ_RECORD *info); ha_rows filesort(THD *thd, TABLE *form,struct st_sort_field *sortorder, uint s_length, SQL_SELECT *select, diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 47184a577ee..660e83db7d1 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -585,6 +585,20 @@ HANDLE smem_event_connect_request= 0; #include "sslopt-vars.h" #ifdef HAVE_OPENSSL +#include <openssl/crypto.h> +#ifndef HAVE_YASSL +typedef struct CRYPTO_dynlock_value +{ + rw_lock_t lock; +} openssl_lock_t; + +static openssl_lock_t *openssl_stdlocks; +static openssl_lock_t *openssl_dynlock_create(const char *, int); +static void openssl_dynlock_destroy(openssl_lock_t *, const char *, int); +static void openssl_lock_function(int, int, const char *, int); +static void openssl_lock(int, openssl_lock_t *, const char *, int); +static unsigned long openssl_id_function(); +#endif char *des_key_file; struct st_VioSSLAcceptorFd *ssl_acceptor_fd; #endif /* HAVE_OPENSSL */ @@ -920,7 +934,7 @@ static void __cdecl kill_server(int sig_ptr) RETURN_FROM_KILL_SERVER; kill_in_progress=TRUE; abort_loop=1; // This should be set - signal(sig,SIG_IGN); + my_sigset(sig,SIG_IGN); if (sig == MYSQL_KILL_SIGNAL || sig == 0) sql_print_information(ER(ER_NORMAL_SHUTDOWN),my_progname); else @@ -969,11 +983,6 @@ pthread_handler_t kill_server_thread(void *arg __attribute__((unused))) } #endif -#if defined(__amiga__) -#undef sigset -#define sigset signal -#endif - extern "C" sig_handler print_signal_warning(int sig) { if (!DBUG_IN_USE) @@ -983,7 +992,7 @@ extern "C" sig_handler print_signal_warning(int sig) sig,my_thread_id()); } #ifdef DONT_REMEMBER_SIGNAL - sigset(sig,print_signal_warning); /* int. thread system calls */ + my_sigset(sig,print_signal_warning); /* int. thread system calls */ #endif #if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) if (sig == SIGALRM) @@ -1173,6 +1182,11 @@ static void clean_up_mutexes() (void) pthread_mutex_destroy(&LOCK_user_conn); #ifdef HAVE_OPENSSL (void) pthread_mutex_destroy(&LOCK_des_key_file); +#ifndef HAVE_YASSL + for (int i= 0; i < CRYPTO_num_locks(); ++i) + (void) rwlock_destroy(&openssl_stdlocks[i].lock); + OPENSSL_free(openssl_stdlocks); +#endif #endif #ifdef HAVE_REPLICATION (void) pthread_mutex_destroy(&LOCK_rpl_status); @@ -1183,6 +1197,7 @@ static void clean_up_mutexes() (void) rwlock_destroy(&LOCK_sys_init_slave); (void) pthread_mutex_destroy(&LOCK_global_system_variables); (void) pthread_mutex_destroy(&LOCK_global_read_lock); + (void) pthread_mutex_destroy(&LOCK_uuid_generator); (void) pthread_cond_destroy(&COND_thread_count); (void) pthread_cond_destroy(&COND_refresh); (void) pthread_cond_destroy(&COND_thread_cache); @@ -2058,8 +2073,8 @@ static void init_signals(void) DBUG_ENTER("init_signals"); if (test_flags & TEST_SIGINT) - sigset(THR_KILL_SIGNAL,end_thread_signal); - sigset(THR_SERVER_ALARM,print_signal_warning); // Should never be called! + my_sigset(THR_KILL_SIGNAL,end_thread_signal); + my_sigset(THR_SERVER_ALARM,print_signal_warning); // Should never be called! if (!(test_flags & TEST_NO_STACKTRACE) || (test_flags & TEST_CORE_ON_SIGNAL)) { @@ -2093,13 +2108,8 @@ static void init_signals(void) } #endif (void) sigemptyset(&set); -#ifdef THREAD_SPECIFIC_SIGPIPE - sigset(SIGPIPE,abort_thread); + my_sigset(SIGPIPE,SIG_IGN); sigaddset(&set,SIGPIPE); -#else - (void) signal(SIGPIPE,SIG_IGN); // Can't know which thread - sigaddset(&set,SIGPIPE); -#endif sigaddset(&set,SIGINT); #ifndef IGNORE_SIGHUP_SIGQUIT sigaddset(&set,SIGQUIT); @@ -2326,6 +2336,8 @@ static int my_message_sql(uint error, const char *str, myf MyFlags) if (thd->spcont && thd->spcont->find_handler(error, MYSQL_ERROR::WARN_LEVEL_ERROR)) { + if (! thd->spcont->found_handler_here()) + thd->net.report_error= 1; /* Make "select" abort correctly */ DBUG_RETURN(0); } @@ -2729,6 +2741,17 @@ static int init_thread_environment() (void) pthread_mutex_init(&LOCK_uuid_generator, MY_MUTEX_INIT_FAST); #ifdef HAVE_OPENSSL (void) pthread_mutex_init(&LOCK_des_key_file,MY_MUTEX_INIT_FAST); +#ifndef HAVE_YASSL + openssl_stdlocks= (openssl_lock_t*) OPENSSL_malloc(CRYPTO_num_locks() * + sizeof(openssl_lock_t)); + for (int i= 0; i < CRYPTO_num_locks(); ++i) + (void) my_rwlock_init(&openssl_stdlocks[i].lock, NULL); + CRYPTO_set_dynlock_create_callback(openssl_dynlock_create); + CRYPTO_set_dynlock_destroy_callback(openssl_dynlock_destroy); + CRYPTO_set_dynlock_lock_callback(openssl_lock); + CRYPTO_set_locking_callback(openssl_lock_function); + CRYPTO_set_id_callback(openssl_id_function); +#endif #endif (void) my_rwlock_init(&LOCK_sys_init_connect, NULL); (void) my_rwlock_init(&LOCK_sys_init_slave, NULL); @@ -2761,6 +2784,75 @@ static int init_thread_environment() } +#if defined(HAVE_OPENSSL) && !defined(HAVE_YASSL) +static unsigned long openssl_id_function() +{ + return (unsigned long) pthread_self(); +} + + +static openssl_lock_t *openssl_dynlock_create(const char *file, int line) +{ + openssl_lock_t *lock= new openssl_lock_t; + my_rwlock_init(&lock->lock, NULL); + return lock; +} + + +static void openssl_dynlock_destroy(openssl_lock_t *lock, const char *file, + int line) +{ + rwlock_destroy(&lock->lock); + delete lock; +} + + +static void openssl_lock_function(int mode, int n, const char *file, int line) +{ + if (n < 0 || n > CRYPTO_num_locks()) + { + /* Lock number out of bounds. */ + sql_print_error("Fatal: OpenSSL interface problem (n = %d)", n); + abort(); + } + openssl_lock(mode, &openssl_stdlocks[n], file, line); +} + + +static void openssl_lock(int mode, openssl_lock_t *lock, const char *file, + int line) +{ + int err; + char const *what; + + switch (mode) { + case CRYPTO_LOCK|CRYPTO_READ: + what = "read lock"; + err = rw_rdlock(&lock->lock); + break; + case CRYPTO_LOCK|CRYPTO_WRITE: + what = "write lock"; + err = rw_wrlock(&lock->lock); + break; + case CRYPTO_UNLOCK|CRYPTO_READ: + case CRYPTO_UNLOCK|CRYPTO_WRITE: + what = "unlock"; + err = rw_unlock(&lock->lock); + break; + default: + /* Unknown locking mode. */ + sql_print_error("Fatal: OpenSSL interface problem (mode=0x%x)", mode); + abort(); + } + if (err) + { + sql_print_error("Fatal: can't %s OpenSSL %s lock", what); + abort(); + } +} +#endif /* HAVE_OPENSSL */ + + static void init_ssl() { #ifdef HAVE_OPENSSL @@ -2772,7 +2864,14 @@ static void init_ssl() opt_ssl_cipher); DBUG_PRINT("info",("ssl_acceptor_fd: 0x%lx", (long) ssl_acceptor_fd)); if (!ssl_acceptor_fd) + { opt_use_ssl = 0; + have_openssl= SHOW_OPTION_DISABLED; + } + } + else + { + have_openssl= SHOW_OPTION_DISABLED; } if (des_key_file) load_des_key_file(des_key_file); @@ -2938,6 +3037,23 @@ server."); sql_print_error("Can't init databases"); unireg_abort(1); } + + /* + Check that the default storage engine is actually available. + */ + if (!ha_storage_engine_is_enabled((enum db_type) + global_system_variables.table_type)) + { + if (!opt_bootstrap) + { + sql_print_error("Default storage engine (%s) is not available", + ha_get_storage_engine((enum db_type) + global_system_variables.table_type)); + unireg_abort(1); + } + global_system_variables.table_type= DB_TYPE_MYISAM; + } + tc_log= (total_ha_2pc > 1 ? (opt_bin_log ? (TC_LOG *) &mysql_bin_log : (TC_LOG *) &tc_log_mmap) : @@ -3594,7 +3710,6 @@ static void bootstrap(FILE *file) THD *thd= new THD; thd->bootstrap=1; - thd->client_capabilities=0; my_net_init(&thd->net,(st_vio*) 0); thd->max_client_packet_length= thd->net.max_packet; thd->security_ctx->master_access= ~(ulong)0; @@ -5164,7 +5279,7 @@ replicating a LOAD DATA INFILE command.", {"sql-bin-update-same", OPT_SQL_BIN_UPDATE_SAME, "The update log is deprecated since version 5.0, is replaced by the binary \ log and this option does nothing anymore.", - 0, 0, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + 0, 0, 0, GET_DISABLED, NO_ARG, 0, 0, 0, 0, 0, 0}, {"sql-mode", OPT_SQL_MODE, "Syntax: sql-mode=option[,option[,option...]] where option can be one of: REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, ONLY_FULL_GROUP_BY, NO_UNSIGNED_SUBTRACTION.", (gptr*) &sql_mode_str, (gptr*) &sql_mode_str, 0, GET_STR, REQUIRED_ARG, 0, @@ -5560,7 +5675,8 @@ The minimum value for this variable is 4096.", GET_ULONG, REQUIRED_ARG, 8192*1024, 4, ~0L, 0, 1, 0}, {"myisam_stats_method", OPT_MYISAM_STATS_METHOD, "Specifies how MyISAM index statistics collection code should threat NULLs. " - "Possible values of name are \"nulls_unequal\" (default behavior for 4.1/5.0), and \"nulls_equal\" (emulate 4.0 behavior).", + "Possible values of name are \"nulls_unequal\" (default behavior for 4.1/5.0), " + "\"nulls_equal\" (emulate 4.0 behavior), and \"nulls_ignored\".", (gptr*) &myisam_stats_method_str, (gptr*) &myisam_stats_method_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"net_buffer_length", OPT_NET_BUFFER_LENGTH, @@ -5638,7 +5754,8 @@ The minimum value for this variable is 4096.", "Persistent buffer for query parsing and execution", (gptr*) &global_system_variables.query_prealloc_size, (gptr*) &max_system_variables.query_prealloc_size, 0, GET_ULONG, - REQUIRED_ARG, QUERY_ALLOC_PREALLOC_SIZE, 16384, ~0L, 0, 1024, 0}, + REQUIRED_ARG, QUERY_ALLOC_PREALLOC_SIZE, QUERY_ALLOC_PREALLOC_SIZE, + ~0L, 0, 1024, 0}, {"range_alloc_block_size", OPT_RANGE_ALLOC_BLOCK_SIZE, "Allocation block size for storing ranges during optimization", (gptr*) &global_system_variables.range_alloc_block_size, @@ -5902,6 +6019,7 @@ struct show_var_st status_vars[]= { {"Com_xa_recover", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_XA_RECOVER]),SHOW_LONG_STATUS}, {"Com_xa_rollback", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_XA_ROLLBACK]),SHOW_LONG_STATUS}, {"Com_xa_start", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_XA_START]),SHOW_LONG_STATUS}, + {"Compression", (char*) 0, SHOW_NET_COMPRESSION}, {"Connections", (char*) &thread_id, SHOW_LONG_CONST}, {"Created_tmp_disk_tables", (char*) offsetof(STATUS_VAR, created_tmp_disk_tables), SHOW_LONG_STATUS}, {"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG}, @@ -6814,6 +6932,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), } case OPT_MYISAM_STATS_METHOD: { + ulong method_conv; myisam_stats_method_str= argument; int method; if ((method=find_type(argument, &myisam_stats_method_typelib, 2)) <= 0) @@ -6821,7 +6940,18 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), fprintf(stderr, "Invalid value of myisam_stats_method: %s.\n", argument); exit(1); } - global_system_variables.myisam_stats_method= method-1; + switch (method-1) { + case 0: + method_conv= MI_STATS_METHOD_NULLS_EQUAL; + break; + case 1: + method_conv= MI_STATS_METHOD_NULLS_NOT_EQUAL; + break; + case 2: + method_conv= MI_STATS_METHOD_IGNORE_NULLS; + break; + } + global_system_variables.myisam_stats_method= method_conv; break; } case OPT_SQL_MODE: @@ -6929,22 +7059,6 @@ static void get_options(int argc,char **argv) !opt_slow_log) sql_print_warning("options --log-slow-admin-statements and --log-queries-not-using-indexes have no effect if --log-slow-queries is not set"); - /* - Check that the default storage engine is actually available. - */ - if (!ha_storage_engine_is_enabled((enum db_type) - global_system_variables.table_type)) - { - if (!opt_bootstrap) - { - sql_print_error("Default storage engine (%s) is not available", - ha_get_storage_engine((enum db_type) - global_system_variables.table_type)); - exit(1); - } - global_system_variables.table_type= DB_TYPE_MYISAM; - } - if (argc > 0) { fprintf(stderr, "%s: Too many arguments (first extra is '%s').\nUse --help to get a list of available options\n", my_progname, *argv); @@ -7106,6 +7220,7 @@ static void fix_paths(void) CHARSET_DIR, NullS); } (void) my_load_path(mysql_charsets_dir, mysql_charsets_dir, buff); + convert_dirname(mysql_charsets_dir, mysql_charsets_dir, NullS); charsets_dir=mysql_charsets_dir; if (init_tmpdir(&mysql_tmpdir_list, opt_mysql_tmpdir)) diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 2f4cf1c4752..2400672a3f9 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -932,7 +932,7 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler) } THD *thd= current_thd; - if (!(file= get_new_handler(head, head->s->db_type))) + if (!(file= get_new_handler(head, thd->mem_root, head->s->db_type))) goto failure; DBUG_PRINT("info", ("Allocated new handler %p", file)); if (file->ha_open(head->s->path, head->db_stat, HA_OPEN_IGNORE_IF_LOCKED)) @@ -1366,6 +1366,95 @@ SEL_ARG *SEL_ARG::clone_tree() /* + Find the best index to retrieve first N records in given order + + SYNOPSIS + get_index_for_order() + table Table to be accessed + order Required ordering + limit Number of records that will be retrieved + + DESCRIPTION + Find the best index that allows to retrieve first #limit records in the + given order cheaper then one would retrieve them using full table scan. + + IMPLEMENTATION + Run through all table indexes and find the shortest index that allows + records to be retrieved in given order. We look for the shortest index + as we will have fewer index pages to read with it. + + This function is used only by UPDATE/DELETE, so we take into account how + the UPDATE/DELETE code will work: + * index can only be scanned in forward direction + * HA_EXTRA_KEYREAD will not be used + Perhaps these assumptions could be relaxed + + RETURN + index number + MAX_KEY if no such index was found. +*/ + +uint get_index_for_order(TABLE *table, ORDER *order, ha_rows limit) +{ + uint idx; + uint match_key= MAX_KEY, match_key_len= MAX_KEY_LENGTH + 1; + ORDER *ord; + + for (ord= order; ord; ord= ord->next) + if (!ord->asc) + return MAX_KEY; + + for (idx= 0; idx < table->s->keys; idx++) + { + if (!(table->keys_in_use_for_query.is_set(idx))) + continue; + KEY_PART_INFO *keyinfo= table->key_info[idx].key_part; + uint partno= 0; + + /* + The below check is sufficient considering we now have either BTREE + indexes (records are returned in order for any index prefix) or HASH + indexes (records are not returned in order for any index prefix). + */ + if (!(table->file->index_flags(idx, 0, 1) & HA_READ_ORDER)) + continue; + for (ord= order; ord; ord= ord->next, partno++) + { + Item *item= order->item[0]; + if (!(item->type() == Item::FIELD_ITEM && + ((Item_field*)item)->field->eq(keyinfo[partno].field))) + break; + } + + if (!ord && table->key_info[idx].key_length < match_key_len) + { + /* + Ok, the ordering is compatible and this key is shorter then + previous match (we want shorter keys as we'll have to read fewer + index pages for the same number of records) + */ + match_key= idx; + match_key_len= table->key_info[idx].key_length; + } + } + + if (match_key != MAX_KEY) + { + /* + Found an index that allows records to be retrieved in the requested + order. Now we'll check if using the index is cheaper then doing a table + scan. + */ + double full_scan_time= table->file->scan_time(); + double index_scan_time= table->file->read_time(match_key, 1, limit); + if (index_scan_time > full_scan_time) + match_key= MAX_KEY; + } + return match_key; +} + + +/* Table rows retrieval plan. Range optimizer creates QUICK_SELECT_I-derived objects from table read plans. */ @@ -3692,6 +3781,7 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, SEL_ARG *tree= 0; MEM_ROOT *alloc= param->mem_root; char *str; + ulong orig_sql_mode; DBUG_ENTER("get_mm_leaf"); /* @@ -3837,13 +3927,20 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, value->result_type() != STRING_RESULT && field->cmp_type() != value->result_type()) goto end; - + /* For comparison purposes allow invalid dates like 2000-01-32 */ + orig_sql_mode= field->table->in_use->variables.sql_mode; + if (value->real_item()->type() == Item::STRING_ITEM && + (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) { + 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 */ tree= &null_element; // cmp with NULL is never TRUE goto end; } + field->table->in_use->variables.sql_mode= orig_sql_mode; str= (char*) alloc_root(alloc, key_part->store_length+1); if (!str) goto end; @@ -7045,19 +7142,15 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) */ if (thd->query_id == cur_field->query_id) { - bool is_covered= FALSE; KEY_PART_INFO *key_part= cur_index_info->key_part; KEY_PART_INFO *key_part_end= key_part + cur_index_info->key_parts; - for (; key_part != key_part_end ; key_part++) + for (;;) { if (key_part->field == cur_field) - { - is_covered= TRUE; break; - } + if (++key_part == key_part_end) + goto next_index; // Field was not part of key } - if (!is_covered) - goto next_index; } } } diff --git a/sql/opt_range.h b/sql/opt_range.h index 37d77033c8d..f84058f3b64 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -716,4 +716,6 @@ public: QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, struct st_table_ref *ref, ha_rows records); +uint get_index_for_order(TABLE *table, ORDER *order, ha_rows limit); + #endif diff --git a/sql/parse_file.cc b/sql/parse_file.cc index 5c7053e6e2a..d3e5645bafc 100644 --- a/sql/parse_file.cc +++ b/sql/parse_file.cc @@ -372,8 +372,10 @@ my_bool rename_in_schema_file(const char *schema, const char *old_name, if (revision > 0 && !access(arc_path, F_OK)) { - ulonglong limit= (revision > num_view_backups) ? revision - num_view_backups : 0; - while (revision > limit) { + ulonglong limit= ((revision > num_view_backups) ? + revision - num_view_backups : 0); + for (; revision > limit ; revision--) + { my_snprintf(old_path, FN_REFLEN, "%s/%s%s-%04lu", arc_path, old_name, reg_ext, (ulong)revision); (void) unpack_filename(old_path, old_path); @@ -381,7 +383,6 @@ my_bool rename_in_schema_file(const char *schema, const char *old_name, arc_path, new_name, reg_ext, (ulong)revision); (void) unpack_filename(new_path, new_path); my_rename(old_path, new_path, MYF(0)); - revision--; } } return 0; diff --git a/sql/password.c b/sql/password.c index aa05be8c740..562df3ae226 100644 --- a/sql/password.c +++ b/sql/password.c @@ -316,18 +316,21 @@ void create_random_string(char *to, uint length, struct rand_struct *rand_st) octet2hex() buf OUT output buffer. Must be at least 2*len+1 bytes str, len IN the beginning and the length of the input string + + RETURN + buf+len*2 */ -void -octet2hex(char *to, const unsigned char *str, uint len) +char *octet2hex(char *to, const char *str, uint len) { - const uint8 *str_end= str + len; + const byte *str_end= str + len; for (; str != str_end; ++str) { - *to++= _dig_vec_upper[(*str & 0xF0) >> 4]; - *to++= _dig_vec_upper[*str & 0x0F]; + *to++= _dig_vec_upper[((uchar) *str) >> 4]; + *to++= _dig_vec_upper[((uchar) *str) & 0x0F]; } *to= '\0'; + return to; } diff --git a/sql/protocol.cc b/sql/protocol.cc index ade94a483a8..8c3e5a62820 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -76,6 +76,8 @@ void net_send_error(THD *thd, uint sql_errno, const char *err) 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 @@ -181,6 +183,8 @@ net_printf_error(THD *thd, uint errcode, ...) 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 diff --git a/sql/records.cc b/sql/records.cc index b3610cf1bbf..4958e39a5a0 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -28,8 +28,50 @@ static int rr_from_pointers(READ_RECORD *info); static int rr_from_cache(READ_RECORD *info); static int init_rr_cache(THD *thd, READ_RECORD *info); static int rr_cmp(uchar *a,uchar *b); +static int rr_index_first(READ_RECORD *info); +static int rr_index(READ_RECORD *info); + + +/* + Initialize READ_RECORD structure to perform full index scan + + SYNOPSIS + init_read_record_idx() + info READ_RECORD structure to initialize. + thd Thread handle + table Table to be accessed + print_error If true, call table->file->print_error() if an error + occurs (except for end-of-records error) + idx index to scan + + DESCRIPTION + Initialize READ_RECORD structure to perform full index scan (in forward + direction) using read_record.read_record() interface. + + This function has been added at late stage and is used only by + UPDATE/DELETE. Other statements perform index scans using + join_read_first/next functions. +*/ + +void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, + bool print_error, uint idx) +{ + bzero((char*) info,sizeof(*info)); + info->table= table; + info->file= table->file; + info->record= table->record[0]; + info->print_error= print_error; + + table->status=0; /* And it's always found */ + if (!table->file->inited) + { + table->file->ha_index_init(idx); + table->file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY); + } + /* read_record will be changed to rr_index in rr_index_first */ + info->read_record= rr_index_first; +} - /* init struct for read with info->read_record */ /* init_read_record is used to scan by using a number of different methods. @@ -225,6 +267,21 @@ void end_read_record(READ_RECORD *info) } } +static int rr_handle_error(READ_RECORD *info, int error) +{ + if (error == HA_ERR_END_OF_FILE) + error= -1; + else + { + if (info->print_error) + info->table->file->print_error(error, MYF(0)); + if (error < 0) // Fix negative BDB errno + error= 1; + } + return error; +} + + /* Read a record from head-database */ static int rr_quick(READ_RECORD *info) @@ -239,15 +296,7 @@ static int rr_quick(READ_RECORD *info) } if (tmp != HA_ERR_RECORD_DELETED) { - if (tmp == HA_ERR_END_OF_FILE) - tmp= -1; - else - { - if (info->print_error) - info->file->print_error(tmp,MYF(0)); - if (tmp < 0) // Fix negative BDB errno - tmp=1; - } + tmp= rr_handle_error(info, tmp); break; } } @@ -255,6 +304,57 @@ static int rr_quick(READ_RECORD *info) } +/* + Reads first row in an index scan + + SYNOPSIS + rr_index_first() + info Scan info + + RETURN + 0 Ok + -1 End of records + 1 Error +*/ + + +static int rr_index_first(READ_RECORD *info) +{ + int tmp= info->file->index_first(info->record); + info->read_record= rr_index; + if (tmp) + tmp= rr_handle_error(info, tmp); + return tmp; +} + + +/* + Reads index sequentially after first row + + SYNOPSIS + rr_index() + info Scan info + + DESCRIPTION + Read the next index record (in forward direction) and translate return + value. + + RETURN + 0 Ok + -1 End of records + 1 Error +*/ + + +static int rr_index(READ_RECORD *info) +{ + int tmp= info->file->index_next(info->record); + if (tmp) + tmp= rr_handle_error(info, tmp); + return tmp; +} + + static int rr_sequential(READ_RECORD *info) { int tmp; @@ -265,17 +365,13 @@ static int rr_sequential(READ_RECORD *info) info->thd->send_kill_message(); return 1; } + /* + rnd_next can return RECORD_DELETED for MyISAM when one thread is + reading and another deleting without locks. + */ if (tmp != HA_ERR_RECORD_DELETED) { - if (tmp == HA_ERR_END_OF_FILE) - tmp= -1; - else - { - if (info->print_error) - info->table->file->print_error(tmp,MYF(0)); - if (tmp < 0) // Fix negative BDB errno - tmp=1; - } + tmp= rr_handle_error(info, tmp); break; } } @@ -286,23 +382,18 @@ static int rr_sequential(READ_RECORD *info) static int rr_from_tempfile(READ_RECORD *info) { int tmp; -tryNext: - if (my_b_read(info->io_cache,info->ref_pos,info->ref_length)) - return -1; /* End of file */ - if ((tmp=info->file->rnd_pos(info->record,info->ref_pos))) + for (;;) { - if (tmp == HA_ERR_END_OF_FILE) - tmp= -1; - else if (tmp == HA_ERR_RECORD_DELETED || - (tmp == HA_ERR_KEY_NOT_FOUND && info->ignore_not_found_rows)) - goto tryNext; - else - { - if (info->print_error) - info->file->print_error(tmp,MYF(0)); - if (tmp < 0) // Fix negative BDB errno - tmp=1; - } + if (my_b_read(info->io_cache,info->ref_pos,info->ref_length)) + return -1; /* End of file */ + if (!(tmp=info->file->rnd_pos(info->record,info->ref_pos))) + break; + /* The following is extremely unlikely to happen */ + if (tmp == HA_ERR_RECORD_DELETED || + (tmp == HA_ERR_KEY_NOT_FOUND && info->ignore_not_found_rows)) + continue; + tmp= rr_handle_error(info, tmp); + break; } return tmp; } /* rr_from_tempfile */ @@ -340,26 +431,23 @@ static int rr_from_pointers(READ_RECORD *info) { int tmp; byte *cache_pos; -tryNext: - if (info->cache_pos == info->cache_end) - return -1; /* End of file */ - cache_pos=info->cache_pos; - info->cache_pos+=info->ref_length; - if ((tmp=info->file->rnd_pos(info->record,cache_pos))) + for (;;) { - if (tmp == HA_ERR_END_OF_FILE) - tmp= -1; - else if (tmp == HA_ERR_RECORD_DELETED || - (tmp == HA_ERR_KEY_NOT_FOUND && info->ignore_not_found_rows)) - goto tryNext; - else - { - if (info->print_error) - info->file->print_error(tmp,MYF(0)); - if (tmp < 0) // Fix negative BDB errno - tmp=1; - } + if (info->cache_pos == info->cache_end) + return -1; /* End of file */ + cache_pos= info->cache_pos; + info->cache_pos+= info->ref_length; + + if (!(tmp=info->file->rnd_pos(info->record,cache_pos))) + break; + + /* The following is extremely unlikely to happen */ + if (tmp == HA_ERR_RECORD_DELETED || + (tmp == HA_ERR_KEY_NOT_FOUND && info->ignore_not_found_rows)) + continue; + tmp= rr_handle_error(info, tmp); + break; } return tmp; } diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 35035a8b5a5..823930121fe 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -68,7 +68,6 @@ static int init_failsafe_rpl_thread(THD* thd) */ thd->system_thread = thd->bootstrap = 1; thd->security_ctx->skip_grants(); - thd->client_capabilities = 0; my_net_init(&thd->net, 0); thd->net.read_timeout = slave_net_timeout; thd->max_client_packet_length=thd->net.max_packet; diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 3aabc4e5b1f..5c2cc77d66a 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5210,8 +5210,7 @@ ER_WARN_VIEW_WITHOUT_KEY rus "ïÂÎÏ×ÌÑÅÍÙÊ view ÎÅ ÓÏÄÅÒÖÉÔ ËÌÀÞÁ ÉÓÐÏÌØÚÏ×ÁÎÎÙÈ(ÏÊ) × ÎÅÍ ÔÁÂÌÉÃ(Ù)" ukr "View, ÝÏ ÏÎÏ×ÌÀÅÔØÓÑ, ΊͦÓÔÉÔØ ÐÏ×ÎÏÇÏ ËÌÀÞÁ ÔÁÂÌÉæ(Ø), ÝÏ ×ÉËÏÒ¦ÓÔÁÎÁ × ÎØÀÏÍÕ" ER_VIEW_INVALID - eng "View '%-.64s.%-.64s' references invalid table(s) or column(s) or function(s)" - rus "View '%-.64s.%-.64s' ÓÓÙÌÁÅÔÓÑ ÎÁ ÎÅÓÕÝÅÓÔ×ÕÀÝÉÅ ÔÁÂÌÉÃÙ ÉÌÉ ÓÔÏÌÂÃÙ ÉÌÉ ÆÕÎËÃÉÉ" + eng "View '%-.64s.%-.64s' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them" ER_SP_NO_DROP_SP eng "Can't drop or alter a %s from within another stored routine" ER_SP_GOTO_IN_HNDLR @@ -5420,6 +5419,8 @@ ER_ROW_IS_REFERENCED_2 23000 eng "Cannot delete or update a parent row: a foreign key constraint fails (%.192s)" ER_NO_REFERENCED_ROW_2 23000 eng "Cannot add or update a child row: a foreign key constraint fails (%.192s)" +ER_SP_BAD_VAR_SHADOW 42000 + eng "Variable '%-.64s' must be quoted with `...`, or renamed" ER_PARTITION_REQUIRES_VALUES_ERROR eng "%s PARTITIONING requires definition of VALUES %s for each partition" swe "%s PARTITIONering kräver definition av VALUES %s för varje partition" diff --git a/sql/slave.cc b/sql/slave.cc index 279be4d9c8c..2ae51e37930 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -564,6 +564,20 @@ int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset, rli->slave_skip_counter=0; pthread_mutex_lock(&rli->data_lock); + + /* + we close the relay log fd possibly left open by the slave SQL thread, + to be able to delete it; the relay log fd possibly left open by the slave + I/O thread will be closed naturally in reset_logs() by the + close(LOG_CLOSE_TO_BE_OPENED) call + */ + if (rli->cur_log_fd >= 0) + { + end_io_cache(&rli->cache_buf); + my_close(rli->cur_log_fd, MYF(MY_WME)); + rli->cur_log_fd= -1; + } + if (rli->relay_log.reset_logs(thd)) { *errmsg = "Failed during log reset"; @@ -1393,9 +1407,26 @@ static int init_relay_log_info(RELAY_LOG_INFO* rli, { char buf[FN_REFLEN]; const char *ln; + static bool name_warning_sent= 0; ln= rli->relay_log.generate_name(opt_relay_logname, "-relay-bin", 1, buf); - + /* We send the warning only at startup, not after every RESET SLAVE */ + if (!opt_relay_logname && !opt_relaylog_index_name && !name_warning_sent) + { + /* + User didn't give us info to name the relay log index file. + Picking `hostname`-relay-bin.index like we do, causes replication to + fail if this slave's hostname is changed later. So, we would like to + instead require a name. But as we don't want to break many existing + setups, we only give warning, not error. + */ + sql_print_warning("Neither --relay-log nor --relay-log-index were used;" + " so replication " + "may break when this MySQL server acts as a " + "slave and has his hostname changed!! Please " + "use '--relay-log=%s' to avoid this problem.", ln); + name_warning_sent= 1; + } /* note, that if open() fails, we'll still have index file open but a destructor will take care of that @@ -1619,6 +1650,55 @@ static int count_relay_log_space(RELAY_LOG_INFO* rli) } +/* + Builds a Rotate from the ignored events' info and writes it to relay log. + + SYNOPSIS + write_ignored_events_info_to_relay_log() + thd pointer to I/O thread's thd + mi + + DESCRIPTION + Slave I/O thread, going to die, must leave a durable trace of the + ignored events' end position for the use of the slave SQL thread, by + calling this function. Only that thread can call it (see assertion). + */ +static void write_ignored_events_info_to_relay_log(THD *thd, MASTER_INFO *mi) +{ + RELAY_LOG_INFO *rli= &mi->rli; + pthread_mutex_t *log_lock= rli->relay_log.get_log_lock(); + DBUG_ASSERT(thd == mi->io_thd); + pthread_mutex_lock(log_lock); + if (rli->ign_master_log_name_end[0]) + { + DBUG_PRINT("info",("writing a Rotate event to track down ignored events")); + Rotate_log_event *ev= new Rotate_log_event(thd, rli->ign_master_log_name_end, + 0, rli->ign_master_log_pos_end, + Rotate_log_event::DUP_NAME); + rli->ign_master_log_name_end[0]= 0; + /* can unlock before writing as slave SQL thd will soon see our Rotate */ + pthread_mutex_unlock(log_lock); + if (likely((bool)ev)) + { + ev->server_id= 0; // don't be ignored by slave SQL thread + if (unlikely(rli->relay_log.append(ev))) + sql_print_error("Slave I/O thread failed to write a Rotate event" + " to the relay log, " + "SHOW SLAVE STATUS may be inaccurate"); + rli->relay_log.harvest_bytes_written(&rli->log_space_total); + flush_master_info(mi, 1); + delete ev; + } + else + sql_print_error("Slave I/O thread failed to create a Rotate event" + " (out of memory?), " + "SHOW SLAVE STATUS may be inaccurate"); + } + else + pthread_mutex_unlock(log_lock); +} + + void init_master_info_with_options(MASTER_INFO* mi) { mi->master_log_name[0] = 0; @@ -2168,7 +2248,7 @@ st_relay_log_info::st_relay_log_info() { group_relay_log_name[0]= event_relay_log_name[0]= group_master_log_name[0]= 0; - last_slave_error[0]=0; until_log_name[0]= 0; + last_slave_error[0]= until_log_name[0]= ign_master_log_name_end[0]= 0; bzero((char*) &info_file, sizeof(info_file)); bzero((char*) &cache_buf, sizeof(cache_buf)); @@ -2371,7 +2451,7 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, else pthread_cond_wait(&data_cond, &data_lock); DBUG_PRINT("info",("Got signal of master update or timed out")); - if (error == ETIMEDOUT) + if (error == ETIMEDOUT || error == ETIME) { error= -1; break; @@ -2427,7 +2507,6 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type) thd->system_thread = (thd_type == SLAVE_THD_SQL) ? SYSTEM_THREAD_SLAVE_SQL : SYSTEM_THREAD_SLAVE_IO; thd->security_ctx->skip_grants(); - thd->client_capabilities = 0; my_net_init(&thd->net, 0); thd->net.read_timeout = slave_net_timeout; thd->slave_thread = 1; @@ -2761,12 +2840,20 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) wait for something for example inside of next_event(). */ pthread_mutex_lock(&rli->data_lock); - + /* + This tests if the position of the end of the last previous executed event + hits the UNTIL barrier. + We would prefer to test if the position of the start (or possibly) end of + the to-be-read event hits the UNTIL barrier, this is different if there + was an event ignored by the I/O thread just before (BUG#13861 to be + fixed). + */ if (rli->until_condition!=RELAY_LOG_INFO::UNTIL_NONE && rli->is_until_satisfied()) { + char buf[22]; sql_print_error("Slave SQL thread stopped because it reached its" - " UNTIL position %ld", (long) rli->until_pos()); + " UNTIL position %s", llstr(rli->until_pos(), buf)); /* Setting abort_slave flag because we do not want additional message about error in query execution to be printed. @@ -2954,6 +3041,7 @@ pthread_handler_t handle_slave_io(void *arg) THD *thd; // needs to be first for thread_stack MYSQL *mysql; MASTER_INFO *mi = (MASTER_INFO*)arg; + RELAY_LOG_INFO *rli= &mi->rli; char llbuff[22]; uint retry_count; @@ -3196,16 +3284,16 @@ reconnect done to recover from failed read"); char llbuf1[22], llbuf2[22]; DBUG_PRINT("info", ("log_space_limit=%s log_space_total=%s \ ignore_log_space_limit=%d", - llstr(mi->rli.log_space_limit,llbuf1), - llstr(mi->rli.log_space_total,llbuf2), - (int) mi->rli.ignore_log_space_limit)); + llstr(rli->log_space_limit,llbuf1), + llstr(rli->log_space_total,llbuf2), + (int) rli->ignore_log_space_limit)); } #endif - if (mi->rli.log_space_limit && mi->rli.log_space_limit < - mi->rli.log_space_total && - !mi->rli.ignore_log_space_limit) - if (wait_for_relay_log_space(&mi->rli)) + if (rli->log_space_limit && rli->log_space_limit < + rli->log_space_total && + !rli->ignore_log_space_limit) + if (wait_for_relay_log_space(rli)) { sql_print_error("Slave I/O thread aborted while waiting for relay \ log space"); @@ -3236,10 +3324,12 @@ err: mysql_close(mysql); mi->mysql=0; } + 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; @@ -3618,6 +3708,7 @@ static int process_io_rotate(MASTER_INFO *mi, Rotate_log_event *rev) if (unlikely(!rev->is_valid())) DBUG_RETURN(1); + /* Safe copy as 'rev' has been "sanitized" in Rotate_log_event's ctor */ memcpy(mi->master_log_name, rev->new_log_ident, rev->ident_len+1); mi->master_log_pos= rev->pos; DBUG_PRINT("info", ("master_log_pos: '%s' %d", @@ -3868,6 +3959,7 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) int error= 0; ulong inc_pos; RELAY_LOG_INFO *rli= &mi->rli; + pthread_mutex_t *log_lock= rli->relay_log.get_log_lock(); DBUG_ENTER("queue_event"); if (mi->rli.relay_log.description_event_for_queue->binlog_version<4 && @@ -3876,11 +3968,6 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) pthread_mutex_lock(&mi->data_lock); - /* - TODO: figure out if other events in addition to Rotate - require special processing. - Guilhem 2003-06 : I don't think so. - */ switch (buf[EVENT_TYPE_OFFSET]) { case STOP_EVENT: /* @@ -3965,14 +4052,21 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) direct master (an unsupported, useless setup!). */ + pthread_mutex_lock(log_lock); + if ((uint4korr(buf + SERVER_ID_OFFSET) == ::server_id) && !replicate_same_server_id) { /* Do not write it to the relay log. - We still want to increment, so that we won't re-read this event from the - master if the slave IO thread is now stopped/restarted (more efficient if - the events we are ignoring are big LOAD DATA INFILE). + a) We still want to increment mi->master_log_pos, so that we won't + re-read this event from the master if the slave IO thread is now + stopped/restarted (more efficient if the events we are ignoring are big + LOAD DATA INFILE). + b) We want to record that we are skipping events, for the information of + the slave SQL thread, otherwise that thread may let + rli->group_relay_log_pos stay too small if the last binlog's event is + ignored. But events which were generated by this slave and which do not exist in the master's binlog (i.e. Format_desc, Rotate & Stop) should not increment mi->master_log_pos. @@ -3980,7 +4074,13 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) if (buf[EVENT_TYPE_OFFSET]!=FORMAT_DESCRIPTION_EVENT && buf[EVENT_TYPE_OFFSET]!=ROTATE_EVENT && buf[EVENT_TYPE_OFFSET]!=STOP_EVENT) + { mi->master_log_pos+= inc_pos; + memcpy(rli->ign_master_log_name_end, mi->master_log_name, FN_REFLEN); + DBUG_ASSERT(rli->ign_master_log_name_end[0]); + rli->ign_master_log_pos_end= mi->master_log_pos; + } + rli->relay_log.signal_update(); // the slave SQL thread needs to re-check DBUG_PRINT("info", ("master_log_pos: %d, event originating from the same server, ignored", (ulong) mi->master_log_pos)); } else @@ -3993,8 +4093,11 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) rli->relay_log.harvest_bytes_written(&rli->log_space_total); } else - error=3; + error= 3; + rli->ign_master_log_name_end[0]= 0; // last event is not ignored } + pthread_mutex_unlock(log_lock); + err: pthread_mutex_unlock(&mi->data_lock); @@ -4377,7 +4480,28 @@ Log_event* next_event(RELAY_LOG_INFO* rli) time_t save_timestamp= rli->last_master_timestamp; rli->last_master_timestamp= 0; - DBUG_ASSERT(rli->relay_log.get_open_count() == rli->cur_log_old_open_count); + DBUG_ASSERT(rli->relay_log.get_open_count() == + rli->cur_log_old_open_count); + + if (rli->ign_master_log_name_end[0]) + { + /* We generate and return a Rotate, to make our positions advance */ + DBUG_PRINT("info",("seeing an ignored end segment")); + ev= new Rotate_log_event(thd, rli->ign_master_log_name_end, + 0, rli->ign_master_log_pos_end, + Rotate_log_event::DUP_NAME); + rli->ign_master_log_name_end[0]= 0; + pthread_mutex_unlock(log_lock); + if (unlikely(!ev)) + { + errmsg= "Slave SQL thread failed to create a Rotate event " + "(out of memory?), SHOW SLAVE STATUS may be inaccurate"; + goto err; + } + ev->server_id= 0; // don't be ignored by slave SQL thread + DBUG_RETURN(ev); + } + /* We can, and should release data_lock while we are waiting for update. If we do not, show slave status will block diff --git a/sql/slave.h b/sql/slave.h index d99145cbe47..c994bfb2d34 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -304,6 +304,17 @@ typedef struct st_relay_log_info */ ulong trans_retries, retried_trans; + /* + If the end of the hot relay log is made of master's events ignored by the + slave I/O thread, these two keep track of the coords (in the master's + binlog) of the last of these events seen by the slave I/O thread. If not, + ign_master_log_name_end[0] == 0. + As they are like a Rotate event read/written from/to the relay log, they + are both protected by rli->relay_log.LOCK_log. + */ + char ign_master_log_name_end[FN_REFLEN]; + ulonglong ign_master_log_pos_end; + st_relay_log_info(); ~st_relay_log_info(); diff --git a/sql/sp.cc b/sql/sp.cc index 18d94a85884..b385c6457a5 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -208,7 +208,7 @@ db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table) { byte key[MAX_KEY_LENGTH]; // db, name, optional key length type DBUG_ENTER("db_find_routine_aux"); - DBUG_PRINT("enter", ("type: %d name: %*s", + DBUG_PRINT("enter", ("type: %d name: %.*s", type, name->m_name.length, name->m_name.str)); /* @@ -275,7 +275,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) ulong sql_mode; Open_tables_state open_tables_state_backup; DBUG_ENTER("db_find_routine"); - DBUG_PRINT("enter", ("type: %d name: %*s", + DBUG_PRINT("enter", ("type: %d name: %.*s", type, name->m_name.length, name->m_name.str)); *sphp= 0; // In case of errors @@ -479,7 +479,8 @@ db_create_routine(THD *thd, int type, sp_head *sp) char olddb[128]; bool dbchanged; DBUG_ENTER("db_create_routine"); - DBUG_PRINT("enter", ("type: %d name: %*s",type,sp->m_name.length,sp->m_name.str)); + DBUG_PRINT("enter", ("type: %d name: %.*s",type,sp->m_name.length, + sp->m_name.str)); dbchanged= FALSE; if ((ret= sp_use_new_db(thd, sp->m_db.str, olddb, sizeof(olddb), @@ -606,7 +607,7 @@ db_drop_routine(THD *thd, int type, sp_name *name) TABLE *table; int ret; DBUG_ENTER("db_drop_routine"); - DBUG_PRINT("enter", ("type: %d name: %*s", + DBUG_PRINT("enter", ("type: %d name: %.*s", type, name->m_name.length, name->m_name.str)); if (!(table= open_proc_table_for_update(thd))) @@ -628,7 +629,7 @@ db_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) int ret; bool opened; DBUG_ENTER("db_update_routine"); - DBUG_PRINT("enter", ("type: %d name: %*s", + DBUG_PRINT("enter", ("type: %d name: %.*s", type, name->m_name.length, name->m_name.str)); if (!(table= open_proc_table_for_update(thd))) @@ -922,7 +923,7 @@ sp_find_procedure(THD *thd, sp_name *name, bool cache_only) { sp_head *sp; DBUG_ENTER("sp_find_procedure"); - DBUG_PRINT("enter", ("name: %*s.%*s", + DBUG_PRINT("enter", ("name: %.*s.%.*s", name->m_db.length, name->m_db.str, name->m_name.length, name->m_name.str)); @@ -980,7 +981,7 @@ sp_create_procedure(THD *thd, sp_head *sp) { int ret; DBUG_ENTER("sp_create_procedure"); - DBUG_PRINT("enter", ("name: %*s", sp->m_name.length, sp->m_name.str)); + DBUG_PRINT("enter", ("name: %.*s", sp->m_name.length, sp->m_name.str)); ret= db_create_routine(thd, TYPE_ENUM_PROCEDURE, sp); DBUG_RETURN(ret); @@ -992,7 +993,7 @@ sp_drop_procedure(THD *thd, sp_name *name) { int ret; DBUG_ENTER("sp_drop_procedure"); - DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); + DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str)); ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name); if (!ret) @@ -1006,7 +1007,7 @@ sp_update_procedure(THD *thd, sp_name *name, st_sp_chistics *chistics) { int ret; DBUG_ENTER("sp_update_procedure"); - DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); + DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str)); ret= db_update_routine(thd, TYPE_ENUM_PROCEDURE, name, chistics); if (!ret) @@ -1020,7 +1021,7 @@ sp_show_create_procedure(THD *thd, sp_name *name) { sp_head *sp; DBUG_ENTER("sp_show_create_procedure"); - DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); + DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str)); if ((sp= sp_find_procedure(thd, name))) { @@ -1072,7 +1073,7 @@ sp_find_function(THD *thd, sp_name *name, bool cache_only) { sp_head *sp; DBUG_ENTER("sp_find_function"); - DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); + DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str)); if (!(sp= sp_cache_lookup(&thd->sp_func_cache, name)) && !cache_only) @@ -1089,7 +1090,7 @@ sp_create_function(THD *thd, sp_head *sp) { int ret; DBUG_ENTER("sp_create_function"); - DBUG_PRINT("enter", ("name: %*s", sp->m_name.length, sp->m_name.str)); + DBUG_PRINT("enter", ("name: %.*s", sp->m_name.length, sp->m_name.str)); ret= db_create_routine(thd, TYPE_ENUM_FUNCTION, sp); DBUG_RETURN(ret); @@ -1101,7 +1102,7 @@ sp_drop_function(THD *thd, sp_name *name) { int ret; DBUG_ENTER("sp_drop_function"); - DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); + DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str)); ret= db_drop_routine(thd, TYPE_ENUM_FUNCTION, name); if (!ret) @@ -1115,7 +1116,7 @@ sp_update_function(THD *thd, sp_name *name, st_sp_chistics *chistics) { int ret; DBUG_ENTER("sp_update_procedure"); - DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); + DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str)); ret= db_update_routine(thd, TYPE_ENUM_FUNCTION, name, chistics); if (!ret) @@ -1129,7 +1130,7 @@ sp_show_create_function(THD *thd, sp_name *name) { sp_head *sp; DBUG_ENTER("sp_show_create_function"); - DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); + DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str)); if ((sp= sp_find_function(thd, name))) { diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index 495f969eeac..fea6a67f32c 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -132,7 +132,7 @@ void sp_cache_insert(sp_cache **cp, sp_head *sp) return; // End of memory error c->version= Cversion; // No need to lock when reading long variable } - DBUG_PRINT("info",("sp_cache: inserting: %*s", sp->m_qname.length, + DBUG_PRINT("info",("sp_cache: inserting: %.*s", sp->m_qname.length, sp->m_qname.str)); c->insert(sp); *cp= c; // Update *cp if it was NULL diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 671acbc2a0c..abc66ce0b21 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -280,7 +280,7 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type, DBUG_PRINT("info", ("STRING_RESULT: null")); goto return_null_item; } - DBUG_PRINT("info",("STRING_RESULT: %*s", + DBUG_PRINT("info",("STRING_RESULT: %.*s", s->length(), s->c_ptr_quick())); /* Reuse mechanism in sp_eval_func_item() is only employed for assignments @@ -293,6 +293,22 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type, if (it == reuse) DBUG_RETURN(it); + /* + For some functions, 's' is now pointing to an argument of the + function, which might be a local variable that is to be reused. + In this case, new(reuse, &rsize) below will call the destructor + and 's' ends up pointing to freed memory. + A somewhat ugly fix is to simply copy the string to our local one + (which is unused by most functions anyway), but only if 's' is + pointing somewhere else than to 'tmp' or 'it->str_value'. + */ + if (reuse && s != &tmp && s != &it->str_value) + { + if (tmp.copy((const String)(*s))) + DBUG_RETURN(NULL); + s= &tmp; + } + CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_string(it->collation.collation), use_callers_arena, &backup_arena); @@ -323,7 +339,7 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type, return_null_item: CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_null(), - use_callers_arena, &backup_arena); + use_callers_arena, &backup_arena); end: it->rsize= rsize; @@ -354,7 +370,7 @@ sp_name::init_qname(THD *thd) return; m_qname.length= m_sroutines_key.length - 1; m_qname.str= m_sroutines_key.str + 1; - sprintf(m_qname.str, "%*s.%*s", + sprintf(m_qname.str, "%.*s.%.*s", m_db.length, (m_db.length ? m_db.str : ""), m_name.length, m_name.str); } @@ -794,6 +810,7 @@ static bool subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) splocal < sp_vars_uses.back(); splocal++) { Item *val; + (*splocal)->thd= thd; // fix_fields() is not yet done /* append the text between sp ref occurences */ res|= qbuf.append(cur + prev_pos, (*splocal)->pos_in_query - prev_pos); prev_pos= (*splocal)->pos_in_query + (*splocal)->m_name.length; @@ -1051,8 +1068,10 @@ int sp_head::execute(THD *thd) original thd->db will then have been freed */ if (dbchanged) { + /* No access check when changing back to where we came from. + (It would generate an error from mysql_change_db() when olddb=="") */ if (! thd->killed) - ret= mysql_change_db(thd, olddb, 0); + ret= mysql_change_db(thd, olddb, 1); } m_flags&= ~IS_INVOKED; DBUG_RETURN(ret); @@ -1110,7 +1129,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) DBUG_RETURN(-1); // QQ Should have some error checking here? (types, etc...) - if (!(nctx= new sp_rcontext(csize, hmax, cmax))) + if (!(nctx= new sp_rcontext(octx, csize, hmax, cmax))) goto end; for (i= 0 ; i < argcount ; i++) { @@ -1254,7 +1273,7 @@ int sp_head::execute_procedure(THD *thd, List<Item> *args) save_spcont= octx= thd->spcont; if (! octx) { // Create a temporary old context - if (!(octx= new sp_rcontext(csize, hmax, cmax))) + if (!(octx= new sp_rcontext(octx, csize, hmax, cmax))) DBUG_RETURN(-1); thd->spcont= octx; @@ -1262,7 +1281,7 @@ int sp_head::execute_procedure(THD *thd, List<Item> *args) thd->spcont->callers_arena= thd; } - if (!(nctx= new sp_rcontext(csize, hmax, cmax))) + if (!(nctx= new sp_rcontext(octx, csize, hmax, cmax))) { thd->spcont= save_spcont; DBUG_RETURN(-1); @@ -2390,7 +2409,10 @@ sp_instr_hreturn::print(String *str) str->append("hreturn "); str->qs_append(m_frame); if (m_dest) + { + str->append(' '); str->qs_append(m_dest); + } } @@ -2650,9 +2672,24 @@ sp_change_security_context(THD *thd, sp_head *sp, Security_context **backup) sp->m_definer_host.str, sp->m_db.str)) { +#ifdef NOT_YET_REPLICATION_SAFE + /* + Until we don't properly replicate information about stored routine + definer with stored routine creation statement all stored routines + on slave are created under ''@'' definer. Therefore we won't be able + to run any routine which was replicated from master on slave server + if we emit error here. This will cause big problems for users + who use slave for fail-over. So until we fully implement WL#2897 + "Complete definer support in the stored routines" we run suid + stored routines for which we were unable to find definer under + invoker security context. + */ my_error(ER_NO_SUCH_USER, MYF(0), sp->m_definer_user.str, sp->m_definer_host.str); return TRUE; +#else + return FALSE; +#endif } *backup= thd->security_ctx; thd->security_ctx= &sp->m_security_ctx; diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 252bd7e5cab..ccb38358049 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -29,9 +29,9 @@ #include "sp_rcontext.h" #include "sp_pcontext.h" -sp_rcontext::sp_rcontext(uint fsize, uint hmax, uint cmax) +sp_rcontext::sp_rcontext(sp_rcontext *prev, uint fsize, uint hmax, uint cmax) : m_count(0), m_fsize(fsize), m_result(NULL), m_hcount(0), m_hsp(0), - m_ihsp(0), m_hfound(-1), m_ccount(0) + m_ihsp(0), m_hfound(-1), m_ccount(0), m_prev_ctx(prev) { m_frame= (Item **)sql_alloc(fsize * sizeof(Item*)); m_handler= (sp_handler_t *)sql_alloc(hmax * sizeof(sp_handler_t)); @@ -116,7 +116,11 @@ sp_rcontext::find_handler(uint sql_errno, } } if (found < 0) + { + if (m_prev_ctx) + return m_prev_ctx->find_handler(sql_errno, level); return FALSE; + } m_hfound= found; return TRUE; } diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 22fa4f6e865..cae5c5467c9 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -66,7 +66,7 @@ class sp_rcontext : public Sql_alloc */ Query_arena *callers_arena; - sp_rcontext(uint fsize, uint hmax, uint cmax); + sp_rcontext(sp_rcontext *prev, uint fsize, uint hmax, uint cmax); ~sp_rcontext() { @@ -149,6 +149,13 @@ class sp_rcontext : public Sql_alloc return m_handler[m_hfound].type; } + // Returns true if we found a handler in this context + inline bool + found_handler_here() + { + return (m_hfound >= 0); + } + // Clears the handler find state inline void clear_handler() @@ -226,6 +233,8 @@ private: sp_cursor **m_cstack; uint m_ccount; + sp_rcontext *m_prev_ctx; // Previous context (NULL if none) + }; // class sp_rcontext : public Sql_alloc diff --git a/sql/spatial.cc b/sql/spatial.cc index 176f1f2fbfe..5af1bec45ca 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -121,24 +121,21 @@ Geometry::Class_info *Geometry::find_class(const char *name, uint32 len) return 0; } -Geometry *Geometry::create_from_wkb(Geometry_buffer *buffer, - const char *data, uint32 data_len) + +Geometry *Geometry::construct(Geometry_buffer *buffer, + const char *data, uint32 data_len) { uint32 geom_type; Geometry *result; + char byte_order; - if (data_len < 1 + 4) + if (data_len < SRID_SIZE + WKB_HEADER_SIZE) // < 4 + (1 + 4) return NULL; - data++; - /* - FIXME: check byte ordering - Also check if we could replace this with one byte - */ - geom_type= uint4korr(data); - data+= 4; + byte_order= data[SRID_SIZE]; + geom_type= uint4korr(data + SRID_SIZE + 1); if (!(result= create_by_typeid(buffer, (int) geom_type))) return NULL; - result->m_data= data; + result->m_data= data+ SRID_SIZE + WKB_HEADER_SIZE; result->m_data_end= data + data_len; return result; } @@ -170,13 +167,71 @@ Geometry *Geometry::create_from_wkt(Geometry_buffer *buffer, return NULL; if (init_stream) { - result->init_from_wkb(wkt->ptr(), wkt->length()); + result->set_data_ptr(wkt->ptr(), wkt->length()); result->shift_wkb_header(); } return result; } +static double wkb_get_double(const char *ptr, Geometry::wkbByteOrder bo) +{ + double res; + if (bo != Geometry::wkb_xdr) + float8get(res, ptr); + else + { + char inv_array[8]; + inv_array[0]= ptr[7]; + inv_array[1]= ptr[6]; + inv_array[2]= ptr[5]; + inv_array[3]= ptr[4]; + inv_array[4]= ptr[3]; + inv_array[5]= ptr[2]; + inv_array[6]= ptr[1]; + inv_array[7]= ptr[0]; + float8get(res, inv_array); + } + return res; +} + + +static uint32 wkb_get_uint(const char *ptr, Geometry::wkbByteOrder bo) +{ + if (bo != Geometry::wkb_xdr) + return uint4korr(ptr); + /* else */ + { + char inv_array[4]; + inv_array[0]= ptr[3]; + inv_array[1]= ptr[2]; + inv_array[2]= ptr[1]; + inv_array[3]= ptr[0]; + return uint4korr(inv_array); + } +} + + +int Geometry::create_from_wkb(Geometry_buffer *buffer, + const char *wkb, uint32 len, String *res) +{ + uint32 geom_type; + Geometry *geom; + + if (len < WKB_HEADER_SIZE) + return 1; + geom_type= wkb_get_uint(wkb+1, (wkbByteOrder)wkb[0]); + if (!(geom= create_by_typeid(buffer, (int) geom_type)) || + res->reserve(WKB_HEADER_SIZE, 512)) + return 1; + + res->q_append((char) wkb_ndr); + res->q_append(geom_type); + return geom->init_from_wkb(wkb+WKB_HEADER_SIZE, len - WKB_HEADER_SIZE, + (wkbByteOrder) wkb[0], res); +} + + bool Geometry::envelope(String *result) const { MBR mbr; @@ -346,6 +401,20 @@ bool Gis_point::init_from_wkt(Gis_read_stream *trs, String *wkb) } +uint Gis_point::init_from_wkb(const char *wkb, uint len, + wkbByteOrder bo, String *res) +{ + double x, y; + if (len < POINT_DATA_SIZE || res->reserve(POINT_DATA_SIZE)) + return 0; + x= wkb_get_double(wkb, bo); + y= wkb_get_double(wkb + SIZEOF_STORED_DOUBLE, bo); + res->q_append(x); + res->q_append(y); + return POINT_DATA_SIZE; +} + + bool Gis_point::get_data_as_wkt(String *txt, const char **end) const { double x, y; @@ -415,6 +484,33 @@ bool Gis_line_string::init_from_wkt(Gis_read_stream *trs, String *wkb) } +uint Gis_line_string::init_from_wkb(const char *wkb, uint len, + wkbByteOrder bo, String *res) +{ + uint32 n_points, proper_length; + const char *wkb_end; + Gis_point p; + + if (len < 4) + return 0; + n_points= wkb_get_uint(wkb, bo); + proper_length= 4 + n_points * POINT_DATA_SIZE; + + if (len < proper_length || res->reserve(proper_length)) + return 0; + + res->q_append(n_points); + wkb_end= wkb + proper_length; + for (wkb+= 4; wkb<wkb_end; wkb+= POINT_DATA_SIZE) + { + if (!p.init_from_wkb(wkb, POINT_DATA_SIZE, bo, res)) + return 0; + } + + return proper_length; +} + + bool Gis_line_string::get_data_as_wkt(String *txt, const char **end) const { uint32 n_points; @@ -594,7 +690,7 @@ bool Gis_polygon::init_from_wkt(Gis_read_stream *trs, String *wkb) trs->check_next_symbol(')')) return 1; - ls.init_from_wkb(wkb->ptr()+ls_pos, wkb->length()-ls_pos); + ls.set_data_ptr(wkb->ptr() + ls_pos, wkb->length() - ls_pos); if (ls.is_closed(&closed) || !closed) { trs->set_error_msg("POLYGON's linear ring isn't closed"); @@ -609,6 +705,43 @@ bool Gis_polygon::init_from_wkt(Gis_read_stream *trs, String *wkb) } +uint Gis_polygon::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, + String *res) +{ + uint32 n_linear_rings; + const char *wkb_orig= wkb; + + if (len < 4) + return 0; + + n_linear_rings= wkb_get_uint(wkb, bo); + if (res->reserve(4, 512)) + return 0; + wkb+= 4; + len-= 4; + res->q_append(n_linear_rings); + + while (n_linear_rings--) + { + Gis_line_string ls; + uint32 ls_pos= res->length(); + int ls_len; + int closed; + + if (!(ls_len= ls.init_from_wkb(wkb, len, bo, res))) + return 0; + + ls.set_data_ptr(res->ptr() + ls_pos, res->length() - ls_pos); + + if (ls.is_closed(&closed) || !closed) + return 0; + wkb+= ls_len; + } + + return (uint) (wkb - wkb_orig); +} + + bool Gis_polygon::get_data_as_wkt(String *txt, const char **end) const { uint32 n_linear_rings; @@ -897,6 +1030,36 @@ bool Gis_multi_point::init_from_wkt(Gis_read_stream *trs, String *wkb) } +uint Gis_multi_point::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, + String *res) +{ + uint32 n_points; + uint proper_size; + Gis_point p; + const char *wkb_end; + + if (len < 4) + return 0; + n_points= wkb_get_uint(wkb, bo); + proper_size= 4 + n_points * (WKB_HEADER_SIZE + POINT_DATA_SIZE); + + if (len < proper_size || res->reserve(proper_size)) + return 0; + + res->q_append(n_points); + wkb_end= wkb + proper_size; + for (wkb+=4; wkb < wkb_end; wkb+= (WKB_HEADER_SIZE + POINT_DATA_SIZE)) + { + res->q_append((char)wkb_ndr); + res->q_append((uint32)wkb_point); + if (!p.init_from_wkb(wkb + WKB_HEADER_SIZE, + POINT_DATA_SIZE, (wkbByteOrder) wkb[0], res)) + return 0; + } + return proper_size; +} + + bool Gis_multi_point::get_data_as_wkt(String *txt, const char **end) const { uint32 n_points; @@ -1006,6 +1169,44 @@ bool Gis_multi_line_string::init_from_wkt(Gis_read_stream *trs, String *wkb) } +uint Gis_multi_line_string::init_from_wkb(const char *wkb, uint len, + wkbByteOrder bo, String *res) +{ + uint32 n_line_strings; + const char *wkb_orig= wkb; + + if (len < 4) + return 0; + n_line_strings= wkb_get_uint(wkb, bo); + + if (res->reserve(4, 512)) + return 0; + res->q_append(n_line_strings); + + wkb+= 4; + while (n_line_strings--) + { + Gis_line_string ls; + int ls_len; + + if ((len < WKB_HEADER_SIZE) || + res->reserve(WKB_HEADER_SIZE, 512)) + return 0; + + res->q_append((char) wkb_ndr); + res->q_append((uint32) wkb_linestring); + + if (!(ls_len= ls.init_from_wkb(wkb + WKB_HEADER_SIZE, len, + (wkbByteOrder) wkb[0], res))) + return 0; + ls_len+= WKB_HEADER_SIZE;; + wkb+= ls_len; + len-= ls_len; + } + return (uint) (wkb - wkb_orig); +} + + bool Gis_multi_line_string::get_data_as_wkt(String *txt, const char **end) const { @@ -1111,7 +1312,7 @@ int Gis_multi_line_string::length(double *len) const double ls_len; Gis_line_string ls; data+= WKB_HEADER_SIZE; - ls.init_from_wkb(data, (uint32) (m_data_end - data)); + ls.set_data_ptr(data, (uint32) (m_data_end - data)); if (ls.length(&ls_len)) return 1; *len+= ls_len; @@ -1140,7 +1341,7 @@ int Gis_multi_line_string::is_closed(int *closed) const Gis_line_string ls; if (no_data(data, 0)) return 1; - ls.init_from_wkb(data, (uint32) (m_data_end - data)); + ls.set_data_ptr(data, (uint32) (m_data_end - data)); if (ls.is_closed(closed)) return 1; if (!*closed) @@ -1222,6 +1423,43 @@ bool Gis_multi_polygon::init_from_wkt(Gis_read_stream *trs, String *wkb) } +uint Gis_multi_polygon::init_from_wkb(const char *wkb, uint len, + wkbByteOrder bo, String *res) +{ + uint32 n_poly; + const char *wkb_orig= wkb; + + if (len < 4) + return 0; + n_poly= wkb_get_uint(wkb, bo); + + if (res->reserve(4, 512)) + return 0; + res->q_append(n_poly); + + wkb+=4; + while (n_poly--) + { + Gis_polygon p; + int p_len; + + if (len < WKB_HEADER_SIZE || + res->reserve(WKB_HEADER_SIZE, 512)) + return 0; + res->q_append((char) wkb_ndr); + res->q_append((uint32) wkb_polygon); + + if (!(p_len= p.init_from_wkb(wkb + WKB_HEADER_SIZE, len, + (wkbByteOrder) wkb[0], res))) + return 0; + p_len+= WKB_HEADER_SIZE; + wkb+= p_len; + len-= p_len; + } + return (uint) (wkb - wkb_orig); +} + + bool Gis_multi_polygon::get_data_as_wkt(String *txt, const char **end) const { uint32 n_polygons; @@ -1358,7 +1596,7 @@ int Gis_multi_polygon::area(double *ar, const char **end_of_data) const Gis_polygon p; data+= WKB_HEADER_SIZE; - p.init_from_wkb(data, (uint32) (m_data_end - data)); + p.set_data_ptr(data, (uint32) (m_data_end - data)); if (p.area(&p_area, &data)) return 1; result+= p_area; @@ -1390,7 +1628,7 @@ int Gis_multi_polygon::centroid(String *result) const while (n_polygons--) { data+= WKB_HEADER_SIZE; - p.init_from_wkb(data, (uint32) (m_data_end - data)); + p.set_data_ptr(data, (uint32) (m_data_end - data)); if (p.area(&cur_area, &data) || p.centroid_xy(&cur_cx, &cur_cy)) return 1; @@ -1444,7 +1682,7 @@ uint32 Gis_geometry_collection::get_data_size() const if (!(geom= create_by_typeid(&buffer, wkb_type))) return GET_SIZE_ERROR; - geom->init_from_wkb(data, (uint) (m_data_end - data)); + geom->set_data_ptr(data, (uint) (m_data_end - data)); if ((object_size= geom->get_data_size()) == GET_SIZE_ERROR) return GET_SIZE_ERROR; data+= object_size; @@ -1484,6 +1722,48 @@ bool Gis_geometry_collection::init_from_wkt(Gis_read_stream *trs, String *wkb) } +uint Gis_geometry_collection::init_from_wkb(const char *wkb, uint len, + wkbByteOrder bo, String *res) +{ + uint32 n_geom; + const char *wkb_orig= wkb; + + if (len < 4) + return 0; + n_geom= wkb_get_uint(wkb, bo); + + if (res->reserve(4, 512)) + return 0; + res->q_append(n_geom); + + wkb+= 4; + while (n_geom--) + { + Geometry_buffer buffer; + Geometry *geom; + int g_len; + uint32 wkb_type; + + if (len < WKB_HEADER_SIZE || + res->reserve(WKB_HEADER_SIZE, 512)) + return 0; + + res->q_append((char) wkb_ndr); + wkb_type= wkb_get_uint(wkb+1, (wkbByteOrder) wkb[0]); + res->q_append(wkb_type); + + if (!(geom= create_by_typeid(&buffer, wkb_type)) || + !(g_len= geom->init_from_wkb(wkb + WKB_HEADER_SIZE, len, + (wkbByteOrder) wkb[0], res))) + return 0; + g_len+= WKB_HEADER_SIZE; + wkb+= g_len; + len-= g_len; + } + return (uint) (wkb - wkb_orig); +} + + bool Gis_geometry_collection::get_data_as_wkt(String *txt, const char **end) const { @@ -1508,7 +1788,7 @@ bool Gis_geometry_collection::get_data_as_wkt(String *txt, if (!(geom= create_by_typeid(&buffer, wkb_type))) return 1; - geom->init_from_wkb(data, (uint) (m_data_end - data)); + geom->set_data_ptr(data, (uint) (m_data_end - data)); if (geom->as_wkt(txt, &data)) return 1; if (txt->append(",", 1, 512)) @@ -1543,7 +1823,7 @@ bool Gis_geometry_collection::get_mbr(MBR *mbr, const char **end) const if (!(geom= create_by_typeid(&buffer, wkb_type))) return 1; - geom->init_from_wkb(data, (uint32) (m_data_end - data)); + geom->set_data_ptr(data, (uint32) (m_data_end - data)); if (geom->get_mbr(mbr, &data)) return 1; } @@ -1584,7 +1864,7 @@ int Gis_geometry_collection::geometry_n(uint32 num, String *result) const if (!(geom= create_by_typeid(&buffer, wkb_type))) return 1; - geom->init_from_wkb(data, (uint) (m_data_end - data)); + geom->set_data_ptr(data, (uint) (m_data_end - data)); if ((length= geom->get_data_size()) == GET_SIZE_ERROR) return 1; data+= length; @@ -1637,7 +1917,7 @@ bool Gis_geometry_collection::dimension(uint32 *res_dim, const char **end) const data+= WKB_HEADER_SIZE; if (!(geom= create_by_typeid(&buffer, wkb_type))) return 1; - geom->init_from_wkb(data, (uint32) (m_data_end - data)); + geom->set_data_ptr(data, (uint32) (m_data_end - data)); if (geom->dimension(&dim, &end_data)) return 1; set_if_bigger(*res_dim, dim); diff --git a/sql/spatial.h b/sql/spatial.h index b5ea7d641d1..4253689c078 100644 --- a/sql/spatial.h +++ b/sql/spatial.h @@ -207,6 +207,10 @@ public: virtual const Class_info *get_class_info() const=0; virtual uint32 get_data_size() const=0; virtual bool init_from_wkt(Gis_read_stream *trs, String *wkb)=0; + + /* returns the length of the wkb that was read */ + virtual uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, + String *res)=0; virtual bool get_data_as_wkt(String *txt, const char **end) const=0; virtual bool get_mbr(MBR *mbr, const char **end) const=0; virtual bool dimension(uint32 *dim, const char **end) const=0; @@ -236,11 +240,13 @@ public: return my_reinterpret_cast(Geometry *)(buffer); } - static Geometry *create_from_wkb(Geometry_buffer *buffer, - const char *data, uint32 data_len); + static Geometry *construct(Geometry_buffer *buffer, + const char *data, uint32 data_len); static Geometry *create_from_wkt(Geometry_buffer *buffer, Gis_read_stream *trs, String *wkt, bool init_stream=1); + static int create_from_wkb(Geometry_buffer *buffer, + const char *wkb, uint32 len, String *res); int as_wkt(String *wkt, const char **end) { uint32 len= get_class_info()->m_name.length; @@ -254,7 +260,7 @@ public: return 0; } - inline void init_from_wkb(const char *data, uint32 data_len) + inline void set_data_ptr(const char *data, uint32 data_len) { m_data= data; m_data_end= data + data_len; @@ -298,6 +304,7 @@ class Gis_point: public Geometry public: uint32 get_data_size() const; bool init_from_wkt(Gis_read_stream *trs, String *wkb); + uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); bool get_data_as_wkt(String *txt, const char **end) const; bool get_mbr(MBR *mbr, const char **end) const; @@ -344,6 +351,7 @@ class Gis_line_string: public Geometry public: uint32 get_data_size() const; bool init_from_wkt(Gis_read_stream *trs, String *wkb); + uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); bool get_data_as_wkt(String *txt, const char **end) const; bool get_mbr(MBR *mbr, const char **end) const; int length(double *len) const; @@ -369,6 +377,7 @@ class Gis_polygon: public Geometry public: uint32 get_data_size() const; bool init_from_wkt(Gis_read_stream *trs, String *wkb); + uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); bool get_data_as_wkt(String *txt, const char **end) const; bool get_mbr(MBR *mbr, const char **end) const; int area(double *ar, const char **end) const; @@ -394,6 +403,7 @@ class Gis_multi_point: public Geometry public: uint32 get_data_size() const; bool init_from_wkt(Gis_read_stream *trs, String *wkb); + uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); bool get_data_as_wkt(String *txt, const char **end) const; bool get_mbr(MBR *mbr, const char **end) const; int num_geometries(uint32 *num) const; @@ -415,6 +425,7 @@ class Gis_multi_line_string: public Geometry public: uint32 get_data_size() const; bool init_from_wkt(Gis_read_stream *trs, String *wkb); + uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); bool get_data_as_wkt(String *txt, const char **end) const; bool get_mbr(MBR *mbr, const char **end) const; int num_geometries(uint32 *num) const; @@ -438,6 +449,7 @@ class Gis_multi_polygon: public Geometry public: uint32 get_data_size() const; bool init_from_wkt(Gis_read_stream *trs, String *wkb); + uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); bool get_data_as_wkt(String *txt, const char **end) const; bool get_mbr(MBR *mbr, const char **end) const; int num_geometries(uint32 *num) const; @@ -461,6 +473,7 @@ class Gis_geometry_collection: public Geometry public: uint32 get_data_size() const; bool init_from_wkt(Gis_read_stream *trs, String *wkb); + uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res); bool get_data_as_wkt(String *txt, const char **end) const; bool get_mbr(MBR *mbr, const char **end) const; int num_geometries(uint32 *num) const; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 7a87f01258a..c47507bdd27 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -193,6 +193,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) my_bool return_val= 1; bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE; char tmp_name[NAME_LEN+1]; + int password_length; DBUG_ENTER("acl_load"); grant_version++; /* Privileges updated */ @@ -249,7 +250,9 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0); VOID(my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100)); - if (table->field[2]->field_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323) + password_length= table->field[2]->field_length / + table->field[2]->charset()->mbmaxlen; + if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323) { sql_print_error("Fatal error: mysql.user table is damaged or in " "unsupported 3.20 format."); @@ -257,10 +260,10 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) } DBUG_PRINT("info",("user table fields: %d, password length: %d", - table->s->fields, table->field[2]->field_length)); + table->s->fields, password_length)); pthread_mutex_lock(&LOCK_global_system_variables); - if (table->field[2]->field_length < SCRAMBLED_PASSWORD_CHAR_LENGTH) + if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH) { if (opt_secure_auth) { @@ -930,6 +933,9 @@ bool acl_getroot_no_password(Security_context *sctx, char *user, char *host, ACL_USER *acl_user= 0; DBUG_ENTER("acl_getroot_no_password"); + DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'", + (host ? host : "(NULL)"), (ip ? ip : "(NULL)"), + (user ? user : "(NULL)"), (db ? db : "(NULL)"))); sctx->user= user; sctx->host= host; sctx->ip= ip; @@ -1486,6 +1492,11 @@ end: bool is_acl_user(const char *host, const char *user) { bool res; + + /* --skip-grants */ + if (!initialized) + return TRUE; + VOID(pthread_mutex_lock(&acl_cache->lock)); res= find_acl_user(host, user, TRUE) != NULL; VOID(pthread_mutex_unlock(&acl_cache->lock)); @@ -3504,17 +3515,38 @@ end: bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, uint show_table, uint number, bool no_errors) { - TABLE_LIST *table; + TABLE_LIST *table, *first_not_own_table= thd->lex->first_not_own_table(); Security_context *sctx= thd->security_ctx; + uint i; DBUG_ENTER("check_grant"); DBUG_ASSERT(number > 0); + /* + Walk through the list of tables that belong to the query and save the + requested access (orig_want_privilege) to be able to use it when + checking access rights to the underlying tables of a view. Our grant + system gradually eliminates checked bits from want_privilege and thus + after all checks are done we can no longer use it. + The check that first_not_own_table is not reached is for the case when + the given table list refers to the list for prelocking (contains tables + of other queries). For simple queries first_not_own_table is 0. + */ + for (i= 0, table= tables; + table != first_not_own_table && i < number; + table= table->next_global, i++) + { + /* Remove SHOW_VIEW_ACL, because it will be checked during making view */ + table->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL); + } + want_access&= ~sctx->master_access; if (!want_access) DBUG_RETURN(0); // ok rw_rdlock(&LOCK_grant); - for (table= tables; table && number--; table= table->next_global) + for (table= tables; + table && number-- && table != first_not_own_table; + table= table->next_global) { GRANT_TABLE *grant_table; if (!(~table->grant.privilege & want_access) || @@ -3524,8 +3556,16 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, It is subquery in the FROM clause. VIEW set table->derived after table opening, but this function always called before table opening. */ - table->grant.want_privilege= 0; - continue; // Already checked + if (!table->referencing_view) + { + /* + If it's a temporary table created for a subquery in the FROM + clause, or an INFORMATION_SCHEMA table, drop the request for + a privilege. + */ + table->grant.want_privilege= 0; + } + continue; } if (!(grant_table= table_hash_search(sctx->host, sctx->ip, table->db, sctx->priv_user, @@ -5834,24 +5874,37 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, const char *db, const char *table) { Security_context *sctx= thd->security_ctx; + DBUG_ENTER("fill_effective_table_privileges"); + DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`", + sctx->priv_host, (sctx->ip ? sctx->ip : "(NULL)"), + (sctx->priv_user ? sctx->priv_user : "(NULL)"), + db, table)); /* --skip-grants */ if (!initialized) { + DBUG_PRINT("info", ("skip grants")); grant->privilege= ~NO_ACCESS; // everything is allowed - return; + DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege)); + DBUG_VOID_RETURN; } /* global privileges */ grant->privilege= sctx->master_access; if (!sctx->priv_user) - return; // it is slave + { + DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege)); + DBUG_VOID_RETURN; // it is slave + } /* db privileges */ grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0); if (!grant_option) - return; + { + DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege)); + DBUG_VOID_RETURN; + } /* table privileges */ if (grant->version != grant_version) @@ -5868,6 +5921,8 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, { grant->privilege|= grant->grant_table->privs; } + DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege)); + DBUG_VOID_RETURN; } #else /* NO_EMBEDDED_ACCESS_CHECKS */ diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index 669f998cde5..e1f4b8f6076 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -466,7 +466,9 @@ void field_real::add() void field_decimal::add() { + /*TODO - remove rounding stuff after decimal_div returns proper frac */ my_decimal dec_buf, *dec= item->val_decimal(&dec_buf); + my_decimal rounded; uint length; TREE_ELEMENT *element; @@ -476,6 +478,9 @@ void field_decimal::add() return; } + my_decimal_round(E_DEC_FATAL_ERROR, dec, item->decimals, FALSE,&rounded); + dec= &rounded; + length= my_decimal_string_length(dec); if (decimal_is_zero(dec)) @@ -1021,10 +1026,16 @@ String *field_decimal::avg(String *s, ha_rows rows) s->set((double) 0.0, 1,my_thd_charset); return s; } - my_decimal num, avg_val; + my_decimal num, avg_val, rounded_avg; + int prec_increment= current_thd->variables.div_precincrement; + int2my_decimal(E_DEC_FATAL_ERROR, rows - nulls, FALSE, &num); - my_decimal_div(E_DEC_FATAL_ERROR, &avg_val, sum+cur_sum, &num, 0); - my_decimal2string(E_DEC_FATAL_ERROR, &avg_val, 0, 0, '0', s); + my_decimal_div(E_DEC_FATAL_ERROR, &avg_val, sum+cur_sum, &num, prec_increment); + /* TODO remove this after decimal_div returns proper frac */ + my_decimal_round(E_DEC_FATAL_ERROR, &avg_val, + min(sum[cur_sum].frac + prec_increment, DECIMAL_MAX_SCALE), + FALSE,&rounded_avg); + my_decimal2string(E_DEC_FATAL_ERROR, &rounded_avg, 0, 0, '0', s); return s; } @@ -1036,13 +1047,19 @@ String *field_decimal::std(String *s, ha_rows rows) s->set((double) 0.0, 1,my_thd_charset); return s; } - my_decimal num, std_val, sum2, sum2d; + my_decimal num, tmp, sum2, sum2d; + double std_sqr; + int prec_increment= current_thd->variables.div_precincrement; + int2my_decimal(E_DEC_FATAL_ERROR, rows - nulls, FALSE, &num); my_decimal_mul(E_DEC_FATAL_ERROR, &sum2, sum+cur_sum, sum+cur_sum); - my_decimal_div(E_DEC_FATAL_ERROR, &std_val, &sum2, &num, 0); - my_decimal_sub(E_DEC_FATAL_ERROR, &sum2, sum_sqr+cur_sum, &std_val); - my_decimal_div(E_DEC_FATAL_ERROR, &std_val, &sum2, &num, 0); - my_decimal2string(E_DEC_FATAL_ERROR, &std_val, 0, 0, '0', s); + my_decimal_div(E_DEC_FATAL_ERROR, &tmp, &sum2, &num, prec_increment); + my_decimal_sub(E_DEC_FATAL_ERROR, &sum2, sum_sqr+cur_sum, &tmp); + my_decimal_div(E_DEC_FATAL_ERROR, &tmp, &sum2, &num, prec_increment); + my_decimal2double(E_DEC_FATAL_ERROR, &tmp, &std_sqr); + s->set(((double) std_sqr <= 0.0 ? 0.0 : sqrt(std_sqr)), + min(item->decimals + prec_increment, NOT_FIXED_DEC), my_thd_charset); + return s; } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 1de9411cad0..30dec528845 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -37,11 +37,11 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, TABLE_LIST *table_list, MEM_ROOT *mem_root); static void free_cache_entry(TABLE *entry); static void mysql_rm_tmp_tables(void); -static my_bool open_new_frm(const char *path, const char *alias, - const char *db, const char *table_name, - uint db_stat, uint prgflag, - uint ha_open_flags, TABLE *outparam, - TABLE_LIST *table_desc, MEM_ROOT *mem_root); +static bool open_new_frm(THD *thd, const char *path, const char *alias, + const char *db, const char *table_name, + uint db_stat, uint prgflag, + uint ha_open_flags, TABLE *outparam, + TABLE_LIST *table_desc, MEM_ROOT *mem_root); extern "C" byte *table_cache_key(const byte *record,uint *length, my_bool not_used __attribute__((unused))) @@ -976,32 +976,57 @@ void wait_for_refresh(THD *thd) } -TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) +/* + Open table which is already name-locked by this thread. + + SYNOPSIS + reopen_name_locked_table() + thd Thread handle + table_list TABLE_LIST object for table to be open, TABLE_LIST::table + member should point to TABLE object which was used for + name-locking. + + NOTE + This function assumes that its caller already acquired LOCK_open mutex. + + RETURN VALUE + FALSE - Success + TRUE - Error +*/ + +bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) { - DBUG_ENTER("reopen_name_locked_table"); - if (thd->killed) - DBUG_RETURN(0); - TABLE *table; + TABLE *table= table_list->table; TABLE_SHARE *share; - if (!(table = table_list->table)) - DBUG_RETURN(0); + char *db= table_list->db; + char *table_name= table_list->table_name; + char key[MAX_DBKEY_LENGTH]; + uint key_length; + TABLE orig_table; + DBUG_ENTER("reopen_name_locked_table"); - char* db = thd->db ? thd->db : table_list->db; - char* table_name = table_list->table_name; - char key[MAX_DBKEY_LENGTH]; - uint key_length; + safe_mutex_assert_owner(&LOCK_open); + + if (thd->killed || !table) + DBUG_RETURN(TRUE); + + orig_table= *table; key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; - pthread_mutex_lock(&LOCK_open); if (open_unireg_entry(thd, table, db, table_name, table_name, 0, thd->mem_root) || !(table->s->table_cache_key= memdup_root(&table->mem_root, (char*) key, key_length))) { - delete table->triggers; - closefrm(table); - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(0); + intern_close_table(table); + /* + If there was an error during opening of table (for example if it + does not exist) '*table' object can be wiped out. To be able + properly release name-lock in this case we should restore this + object to its original state. + */ + *table= orig_table; + DBUG_RETURN(TRUE); } share= table->s; @@ -1011,7 +1036,6 @@ TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) share->flush_version=0; table->in_use = thd; check_unused(); - pthread_mutex_unlock(&LOCK_open); table->next = thd->open_tables; thd->open_tables = table; table->tablenr=thd->current_tablenr++; @@ -1021,7 +1045,7 @@ TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) table->status=STATUS_NO_RECORD; table->keys_in_use_for_query= share->keys_in_use; table->used_keys= share->keys_for_keyread; - DBUG_RETURN(table); + DBUG_RETURN(FALSE); } @@ -1169,10 +1193,11 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, */ { char path[FN_REFLEN]; + db_type not_used; strxnmov(path, FN_REFLEN, mysql_data_home, "/", table_list->db, "/", table_list->table_name, reg_ext, NullS); (void) unpack_filename(path, path); - if (mysql_frm_type(path) == FRMTYPE_VIEW) + if (mysql_frm_type(thd, path, ¬_used) == FRMTYPE_VIEW) { TABLE tab;// will not be used (because it's VIEW) but have to be passed table= &tab; @@ -1731,7 +1756,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, thd->open_options, entry)) && (error != 5 || (fn_format(path, path, 0, reg_ext, MY_UNPACK_FILENAME), - open_new_frm(path, alias, db, name, + 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, @@ -1947,11 +1972,11 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) derived/information schema tables and views possible. Thus "counter" may be still zero for prelocked statement... - NOTE: The above notes may be out of date. Please wait for psergey to + NOTE: The above notes may be out of date. Please wait for psergey to document new prelocked behavior. */ - - if (!thd->prelocked_mode && !thd->lex->requires_prelocking() && + + if (!thd->prelocked_mode && !thd->lex->requires_prelocking() && thd->lex->sroutines_list.elements) { bool first_no_prelocking, need_prelocking; @@ -2001,7 +2026,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) /* VIEW placeholder */ (*counter)--; - /* + /* tables->next_global list consists of two parts: 1) Query tables and underlying tables of views. 2) Tables used by all stored routines that this statement invokes on @@ -2610,7 +2635,7 @@ bool rm_temporary_table(enum db_type base, char *path) if (my_delete(path,MYF(0))) error=1; /* purecov: inspected */ *fn_ext(path)='\0'; // remove extension - handler *file=get_new_handler((TABLE*) 0, base); + handler *file= get_new_handler((TABLE*) 0, current_thd->mem_root, base); if (file && file->delete_table(path)) { error=1; @@ -2658,6 +2683,47 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table) } +#ifndef NO_EMBEDDED_ACCESS_CHECKS +/* + Check column rights in given security context + + SYNOPSIS + check_grant_column_in_sctx() + thd thread handler + grant grant information structure + db db name + table table name + name column name + length column name length + check_grants need to check grants + sctx 0 or security context + + RETURN + FALSE OK + TRUE access denied +*/ + +static bool check_grant_column_in_sctx(THD *thd, GRANT_INFO *grant, + const char *db, const char *table, + const char *name, uint length, + bool check_grants, + Security_context *sctx) +{ + if (!check_grants) + return FALSE; + Security_context *save_security_ctx= thd->security_ctx; + bool res; + if (sctx) + { + thd->security_ctx= sctx; + } + res= check_grant_column(thd, grant, db, table, name, length); + thd->security_ctx= save_security_ctx; + return res; +} +#endif + + /* Find a field by name in a view that uses merge algorithm. @@ -2708,11 +2774,11 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list, */ DBUG_RETURN(((Item_field*) (field_it.item()))->field); #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (check_grants && - check_grant_column(thd, &table_list->grant, - table_list->view_db.str, - table_list->view_name.str, - name, length)) + if (check_grant_column_in_sctx(thd, &table_list->grant, + table_list->view_db.str, + table_list->view_name.str, name, length, + check_grants, + table_list->security_ctx)) DBUG_RETURN(WRONG_GRANT); #endif // in PS use own arena or data will be freed after prepare @@ -2789,7 +2855,6 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, Natural_join_column *nj_col; Field *found_field; Query_arena *arena, backup; - DBUG_ENTER("find_field_in_natural_join"); DBUG_PRINT("enter", ("field name: '%s', ref 0x%lx", name, (ulong) ref)); @@ -2814,6 +2879,7 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, if (nj_col->view_field) { + Item *item; /* The found field is a view field, we do as in find_field_in_view() and return a pointer to pointer to the Item of that field. @@ -2821,7 +2887,7 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, if (register_tree_change) arena= thd->activate_stmt_arena_if_needed(&backup); - Item *item= nj_col->create_item(thd); + item= nj_col->create_item(thd); if (register_tree_change && arena) thd->restore_active_arena(arena, &backup); @@ -2881,7 +2947,8 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, Field * find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, bool check_grants, bool allow_rowid, - uint *cached_field_index_ptr) + uint *cached_field_index_ptr, + Security_context *sctx) { Field **field_ptr, *field; uint cached_field_index= *cached_field_index_ptr; @@ -2890,7 +2957,7 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, /* We assume here that table->field < NO_CACHED_FIELD_INDEX = UINT_MAX */ if (cached_field_index < table->s->fields && - !my_strcasecmp(system_charset_info, + !my_strcasecmp(system_charset_info, table->field[cached_field_index]->field_name, name)) field_ptr= table->field + cached_field_index; else if (table->s->name_hash.records) @@ -2921,9 +2988,10 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, update_field_dependencies(thd, field, table); #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (check_grants && check_grant_column(thd, &table->grant, - table->s->db, - table->s->table_name, name, length)) + if (check_grant_column_in_sctx(thd, &table->grant, + table->s->db, table->s->table_name, + name, length, + check_grants, sctx)) field= WRONG_GRANT; #endif DBUG_RETURN(field); @@ -3035,7 +3103,8 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, DBUG_ASSERT(table_list->table); if ((fld= find_field_in_table(thd, table_list->table, name, length, check_grants_table, allow_rowid, - cached_field_index_ptr))) + cached_field_index_ptr, + table_list->security_ctx))) *actual_table= table_list; #ifndef NO_EMBEDDED_ACCESS_CHECKS /* check for views with temporary table algorithm */ @@ -3205,7 +3274,8 @@ find_field_in_tables(THD *thd, Item_ident *item, test(table_ref->table-> grant.want_privilege) && check_privileges, - 1, &(item->cached_field_index)); + 1, &(item->cached_field_index), + table_ref->security_ctx); else found= find_field_in_table_ref(thd, table_ref, name, item->name, NULL, NULL, length, ref, @@ -4293,7 +4363,7 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, thd->set_query_id=set_query_id; thd->allow_sum_func= allow_sum_func; - thd->where="field list"; + thd->where= THD::DEFAULT_WHERE; /* To prevent fail on forward lookup we fill it with zerows, @@ -4345,8 +4415,12 @@ TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables) { for (TABLE_LIST *table= tables; table; table= table->next_local) { - if (table->view && table->effective_algorithm == VIEW_ALGORITHM_MERGE) - list= make_leaves_list(list, table->ancestor); + if (table->merge_underlying_list) + { + DBUG_ASSERT(table->view && + table->effective_algorithm == VIEW_ALGORITHM_MERGE); + list= make_leaves_list(list, table->merge_underlying_list); + } else { *list= table; @@ -4446,16 +4520,17 @@ bool setup_tables(THD *thd, Name_resolution_context *context, table_list; table_list= table_list->next_local) { - if (table_list->ancestor) + if (table_list->merge_underlying_list) { - DBUG_ASSERT(table_list->view); + DBUG_ASSERT(table_list->view && + table_list->effective_algorithm == VIEW_ALGORITHM_MERGE); Query_arena *arena= thd->stmt_arena, backup; bool res; if (arena->is_conventional()) arena= 0; // For easier test else thd->set_n_backup_active_arena(arena, &backup); - res= table_list->setup_ancestor(thd); + res= table_list->setup_underlying(thd); if (arena) thd->restore_active_arena(arena, &backup); if (res) @@ -5233,6 +5308,7 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order) SYNOPSIS open_new_frm() + THD thread handler path path to .frm alias alias for table db database @@ -5246,8 +5322,8 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order) mem_root temporary MEM_ROOT for parsing */ -static my_bool -open_new_frm(const char *path, const char *alias, +static bool +open_new_frm(THD *thd, const char *path, const char *alias, const char *db, const char *table_name, uint db_stat, uint prgflag, uint ha_open_flags, TABLE *outparam, TABLE_LIST *table_desc, @@ -5269,7 +5345,7 @@ open_new_frm(const char *path, const char *alias, my_error(ER_WRONG_OBJECT, MYF(0), db, table_name, "BASE TABLE"); goto err; } - if (mysql_make_view(parser, table_desc)) + if (mysql_make_view(thd, parser, table_desc)) goto err; } else diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index f17825e6d2c..c150489f311 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1204,6 +1204,7 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu", #endif /*!EMBEDDED_LIBRARY*/ thd->limit_found_rows = query->found_rows(); + thd->status_var.last_query_cost= 0.0; BLOCK_UNLOCK_RD(query_block); DBUG_RETURN(1); // Result sent to client @@ -2207,15 +2208,10 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used, tables_used->view_db.length + 1, HA_CACHE_TBL_NONTRANSACT, 0, 0)) DBUG_RETURN(0); - { - TABLE_COUNTER_TYPE inc= register_tables_from_list(tables_used->ancestor, - n + 1, - block_table + 1); - if (!inc) - DBUG_RETURN(0); - n+= inc; - block_table+= inc; - } + /* + We do not need to register view tables here because they are already + present in the global list. + */ } else { @@ -2831,13 +2827,6 @@ static TABLE_COUNTER_TYPE process_and_count_tables(TABLE_LIST *tables_used, tables_used->view_name.str, tables_used->view_db.str)); *tables_type|= HA_CACHE_TBL_NONTRANSACT; - { - TABLE_COUNTER_TYPE subcount; - if (!(subcount= process_and_count_tables(tables_used->ancestor, - tables_type))) - DBUG_RETURN(0); - table_count+= subcount; - } } else { diff --git a/sql/sql_class.cc b/sql/sql_class.cc index fc7ea6a2794..fc9df020b6c 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -44,6 +44,8 @@ */ char internal_table_name[2]= "*"; +const char * const THD::DEFAULT_WHERE= "field list"; + /***************************************************************************** ** Instansiate templates @@ -218,6 +220,7 @@ THD::THD() #ifndef EMBEDDED_LIBRARY net.vio=0; #endif + client_capabilities= 0; // minimalistic client net.last_error[0]=0; // If error on boot net.query_cache_query=0; // If error on boot ull=0; @@ -233,7 +236,7 @@ THD::THD() /* Variables with default values */ proc_info="login"; - where="field list"; + where= THD::DEFAULT_WHERE; server_id = ::server_id; slave_net = 0; command=COM_CONNECT; @@ -544,6 +547,8 @@ void THD::cleanup_after_query() } /* Free Items that were created during this execution */ free_items(); + /* Reset where. */ + where= THD::DEFAULT_WHERE; } /* @@ -1703,15 +1708,19 @@ Statement_map::Statement_map() : int Statement_map::insert(Statement *statement) { - int rc= my_hash_insert(&st_hash, (byte *) statement); + int res= my_hash_insert(&st_hash, (byte *) statement); + if (res) + return res; if (statement->name.str) { - if ((rc= my_hash_insert(&names_hash, (byte*)statement))) + if ((res= my_hash_insert(&names_hash, (byte*)statement))) + { hash_delete(&st_hash, (byte*)statement); + return res; + } } - if (rc == 0) - last_found_statement= statement; - return rc; + last_found_statement= statement; + return res; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 9e751c37608..0a34de11528 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -189,8 +189,11 @@ class MYSQL_LOG: public TC_LOG { private: /* LOCK_log and LOCK_index are inited by init_pthread_objects() */ - pthread_mutex_t LOCK_log, LOCK_index; + pthread_mutex_t LOCK_log, LOCK_index, LOCK_readers; + pthread_mutex_t LOCK_prep_xids; + pthread_cond_t COND_prep_xids; pthread_cond_t update_cond; + pthread_cond_t reset_cond; ulonglong bytes_written; time_t last_time,query_start; IO_CACHE log_file; @@ -198,21 +201,6 @@ class MYSQL_LOG: public TC_LOG char *name; char time_buff[20],db[NAME_LEN+1]; char log_file_name[FN_REFLEN],index_file_name[FN_REFLEN]; - // current file sequence number for load data infile binary logging - uint file_id; - uint open_count; // For replication - volatile enum_log_type log_type; - enum cache_type io_cache_type; - bool write_error, inited; - bool need_start_event; - /* - no_auto_events means we don't want any of these automatic events : - Start/Rotate/Stop. That is, in 4.x when we rotate a relay log, we don't want - a Rotate_log event to be written to the relay log. When we start a relay log - etc. So in 4.x this is 1 for relay logs, 0 for binlogs. - In 5.0 it's 0 for relay logs too! - */ - bool no_auto_events; /* The max size before rotation (usable only if log_type == LOG_BIN: binary logs and relay logs). @@ -224,13 +212,38 @@ class MYSQL_LOG: public TC_LOG fix_max_relay_log_size). */ ulong max_size; - ulong prepared_xids; /* for tc log - number of xids to remember */ - pthread_mutex_t LOCK_prep_xids; - pthread_cond_t COND_prep_xids; + volatile enum_log_type log_type; + enum cache_type io_cache_type; + // current file sequence number for load data infile binary logging + uint file_id; + uint open_count; // For replication + int readers_count; + bool reset_pending; + bool write_error, inited; + bool need_start_event; + /* + no_auto_events means we don't want any of these automatic events : + Start/Rotate/Stop. That is, in 4.x when we rotate a relay log, we don't + want a Rotate_log event to be written to the relay log. When we start a + relay log etc. So in 4.x this is 1 for relay logs, 0 for binlogs. + In 5.0 it's 0 for relay logs too! + */ + bool no_auto_events; friend class Log_event; public: + /* + These describe the log's format. This is used only for relay logs. + _for_exec is used by the SQL thread, _for_queue by the I/O thread. It's + necessary to have 2 distinct objects, because the I/O thread may be reading + events in a different format from what the SQL thread is reading (consider + the case of a master which has been upgraded from 5.0 to 5.1 without doing + RESET MASTER, or from 4.x to 5.0). + */ + Format_description_log_event *description_event_for_exec, + *description_event_for_queue; + MYSQL_LOG(); /* note that there's no destructor ~MYSQL_LOG() ! @@ -243,18 +256,6 @@ public: int log(THD *thd, my_xid xid); void unlog(ulong cookie, my_xid xid); int recover(IO_CACHE *log, Format_description_log_event *fdle); - - /* - These describe the log's format. This is used only for relay logs. - _for_exec is used by the SQL thread, _for_queue by the I/O thread. It's - necessary to have 2 distinct objects, because the I/O thread may be reading - events in a different format from what the SQL thread is reading (consider - the case of a master which has been upgraded from 5.0 to 5.1 without doing - RESET MASTER, or from 4.x to 5.0). - */ - Format_description_log_event *description_event_for_exec, - *description_event_for_queue; - void reset_bytes_written() { bytes_written = 0; @@ -334,6 +335,9 @@ public: int purge_logs_before_date(time_t purge_time); int purge_first_log(struct st_relay_log_info* rli, bool included); bool reset_logs(THD* thd); + inline bool is_reset_pending() { return reset_pending; } + void readers_addref(); + void readers_release(); void close(uint exiting); // iterating through the log index file @@ -1115,6 +1119,14 @@ class THD :public Statement, public Open_tables_state { public: + /* + Constant for THD::where initialization in the beginning of every query. + + It's needed because we do not save/restore THD::where normally during + primary (non subselect) query execution. + */ + static const char * const DEFAULT_WHERE; + #ifdef EMBEDDED_LIBRARY struct st_mysql *mysql; struct st_mysql_data *data; @@ -1911,6 +1923,7 @@ typedef struct st_sort_field { Field *field; /* Field to sort */ Item *item; /* Item if not sorting fields */ uint length; /* Length of sort field */ + uint suffix_length; /* Length suffix (0-4) */ Item_result result_type; /* Type of item */ bool reverse; /* if descending sort */ bool need_strxnfrm; /* If we have to use strxnfrm() */ diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 584601fe202..816aba25218 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -38,6 +38,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, bool using_limit=limit != HA_POS_ERROR; bool transactional_table, safe_update, const_cond; ha_rows deleted; + uint usable_index= MAX_KEY; SELECT_LEX *select_lex= &thd->lex->select_lex; DBUG_ENTER("mysql_delete"); @@ -141,27 +142,42 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, tables.table = table; tables.alias = table_list->alias; - table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE), - MYF(MY_FAE | MY_ZEROFILL)); if (select_lex->setup_ref_array(thd, order->elements) || setup_order(thd, select_lex->ref_pointer_array, &tables, - fields, all_fields, (ORDER*) order->first) || - !(sortorder=make_unireg_sortorder((ORDER*) order->first, &length)) || + fields, all_fields, (ORDER*) order->first)) + { + delete select; + free_underlaid_joins(thd, &thd->lex->select_lex); + DBUG_RETURN(TRUE); + } + + if (!select && limit != HA_POS_ERROR) + usable_index= get_index_for_order(table, (ORDER*)(order->first), limit); + + if (usable_index == MAX_KEY) + { + table->sort.io_cache= (IO_CACHE *) my_malloc(sizeof(IO_CACHE), + MYF(MY_FAE | MY_ZEROFILL)); + + if (!(sortorder= make_unireg_sortorder((ORDER*) order->first, + &length)) || (table->sort.found_records = filesort(thd, table, sortorder, length, - select, HA_POS_ERROR, - &examined_rows)) + select, HA_POS_ERROR, + &examined_rows)) == HA_POS_ERROR) - { + { + delete select; + free_underlaid_joins(thd, &thd->lex->select_lex); + DBUG_RETURN(TRUE); + } + /* + Filesort has already found and selected the rows we want to delete, + so we don't need the where clause + */ delete select; free_underlaid_joins(thd, select_lex); - DBUG_RETURN(TRUE); + select= 0; } - /* - Filesort has already found and selected the rows we want to delete, - so we don't need the where clause - */ - delete select; - select= 0; } /* If quick select is used, initialize it before retrieving rows. */ @@ -171,7 +187,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, free_underlaid_joins(thd, select_lex); DBUG_RETURN(TRUE); } - init_read_record(&info,thd,table,select,1,1); + if (usable_index==MAX_KEY) + init_read_record(&info,thd,table,select,1,1); + else + init_read_record_idx(&info, thd, table, 1, usable_index); + deleted=0L; init_ftfuncs(thd, select_lex, 1); thd->proc_info="updating"; @@ -404,8 +424,9 @@ bool mysql_multi_delete_prepare(THD *thd) if (!(target_tbl->table= target_tbl->correspondent_table->table)) { DBUG_ASSERT(target_tbl->correspondent_table->view && - target_tbl->correspondent_table->ancestor && - target_tbl->correspondent_table->ancestor->next_local); + target_tbl->correspondent_table->merge_underlying_list && + target_tbl->correspondent_table->merge_underlying_list-> + next_local); my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), target_tbl->correspondent_table->view_db.str, target_tbl->correspondent_table->view_name.str); @@ -830,7 +851,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) if (!dont_send_ok) { db_type table_type; - if ((table_type=get_table_type(thd, path)) == DB_TYPE_UNKNOWN) + mysql_frm_type(thd, path, &table_type); + if (table_type == DB_TYPE_UNKNOWN) { my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->table_name); diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 74b239e1637..e1817985cbd 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -35,14 +35,14 @@ processor procedure of derived table processing RETURN - 0 ok - 1 Error and error message given + FALSE OK + TRUE Error */ -int -mysql_handle_derived(LEX *lex, int (*processor)(THD*, LEX*, TABLE_LIST*)) +bool +mysql_handle_derived(LEX *lex, bool (*processor)(THD*, LEX*, TABLE_LIST*)) { - int res= 0; + bool res= FALSE; if (lex->derived_tables) { lex->thd->derived_tables_processing= TRUE; @@ -95,16 +95,16 @@ out: close_thread_tables() RETURN - 0 ok - 1 Error and an error message was given + FALSE OK + TRUE Error */ -int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list) +bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list) { SELECT_LEX_UNIT *unit= orig_table_list->derived; - int res= 0; ulonglong create_options; DBUG_ENTER("mysql_derived_prepare"); + bool res= FALSE; if (unit) { SELECT_LEX *first_select= unit->first_select(); @@ -118,7 +118,7 @@ int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list) sl->context.outer_context= 0; if (!(derived_result= new select_union)) - DBUG_RETURN(1); // out of memory + DBUG_RETURN(TRUE); // out of memory // st_select_lex_unit::prepare correctly work for single select if ((res= unit->prepare(thd, derived_result, 0))) @@ -184,7 +184,10 @@ exit: table->derived_select_number= first_select->select_number; table->s->tmp_table= TMP_TABLE; #ifndef NO_EMBEDDED_ACCESS_CHECKS - table->grant.privilege= SELECT_ACL; + if (orig_table_list->referencing_view) + table->grant= orig_table_list->grant; + else + table->grant.privilege= SELECT_ACL; #endif orig_table_list->db= (char *)""; orig_table_list->db_length= 0; @@ -195,8 +198,8 @@ exit: thd->derived_tables= table; } } - else if (orig_table_list->ancestor) - orig_table_list->set_ancestor(); + else if (orig_table_list->merge_underlying_list) + orig_table_list->set_underlying_merge(); DBUG_RETURN(res); } @@ -220,15 +223,15 @@ exit: Due to evaluation of LIMIT clause it can not be used at prepared stage. RETURN - 0 ok - 1 Error and an error message was given + FALSE OK + TRUE Error */ -int mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *orig_table_list) +bool mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *orig_table_list) { TABLE *table= orig_table_list->table; SELECT_LEX_UNIT *unit= orig_table_list->derived; - int res= 0; + bool res= FALSE; /*check that table creation pass without problem and it is derived table */ if (table && unit) @@ -271,7 +274,7 @@ int mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *orig_table_list) there were no derived tables */ if (derived_result->flush()) - res= 1; + res= TRUE; if (!lex->describe) unit->cleanup(); diff --git a/sql/sql_error.cc b/sql/sql_error.cc index 2e262569386..191a6e0a1fd 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -144,6 +144,8 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, thd->really_abort_on_warning()) ? MYSQL_ERROR::WARN_LEVEL_ERROR : level)) { + 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_help.cc b/sql/sql_help.cc index dc2255fd6e8..0715aeeaa5b 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -599,7 +599,8 @@ SQL_SELECT *prepare_select_for_name(THD *thd, const char *mask, uint mlen, { Item *cond= new Item_func_like(new Item_field(pfname), new Item_string(mask,mlen,pfname->charset()), - new Item_string("\\",1,&my_charset_latin1)); + new Item_string("\\",1,&my_charset_latin1), + FALSE); if (thd->is_fatal_error) return 0; // OOM return prepare_simple_select(thd, cond, table, error); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index beca9842e6d..8877cc17bf0 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1822,7 +1822,7 @@ pthread_handler_t handle_delayed_insert(void *arg) #endif if (thd->killed || di->status) break; - if (error == ETIMEDOUT) + if (error == ETIMEDOUT || error == ETIME) { thd->killed= THD::KILL_CONNECTION; break; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index d76e8d04ae6..64f9256cc83 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -176,6 +176,7 @@ void lex_start(THD *thd, uchar *buf,uint length) lex->spcont= NULL; lex->proc_list.first= 0; lex->query_tables_own_last= 0; + lex->escape_used= FALSE; if (lex->sroutines.records) my_hash_reset(&lex->sroutines); @@ -1134,6 +1135,7 @@ void st_select_lex::init_query() ref_pointer_array= 0; select_n_having_items= 0; subquery_in_having= explicit_limit= 0; + is_item_list_lookup= 0; first_execution= 1; first_cond_optimization= 1; parsing_place= NO_MATTER; @@ -1166,6 +1168,7 @@ void st_select_lex::init_select() select_limit= 0; /* denotes the default limit = HA_POS_ERROR */ offset_limit= 0; /* denotes the default offset = 0 */ with_sum_func= 0; + } /* diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 7640b6eb569..96ed32a0d2e 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -362,8 +362,8 @@ public: friend class st_select_lex_unit; friend bool mysql_new_select(struct st_lex *lex, bool move_down); - friend my_bool mysql_make_view (File_parser *parser, - TABLE_LIST *table); + friend bool mysql_make_view(THD *thd, File_parser *parser, + TABLE_LIST *table); private: void fast_exclude(); }; @@ -387,12 +387,12 @@ protected: select_result *result; ulong found_rows_for_union; bool res; +public: bool prepared, // prepare phase already performed for UNION (unit) optimized, // optimize phase already performed for UNION (unit) executed, // already executed cleaned; -public: // list of fields which points to temporary table for union List<Item> item_list; /* @@ -483,6 +483,7 @@ public: List<Item> item_list; /* list of fields & expressions */ List<String> interval_list, use_index, *use_index_ptr, ignore_index, *ignore_index_ptr; + bool is_item_list_lookup; /* Usualy it is pointer to ftfunc_list_alloc, but in union used to create fake select_lex for calling mysql_select under results of union @@ -639,6 +640,11 @@ public: SELECT_LEX and all nested SELECT_LEXes and SELECT_LEX_UNITs). */ bool cleanup(); + /* + Recursively cleanup the join of this select lex and of all nested + select lexes. + */ + void cleanup_all_joins(bool full); }; typedef class st_select_lex SELECT_LEX; @@ -746,6 +752,7 @@ typedef struct st_lex /* store original leaf_tables for INSERT SELECT and PS/SP */ TABLE_LIST *leaf_tables_insert; st_lex_user *create_view_definer; + char *create_view_start; char *create_view_select_start; /* Partition info structure filled in by PARTITION BY parse part */ partition_info *part_info; @@ -810,6 +817,11 @@ typedef struct st_lex */ uint table_count; uint8 describe; + /* + A flag that indicates what kinds of derived tables are present in the + query (0 if no derived tables, otherwise a combination of flags + DERIVED_SUBQUERY and DERIVED_VIEW). + */ uint8 derived_tables; uint8 create_view_algorithm; uint8 create_view_check; @@ -904,6 +916,8 @@ typedef struct st_lex during replication ("LOCAL 'filename' REPLACE INTO" part). */ uchar *fname_start, *fname_end; + + bool escape_used; st_lex(); diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 895065241f4..44ddf11757a 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -1025,8 +1025,20 @@ int READ_INFO::read_field() *to++= (byte) escape_char; goto found_eof; } - *to++ = (byte) unescape((char) chr); - continue; + /* + When escape_char == enclosed_char, we treat it like we do for + handling quotes in SQL parsing -- you can double-up the + escape_char to include it literally, but it doesn't do escapes + like \n. This allows: LOAD DATA ... ENCLOSED BY '"' ESCAPED BY '"' + with data like: "fie""ld1", "field2" + */ + if (escape_char != enclosed_char || chr == escape_char) + { + *to++ = (byte) unescape((char) chr); + continue; + } + PUSH(chr); + chr= escape_char; } #ifdef ALLOW_LINESEPARATOR_IN_STRINGS if (chr == line_term_char) diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc index 34334d3a01d..1d3acd1696c 100644 --- a/sql/sql_manager.cc +++ b/sql/sql_manager.cc @@ -58,12 +58,14 @@ pthread_handler_t handle_manager(void *arg __attribute__((unused))) set_timespec(abstime, flush_time); reset_flush_time = FALSE; } - while (!manager_status && !error && !abort_loop) - error = pthread_cond_timedwait(&COND_manager, &LOCK_manager, &abstime); + while (!manager_status && (!error || error == EINTR) && !abort_loop) + error= pthread_cond_timedwait(&COND_manager, &LOCK_manager, &abstime); } else - while (!manager_status && !error && !abort_loop) - error = pthread_cond_wait(&COND_manager, &LOCK_manager); + { + while (!manager_status && (!error || error == EINTR) && !abort_loop) + error= pthread_cond_wait(&COND_manager, &LOCK_manager); + } status = manager_status; manager_status = 0; pthread_mutex_unlock(&LOCK_manager); @@ -71,7 +73,7 @@ pthread_handler_t handle_manager(void *arg __attribute__((unused))) if (abort_loop) break; - if (error) /* == ETIMEDOUT */ + if (error == ETIMEDOUT || error == ETIME) { flush_tables(); error = 0; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index f5391cd546d..939961ab7e8 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1769,6 +1769,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, /* Saved variable value */ my_bool old_innodb_table_locks= IF_INNOBASE_DB(thd->variables.innodb_table_locks, FALSE); + /* used as fields initializator */ + lex_start(thd, 0, 0); statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS], @@ -2319,8 +2321,6 @@ mysql_execute_command(THD *thd) LEX *lex= thd->lex; /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ SELECT_LEX *select_lex= &lex->select_lex; - bool slave_fake_lock= 0; - MYSQL_LOCK *fake_prev_lock= 0; /* first table of first SELECT_LEX */ TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first; /* list of all tables in query */ @@ -2369,34 +2369,21 @@ mysql_execute_command(THD *thd) #ifdef HAVE_REPLICATION if (thd->slave_thread) { - if (lex->sql_command == SQLCOM_UPDATE_MULTI) - { - DBUG_PRINT("info",("need faked locked tables")); - - if (check_multi_update_lock(thd)) - goto error; - - /* Fix for replication, the tables are opened and locked, - now we pretend that we have performed a LOCK TABLES action */ - - fake_prev_lock= thd->locked_tables; - if (thd->lock) - thd->locked_tables= thd->lock; - thd->lock= 0; - slave_fake_lock= 1; - } /* - Skip if we are in the slave thread, some table rules have been - given and the table list says the query should not be replicated. + Check if statment should be skipped because of slave filtering + rules Exceptions are: + - UPDATE MULTI: For this statement, we want to check the filtering + rules later in the code - SET: we always execute it (Not that many SET commands exists in the binary log anyway -- only 4.1 masters write SET statements, in 5.0 there are no SET statements in the binary log) - DROP TEMPORARY TABLE IF EXISTS: we always execute it (otherwise we have stale files on slave caused by exclusion of one tmp table). */ - if (!(lex->sql_command == SQLCOM_SET_OPTION) && + if (!(lex->sql_command == SQLCOM_UPDATE_MULTI) && + !(lex->sql_command == SQLCOM_SET_OPTION) && !(lex->sql_command == SQLCOM_DROP_TABLE && lex->drop_temporary && lex->drop_if_exists) && all_tables_not_ok(thd, all_tables)) @@ -2419,7 +2406,7 @@ mysql_execute_command(THD *thd) } #endif } -#endif /* !HAVE_REPLICATION */ +#endif /* HAVE_REPLICATION */ /* When option readonly is set deny operations which change tables. @@ -3211,23 +3198,36 @@ end_with_restore_list: if (result != 2) break; case SQLCOM_UPDATE_MULTI: + { + DBUG_ASSERT(first_table == all_tables && first_table != 0); + /* if we switched from normal update, rights are checked */ + if (result != 2) { - DBUG_ASSERT(first_table == all_tables && first_table != 0); - /* if we switched from normal update, rights are checked */ - if (result != 2) - { - if ((res= multi_update_precheck(thd, all_tables))) - break; - } - else - res= 0; - - res= mysql_multi_update(thd, all_tables, - &select_lex->item_list, - &lex->value_list, - select_lex->where, - select_lex->options, - lex->duplicates, lex->ignore, unit, select_lex); + if ((res= multi_update_precheck(thd, all_tables))) + break; + } + else + res= 0; + + if ((res= mysql_multi_update_prepare(thd))) + break; + +#ifdef HAVE_REPLICATION + /* Check slave filtering rules */ + if (thd->slave_thread && all_tables_not_ok(thd, all_tables)) + { + /* we warn the slave SQL thread */ + my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); + break; + } +#endif /* HAVE_REPLICATION */ + + res= mysql_multi_update(thd, all_tables, + &select_lex->item_list, + &lex->value_list, + select_lex->where, + select_lex->options, + lex->duplicates, lex->ignore, unit, select_lex); break; } case SQLCOM_REPLACE: @@ -3688,6 +3688,8 @@ end_with_restore_list: if (check_access(thd, INSERT_ACL, "mysql", 0, 1, 1, 0) && check_global_access(thd,CREATE_USER_ACL)) break; + if (end_active_trans(thd)) + goto error; if (!(res= mysql_create_user(thd, lex->users_list))) { if (mysql_bin_log.is_open()) @@ -3704,6 +3706,8 @@ end_with_restore_list: if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 1, 0) && check_global_access(thd,CREATE_USER_ACL)) break; + if (end_active_trans(thd)) + goto error; if (!(res= mysql_drop_user(thd, lex->users_list))) { if (mysql_bin_log.is_open()) @@ -3720,6 +3724,8 @@ end_with_restore_list: if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) && check_global_access(thd,CREATE_USER_ACL)) break; + if (end_active_trans(thd)) + goto error; if (!(res= mysql_rename_user(thd, lex->users_list))) { if (mysql_bin_log.is_open()) @@ -4066,6 +4072,19 @@ end_with_restore_list: DBUG_ASSERT(lex->sphead != 0); + if (!lex->sphead->m_db.str || !lex->sphead->m_db.str[0]) + { + if (! thd->db) + { + my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); + delete lex->sphead; + lex->sphead= 0; + goto error; + } + lex->sphead->m_db.length= strlen(thd->db); + lex->sphead->m_db.str= thd->db; + } + if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str, 0, 0, 0, is_schema_db(lex->sphead->m_db.str))) { @@ -4075,13 +4094,10 @@ end_with_restore_list: } if (end_active_trans(thd)) - goto error; - - if (!lex->sphead->m_db.str || !lex->sphead->m_db.str[0]) { - lex->sphead->m_db.length= strlen(thd->db); - lex->sphead->m_db.str= strmake_root(thd->mem_root, thd->db, - lex->sphead->m_db.length); + delete lex->sphead; + lex->sphead= 0; + goto error; } name= lex->sphead->name(&namelen); @@ -4108,11 +4124,23 @@ end_with_restore_list: goto error; } + /* + We need to copy name and db in order to use them for + check_routine_access which is called after lex->sphead has + been deleted. + */ name= thd->strdup(name); - db= thd->strmake(lex->sphead->m_db.str, lex->sphead->m_db.length); + lex->sphead->m_db.str= db= thd->strmake(lex->sphead->m_db.str, + lex->sphead->m_db.length); res= (result= lex->sphead->create(thd)); if (result == SP_OK) { + /* + We must cleanup the unit and the lex here because + sp_grant_privileges calls (indirectly) db_find_routine, + which in turn may call yyparse with THD::lex. + TODO: fix db_find_routine to use a temporary lex. + */ lex->unit.cleanup(); delete lex->sphead; lex->sphead= 0; @@ -4514,6 +4542,9 @@ end_with_restore_list: } case SQLCOM_CREATE_VIEW: { + if (end_active_trans(thd)) + goto error; + if (!(res= mysql_create_view(thd, thd->lex->create_view_mode)) && mysql_bin_log.is_open()) { @@ -4528,7 +4559,8 @@ end_with_restore_list: command[thd->lex->create_view_mode].length); view_store_options(thd, first_table, &buff); buff.append("VIEW ", 5); - if (!first_table->current_db_used) + /* Test if user supplied a db (ie: we did not use thd->db) */ + if (first_table->db != thd->db && first_table->db[0]) { append_identifier(thd, &buff, first_table->db, first_table->db_length); @@ -4560,6 +4592,9 @@ end_with_restore_list: } case SQLCOM_CREATE_TRIGGER: { + if (end_active_trans(thd)) + goto error; + res= mysql_create_or_drop_trigger(thd, all_tables, 1); /* We don't care about trigger body after this point */ @@ -4569,6 +4604,9 @@ end_with_restore_list: } case SQLCOM_DROP_TRIGGER: { + if (end_active_trans(thd)) + goto error; + res= mysql_create_or_drop_trigger(thd, all_tables, 0); break; } @@ -4776,14 +4814,6 @@ error: res= 1; cleanup: - if (unlikely(slave_fake_lock)) - { - DBUG_PRINT("info",("undoing faked lock")); - thd->lock= thd->locked_tables; - thd->locked_tables= fake_prev_lock; - if (thd->lock == thd->locked_tables) - thd->lock= 0; - } DBUG_RETURN(res || thd->net.report_error); } @@ -4854,7 +4884,6 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, bool db_is_pattern= test(want_access & GRANT_ACL); #endif ulong dummy; - const char *db_name; DBUG_ENTER("check_access"); DBUG_PRINT("enter",("db: %s want_access: %lu master_access: %lu", db ? db : "", want_access, sctx->master_access)); @@ -4872,15 +4901,16 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, DBUG_RETURN(TRUE); /* purecov: tested */ } - db_name= db ? db : thd->db; if (schema_db) { if (want_access & ~(SELECT_ACL | EXTRA_ACL)) { if (!no_errors) + { + const char *db_name= db ? db : thd->db; my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), - sctx->priv_user, - sctx->priv_host, db_name); + sctx->priv_user, sctx->priv_host, db_name); + } DBUG_RETURN(TRUE); } else @@ -5000,8 +5030,14 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, { uint found=0; ulong found_access=0; - TABLE_LIST *org_tables=tables; - for (; tables; tables= tables->next_global) + TABLE_LIST *org_tables= tables; + TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table(); + /* + The check that first_not_own_table is not reached is for the case when + the given table list refers to the list for prelocking (contains tables + of other queries). For simple queries first_not_own_table is 0. + */ + for (; tables != first_not_own_table; tables= tables->next_global) { if (tables->schema_table && (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL))) @@ -5012,6 +5048,11 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, information_schema_name.str); return TRUE; } + /* + Register access for view underlying table. + Remove SHOW_VIEW_ACL, because it will be checked during making view + */ + tables->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL); if (tables->derived || tables->schema_table || tables->belong_to_view || (tables->table && (int)tables->table->s->tmp_table) || my_tz_check_n_skip_implicit_tables(&tables, @@ -5055,11 +5096,16 @@ check_routine_access(THD *thd, ulong want_access,char *db, char *name, tables->db= db; tables->table_name= tables->alias= name; - if ((thd->security_ctx->master_access & want_access) == want_access && - !thd->db) + /* + The following test is just a shortcut for check_access() (to avoid + calculating db_access) under the assumption that it's common to + give persons global right to execute all stored SP (but not + necessary to create them). + */ + if ((thd->security_ctx->master_access & want_access) == want_access) tables->grant.privilege= want_access; else if (check_access(thd,want_access,db,&tables->grant.privilege, - 0, no_errors, test(tables->schema_table))) + 0, no_errors, 0)) return TRUE; #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -5760,7 +5806,7 @@ new_create_field(THD *thd, char *field_name, enum_field_types type, case FIELD_TYPE_NULL: break; case FIELD_TYPE_NEWDECIMAL: - if (!length) + if (!length && !new_field->decimals) new_field->length= 10; if (new_field->length > DECIMAL_MAX_PRECISION) { @@ -6146,14 +6192,12 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, { ptr->db= thd->db; ptr->db_length= thd->db_length; - ptr->current_db_used= 1; } else { /* The following can't be "" as we may do 'casedn_str()' on it */ ptr->db= empty_c_string; ptr->db_length= 0; - ptr->current_db_used= 1; } if (thd->stmt_arena->is_stmt_prepare_or_first_sp_execute()) ptr->db= thd->strdup(ptr->db); @@ -6168,8 +6212,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX); ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES); ptr->derived= table->sel; - if (!my_strcasecmp(system_charset_info, ptr->db, - information_schema_name.str)) + if (!ptr->derived && !my_strcasecmp(system_charset_info, ptr->db, + information_schema_name.str)) { ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, ptr->table_name); if (!schema_table || @@ -6916,57 +6960,6 @@ bool check_simple_select() return 0; } -/* - Setup locking for multi-table updates. Used by the replication slave. - Replication slave SQL thread examines (all_tables_not_ok()) the - locking state of referenced tables to determine if the query has to - be executed or ignored. Since in multi-table update, the - 'default' lock is read-only, this lock is corrected early enough by - calling this function, before the slave decides to execute/ignore. - - SYNOPSIS - check_multi_update_lock() - thd Current thread - - RETURN VALUES - 0 ok - 1 error -*/ -static bool check_multi_update_lock(THD *thd) -{ - bool res= 1; - LEX *lex= thd->lex; - TABLE_LIST *table, *tables= lex->query_tables; - DBUG_ENTER("check_multi_update_lock"); - - if (check_db_used(thd, tables)) - goto error; - - /* - Ensure that we have UPDATE or SELECT privilege for each table - The exact privilege is checked in mysql_multi_update() - */ - for (table= tables ; table ; table= table->next_local) - { - TABLE_LIST *save= table->next_local; - table->next_local= 0; - if ((check_access(thd, UPDATE_ACL, table->db, - &table->grant.privilege,0,1, test(table->schema_table)) || - (grant_option && check_grant(thd, UPDATE_ACL, table,0,1,1))) && - check_one_table_access(thd, SELECT_ACL, table)) - goto error; - table->next_local= save; - } - - if (mysql_multi_update_prepare(thd)) - goto error; - - res= 0; - -error: - DBUG_RETURN(res); -} - Comp_creator *comp_eq_creator(bool invert) { @@ -7453,9 +7446,9 @@ Item *negate_expression(THD *thd, Item *expr) Assign as view definer current user SYNOPSIS - default_definer() - Secytity_context current decurity context - definer structure where it should be assigned + default_view_definer() + sctx current security context + definer structure where it should be assigned RETURN FALSE OK @@ -7466,15 +7459,14 @@ bool default_view_definer(Security_context *sctx, st_lex_user *definer) { definer->user.str= sctx->priv_user; definer->user.length= strlen(sctx->priv_user); - if (*sctx->priv_host != 0) - { - definer->host.str= sctx->priv_host; - definer->host.length= strlen(sctx->priv_host); - } - else + + if (!*sctx->priv_host) { my_error(ER_NO_VIEW_USER, MYF(0)); return TRUE; } + + definer->host.str= sctx->priv_host; + definer->host.length= strlen(sctx->priv_host); return FALSE; } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 16433e42e3c..6e6961080cd 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -101,6 +101,11 @@ public: class Prepared_statement: public Statement { public: + enum flag_values + { + IS_IN_USE= 1 + }; + THD *thd; Select_fetch_protocol_prep result; Protocol *protocol; @@ -131,19 +136,8 @@ public: bool execute(String *expanded_query, bool open_cursor); /* Destroy this statement */ bool deallocate(); - - /* Possible values of flags */ -#if defined(_MSC_VER) && _MSC_VER < 1300 - static const int IS_IN_USE; -#else - static const int IS_IN_USE= 1; -#endif }; -/* VC6 can't handle initializing in declaration */ -#if defined(_MSC_VER) && _MSC_VER < 1300 -const int Prepared_statement::IS_IN_USE= 1; -#endif /****************************************************************************** Implementation @@ -1164,6 +1158,7 @@ static int mysql_test_update(Prepared_statement *stmt, #ifndef NO_EMBEDDED_ACCESS_CHECKS table_list->grant.want_privilege= want_privilege; table_list->table->grant.want_privilege= want_privilege; + table_list->register_want_access(want_privilege); #endif thd->lex->select_lex.no_wrap_view_item= TRUE; res= setup_fields(thd, 0, select->item_list, 1, 0, 0); @@ -1175,6 +1170,7 @@ static int mysql_test_update(Prepared_statement *stmt, table_list->grant.want_privilege= table_list->table->grant.want_privilege= (SELECT_ACL & ~table_list->table->grant.privilege); + table_list->register_want_access(SELECT_ACL); #endif if (setup_fields(thd, 0, stmt->lex->value_list, 0, 0, 0)) goto error; @@ -1748,6 +1744,8 @@ static bool check_prepared_statement(Prepared_statement *stmt, case SQLCOM_ROLLBACK: case SQLCOM_TRUNCATE: case SQLCOM_CALL: + case SQLCOM_CREATE_VIEW: + case SQLCOM_DROP_VIEW: break; default: @@ -1830,7 +1828,7 @@ static bool init_param_array(Prepared_statement *stmt) void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length) { Prepared_statement *stmt= new Prepared_statement(thd, &thd->protocol_prep); - bool rc; + bool error; DBUG_ENTER("mysql_stmt_prepare"); DBUG_PRINT("prep_query", ("%s", packet)); @@ -1853,12 +1851,12 @@ void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length) if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),QUERY_PRIOR); - rc= stmt->prepare(packet, packet_length); + error= stmt->prepare(packet, packet_length); if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),WAIT_PRIOR); - if (rc) + if (error) { /* Statement map deletes statement on erase */ thd->stmt_map.erase(stmt); @@ -1900,7 +1898,7 @@ static const char *get_dynamic_sql_string(LEX *lex, uint *query_len) CHARSET_INFO *to_cs= thd->variables.collation_connection; bool needs_conversion; user_var_entry *entry; - String *pstr= &str; + String *var_value= &str; uint32 unused, len; /* Convert @var contents to string in connection character set. Although @@ -1914,13 +1912,13 @@ static const char *get_dynamic_sql_string(LEX *lex, uint *query_len) && entry->value) { my_bool is_var_null; - pstr= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC); + var_value= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC); /* NULL value of variable checked early as entry->value so here we can't get NULL in normal conditions */ DBUG_ASSERT(!is_var_null); - if (!pstr) + if (!var_value) goto end; } else @@ -1932,22 +1930,25 @@ static const char *get_dynamic_sql_string(LEX *lex, uint *query_len) str.set("NULL", 4, &my_charset_latin1); } - needs_conversion= String::needs_conversion(pstr->length(), - pstr->charset(), to_cs, &unused); + needs_conversion= String::needs_conversion(var_value->length(), + var_value->charset(), to_cs, + &unused); - len= needs_conversion ? pstr->length() * to_cs->mbmaxlen : pstr->length(); + len= (needs_conversion ? var_value->length() * to_cs->mbmaxlen : + var_value->length()); if (!(query_str= alloc_root(thd->mem_root, len+1))) goto end; if (needs_conversion) { uint dummy_errors; - len= copy_and_convert(query_str, len, to_cs, pstr->ptr(), pstr->length(), - pstr->charset(), &dummy_errors); + len= copy_and_convert(query_str, len, to_cs, var_value->ptr(), + var_value->length(), var_value->charset(), + &dummy_errors); } else - memcpy(query_str, pstr->ptr(), pstr->length()); - query_str[len]= '\0'; + memcpy(query_str, var_value->ptr(), var_value->length()); + query_str[len]= '\0'; // Safety (mostly for debug) *query_len= len; } else @@ -1997,10 +1998,9 @@ void mysql_sql_stmt_prepare(THD *thd) Prepared_statement *stmt; const char *query; uint query_len; - DBUG_ENTER("mysql_sql_stmt_prepare"); - DBUG_ASSERT(thd->protocol == &thd->protocol_simple); + if ((stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name))) { /* @@ -2172,8 +2172,9 @@ static void reset_stmt_params(Prepared_statement *stmt) client, otherwise an error message is set in THD. */ -void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) +void mysql_stmt_execute(THD *thd, char *packet_arg, uint packet_length) { + uchar *packet= (uchar*)packet_arg; // GCC 4.0.1 workaround ulong stmt_id= uint4korr(packet); ulong flags= (ulong) ((uchar) packet[4]); /* Query text for binary, general or slow log, if any of them is open */ @@ -2182,7 +2183,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) uchar *packet_end= (uchar *) packet + packet_length - 1; #endif Prepared_statement *stmt; - bool rc; + bool error; DBUG_ENTER("mysql_stmt_execute"); packet+= 9; /* stmt_id + 5 bytes of flags */ @@ -2217,12 +2218,11 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) #endif if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),QUERY_PRIOR); - rc= stmt->execute(&expanded_query, - test(flags & (ulong) CURSOR_TYPE_READ_ONLY)); + error= stmt->execute(&expanded_query, + test(flags & (ulong) CURSOR_TYPE_READ_ONLY)); if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(), WAIT_PRIOR); - - if (rc == 0) + if (error == 0) mysql_log.write(thd, COM_STMT_EXECUTE, "[%lu] %s", stmt->id, thd->query); DBUG_VOID_RETURN; @@ -2261,9 +2261,7 @@ void mysql_sql_stmt_execute(THD *thd) LEX_STRING *name= &lex->prepared_stmt_name; /* Query text for binary, general or slow log, if any of them is open */ String expanded_query; - DBUG_ENTER("mysql_sql_stmt_execute"); - DBUG_PRINT("info", ("EXECUTE: %.*s\n", name->length, name->str)); if (!(stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name))) @@ -2408,7 +2406,6 @@ void mysql_stmt_close(THD *thd, char *packet) /* There is always space for 4 bytes in packet buffer */ ulong stmt_id= uint4korr(packet); Prepared_statement *stmt; - DBUG_ENTER("mysql_stmt_close"); if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_close"))) @@ -2418,7 +2415,7 @@ void mysql_stmt_close(THD *thd, char *packet) The only way currently a statement can be deallocated when it's in use is from within Dynamic SQL. */ - DBUG_ASSERT(! (stmt->flags & Prepared_statement::IS_IN_USE)); + DBUG_ASSERT(! (stmt->flags & (uint) Prepared_statement::IS_IN_USE)); (void) stmt->deallocate(); DBUG_VOID_RETURN; @@ -2462,7 +2459,7 @@ void mysql_sql_stmt_close(THD *thd) mysql_stmt_get_longdata() thd Thread handle packet String to append - packet_length Length of string + packet_length Length of string (including end \0) DESCRIPTION Get a part of a long data. To make the protocol efficient, we are @@ -2479,13 +2476,12 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length) Prepared_statement *stmt; Item_param *param; char *packet_end= packet + packet_length - 1; - DBUG_ENTER("mysql_stmt_get_longdata"); statistic_increment(thd->status_var.com_stmt_send_long_data, &LOCK_status); #ifndef EMBEDDED_LIBRARY /* Minimal size of long data packet is 6 bytes */ - if ((ulong) (packet_end - packet) < MYSQL_LONG_DATA_HEADER) + if (packet_length <= MYSQL_LONG_DATA_HEADER) { my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_send_long_data"); DBUG_VOID_RETURN; @@ -2592,7 +2588,7 @@ Prepared_statement::Prepared_statement(THD *thd_arg, Protocol *protocol_arg) param_array(0), param_count(0), last_errno(0), - flags(IS_IN_USE) + flags((uint) IS_IN_USE) { *last_error= '\0'; } @@ -2709,7 +2705,7 @@ bool Prepared_statement::set_name(LEX_STRING *name_arg) bool Prepared_statement::prepare(const char *packet, uint packet_len) { - bool rc; + bool error; Statement stmt_backup; Query_arena *old_stmt_arena; DBUG_ENTER("Prepared_statement::prepare"); @@ -2739,7 +2735,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) lex_start(thd, (uchar*) thd->query, thd->query_length); lex->stmt_prepare_mode= TRUE; - rc= yyparse((void *)thd) || thd->is_fatal_error || + error= yyparse((void *)thd) || thd->is_fatal_error || thd->net.report_error || init_param_array(this); lex->safe_to_cache_query= FALSE; /* @@ -2764,10 +2760,10 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) */ DBUG_ASSERT(thd->free_list == NULL); - if (rc == 0) - rc= check_prepared_statement(this, name.str != 0); + if (error == 0) + error= check_prepared_statement(this, name.str != 0); - if (rc && lex->sphead) + if (error && lex->sphead) { delete lex->sphead; lex->sphead= NULL; @@ -2777,14 +2773,14 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) thd->restore_backup_statement(this, &stmt_backup); thd->stmt_arena= old_stmt_arena; - if (rc == 0) + if (error == 0) { setup_set_params(); init_stmt_after_parse(lex); state= Query_arena::PREPARED; - flags&= ~IS_IN_USE; + flags&= ~ (uint) IS_IN_USE; } - DBUG_RETURN(rc); + DBUG_RETURN(error); } /* @@ -2806,6 +2802,10 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) Preconditions, postconditions. ------------------------------ See the comment for Prepared_statement::prepare(). + + RETURN + FALSE ok + TRUE Error */ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) @@ -2813,7 +2813,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) Statement stmt_backup; Query_arena *old_stmt_arena; Item *old_free_list; - bool rc= TRUE; + bool error= TRUE; statistic_increment(thd->status_var.com_stmt_execute, &LOCK_status); @@ -2823,7 +2823,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) my_message(last_errno, last_error, MYF(0)); return TRUE; } - if (flags & IS_IN_USE) + if (flags & (uint) IS_IN_USE) { my_error(ER_PS_NO_RECURSION, MYF(0)); return TRUE; @@ -2885,13 +2885,14 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) reinit_stmt_before_use(thd, lex); thd->protocol= protocol; /* activate stmt protocol */ - rc= open_cursor ? mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR, - &result, &cursor) : - mysql_execute_command(thd); + error= (open_cursor ? + mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR, + &result, &cursor) : + mysql_execute_command(thd)); thd->protocol= &thd->protocol_simple; /* use normal protocol */ /* Assert that if an error, no cursor is open */ - DBUG_ASSERT(! (rc && cursor)); + DBUG_ASSERT(! (error && cursor)); if (! cursor) { @@ -2906,8 +2907,8 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) state= Query_arena::EXECUTED; error: - flags&= ~IS_IN_USE; - return rc; + flags&= ~ (uint) IS_IN_USE; + return error; } @@ -2917,7 +2918,7 @@ bool Prepared_statement::deallocate() { /* We account deallocate in the same manner as mysql_stmt_close */ statistic_increment(thd->status_var.com_stmt_close, &LOCK_status); - if (flags & IS_IN_USE) + if (flags & (uint) IS_IN_USE) { my_error(ER_PS_NO_RECURSION, MYF(0)); return TRUE; diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 154e828b47e..80fcb973028 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -134,6 +134,8 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) { TABLE_LIST *ren_table,*new_table; frm_type_enum frm_type; + db_type table_type; + DBUG_ENTER("rename_tables"); for (ren_table= table_list; ren_table; ren_table= new_table->next_local) @@ -166,13 +168,12 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) reg_ext); unpack_filename(name, name); - frm_type= mysql_frm_type(name); + frm_type= mysql_frm_type(thd, name, &table_type); switch (frm_type) { case FRMTYPE_TABLE: { - db_type table_type; - if ((table_type= get_table_type(thd, name)) == DB_TYPE_UNKNOWN) + if (table_type == DB_TYPE_UNKNOWN) my_error(ER_FILE_NOT_FOUND, MYF(0), name, my_errno); else rc= mysql_rename_table(table_type, ren_table->db, old_alias, diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 28b0ae8c78e..b5d12c0d2d2 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -373,6 +373,11 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, goto err; } + /* + Call readers_addref before opening log to track count + of binlog readers + */ + mysql_bin_log.readers_addref(); if ((file=open_binlog(&log, log_file_name, &errmsg)) < 0) { my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; @@ -570,7 +575,8 @@ impossible position"; goto err; if (!(flags & BINLOG_DUMP_NON_BLOCK) && - mysql_bin_log.is_active(log_file_name)) + mysql_bin_log.is_active(log_file_name) && + !mysql_bin_log.is_reset_pending()) { /* Block until there is more data in the log @@ -684,6 +690,13 @@ impossible position"; { bool loop_breaker = 0; // need this to break out of the for loop from switch + + // if we are going to switch log file anyway, close current log first + end_io_cache(&log); + (void) my_close(file, MYF(MY_WME)); + // decrease reference count of binlog readers + mysql_bin_log.readers_release(); + thd->proc_info = "Finished reading one binlog; switching to next binlog"; switch (mysql_bin_log.find_next_log(&linfo, 1)) { case LOG_INFO_EOF: @@ -692,16 +705,25 @@ impossible position"; case 0: break; default: + // need following call to do release on err label + mysql_bin_log.readers_addref(); errmsg = "could not find next log"; my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; goto err; } - if (loop_breaker) - break; + if (loop_breaker) + { + // need following call to do release on end label + mysql_bin_log.readers_addref(); + break; + } - end_io_cache(&log); - (void) my_close(file, MYF(MY_WME)); + /* + Call readers_addref before opening log to track count + of binlog readers + */ + mysql_bin_log.readers_addref(); /* Call fake_rotate_event() in case the previous log (the one which @@ -734,6 +756,8 @@ end: end_io_cache(&log); (void)my_close(file, MYF(MY_WME)); + // decrease reference count of binlog readers + mysql_bin_log.readers_release(); send_eof(thd); thd->proc_info = "Waiting to finalize termination"; @@ -760,6 +784,9 @@ err: pthread_mutex_unlock(&LOCK_thread_count); if (file >= 0) (void) my_close(file, MYF(MY_WME)); + // decrease reference count of binlog readers + mysql_bin_log.readers_release(); + my_message(my_errno, errmsg, MYF(0)); DBUG_VOID_RETURN; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a02a61b7a86..360b161a78a 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -102,8 +102,6 @@ static COND *optimize_cond(JOIN *join, COND *conds, List<TABLE_LIST> *join_list, Item::cond_result *cond_value); static bool resolve_nested_join (TABLE_LIST *table); -static COND *remove_eq_conds(THD *thd, COND *cond, - Item::cond_result *cond_value); static bool const_expression_in_where(COND *conds,Item *item, Item **comp_item); static bool open_tmp_table(TABLE *table); static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param, @@ -537,6 +535,9 @@ JOIN::optimize() DBUG_RETURN(0); optimized= 1; + if (thd->lex->orig_sql_command != SQLCOM_SHOW_STATUS) + thd->status_var.last_query_cost= 0.0; + row_limit= ((select_distinct || order || group_list) ? HA_POS_ERROR : unit->select_limit_cnt); /* select_limit is used to decide if we are likely to scan the whole table */ @@ -822,6 +823,7 @@ JOIN::optimize() { order=0; // The output has only one row simple_order=1; + select_distinct= 0; // No need in distinct for 1 row } calc_group_buffer(this, group_list); @@ -1167,18 +1169,21 @@ JOIN::save_join_tab() void JOIN::exec() { + List<Item> *columns_list= &fields_list; int tmp_error; DBUG_ENTER("JOIN::exec"); error= 0; if (procedure) { - if (procedure->change_columns(fields_list) || - result->prepare(fields_list, unit)) + procedure_fields_list= fields_list; + if (procedure->change_columns(procedure_fields_list) || + result->prepare(procedure_fields_list, unit)) { thd->limit_found_rows= thd->examined_row_count= 0; DBUG_VOID_RETURN; } + columns_list= &procedure_fields_list; } (void) result->prepare2(); // Currently, this cannot fail. @@ -1189,7 +1194,7 @@ JOIN::exec() (zero_result_cause?zero_result_cause:"No tables used")); else { - result->send_fields(fields_list, + result->send_fields(*columns_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); /* We have to test for 'conds' here as the WHERE may not be constant @@ -1200,9 +1205,9 @@ JOIN::exec() (!conds || conds->val_int()) && (!having || having->val_int())) { - if (do_send_rows && (procedure ? (procedure->send_row(fields_list) || - procedure->end_of_records()) - : result->send_data(fields_list))) + if (do_send_rows && + (procedure ? (procedure->send_row(procedure_fields_list) || + procedure->end_of_records()) : result->send_data(fields_list))) error= 1; else { @@ -1226,7 +1231,8 @@ JOIN::exec() if (zero_result_cause) { - (void) return_zero_rows(this, result, select_lex->leaf_tables, fields_list, + (void) return_zero_rows(this, result, select_lex->leaf_tables, + *columns_list, send_row_on_empty_set(), select_options, zero_result_cause, @@ -1376,7 +1382,7 @@ JOIN::exec() DBUG_PRINT("info",("Creating group table")); /* Free first data from old join */ - curr_join->join_free(0); + curr_join->join_free(); if (make_simple_join(curr_join, curr_tmp_table)) DBUG_VOID_RETURN; calc_group_buffer(curr_join, group_list); @@ -1477,7 +1483,7 @@ JOIN::exec() if (curr_tmp_table->distinct) curr_join->select_distinct=0; /* Each row is unique */ - curr_join->join_free(0); /* Free quick selects */ + curr_join->join_free(); /* Free quick selects */ if (curr_join->select_distinct && ! curr_join->group_list) { thd->proc_info="Removing duplicates"; @@ -1676,7 +1682,8 @@ JOIN::exec() { thd->proc_info="Sending data"; DBUG_PRINT("info", ("%s", thd->proc_info)); - result->send_fields(*curr_fields_list, + result->send_fields((procedure ? curr_join->procedure_fields_list : + *curr_fields_list), Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); error= do_select(curr_join, curr_fields_list, NULL, procedure); thd->limit_found_rows= curr_join->send_records; @@ -2486,11 +2493,11 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, and use them in equality propagation process (see details in OptimizerKBAndTodo) */ - if ((cond->functype() == Item_func::BETWEEN) && - value[0]->eq(value[1], field->binary())) - eq_func= TRUE; - else + if ((cond->functype() != Item_func::BETWEEN) || + ((Item_func_between*) cond)->negated || + !value[0]->eq(value[1], field->binary())) return; + eq_func= TRUE; } if (field->result_type() == STRING_RESULT) @@ -5734,33 +5741,88 @@ void JOIN_TAB::cleanup() } -void JOIN::join_free(bool full) +/* + Partially cleanup JOIN after it has executed: close index or rnd read + (table cursors), free quick selects. + + DESCRIPTION + This function is called in the end of execution of a JOIN, before the used + tables are unlocked and closed. + + For a join that is resolved using a temporary table, the first sweep is + performed against actual tables and an intermediate result is inserted + into the temprorary table. + The last sweep is performed against the temporary table. Therefore, + the base tables and associated buffers used to fill the temporary table + are no longer needed, and this function is called to free them. + + For a join that is performed without a temporary table, this function + is called after all rows are sent, but before EOF packet is sent. + + For a simple SELECT with no subqueries this function performs a full + cleanup of the JOIN and calls mysql_unlock_read_tables to free used base + tables. + + If a JOIN is executed for a subquery or if it has a subquery, we can't + do the full cleanup and need to do a partial cleanup only. + o If a JOIN is not the top level join, we must not unlock the tables + because the outer select may not have been evaluated yet, and we + can't unlock only selected tables of a query. + + o Additionally, if this JOIN corresponds to a correlated subquery, we + should not free quick selects and join buffers because they will be + needed for the next execution of the correlated subquery. + + o However, if this is a JOIN for a [sub]select, which is not + a correlated subquery itself, but has subqueries, we can free it + fully and also free JOINs of all its subqueries. The exception + is a subquery in SELECT list, e.g: + SELECT a, (select max(b) from t1) group by c + This subquery will not be evaluated at first sweep and its value will + not be inserted into the temporary table. Instead, it's evaluated + when selecting from the temporary table. Therefore, it can't be freed + here even though it's not correlated. +*/ + +void JOIN::join_free() { SELECT_LEX_UNIT *unit; SELECT_LEX *sl; - DBUG_ENTER("JOIN::join_free"); - /* Optimization: if not EXPLAIN and we are done with the JOIN, free all tables. */ - full= full || (!select_lex->uncacheable && !thd->lex->describe); + bool full= (!select_lex->uncacheable && !thd->lex->describe); + bool can_unlock= full; + DBUG_ENTER("JOIN::join_free"); cleanup(full); for (unit= select_lex->first_inner_unit(); unit; unit= unit->next_unit()) for (sl= unit->first_select(); sl; sl= sl->next_select()) { - JOIN *join= sl->join; - if (join) - join->join_free(full); + Item_subselect *subselect= sl->master_unit()->item; + bool full_local= full && (!subselect || subselect->is_evaluated()); + /* + If this join is evaluated, we can fully clean it up and clean up all + its underlying joins even if they are correlated -- they will not be + used any more anyway. + If this join is not yet evaluated, we still must clean it up to + close its table cursors -- it may never get evaluated, as in case of + ... HAVING FALSE OR a IN (SELECT ...)) + but all table cursors must be closed before the unlock. + */ + sl->cleanup_all_joins(full_local); + /* Can't unlock if at least one JOIN is still needed */ + can_unlock= can_unlock && full_local; } /* We are not using tables anymore Unlock all tables. We may be in an INSERT .... SELECT statement. */ - if (full && lock && thd->lock && !(select_options & SELECT_NO_UNLOCK) && + if (can_unlock && lock && thd->lock && + !(select_options & SELECT_NO_UNLOCK) && !select_lex->subquery_in_having && (select_lex == (thd->lex->unit.fake_select_lex ? thd->lex->unit.fake_select_lex : &thd->lex->select_lex))) @@ -5866,7 +5928,7 @@ eq_ref_table(JOIN *join, ORDER *start_order, JOIN_TAB *tab) tab->cached_eq_ref_table=1; if (tab->type == JT_CONST) // We can skip const tables return (tab->eq_ref_table=1); /* purecov: inspected */ - if (tab->type != JT_EQ_REF) + if (tab->type != JT_EQ_REF || tab->table->maybe_null) return (tab->eq_ref_table=0); // We must use this Item **ref_item=tab->ref.items; Item **end=ref_item+tab->ref.key_parts; @@ -6074,7 +6136,7 @@ return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables, DBUG_RETURN(0); } - join->join_free(0); + join->join_free(); if (send_row) { @@ -6269,6 +6331,21 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal) { Item *left_item= ((Item_func*) item)->arguments()[0]; Item *right_item= ((Item_func*) item)->arguments()[1]; + + if (left_item->type() == Item::REF_ITEM && + ((Item_ref*)left_item)->ref_type() == Item_ref::VIEW_REF) + { + 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 && @@ -7410,7 +7487,7 @@ optimize_cond(JOIN *join, COND *conds, List<TABLE_LIST> *join_list, COND_FALSE always false ( 1 = 2 ) */ -static COND * +COND * remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) { if (cond->type() == Item::COND_ITEM) @@ -7967,7 +8044,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, statistic_increment(thd->status_var.created_tmp_tables, &LOCK_status); - if (use_temp_pool) + if (use_temp_pool && !(test_flags & TEST_KEEP_TMP_TABLES)) temp_pool_slot = bitmap_lock_set_next(&temp_pool); if (temp_pool_slot != MY_BIT_NONE) // we got a slot @@ -8221,7 +8298,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, (select_options & (OPTION_BIG_TABLES | SELECT_SMALL_RESULT)) == OPTION_BIG_TABLES || (select_options & TMP_TABLE_FORCE_MYISAM)) { - table->file=get_new_handler(table,table->s->db_type= DB_TYPE_MYISAM); + table->file= get_new_handler(table, &table->mem_root, + table->s->db_type= DB_TYPE_MYISAM); if (group && (param->group_parts > table->file->max_key_parts() || param->group_length > table->file->max_key_length())) @@ -8229,7 +8307,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, } else { - table->file=get_new_handler(table,table->s->db_type= DB_TYPE_HEAP); + table->file= get_new_handler(table, &table->mem_root, + table->s->db_type= DB_TYPE_HEAP); } if (table->s->fields) { @@ -8816,7 +8895,8 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param, new_table= *table; new_table.s= &new_table.share_not_to_be_used; new_table.s->db_type= DB_TYPE_MYISAM; - if (!(new_table.file= get_new_handler(&new_table,DB_TYPE_MYISAM))) + if (!(new_table.file= get_new_handler(&new_table, &new_table.mem_root, + DB_TYPE_MYISAM))) DBUG_RETURN(1); // End of memory save_proc_info=thd->proc_info; @@ -9006,7 +9086,11 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) error= (*end_select)(join,join_tab,1); } else if (join->send_row_on_empty_set()) - rc= join->result->send_data(*join->fields); + { + List<Item> *columns_list= (procedure ? &join->procedure_fields_list : + fields); + rc= join->result->send_data(*columns_list); + } } else { @@ -9031,7 +9115,7 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) The following will unlock all cursors if the command wasn't an update command */ - join->join_free(0); // Unlock all cursors + join->join_free(); // Unlock all cursors if (join->result->send_eof()) rc= 1; // Don't send error } @@ -10039,7 +10123,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), DBUG_RETURN(NESTED_LOOP_OK); // Didn't match having error=0; if (join->procedure) - error=join->procedure->send_row(*join->fields); + error=join->procedure->send_row(join->procedure_fields_list); else if (join->do_send_rows) error=join->result->send_data(*join->fields); if (error) @@ -11371,7 +11455,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table, ulong total_length= 0; for (ptr= first_field, field_length=field_lengths ; *ptr ; ptr++) { - uint length= (*ptr)->pack_length(); + uint length= (*ptr)->sort_length(); (*field_length++)= length; total_length+= length; } @@ -11911,11 +11995,16 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, We check order_item->fixed because Item_func_group_concat can put arguments for which fix_fields already was called. */ + thd->lex->current_select->is_item_list_lookup= 1; if (!order_item->fixed && (order_item->fix_fields(thd, order->item) || (order_item= *order->item)->check_cols(1) || thd->is_fatal_error)) + { + thd->lex->current_select->is_item_list_lookup= 0; return TRUE; /* Wrong field. */ + } + thd->lex->current_select->is_item_list_lookup= 0; uint el= all_fields.elements; all_fields.push_front(order_item); /* Add new field to field list. */ diff --git a/sql/sql_select.h b/sql/sql_select.h index be51851e760..ec969e51c8d 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -245,6 +245,7 @@ class JOIN :public Sql_alloc //Part, shared with list above, emulate following list List<Item> tmp_fields_list1, tmp_fields_list2, tmp_fields_list3; List<Item> &fields_list; // hold field list passed to mysql_select + List<Item> procedure_fields_list; int error; ORDER *order, *group_list, *proc_param; //hold parameters of mysql_select @@ -359,7 +360,7 @@ class JOIN :public Sql_alloc the end of execution in order to increase concurrency and reduce memory consumption. */ - void join_free(bool full); + void join_free(); /* Cleanup this JOIN, possibly for reuse */ void cleanup(bool full); void clear(); @@ -524,3 +525,4 @@ bool cp_buffer_from_ref(THD *thd, TABLE_REF *ref); bool error_if_full_join(JOIN *join); int report_error(TABLE *table, int error); int safe_index_read(JOIN_TAB *tab); +COND *remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 24acb1fb6d1..d06b09f9793 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1031,6 +1031,11 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet) packet->append(" COMMENT=", 9); append_unescaped(packet, share->comment, strlen(share->comment)); } + if (share->connect_string.length) + { + packet->append(" CONNECTION=", 12); + append_unescaped(packet, share->connect_string.str, share->connect_string.length); + } if (file->raid_type) { uint length; @@ -1283,9 +1288,6 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) VOID(pthread_mutex_unlock(&LOCK_thread_count)); thread_info *thd_info; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - Security_context *sctx; -#endif time_t now= time(0); while ((thd_info=thread_infos.get())) { @@ -1665,6 +1667,9 @@ static bool show_status_array(THD *thd, const char *wild, value= (value-(char*) &dflt_key_cache_var)+ (char*) dflt_key_cache; end= longlong10_to_str(*(longlong*) value, buff, 10); break; + case SHOW_NET_COMPRESSION: + end= strmov(buff, thd->net.compress ? "ON" : "OFF"); + break; case SHOW_UNDEF: // Show never happen case SHOW_SYS: break; // Return empty string @@ -2026,6 +2031,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) Security_context *sctx= thd->security_ctx; uint derived_tables= lex->derived_tables; int error= 1; + db_type not_used; Open_tables_state open_tables_state_backup; DBUG_ENTER("get_all_tables"); @@ -2137,7 +2143,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) else { my_snprintf(end, len, "/%s%s", file_name, reg_ext); - switch (mysql_frm_type(path)) { + switch (mysql_frm_type(thd, path, ¬_used)) { case FRMTYPE_ERROR: table->field[3]->store("ERROR", 5, system_charset_info); break; @@ -2356,14 +2362,14 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables, table->field[7]->store((longlong) file->records, TRUE); table->field[7]->set_notnull(); } - table->field[8]->store((longlong) file->mean_rec_length); - table->field[9]->store((longlong) file->data_file_length); + table->field[8]->store((longlong) file->mean_rec_length, TRUE); + table->field[9]->store((longlong) file->data_file_length, TRUE); if (file->max_data_file_length) { - table->field[10]->store((longlong) file->max_data_file_length); + table->field[10]->store((longlong) file->max_data_file_length, TRUE); } - table->field[11]->store((longlong) file->index_file_length); - table->field[12]->store((longlong) file->delete_length); + table->field[11]->store((longlong) file->index_file_length, TRUE); + table->field[12]->store((longlong) file->delete_length, TRUE); if (show_table->found_next_number_field) { table->field[13]->store((longlong) file->auto_increment_value, TRUE); @@ -2582,11 +2588,12 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, is_blob= (field->type() == FIELD_TYPE_BLOB); if (field->has_charset() || is_blob) { - longlong c_octet_len= is_blob ? (longlong) field->max_length() : - (longlong) field->max_length()/field->charset()->mbmaxlen; - table->field[8]->store(c_octet_len, TRUE); + longlong char_max_len= is_blob ? + (longlong) field->max_length() / field->charset()->mbminlen : + (longlong) field->max_length() / field->charset()->mbmaxlen; + table->field[8]->store(char_max_len, TRUE); table->field[8]->set_notnull(); - table->field[9]->store((longlong) field->max_length()); + table->field[9]->store((longlong) field->max_length(), TRUE); table->field[9]->set_notnull(); } @@ -2627,7 +2634,7 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, if (field_length >= 0) { - table->field[10]->store((longlong) field_length); + table->field[10]->store((longlong) field_length, TRUE); table->field[10]->set_notnull(); } if (decimals >= 0) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index a1f2be7e11c..a990cf777d6 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -277,6 +277,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, String wrong_tables; int error; bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0; + DBUG_ENTER("mysql_rm_table_part2"); if (lock_table_names(thd, tables)) @@ -288,6 +289,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, for (table= tables; table; table= table->next_local) { char *db=table->db; + db_type table_type= DB_TYPE_UNKNOWN; + mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL); if (!close_temporary_table(thd, db, table->table_name)) { @@ -315,7 +318,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, if (drop_temporary || (access(path,F_OK) && ha_create_table_from_engine(thd,db,alias)) || - (!drop_view && mysql_frm_type(path) != FRMTYPE_TABLE)) + (!drop_view && + mysql_frm_type(thd, path, &table_type) != FRMTYPE_TABLE)) { // Table was not found on disk and table can't be created from engine if (if_exists) @@ -328,11 +332,13 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, else { char *end; - db_type table_type= get_table_type(thd, path); + if (table_type == DB_TYPE_UNKNOWN) + mysql_frm_type(thd, path, &table_type); *(end=fn_ext(path))=0; // Remove extension for delete error= ha_delete_table(thd, table_type, path, table->table_name, !dont_log_query); - if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) && if_exists) + if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) && + (if_exists || table_type == DB_TYPE_UNKNOWN)) error= 0; if (error == HA_ERR_ROW_IS_REFERENCED) { @@ -897,8 +903,8 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, sql_field->charset= (dup_field->charset ? dup_field->charset : create_info->default_table_charset); - sql_field->length= dup_field->length; - sql_field->pack_length= dup_field->pack_length; + sql_field->length= dup_field->chars_length; + sql_field->pack_length= dup_field->pack_length; sql_field->key_length= dup_field->key_length; sql_field->create_length_to_internal_length(); sql_field->decimals= dup_field->decimals; @@ -1207,13 +1213,17 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, { column->length*= sql_field->charset->mbmaxlen; - if (f_is_blob(sql_field->pack_flag)) + if (f_is_blob(sql_field->pack_flag) || + (f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL)) { if (!(file->table_flags() & HA_CAN_INDEX_BLOBS)) { my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name); DBUG_RETURN(-1); } + if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type == + Field::GEOM_POINT) + column->length= 21; if (!column->length) { my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name); @@ -1590,7 +1600,7 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name, if (create_info->row_type == ROW_TYPE_DYNAMIC) db_options|=HA_OPTION_PACK_RECORD; alias= table_case_name(create_info, table_name); - if (!(file=get_new_handler((TABLE*) 0, create_info->db_type))) + if (!(file=get_new_handler((TABLE*) 0, thd->mem_root, create_info->db_type))) { my_error(ER_OUTOFMEMORY, MYF(0), 128);//128 bytes invented DBUG_RETURN(TRUE); @@ -1626,7 +1636,8 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name, } if (check_partition_info(part_info, part_engine_type, file, create_info->max_rows)) - DBUG_RETURN(TRUE); + goto err; + /* We reverse the partitioning parser and generate a standard format for syntax stored in frm file. @@ -1634,7 +1645,7 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name, if (!(part_syntax_buf= generate_partition_syntax(part_info, &syntax_len, TRUE,TRUE))) - DBUG_RETURN(TRUE); + goto err; part_info->part_info_string= part_syntax_buf; part_info->part_info_len= syntax_len; if ((!(file->partition_flags() & HA_CAN_PARTITION)) || @@ -1644,7 +1655,8 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name, The handler assigned to the table cannot handle partitioning. Assign the partition handler as the handler of the table. */ - DBUG_PRINT("info", ("db_type= %d, part_flag= %d", create_info->db_type,file->partition_flags())); + DBUG_PRINT("info", ("db_type: %d part_flag: %d", + create_info->db_type,file->partition_flags())); delete file; create_info->db_type= DB_TYPE_PARTITION_DB; if (!(file= get_ha_partition(part_info))) @@ -1702,7 +1714,8 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name, push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR), alias); - goto no_err; + error= 0; + goto err; } my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias); goto err; @@ -1717,7 +1730,7 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name, if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) goto warn; my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name); - goto end; + goto unlock_and_end; } } @@ -1741,7 +1754,7 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name, if (create_if_not_exists) goto warn; my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name); - goto end; + goto unlock_and_end; } } @@ -1755,14 +1768,14 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name, if (rea_create_table(thd, path, db, table_name, create_info, fields, key_count, key_info_buffer, file)) - goto end; + goto unlock_and_end; if (create_info->options & HA_LEX_CREATE_TMP_TABLE) { /* Open table and put in temporary table list */ if (!(open_temporary_table(thd, path, db, table_name, 1))) { (void) rm_temporary_table(create_info->db_type, path); - goto end; + goto unlock_and_end; } thd->tmp_table_used= 1; } @@ -1772,29 +1785,24 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name, Query_log_event qinfo(thd, thd->query, thd->query_length, FALSE, FALSE); mysql_bin_log.write(&qinfo); } + error= FALSE; - goto end; +unlock_and_end: + VOID(pthread_mutex_unlock(&LOCK_open)); + start_waiting_global_read_lock(thd); + +err: + thd->proc_info="After create"; + delete file; + DBUG_RETURN(error); warn: - error= 0; + error= FALSE; push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR), alias); create_info->table_existed= 1; // Mark that table existed - -end: - VOID(pthread_mutex_unlock(&LOCK_open)); - start_waiting_global_read_lock(thd); - delete file; - thd->proc_info="After create"; - DBUG_RETURN(error); - -err: - delete file; - DBUG_RETURN(TRUE); -no_err: - delete file; - DBUG_RETURN(FALSE); + goto unlock_and_end; } /* @@ -1946,10 +1954,12 @@ mysql_rename_table(enum db_type base, const char *new_db, const char *new_name) { + THD *thd= current_thd; char from[FN_REFLEN], to[FN_REFLEN], lc_from[FN_REFLEN], lc_to[FN_REFLEN]; char *from_base= from, *to_base= to; char tmp_name[NAME_LEN+1]; - handler *file=(base == DB_TYPE_UNKNOWN ? 0 : get_new_handler((TABLE*) 0, base)); + handler *file= (base == DB_TYPE_UNKNOWN ? 0 : + get_new_handler((TABLE*) 0, thd->mem_root, base)); int error=0; DBUG_ENTER("mysql_rename_table"); @@ -2095,8 +2105,8 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table, { char* backup_dir= thd->lex->backup_dir; char src_path[FN_REFLEN], dst_path[FN_REFLEN]; - char* table_name = table->table_name; - char* db = thd->db ? thd->db : table->db; + char* table_name= table->table_name; + char* db= table->db; if (fn_format_relative_to_data_home(src_path, table_name, backup_dir, reg_ext)) @@ -2132,12 +2142,15 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table, Now we should be able to open the partially restored table to finish the restore in the handler later on */ - if (!(table->table = reopen_name_locked_table(thd, table))) + pthread_mutex_lock(&LOCK_open); + if (reopen_name_locked_table(thd, table)) { - pthread_mutex_lock(&LOCK_open); unlock_table_name(thd, table); pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(send_check_errmsg(thd, table, "restore", + "Failed to open partially restored table")); } + pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); } @@ -2234,12 +2247,16 @@ static int prepare_for_repair(THD* thd, TABLE_LIST *table_list, Now we should be able to open the partially repaired table to finish the repair in the handler later on. */ - if (!(table_list->table = reopen_name_locked_table(thd, table_list))) + pthread_mutex_lock(&LOCK_open); + if (reopen_name_locked_table(thd, table_list)) { - pthread_mutex_lock(&LOCK_open); unlock_table_name(thd, table_list); pthread_mutex_unlock(&LOCK_open); + error= send_check_errmsg(thd, table_list, "repair", + "Failed to open partially repaired table"); + goto end; } + pthread_mutex_unlock(&LOCK_open); end: if (table == &tmp_table) @@ -2748,6 +2765,8 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, char *src_table= table_ident->table.str; int err; bool res= TRUE; + db_type not_used; + TABLE_LIST src_tables_list; DBUG_ENTER("mysql_create_like_table"); src_db= table_ident->db.str ? table_ident->db.str : thd->db; @@ -2795,7 +2814,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, /* create like should be not allowed for Views, Triggers, ... */ - if (mysql_frm_type(src_path) != FRMTYPE_TABLE) + if (mysql_frm_type(thd, src_path, ¬_used) != FRMTYPE_TABLE) { my_error(ER_WRONG_OBJECT, MYF(0), src_db, src_table, "BASE TABLE"); goto err; @@ -3867,15 +3886,14 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, if (create_info->row_type == ROW_TYPE_NOT_USED) create_info->row_type= table->s->row_type; - DBUG_PRINT("info", ("old type: %d new type: %d", old_db_type, new_db_type)); - if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) - || ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED)) + DBUG_PRINT("info", ("old type: %d new type: %d", old_db_type, new_db_type)); + if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) || + ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED)) { DBUG_PRINT("info", ("doesn't support alter")); my_error(ER_ILLEGAL_HA, MYF(0), table_name); DBUG_RETURN(TRUE); } - DBUG_PRINT("info", ("supports alter")); thd->proc_info="setup"; if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) && diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index df8de59508d..dbad8dcffb5 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -103,8 +103,7 @@ static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig); bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) { TABLE *table; - bool result= 0; - + bool result= TRUE; DBUG_ENTER("mysql_create_or_drop_trigger"); /* @@ -119,9 +118,6 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) /* We should have only one table in table list. */ DBUG_ASSERT(tables->next_global == 0); - if (!(table= open_ltable(thd, tables, tables->lock_type))) - DBUG_RETURN(TRUE); - /* TODO: We should check if user has TRIGGER privilege for table here. Now we just require SUPER privilege for creating/dropping because @@ -131,28 +127,24 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) DBUG_RETURN(TRUE); /* - We do not allow creation of triggers on temporary tables. We also don't - allow creation of triggers on views but fulfilment of this restriction - is guaranteed by open_ltable(). It is better to have this check here - than do it in Table_triggers_list::create_trigger() and mess with table - cache. + There is no DETERMINISTIC clause for triggers, so can't check it. + But a trigger can in theory be used to do nasty things (if it supported + DROP for example) so we do the check for privileges. For now there is + already a stronger test right above; but when this stronger test will + be removed, the test below will hold. */ - if (table->s->tmp_table != NO_TMP_TABLE) + if (!trust_routine_creators && mysql_bin_log.is_open() && + !(thd->security_ctx->master_access & SUPER_ACL)) { - my_error(ER_TRG_ON_VIEW_OR_TEMP_TABLE, MYF(0), tables->alias); + my_error(ER_BINLOG_CREATE_ROUTINE_NEED_SUPER, MYF(0)); DBUG_RETURN(TRUE); } - if (!table->triggers) + /* We do not allow creation of triggers on temporary tables. */ + if (create && find_temporary_table(thd, tables->db, tables->table_name)) { - if (!create) - { - my_message(ER_TRG_DOES_NOT_EXIST, ER(ER_TRG_DOES_NOT_EXIST), MYF(0)); - DBUG_RETURN(TRUE); - } - - if (!(table->triggers= new (&table->mem_root) Table_triggers_list(table))) - DBUG_RETURN(TRUE); + my_error(ER_TRG_ON_VIEW_OR_TEMP_TABLE, MYF(0), tables->alias); + DBUG_RETURN(TRUE); } /* @@ -161,31 +153,41 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) again until we are done. (Acquiring LOCK_open is not enough because global read lock is held without helding LOCK_open). */ - if (wait_if_global_read_lock(thd, 0, 0)) + if (wait_if_global_read_lock(thd, 0, 1)) DBUG_RETURN(TRUE); - /* - There is no DETERMINISTIC clause for triggers, so can't check it. - But a trigger can in theory be used to do nasty things (if it supported - DROP for example) so we do the check for privileges. For now there is - already a stronger test above (see start of the function); but when this - stronger test will be removed, the test below will hold. - */ - if (!trust_routine_creators && mysql_bin_log.is_open() && - !(thd->security_ctx->master_access & SUPER_ACL)) + VOID(pthread_mutex_lock(&LOCK_open)); + + if (lock_table_names(thd, tables)) + goto end; + + /* We also don't allow creation of triggers on views. */ + tables->required_type= FRMTYPE_TABLE; + + if (reopen_name_locked_table(thd, tables)) { - my_message(ER_BINLOG_CREATE_ROUTINE_NEED_SUPER, - ER(ER_BINLOG_CREATE_ROUTINE_NEED_SUPER), MYF(0)); - DBUG_RETURN(TRUE); + unlock_table_name(thd, tables); + goto end; + } + table= tables->table; + + if (!table->triggers) + { + if (!create) + { + my_error(ER_TRG_DOES_NOT_EXIST, MYF(0)); + goto end; + } + + if (!(table->triggers= new (&table->mem_root) Table_triggers_list(table))) + goto end; } - VOID(pthread_mutex_lock(&LOCK_open)); result= (create ? table->triggers->create_trigger(thd, tables): table->triggers->drop_trigger(thd, tables)); - /* It is sensible to invalidate table in any case */ - close_cached_table(thd, table); +end: VOID(pthread_mutex_unlock(&LOCK_open)); start_waiting_global_read_lock(thd); diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 951248e8cd8..dee88af7d83 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -720,3 +720,17 @@ bool st_select_lex::cleanup() DBUG_RETURN(error); } + +void st_select_lex::cleanup_all_joins(bool full) +{ + SELECT_LEX_UNIT *unit; + SELECT_LEX *sl; + + if (join) + join->cleanup(full); + + for (unit= first_inner_unit(); unit; unit= unit->next_unit()) + for (sl= unit->first_select(); sl; sl= sl->next_select()) + sl->cleanup_all_joins(full); +} + diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 72a8eef9a55..41961b66f0f 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -121,8 +121,9 @@ int mysql_update(THD *thd, bool safe_update= thd->options & OPTION_SAFE_UPDATES; bool used_key_is_modified, transactional_table, will_batch; int res; - int error=0, loc_error; - uint used_index, dup_key_found; + int error, loc_error; + uint used_index= MAX_KEY, dup_key_found; + bool need_sort= TRUE; #ifndef NO_EMBEDDED_ACCESS_CHECKS uint want_privilege; #endif @@ -194,6 +195,7 @@ int mysql_update(THD *thd, /* Check the fields we are going to modify */ #ifndef NO_EMBEDDED_ACCESS_CHECKS table_list->grant.want_privilege= table->grant.want_privilege= want_privilege; + table_list->register_want_access(want_privilege); #endif /* Indicate that the set of fields is to be updated by passing 2 for @@ -233,11 +235,18 @@ int mysql_update(THD *thd, DBUG_RETURN(1); /* purecov: inspected */ } + if (conds) + { + Item::cond_result cond_value; + conds= remove_eq_conds(thd, conds, &cond_value); + if (cond_value == Item::COND_FALSE) + limit= 0; // Impossible WHERE + } // Don't count on usage of 'only index' when calculating which key to use table->used_keys.clear_all(); select= make_select(table, 0, 0, conds, 0, &error); - if (error || - (select && select->check_quick(thd, safe_update, limit)) || !limit) + if (error || !limit || + (select && select->check_quick(thd, safe_update, limit))) { delete select; free_underlaid_joins(thd, select_lex); @@ -248,6 +257,11 @@ int mysql_update(THD *thd, send_ok(thd); // No matching records DBUG_RETURN(0); } + if (!select && limit != HA_POS_ERROR) + { + if ((used_index= get_index_for_order(table, order, limit)) != MAX_KEY) + need_sort= FALSE; + } /* If running in safe sql mode, don't allow updates without keys */ if (table->quick_keys.is_clear_all()) { @@ -268,10 +282,14 @@ int mysql_update(THD *thd, used_key_is_modified= (!select->quick->unique_key_range() && select->quick->check_if_keys_used(&fields)); } - else if ((used_index=table->file->key_used_on_scan) < MAX_KEY) - used_key_is_modified=check_if_key_used(table, used_index, fields); else - used_key_is_modified=0; + { + used_key_is_modified= 0; + if (used_index == MAX_KEY) // no index for sort order + used_index= table->file->key_used_on_scan; + if (used_index != MAX_KEY) + used_key_is_modified= check_if_key_used(table, used_index, fields); + } #ifdef HAVE_PARTITION_DB if (used_key_is_modified || order || @@ -291,7 +309,8 @@ int mysql_update(THD *thd, table->file->extra(HA_EXTRA_KEYREAD); } - if (order) + /* note: can actually avoid sorting below.. */ + if (order && (need_sort || used_key_is_modified)) { /* Doing an ORDER BY; Let filesort find and sort the rows we are going @@ -301,6 +320,7 @@ int mysql_update(THD *thd, SORT_FIELD *sortorder; ha_rows examined_rows; + used_index= MAX_KEY; // For call to init_read_record() table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE), MYF(MY_FAE | MY_ZEROFILL)); if (!(sortorder=make_unireg_sortorder(order, &length)) || @@ -335,7 +355,10 @@ int mysql_update(THD *thd, /* If quick select is used, initialize it before retrieving rows. */ if (select && select->quick && select->quick->reset()) goto err; - init_read_record(&info,thd,table,select,0,1); + if (used_index == MAX_KEY || (select && select->quick)) + init_read_record(&info,thd,table,select,0,1); + else + init_read_record_idx(&info, thd, table, 1, used_index); thd->proc_info="Searching rows for update"; uint tmp_limit= limit; @@ -364,6 +387,7 @@ int mysql_update(THD *thd, error= 1; // Aborted limit= tmp_limit; end_read_record(&info); + /* Change select to use tempfile */ if (select) { @@ -671,6 +695,7 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, #ifndef NO_EMBEDDED_ACCESS_CHECKS table_list->grant.want_privilege= table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege); + table_list->register_want_access(SELECT_ACL); #endif bzero((char*) &tables,sizeof(tables)); // For ORDER BY @@ -946,9 +971,6 @@ bool mysql_multi_update(THD *thd, multi_update *result; DBUG_ENTER("mysql_multi_update"); - if (mysql_multi_update_prepare(thd)) - DBUG_RETURN(TRUE); - if (!(result= new multi_update(table_list, thd->lex->select_lex.leaf_tables, fields, values, diff --git a/sql/sql_view.cc b/sql/sql_view.cc index f956d9d4928..71c5d198b27 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -643,7 +643,8 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, view->query.length= str.length()-1; // we do not need last \0 view->source.str= thd->lex->create_view_select_start; view->source.length= (thd->query_length - - (thd->lex->create_view_select_start - thd->query)); + (thd->lex->create_view_select_start - + thd->lex->create_view_start)); view->file_version= 1; view->calc_md5(md5); view->md5.str= md5; @@ -721,44 +722,59 @@ loop_out: } + /* read VIEW .frm and create structures SYNOPSIS mysql_make_view() - parser - parser object; - table - TABLE_LIST structure for filling + thd Thread handler + parser parser object + table TABLE_LIST structure for filling RETURN 0 ok 1 error */ -my_bool -mysql_make_view(File_parser *parser, TABLE_LIST *table) +bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table) { + SELECT_LEX *end, *view_select; + LEX *old_lex, *lex; + Query_arena *arena, backup; + int res; + bool result; DBUG_ENTER("mysql_make_view"); - DBUG_PRINT("info", ("table=%p (%s)", table, table->table_name)); + DBUG_PRINT("info", ("table: 0x%lx (%s)", (ulong) table, table->table_name)); if (table->view) { + /* + It's an execution of a PS/SP and the view has already been unfolded + into a list of used tables. Now we only need to update the information + about granted privileges in the view tables with the actual data + stored in MySQL privilege system. We don't need to restore the + required privileges (by calling register_want_access) because they has + not changed since PREPARE or the previous execution: the only case + when this information is changed is execution of UPDATE on a view, but + the original want_access is restored in its end. + */ + if (!table->prelocking_placeholder && table->prepare_security(thd)) + { + DBUG_RETURN(1); + } DBUG_PRINT("info", ("VIEW %s.%s is already processed on previous PS/SP execution", table->view_db.str, table->view_name.str)); DBUG_RETURN(0); } - SELECT_LEX *end; - THD *thd= current_thd; - LEX *old_lex= thd->lex, *lex; - SELECT_LEX *view_select; - int res= 0; - /* For now we assume that tables will not be changed during PS life (it will be TRUE as far as we make new table cache). */ - Query_arena *arena= thd->stmt_arena, backup; + old_lex= thd->lex; + arena= thd->stmt_arena; if (arena->is_conventional()) arena= 0; else @@ -768,7 +784,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) if (!table->timestamp.str) table->timestamp.str= table->timestamp_buffer; /* prepare default values for old format */ - table->view_suid= 1; + table->view_suid= TRUE; table->definer.user.str= table->definer.host.str= 0; table->definer.user.length= table->definer.host.length= 0; @@ -879,6 +895,10 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) goto err; } + + if (!(table->view_tables= + (List<TABLE_LIST>*) new(thd->mem_root) List<TABLE_LIST>)) + goto err; /* mark to avoid temporary table using and put view reference and find last view table @@ -889,6 +909,22 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) { tbl->skip_temporary= 1; tbl->belong_to_view= top_view; + tbl->referencing_view= table; + /* + First we fill want_privilege with SELECT_ACL (this is needed for the + tables which belongs to view subqueries and temporary table views, + then for the merged view underlying tables we will set wanted + privileges of top_view + */ + tbl->grant.want_privilege= SELECT_ACL; + /* + After unfolding the view we lose the list of tables referenced in it + (we will have only a list of underlying tables in case of MERGE + algorithm, which does not include the tables referenced from + subqueries used in view definition). + Let's build a list of all tables referenced in the view. + */ + table->view_tables->push_back(tbl); } /* @@ -914,16 +950,6 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) } /* - 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. - */ - if (!lex->select_lex.next_select()) - for (tbl= lex->select_lex.get_table_list(); 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 further processing. @@ -931,7 +957,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) if (table->prelocking_placeholder) goto ok2; - old_lex->derived_tables|= DERIVED_VIEW; + old_lex->derived_tables|= (DERIVED_VIEW | lex->derived_tables); /* move SQL_NO_CACHE & Co to whole query */ old_lex->safe_to_cache_query= (old_lex->safe_to_cache_query && @@ -940,6 +966,37 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) if (view_select->options & OPTION_TO_QUERY_CACHE) old_lex->select_lex.options|= OPTION_TO_QUERY_CACHE; + if (table->view_suid) + { + /* + Prepare a security context to check underlying objects of the view + */ + Security_context *save_security_ctx= thd->security_ctx; + if (!(table->view_sctx= (Security_context *) + thd->stmt_arena->alloc(sizeof(Security_context)))) + goto err; + /* Assign the context to the tables referenced in the view */ + for (tbl= view_tables; tbl; tbl= tbl->next_global) + tbl->security_ctx= table->view_sctx; + /* assign security context to SELECT name resolution contexts of view */ + for(SELECT_LEX *sl= lex->all_selects_list; + sl; + sl= sl->next_select_in_list()) + sl->context.security_ctx= table->view_sctx; + } + + /* + Setup an error processor to hide error messages issued by stored + routines referenced in the view + */ + for (SELECT_LEX *sl= lex->all_selects_list; + sl; + sl= sl->next_select_in_list()) + { + sl->context.error_processor= &view_error_processor; + sl->context.error_processor_data= (void *)table; + } + /* check MERGE algorithm ability - algorithm is not explicit TEMPORARY TABLE @@ -961,24 +1018,28 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) table->updatable= (table->updatable_view != 0); table->effective_with_check= old_lex->get_effective_with_check(table); + table->merge_underlying_list= view_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; + tbl->grant.want_privilege= top_view->grant.orig_want_privilege; + } /* prepare view context */ - lex->select_lex.context.resolve_in_table_list_only(table->ancestor= - view_tables); + lex->select_lex.context.resolve_in_table_list_only(view_tables); lex->select_lex.context.outer_context= 0; lex->select_lex.context.select_lex= table->select_lex; lex->select_lex.select_n_having_items+= table->select_lex->select_n_having_items; - /* do not check privileges & hide errors for view underlyings */ - for (SELECT_LEX *sl= lex->all_selects_list; - sl; - sl= sl->next_select_in_list()) - { - sl->context.check_privileges= FALSE; - sl->context.error_processor= &view_error_processor; - sl->context.error_processor_data= (void *)table; - } /* Tables of the main select of the view should be marked as belonging to the same select as original view (again we can use LEX::select_lex @@ -1011,7 +1072,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) } } - /* Store WHERE clause for post-processing in setup_ancestor */ + /* Store WHERE clause for post-processing in setup_underlying */ table->where= view_select->where; /* Add subqueries units to SELECT into which we merging current view. @@ -1072,20 +1133,21 @@ ok: (st_select_lex_node**)&old_lex->all_selects_list; ok2: - if (arena) - thd->restore_active_arena(arena, &backup); if (!old_lex->time_zone_tables_used && thd->lex->time_zone_tables_used) old_lex->time_zone_tables_used= thd->lex->time_zone_tables_used; - thd->lex= old_lex; - DBUG_RETURN(0); + result= !table->prelocking_placeholder && table->prepare_security(thd); -err: +end: if (arena) thd->restore_active_arena(arena, &backup); + thd->lex= old_lex; + DBUG_RETURN(result); + +err: delete table->view; table->view= 0; // now it is not VIEW placeholder - thd->lex= old_lex; - DBUG_RETURN(1); + result= 1; + goto end; } @@ -1109,6 +1171,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) char path[FN_REFLEN]; TABLE_LIST *view; bool type= 0; + db_type not_used; for (view= views; view; view= view->next_local) { @@ -1116,7 +1179,8 @@ 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(path) != FRMTYPE_VIEW))) + if (access(path, F_OK) || + (type= (mysql_frm_type(thd, path, ¬_used) != FRMTYPE_VIEW))) { char name[FN_REFLEN]; my_snprintf(name, sizeof(name), "%s.%s", view->db, view->table_name); @@ -1163,24 +1227,36 @@ err: FRMTYPE_VIEW view */ -frm_type_enum mysql_frm_type(char *path) +frm_type_enum mysql_frm_type(THD *thd, char *path, db_type *dbt) { File file; - char header[10]; //"TYPE=VIEW\n" it is 10 characters - int length; + uchar header[10]; //"TYPE=VIEW\n" it is 10 characters + int error; DBUG_ENTER("mysql_frm_type"); + *dbt= DB_TYPE_UNKNOWN; + if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(0))) < 0) - { DBUG_RETURN(FRMTYPE_ERROR); - } - length= my_read(file, (byte*) header, sizeof(header), MYF(MY_WME)); + error= my_read(file, (byte*) header, sizeof(header), MYF(MY_WME | MY_NABP)); my_close(file, MYF(MY_WME)); - if (length == (int) MY_FILE_ERROR) + + if (error) DBUG_RETURN(FRMTYPE_ERROR); - if (length < (int) sizeof(header) || - !strncmp(header, "TYPE=VIEW\n", sizeof(header))) + if (!strncmp((char*) header, "TYPE=VIEW\n", sizeof(header))) DBUG_RETURN(FRMTYPE_VIEW); + + /* + This is just a check for DB_TYPE. We'll return default unknown type + if the following test is true (arg #3). This should not have effect + on return value from this function (default FRMTYPE_TABLE) + */ + if (header[0] != (uchar) 254 || header[1] != 1 || + (header[2] != FRM_VER && header[2] != FRM_VER+1 && + (header[2] < FRM_VER+3 || header[2] > FRM_VER+4))) + DBUG_RETURN(FRMTYPE_TABLE); + + *dbt= ha_checktype(thd, (enum db_type) (uint) *(header + 3), 0, 0); DBUG_RETURN(FRMTYPE_TABLE); // Is probably a .frm table } @@ -1427,7 +1503,7 @@ mysql_rename_view(THD *thd, /* get view definition and source */ if (parser->parse((gptr)&view_def, thd->mem_root, view_parameters, - sizeof(view_parameters)/sizeof(view_parameters[0])-1)) + array_elements(view_parameters)-1)) goto err; /* rename view and it's backups */ diff --git a/sql/sql_view.h b/sql/sql_view.h index 4cc9eb454fb..cd61d7e9e71 100644 --- a/sql/sql_view.h +++ b/sql/sql_view.h @@ -19,7 +19,7 @@ bool mysql_create_view(THD *thd, enum_view_create_mode mode); -my_bool mysql_make_view(File_parser *parser, TABLE_LIST *table); +bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table); bool mysql_drop_view(THD *thd, TABLE_LIST *view, enum_drop_mode drop_mode); @@ -27,7 +27,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST * view); bool insert_view_fields(THD *thd, List<Item> *list, TABLE_LIST *view); -frm_type_enum mysql_frm_type(char *path); +frm_type_enum mysql_frm_type(THD *thd, char *path, db_type *dbt); int view_checksum(THD *thd, TABLE_LIST *view); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 77c1a37e16e..e6c0216ef3a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -670,7 +670,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token YEAR_SYM %token ZEROFILL -%left JOIN_SYM +%left JOIN_SYM INNER_SYM STRAIGHT_JOIN CROSS LEFT RIGHT /* A dummy token to force the priority of table_ref production in a join. */ %left TABLE_REF_PRIORITY %left SET_VAR @@ -1279,6 +1279,7 @@ create: THD *thd= YYTHD; LEX *lex= thd->lex; lex->sql_command= SQLCOM_CREATE_VIEW; + lex->create_view_start= thd->query; /* first table in list is target VIEW name */ if (!lex->select_lex.add_table_to_list(thd, $7, NULL, 0)) YYABORT; @@ -3169,9 +3170,21 @@ create_table_option: | CHECKSUM_SYM opt_equal ulong_num { Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM; } | DELAY_KEY_WRITE_SYM opt_equal ulong_num { Lex->create_info.table_options|= $3 ? HA_OPTION_DELAY_KEY_WRITE : HA_OPTION_NO_DELAY_KEY_WRITE; Lex->create_info.used_fields|= HA_CREATE_USED_DELAY_KEY_WRITE; } | ROW_FORMAT_SYM opt_equal row_types { Lex->create_info.row_type= $3; Lex->create_info.used_fields|= HA_CREATE_USED_ROW_FORMAT; } - | RAID_TYPE opt_equal raid_types { Lex->create_info.raid_type= $3; Lex->create_info.used_fields|= HA_CREATE_USED_RAID;} - | RAID_CHUNKS opt_equal ulong_num { Lex->create_info.raid_chunks= $3; Lex->create_info.used_fields|= HA_CREATE_USED_RAID;} - | RAID_CHUNKSIZE opt_equal ulong_num { Lex->create_info.raid_chunksize= $3*RAID_BLOCK_SIZE; Lex->create_info.used_fields|= HA_CREATE_USED_RAID;} + | RAID_TYPE opt_equal raid_types + { + my_error(ER_WARN_DEPRECATED_SYNTAX, MYF(0), "RAID_TYPE", "PARTITION"); + YYABORT; + } + | RAID_CHUNKS opt_equal ulong_num + { + my_error(ER_WARN_DEPRECATED_SYNTAX, MYF(0), "RAID_CHUNKS", "PARTITION"); + YYABORT; + } + | RAID_CHUNKSIZE opt_equal ulong_num + { + my_error(ER_WARN_DEPRECATED_SYNTAX, MYF(0), "RAID_CHUNKSIZE", "PARTITION"); + YYABORT; + } | UNION_SYM opt_equal '(' table_list ')' { /* Move the union list to the merge_list */ @@ -3469,7 +3482,9 @@ type: spatial_type: GEOMETRY_SYM { $$= Field::GEOM_GEOMETRY; } | GEOMETRYCOLLECTION { $$= Field::GEOM_GEOMETRYCOLLECTION; } - | POINT_SYM { $$= Field::GEOM_POINT; } + | POINT_SYM { Lex->length= (char*)"21"; + $$= Field::GEOM_POINT; + } | MULTIPOINT { $$= Field::GEOM_MULTIPOINT; } | LINESTRING { $$= Field::GEOM_LINESTRING; } | MULTILINESTRING { $$= Field::GEOM_MULTILINESTRING; } @@ -3925,6 +3940,7 @@ alter: THD *thd= YYTHD; LEX *lex= thd->lex; lex->sql_command= SQLCOM_CREATE_VIEW; + lex->create_view_start= thd->query; lex->create_view_mode= VIEW_ALTER; /* first table in list is target VIEW name */ lex->select_lex.add_table_to_list(thd, $6, NULL, 0); @@ -4905,9 +4921,9 @@ predicate: { $$= new Item_func_eq(new Item_func_soundex($1), new Item_func_soundex($4)); } | bit_expr LIKE simple_expr opt_escape - { $$= new Item_func_like($1,$3,$4); } + { $$= new Item_func_like($1,$3,$4,Lex->escape_used); } | bit_expr not LIKE simple_expr opt_escape - { $$= new Item_func_not(new Item_func_like($1,$4,$5)); } + { $$= new Item_func_not(new Item_func_like($1,$4,$5, Lex->escape_used)); } | bit_expr REGEXP bit_expr { $$= new Item_func_regex($1,$3); } | bit_expr not REGEXP bit_expr { $$= negate_expression(YYTHD, new Item_func_regex($1,$4)); } @@ -5127,6 +5143,8 @@ simple_expr: { $$= new Item_func_atan($3,$5); } | CHAR_SYM '(' expr_list ')' { $$= new Item_func_char(*$3); } + | CHAR_SYM '(' expr_list USING charset_name ')' + { $$= new Item_func_char(*$3, $5); } | CHARSET '(' expr ')' { $$= new Item_func_charset($3); } | COALESCE '(' expr_list ')' @@ -5814,14 +5832,22 @@ derived_table_list: } ; +/* + Notice that JOIN is a left-associative operation, and it must be parsed + as such, that is, the parser must process first the left join operand + then the right one. Such order of processing ensures that the parser + produces correct join trees which is essential for semantic analysis + and subsequent optimization phases. +*/ join_table: +/* INNER JOIN variants */ /* - Evaluate production 'table_ref' before 'normal_join' so that - [INNER | CROSS] JOIN is properly nested as other left-associative - joins. + Use %prec to evaluate production 'table_ref' before 'normal_join' + so that [INNER | CROSS] JOIN is properly nested as other + left-associative joins. */ table_ref %prec TABLE_REF_PRIORITY normal_join table_ref - { YYERROR_UNLESS($1 && ($$=$3)); } + { YYERROR_UNLESS($1 && ($$=$3)); } | table_ref STRAIGHT_JOIN table_factor { YYERROR_UNLESS($1 && ($$=$3)); $3->straight=1; } | table_ref normal_join table_ref @@ -5863,6 +5889,13 @@ join_table: } '(' using_list ')' { add_join_natural($1,$3,$7); $$=$3; } + | table_ref NATURAL JOIN_SYM table_factor + { + YYERROR_UNLESS($1 && ($$=$4)); + add_join_natural($1,$4,NULL); + } + +/* LEFT JOIN variants */ | table_ref LEFT opt_outer JOIN_SYM table_ref ON { @@ -5894,6 +5927,8 @@ join_table: $6->outer_join|=JOIN_TYPE_LEFT; $$=$6; } + +/* RIGHT JOIN variants */ | table_ref RIGHT opt_outer JOIN_SYM table_ref ON { @@ -5931,10 +5966,7 @@ join_table: LEX *lex= Lex; if (!($$= lex->current_select->convert_right_join())) YYABORT; - } - | table_ref NATURAL JOIN_SYM table_factor - { YYERROR_UNLESS($1 && ($$=$4)); add_join_natural($1,$4,NULL); }; - + }; normal_join: JOIN_SYM {} @@ -6267,10 +6299,14 @@ having_clause: ; opt_escape: - ESCAPE_SYM simple_expr { $$= $2; } + ESCAPE_SYM simple_expr + { + Lex->escape_used= TRUE; + $$= $2; + } | /* empty */ { - + Lex->escape_used= FALSE; $$= ((YYTHD->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) ? new Item_string("", 0, &my_charset_latin1) : new Item_string("\\", 1, &my_charset_latin1)); @@ -8594,6 +8630,18 @@ option_value: $2= $2 ? $2: global_system_variables.character_set_client; lex->var_list.push_back(new set_var_collation_client($2,thd->variables.collation_database,$2)); } + | NAMES_SYM equal expr + { + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + LEX_STRING names; + + names.str= (char *)"names"; + names.length= 5; + if (spc && spc->find_pvar(&names)) + my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), names.str); + YYABORT; + } | NAMES_SYM charset_name_or_default opt_collate { LEX *lex= Lex; @@ -8611,6 +8659,17 @@ option_value: { THD *thd=YYTHD; LEX_USER *user; + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + LEX_STRING pw; + + pw.str= (char *)"password"; + pw.length= 8; + if (spc && spc->find_pvar(&pw)) + { + my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str); + YYABORT; + } if (!(user=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))) YYABORT; user->host=null_lex_str; @@ -9569,8 +9628,7 @@ view_user: (LEX_USER*) thd->alloc(sizeof(st_lex_user)))) YYABORT; view_user->user = $3; view_user->host=$5; - if (strchr(view_user->host.str, wild_many) || - strchr(view_user->host.str, wild_one)) + if (view_user->host.length == 0) { my_error(ER_NO_VIEW_USER, MYF(0)); YYABORT; diff --git a/sql/structs.h b/sql/structs.h index e490a2de60c..3c651c491d3 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -20,7 +20,7 @@ struct st_table; class Field; -#define STRING_WITH_LEN(X) X, (sizeof(X)-1) +#define STRING_WITH_LEN(X) ((char*) X), (sizeof(X)-1) typedef struct st_lex_string { @@ -185,6 +185,7 @@ enum SHOW_TYPE SHOW_SSL_CTX_SESS_TIMEOUTS, SHOW_SSL_CTX_SESS_CACHE_FULL, SHOW_SSL_GET_CIPHER_LIST, #endif /* HAVE_OPENSSL */ + SHOW_NET_COMPRESSION, SHOW_RPL_STATUS, SHOW_SLAVE_RUNNING, SHOW_SLAVE_RETRIED_TRANS, SHOW_KEY_CACHE_LONG, SHOW_KEY_CACHE_CONST_LONG, SHOW_KEY_CACHE_LONGLONG, SHOW_LONG_STATUS, SHOW_LONG_CONST_STATUS, SHOW_SLAVE_SKIP_ERRORS diff --git a/sql/table.cc b/sql/table.cc index d3f7d2b3b9f..776dcdf66fe 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -303,6 +303,44 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, } #endif + record_offset= (ulong) (uint2korr(head+6)+ + ((uint2korr(head+14) == 0xffff ? + uint4korr(head+47) : uint2korr(head+14)))); + + if ((n_length= uint2korr(head+55))) + { + /* Read extra data segment */ + char *buff, *next_chunk, *buff_end; + if (!(next_chunk= buff= my_malloc(n_length, MYF(MY_WME)))) + goto err; + buff_end= buff + n_length; + if (my_pread(file, (byte*)buff, n_length, record_offset + share->reclength, + MYF(MY_NABP))) + { + my_free(buff, MYF(0)); + goto err; + } + share->connect_string.length= uint2korr(buff); + if (! (share->connect_string.str= strmake_root(&outparam->mem_root, + next_chunk + 2, share->connect_string.length))) + { + my_free(buff, MYF(0)); + goto err; + } + next_chunk+= share->connect_string.length + 2; + if (next_chunk + 2 < buff_end) + { + uint str_db_type_length= uint2korr(next_chunk); + share->db_type= ha_resolve_by_name(next_chunk + 2, str_db_type_length); + DBUG_PRINT("enter", ("Setting dbtype to: %d - %d - '%.*s'\n", + share->db_type, + str_db_type_length, str_db_type_length, + next_chunk + 2)); + next_chunk+= str_db_type_length + 2; + } + my_free(buff, MYF(0)); + } + error=4; outparam->reginfo.lock_type= TL_UNLOCK; outparam->current_lock=F_UNLCK; @@ -322,9 +360,6 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, goto err; /* purecov: inspected */ share->default_values= (byte *) record; - record_offset= (ulong) (uint2korr(head+6)+ - ((uint2korr(head+14) == 0xffff ? - uint4korr(head+47) : uint2korr(head+14)))); if (my_pread(file,(byte*) record, (uint) share->reclength, record_offset, MYF(MY_NABP))) goto err; /* purecov: inspected */ @@ -342,20 +377,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, else outparam->record[1]= outparam->record[0]; // Safety } - - if ((n_length= uint2korr(head+55))) - { - /* Read extra block information */ - char *buff; - if (!(buff= alloc_root(&outparam->mem_root, n_length))) - goto err; - if (my_pread(file, (byte*)buff, n_length, record_offset + share->reclength, - MYF(MY_NABP))) - goto err; - share->connect_string.length= uint2korr(buff); - share->connect_string.str= buff+2; - } - + #ifdef HAVE_purify /* We need this because when we read var-length rows, we are not updating @@ -463,7 +485,8 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, file= -1; /* Allocate handler */ - if (!(outparam->file= get_new_handler(outparam, share->db_type))) + if (!(outparam->file= get_new_handler(outparam, &outparam->mem_root, + share->db_type))) goto err; record= (char*) outparam->record[0]-1; /* Fieldstart = 1 */ @@ -471,9 +494,11 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, { outparam->null_flags=null_pos=(uchar*) record+1; null_bit_pos= (db_create_options & HA_OPTION_PACK_RECORD) ? 0 : 1; - /* null_bytes below is only correct under the condition that - there are no bit fields. Correct values is set below after the - table struct is initialized */ + /* + null_bytes below is only correct under the condition that + there are no bit fields. Correct values is set below after the + table struct is initialized + */ share->null_bytes= (share->null_fields + null_bit_pos + 7) / 8; } else @@ -897,8 +922,12 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, #endif goto err; - /* the correct null_bytes can now be set, since bitfields have been taken into account */ - share->null_bytes= null_pos - (uchar*) outparam->null_flags + (null_bit_pos + 7) / 8; + /* + the correct null_bytes can now be set, since bitfields have been taken + into account + */ + share->null_bytes= (null_pos - (uchar*) outparam->null_flags + + (null_bit_pos + 7) / 8); share->last_null_bit_pos= null_bit_pos; /* The table struct is now initialized; Open the table */ @@ -1410,15 +1439,10 @@ File create_frm(THD *thd, my_string name, const char *db, ulong length; char fill[IO_SIZE]; int create_flags= O_RDWR | O_TRUNC; - uint extra_size; if (create_info->options & HA_LEX_CREATE_TMP_TABLE) create_flags|= O_EXCL | O_NOFOLLOW; - extra_size= 0; - if (create_info->connect_string.length) - extra_size= 2+create_info->connect_string.length; - #if SIZEOF_OFF_T > 4 /* Fix this when we have new .frm files; Current limit is 4G rows (QQ) */ if (create_info->max_rows > ~(ulong) 0) @@ -1446,7 +1470,8 @@ File create_frm(THD *thd, my_string name, const char *db, fileinfo[4]=1; int2store(fileinfo+6,IO_SIZE); /* Next block starts here */ key_length=keys*(7+NAME_LEN+MAX_REF_PARTS*9)+16; - length= next_io_size((ulong) (IO_SIZE+key_length+reclength+extra_size)); + length= next_io_size((ulong) (IO_SIZE+key_length+reclength+ + create_info->extra_size)); int4store(fileinfo+10,length); tmp_key_length= (key_length < 0xffff) ? key_length : 0xffff; int2store(fileinfo+14,tmp_key_length); @@ -1468,7 +1493,7 @@ File create_frm(THD *thd, my_string name, const char *db, int4store(fileinfo+47, key_length); tmp= MYSQL_VERSION_ID; // Store to avoid warning from int4store int4store(fileinfo+51, tmp); - int2store(fileinfo+55, extra_size); + int2store(fileinfo+55, create_info->extra_size); bzero(fill,IO_SIZE); for (; length > IO_SIZE ; length-= IO_SIZE) { @@ -1709,29 +1734,6 @@ bool check_column_name(const char *name) } /* -** Get type of table from .frm file -*/ - -db_type get_table_type(THD *thd, const char *name) -{ - File file; - uchar head[4]; - int error; - DBUG_ENTER("get_table_type"); - DBUG_PRINT("enter",("name: '%s'",name)); - - if ((file=my_open(name,O_RDONLY, MYF(0))) < 0) - DBUG_RETURN(DB_TYPE_UNKNOWN); - error=my_read(file,(byte*) head,4,MYF(MY_NABP)); - my_close(file,MYF(0)); - if (error || head[0] != (uchar) 254 || head[1] != 1 || - (head[2] != FRM_VER && head[2] != FRM_VER+1 && - (head[2] < FRM_VER+3 || head[2] > FRM_VER+4))) - DBUG_RETURN(DB_TYPE_UNKNOWN); - DBUG_RETURN(ha_checktype(thd,(enum db_type) (uint) *(head+3),0,0)); -} - -/* Create Item_field for each column in the table. SYNPOSIS @@ -1814,41 +1816,43 @@ void st_table_list::calc_md5(char *buffer) /* - set ancestor TABLE for table place holder of VIEW + set underlying TABLE for table place holder of VIEW DESCRIPTION Replace all views that only uses one table with the table itself. This allows us to treat the view as a simple table and even update - it + it (it is a kind of optimisation) SYNOPSIS - st_table_list::set_ancestor() + st_table_list::set_underlying_merge() */ -void st_table_list::set_ancestor() +void st_table_list::set_underlying_merge() { TABLE_LIST *tbl; - if ((tbl= ancestor)) + if ((tbl= merge_underlying_list)) { /* This is a view. Process all tables of view */ - DBUG_ASSERT(view); + DBUG_ASSERT(view && effective_algorithm == VIEW_ALGORITHM_MERGE); do { - if (tbl->ancestor) // This is a view + if (tbl->merge_underlying_list) // This is a view { + DBUG_ASSERT(tbl->view && + tbl->effective_algorithm == VIEW_ALGORITHM_MERGE); /* This is the only case where set_ancestor is called on an object that may not be a view (in which case ancestor is 0) */ - tbl->ancestor->set_ancestor(); + tbl->merge_underlying_list->set_underlying_merge(); } } while ((tbl= tbl->next_local)); if (!multitable_view) { - table= ancestor->table; - schema_table= ancestor->schema_table; + table= merge_underlying_list->table; + schema_table= merge_underlying_list->schema_table; } } } @@ -1858,12 +1862,9 @@ void st_table_list::set_ancestor() setup fields of placeholder of merged VIEW SYNOPSIS - st_table_list::setup_ancestor() + st_table_list::setup_underlying() thd - thread handler - NOTES - ancestor is list of tables and views used by view (underlying tables/views) - DESCRIPTION It is: - preparing translation table for view columns @@ -1874,10 +1875,11 @@ void st_table_list::set_ancestor() TRUE - error */ -bool st_table_list::setup_ancestor(THD *thd) +bool st_table_list::setup_underlying(THD *thd) { - DBUG_ENTER("st_table_list::setup_ancestor"); - if (!field_translation) + DBUG_ENTER("st_table_list::setup_underlying"); + + if (!field_translation && merge_underlying_list) { Field_translator *transl; SELECT_LEX *select= &view->select_lex; @@ -1891,10 +1893,10 @@ bool st_table_list::setup_ancestor(THD *thd) DBUG_RETURN(TRUE); } - for (tbl= ancestor; tbl; tbl= tbl->next_local) + for (tbl= merge_underlying_list; tbl; tbl= tbl->next_local) { - if (tbl->ancestor && - tbl->setup_ancestor(thd)) + if (tbl->merge_underlying_list && + tbl->setup_underlying(thd)) { DBUG_RETURN(TRUE); } @@ -1957,7 +1959,7 @@ bool st_table_list::prep_where(THD *thd, Item **conds, { DBUG_ENTER("st_table_list::prep_where"); - for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local) + for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) { if (tbl->view && tbl->prep_where(thd, conds, no_where_clause)) { @@ -2039,7 +2041,7 @@ bool st_table_list::prep_check_option(THD *thd, uint8 check_opt_type) { DBUG_ENTER("st_table_list::prep_check_option"); - for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local) + for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) { /* see comment of check_opt_type parameter */ if (tbl->view && @@ -2062,7 +2064,7 @@ bool st_table_list::prep_check_option(THD *thd, uint8 check_opt_type) } if (check_opt_type == VIEW_CHECK_CASCADED) { - for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local) + for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) { if (tbl->check_option) item= and_conds(item, tbl->check_option); @@ -2101,16 +2103,21 @@ void st_table_list::hide_view_error(THD *thd) { /* Hide "Unknown column" or "Unknown function" error */ if (thd->net.last_errno == ER_BAD_FIELD_ERROR || - thd->net.last_errno == ER_SP_DOES_NOT_EXIST) + thd->net.last_errno == ER_SP_DOES_NOT_EXIST || + thd->net.last_errno == ER_PROCACCESS_DENIED_ERROR || + thd->net.last_errno == ER_COLUMNACCESS_DENIED_ERROR) { + TABLE_LIST *top= top_table(); thd->clear_error(); - my_error(ER_VIEW_INVALID, MYF(0), view_db.str, view_name.str); + my_error(ER_VIEW_INVALID, MYF(0), top->view_db.str, top->view_name.str); } else if (thd->net.last_errno == ER_NO_DEFAULT_FOR_FIELD) { + TABLE_LIST *top= top_table(); thd->clear_error(); // TODO: make correct error message - my_error(ER_NO_DEFAULT_FOR_VIEW_FIELD, MYF(0), view_db.str, view_name.str); + my_error(ER_NO_DEFAULT_FOR_VIEW_FIELD, MYF(0), + top->view_db.str, top->view_name.str); } } @@ -2131,10 +2138,10 @@ void st_table_list::hide_view_error(THD *thd) st_table_list *st_table_list::find_underlying_table(TABLE *table_to_find) { /* is this real table and table which we are looking for? */ - if (table == table_to_find && ancestor == 0) + if (table == table_to_find && merge_underlying_list == 0) return this; - for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local) + for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) { TABLE_LIST *result; if ((result= tbl->find_underlying_table(table_to_find))) @@ -2217,7 +2224,7 @@ int st_table_list::view_check_option(THD *thd, bool ignore_failure) bool st_table_list::check_single_table(st_table_list **table, table_map map, st_table_list *view) { - for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local) + for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) { if (tbl->table) { @@ -2259,8 +2266,8 @@ bool st_table_list::set_insert_values(MEM_ROOT *mem_root) } else { - DBUG_ASSERT(view && ancestor); - for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local) + DBUG_ASSERT(view && merge_underlying_list); + for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) if (tbl->set_insert_values(mem_root)) return TRUE; } @@ -2406,6 +2413,157 @@ TABLE_LIST *st_table_list::last_leaf_for_name_resolution() } +/* + Register access mode which we need for underlying tables + + SYNOPSIS + register_want_access() + want_access Acess which we require +*/ + +void st_table_list::register_want_access(ulong want_access) +{ + /* Remove SHOW_VIEW_ACL, because it will be checked during making view */ + want_access&= ~SHOW_VIEW_ACL; + if (belong_to_view) + { + grant.want_privilege= want_access; + if (table) + table->grant.want_privilege= want_access; + } + for (TABLE_LIST *tbl= merge_underlying_list; tbl; tbl= tbl->next_local) + tbl->register_want_access(want_access); +} + + +/* + Load security context information for this view + + SYNOPSIS + st_table_list::prepare_view_securety_context() + thd [in] thread handler + + RETURN + FALSE OK + TRUE Error +*/ + +#ifndef NO_EMBEDDED_ACCESS_CHECKS +bool st_table_list::prepare_view_securety_context(THD *thd) +{ + DBUG_ENTER("st_table_list::prepare_view_securety_context"); + DBUG_PRINT("enter", ("table: %s", alias)); + + DBUG_ASSERT(!prelocking_placeholder && view); + if (view_suid) + { + DBUG_PRINT("info", ("This table is suid view => load contest")); + DBUG_ASSERT(view && view_sctx); + if (acl_getroot_no_password(view_sctx, + definer.user.str, + definer.host.str, + definer.host.str, + thd->db)) + { + my_error(ER_NO_SUCH_USER, MYF(0), definer.user.str, definer.host.str); + DBUG_RETURN(TRUE); + } + } + DBUG_RETURN(FALSE); +} +#endif + + +/* + Find security context of current view + + SYNOPSIS + st_table_list::find_view_security_context() + thd [in] thread handler + +*/ + +#ifndef NO_EMBEDDED_ACCESS_CHECKS +Security_context *st_table_list::find_view_security_context(THD *thd) +{ + Security_context *sctx; + TABLE_LIST *upper_view= this; + DBUG_ENTER("st_table_list::find_view_security_context"); + + DBUG_ASSERT(view); + while (upper_view && !upper_view->view_suid) + { + DBUG_ASSERT(!upper_view->prelocking_placeholder); + upper_view= upper_view->referencing_view; + } + if (upper_view) + { + DBUG_PRINT("info", ("Securety context of view %s will be used", + upper_view->alias)); + sctx= upper_view->view_sctx; + DBUG_ASSERT(sctx); + } + else + { + DBUG_PRINT("info", ("Current global context will be used")); + sctx= thd->security_ctx; + } + DBUG_RETURN(sctx); +} +#endif + + +/* + Prepare security context and load underlying tables priveleges for view + + SYNOPSIS + st_table_list::prepare_security() + thd [in] thread handler + + RETURN + FALSE OK + TRUE Error +*/ + +bool st_table_list::prepare_security(THD *thd) +{ + List_iterator_fast<TABLE_LIST> tb(*view_tables); + TABLE_LIST *tbl; + DBUG_ENTER("st_table_list::prepare_security"); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + Security_context *save_security_ctx= thd->security_ctx; + + DBUG_ASSERT(!prelocking_placeholder); + if (prepare_view_securety_context(thd)) + DBUG_RETURN(TRUE); + thd->security_ctx= find_view_security_context(thd); + while ((tbl= tb++)) + { + DBUG_ASSERT(tbl->referencing_view); + char *db, *table_name; + if (tbl->view) + { + db= tbl->view_db.str; + table_name= tbl->view_name.str; + } + else + { + db= tbl->db; + table_name= tbl->table_name; + } + fill_effective_table_privileges(thd, &tbl->grant, db, table_name); + if (tbl->table) + tbl->table->grant= grant; + } + thd->security_ctx= save_security_ctx; +#else + while ((tbl= tb++)) + tbl->grant.privilege= ~NO_ACCESS; +#endif + DBUG_RETURN(FALSE); +} + + Natural_join_column::Natural_join_column(Field_translator *field_param, TABLE_LIST *tab) { @@ -2514,6 +2672,9 @@ Natural_join_column::check_grants(THD *thd, const char *name, uint length) GRANT_INFO *grant; const char *db_name; const char *table_name; + Security_context *save_security_ctx= thd->security_ctx; + Security_context *new_sctx= table_ref->security_ctx; + bool res; if (view_field) { @@ -2530,7 +2691,11 @@ Natural_join_column::check_grants(THD *thd, const char *name, uint length) table_name= table_ref->table->s->table_name; } - return check_grant_column(thd, grant, db_name, table_name, name, length); + if (new_sctx) + thd->security_ctx= new_sctx; + res= check_grant_column(thd, grant, db_name, table_name, name, length); + thd->security_ctx= save_security_ctx; + return res; } #endif diff --git a/sql/table.h b/sql/table.h index c1de61ef273..40c01a1c051 100644 --- a/sql/table.h +++ b/sql/table.h @@ -23,6 +23,7 @@ class st_select_lex_unit; class st_select_lex; class partition_info; class COND_EQUAL; +class Security_context; /* Order clause list element */ @@ -48,6 +49,11 @@ typedef struct st_grant_info uint version; ulong privilege; ulong want_privilege; + /* + Stores the requested access acl of top level tables list. Is used to + check access rights to the underlying tables of a view. + */ + ulong orig_want_privilege; } GRANT_INFO; enum tmp_table_type {NO_TMP_TABLE=0, TMP_TABLE=1, TRANSACTIONAL_TMP_TABLE=2, @@ -366,7 +372,6 @@ typedef struct st_schema_table #define VIEW_CHECK_SKIP 2 struct st_lex; -struct st_table_list; class select_union; class TMP_TABLE_PARAM; @@ -532,11 +537,36 @@ typedef struct st_table_list Field_translator *field_translation; /* array of VIEW fields */ /* pointer to element after last one in translation table above */ Field_translator *field_translation_end; - /* list of ancestor(s) of this table (underlying table(s)/view(s) */ - st_table_list *ancestor; + /* + List (based on next_local) of underlying tables of this view. I.e. it + does not include the tables of subqueries used in the view. Is set only + for merged views. + */ + st_table_list *merge_underlying_list; + /* + - 0 for base tables + - in case of the view it is the list of all (not only underlying + tables but also used in subquery ones) tables of the view. + */ + List<st_table_list> *view_tables; /* most upper view this table belongs to */ st_table_list *belong_to_view; /* + The view directly referencing this table + (non-zero only for merged underlying tables of a view). + */ + st_table_list *referencing_view; + /* + Security context (non-zero only for tables which belong + to view with SQL SECURITY DEFINER) + */ + Security_context *security_ctx; + /* + This view security context (non-zero only for views with + SQL SECURITY DEFINER) + */ + Security_context *view_sctx; + /* List of all base tables local to a subquery including all view tables. Unlike 'next_local', this in this list views are *not* leaves. Created in setup_tables() -> make_leaves_list(). @@ -592,8 +622,6 @@ typedef struct st_table_list bool compact_view_format; /* Use compact format for SHOW CREATE VIEW */ /* view where processed */ bool where_processed; - /* db part was not defined in table definition */ - bool current_db_used; /* FRMTYPE_ERROR if any type is acceptable */ enum frm_type_enum required_type; char timestamp_buffer[20]; /* buffer for timestamp (19+1) */ @@ -604,9 +632,9 @@ typedef struct st_table_list bool prelocking_placeholder; void calc_md5(char *buffer); - void set_ancestor(); + void set_underlying_merge(); int view_check_option(THD *thd, bool ignore_failure); - bool setup_ancestor(THD *thd); + bool setup_underlying(THD *thd); void cleanup_items(); bool placeholder() {return derived || view; } void print(THD *thd, String *str); @@ -634,6 +662,14 @@ typedef struct st_table_list return prep_where(thd, conds, no_where_clause); return FALSE; } + + void register_want_access(ulong want_access); + bool prepare_security(THD *thd); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + Security_context *find_view_security_context(THD *thd); + bool prepare_view_securety_context(THD *thd); +#endif + private: bool prep_check_option(THD *thd, uint8 check_opt_type); bool prep_where(THD *thd, Item **conds, bool no_where_clause); diff --git a/sql/unireg.cc b/sql/unireg.cc index 592093350f1..86f4b49292a 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -76,6 +76,7 @@ bool mysql_create_frm(THD *thd, my_string file_name, uint keys, KEY *key_info, handler *db_file) { + LEX_STRING str_db_type; uint reclength,info_length,screens,key_info_length,maxlength; ulong key_buff_length; File file; @@ -83,6 +84,7 @@ bool mysql_create_frm(THD *thd, my_string file_name, uchar fileinfo[64],forminfo[288],*keybuff; TYPELIB formnames; uchar *screen_buff; + char buff[2]; #ifdef HAVE_PARTITION_DB partition_info *part_info= thd->lex->part_info; #endif @@ -122,6 +124,12 @@ bool mysql_create_frm(THD *thd, my_string file_name, } reclength=uint2korr(forminfo+266); + /* Calculate extra data segment length */ + str_db_type.str= (char *) ha_get_storage_engine(create_info->db_type); + str_db_type.length= strlen(str_db_type.str); + create_info->extra_size= (2 + str_db_type.length + + 2 + create_info->connect_string.length); + if ((file=create_frm(thd, file_name, db, table, reclength, fileinfo, create_info, keys)) < 0) { @@ -163,16 +171,19 @@ bool mysql_create_frm(THD *thd, my_string file_name, if (make_empty_rec(thd,file,create_info->db_type,create_info->table_options, create_fields,reclength, data_offset, db_file)) goto err; - if (create_info->connect_string.length) - { - char buff[2]; - int2store(buff,create_info->connect_string.length); - if (my_write(file, (const byte*)buff, sizeof(buff), MYF(MY_NABP)) || - my_write(file, (const byte*)create_info->connect_string.str, - create_info->connect_string.length, MYF(MY_NABP))) + + int2store(buff, create_info->connect_string.length); + if (my_write(file, (const byte*)buff, sizeof(buff), MYF(MY_NABP)) || + my_write(file, (const byte*)create_info->connect_string.str, + create_info->connect_string.length, MYF(MY_NABP))) goto err; - } + int2store(buff, str_db_type.length); + if (my_write(file, (const byte*)buff, sizeof(buff), MYF(MY_NABP)) || + my_write(file, (const byte*)str_db_type.str, + str_db_type.length, MYF(MY_NABP))) + goto err; + VOID(my_seek(file,filepos,MY_SEEK_SET,MYF(0))); if (my_write(file,(byte*) forminfo,288,MYF_RW) || my_write(file,(byte*) screen_buff,info_length,MYF_RW) || @@ -497,16 +508,10 @@ static bool pack_header(uchar *forminfo, enum db_type table_type, char *dst; uint length= field->interval->type_lengths[pos], hex_length; const char *src= field->interval->type_names[pos]; - const char *srcend= src + length; hex_length= length * 2; field->interval->type_lengths[pos]= hex_length; field->interval->type_names[pos]= dst= sql_alloc(hex_length + 1); - for ( ; src < srcend; src++) - { - *dst++= _dig_vec_upper[((uchar) *src) >> 4]; - *dst++= _dig_vec_upper[((uchar) *src) & 15]; - } - *dst= '\0'; + octet2hex(dst, src, length); } } |