diff options
author | unknown <petr@mysql.com> | 2004-11-04 02:57:31 +0300 |
---|---|---|
committer | unknown <petr@mysql.com> | 2004-11-04 02:57:31 +0300 |
commit | 14437e9c8f82a77b4f3f04e5b24b3c58db8e3a2b (patch) | |
tree | bae058b5ba3fbcdf419cf63b3c15c4aed5e43d4a /sql | |
parent | bb63229331d461baedecd0ef439914ff9458e441 (diff) | |
parent | e981c836104675533a2eb6810f7c1606377b0491 (diff) | |
download | mariadb-git-14437e9c8f82a77b4f3f04e5b24b3c58db8e3a2b.tar.gz |
Merge mysql.com:/home/cps/mysql/trees/mysql-5.0
into mysql.com:/home/cps/mysql/devel/im/prereview/mysql-5.0
sql/net_serv.cc:
Auto merged
Diffstat (limited to 'sql')
130 files changed, 6615 insertions, 3030 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am index 2c99d42f3ee..a3935835a2d 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -37,7 +37,7 @@ LDADD = @isam_libs@ \ $(top_builddir)/mysys/libmysys.a \ $(top_builddir)/dbug/libdbug.a \ $(top_builddir)/regex/libregex.a \ - $(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@ + $(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@ @NDB_SCI_LIBS@ mysqld_LDADD = @MYSQLD_EXTRA_LDFLAGS@ \ @bdb_libs@ @innodb_libs@ @pstack_libs@ \ diff --git a/sql/discover.cc b/sql/discover.cc index 696be193148..1251055c70e 100644 --- a/sql/discover.cc +++ b/sql/discover.cc @@ -125,47 +125,4 @@ int writefrm(const char *name, const void *frmdata, uint len) -/* - Try to discover table from handler and - if found, write the frm file to disk. - - RETURN VALUES: - 0 : Table existed in handler and created - on disk if so requested - 1 : Table does not exist - >1 : error - -*/ -int create_table_from_handler(const char *db, - const char *name, - bool create_if_found) -{ - int error= 0; - const void* frmblob = NULL; - char path[FN_REFLEN]; - uint frmlen = 0; - DBUG_ENTER("create_table_from_handler"); - DBUG_PRINT("enter", ("create_if_found: %d", create_if_found)); - - if (ha_discover(db, name, &frmblob, &frmlen)) - DBUG_RETURN(1); // Table does not exist - - // Table exists in handler - if (create_if_found) - { - (void)strxnmov(path,FN_REFLEN,mysql_data_home,"/",db,"/",name,NullS); - // Save the frm file - error = writefrm(path, frmblob, frmlen); - } - - if (frmblob) - my_free((char*) frmblob,MYF(0)); - DBUG_RETURN(error); -} - -int table_exists_in_handler(const char *db, - const char *name) -{ - return (create_table_from_handler(db, name, false) == 0); -} diff --git a/sql/examples/ha_archive.cc b/sql/examples/ha_archive.cc index c004330932c..6fbfb3f9f9d 100644 --- a/sql/examples/ha_archive.cc +++ b/sql/examples/ha_archive.cc @@ -42,7 +42,18 @@ handle bulk inserts as well (that is if someone was trying to read at the same time since we would want to flush). - No attempts at durability are made. You can corrupt your data. + A "meta" file is kept. All this file does is contain information on + the number of rows. + + No attempts at durability are made. You can corrupt your data. A repair + method was added to repair the meta file that stores row information, + but if your data file gets corrupted I haven't solved that. I could + create a repair that would solve this, but do you want to take a + chance of loosing your data? + + Locks are row level, and you will get a consistant read. Transactions + will be added later (they are not that hard to add at this + stage). For performance as far as table scans go it is quite fast. I don't have good numbers but locally it has out performed both Innodb and MyISAM. For @@ -70,16 +81,37 @@ Allow users to set compression level. Add truncate table command. Implement versioning, should be easy. - Implement optimize so we can fix broken tables. Allow for errors, find a way to mark bad rows. - See if during an optimize you can make the table smaller. Talk to the gzip guys, come up with a writable format so that updates are doable without switching to a block method. Add optional feature so that rows can be flushed at interval (which will cause less - compression but may speed up ordered searches). + compression but may speed up ordered searches). + Checkpoint the meta file to allow for faster rebuilds. + Dirty open (right now the meta file is repaired if a crash occured). + Transactions. + Option to allow for dirty reads, this would lower the sync calls, which would make + inserts a lot faster, but would mean highly arbitrary reads. -Brian */ +/* + Notes on file formats. + The Meta file is layed out as: + check - Just an int of 254 to make sure that the the file we are opening was + never corrupted. + version - The current version of the file format. + rows - This is an unsigned long long which is the number of rows in the data + file. + check point - Reserved for future use + dirty - Status of the file, whether or not its values are the latest. This + flag is what causes a repair to occur + + The data file: + check - Just an int of 254 to make sure that the the file we are opening was + never corrupted. + version - The current version of the file format. + data - The data is stored in a "row +blobs" format. +*/ /* Variables for archive share methods */ pthread_mutex_t archive_mutex; @@ -87,7 +119,18 @@ static HASH archive_open_tables; static int archive_init= 0; /* The file extension */ -#define ARZ ".ARZ" +#define ARZ ".ARZ" // The data file +#define ARN ".ARN" // Files used during an optimize call +#define ARM ".ARM" // Meta file +/* + uchar + uchar + ulonglong + ulonglong + uchar +*/ +#define META_BUFFER_SIZE 19 // Size of the data used in the meta file +/* + uchar + uchar +*/ +#define DATA_BUFFER_SIZE 2 // Size of the data used in the data file +#define ARCHIVE_CHECK_HEADER 254 // The number we use to determine corruption /* Used for hash table that tracks open tables. @@ -99,14 +142,130 @@ static byte* archive_get_key(ARCHIVE_SHARE *share,uint *length, return (byte*) share->table_name; } +/* + This method reads the header of a datafile and returns whether or not it was successful. +*/ +int ha_archive::read_data_header(gzFile file_to_read) +{ + uchar data_buffer[DATA_BUFFER_SIZE]; + DBUG_ENTER("ha_archive::read_data_header"); + + if (gzrewind(file_to_read) == -1) + DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); + + if (gzread(file_to_read, data_buffer, DATA_BUFFER_SIZE) != DATA_BUFFER_SIZE) + DBUG_RETURN(errno ? errno : -1); + + DBUG_PRINT("ha_archive::read_data_header", ("Check %u", data_buffer[0])); + DBUG_PRINT("ha_archive::read_data_header", ("Version %u", data_buffer[1])); + + if ((data_buffer[0] != (uchar)ARCHIVE_CHECK_HEADER) && + (data_buffer[1] != (uchar)ARCHIVE_VERSION)) + DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); + + DBUG_RETURN(0); +} /* - Example of simple lock controls. - See ha_example.cc for a description. + This method writes out the header of a datafile and returns whether or not it was successful. */ -static ARCHIVE_SHARE *get_share(const char *table_name, TABLE *table) +int ha_archive::write_data_header(gzFile file_to_write) +{ + uchar data_buffer[DATA_BUFFER_SIZE]; + DBUG_ENTER("ha_archive::write_data_header"); + + data_buffer[0]= (uchar)ARCHIVE_CHECK_HEADER; + data_buffer[1]= (uchar)ARCHIVE_VERSION; + + if (gzwrite(file_to_write, &data_buffer, DATA_BUFFER_SIZE) != + sizeof(DATA_BUFFER_SIZE)) + goto error; + DBUG_PRINT("ha_archive::write_data_header", ("Check %u", (uint)data_buffer[0])); + DBUG_PRINT("ha_archive::write_data_header", ("Version %u", (uint)data_buffer[1])); + + DBUG_RETURN(0); +error: + DBUG_RETURN(errno); +} + +/* + This method reads the header of a meta file and returns whether or not it was successful. + *rows will contain the current number of rows in the data file upon success. +*/ +int ha_archive::read_meta_file(File meta_file, ulonglong *rows) +{ + uchar meta_buffer[META_BUFFER_SIZE]; + ulonglong check_point; + + DBUG_ENTER("ha_archive::read_meta_file"); + + VOID(my_seek(meta_file, 0, MY_SEEK_SET, MYF(0))); + if (my_read(meta_file, (byte*)meta_buffer, META_BUFFER_SIZE, 0) != META_BUFFER_SIZE) + DBUG_RETURN(-1); + + /* + Parse out the meta data, we ignore version at the moment + */ + *rows= uint8korr(meta_buffer + 2); + check_point= uint8korr(meta_buffer + 10); + + DBUG_PRINT("ha_archive::read_meta_file", ("Check %d", (uint)meta_buffer[0])); + DBUG_PRINT("ha_archive::read_meta_file", ("Version %d", (uint)meta_buffer[1])); + DBUG_PRINT("ha_archive::read_meta_file", ("Rows %lld", *rows)); + DBUG_PRINT("ha_archive::read_meta_file", ("Checkpoint %lld", check_point)); + DBUG_PRINT("ha_archive::read_meta_file", ("Dirty %d", (int)meta_buffer[18])); + + if ((meta_buffer[0] != (uchar)ARCHIVE_CHECK_HEADER) || + ((bool)meta_buffer[18] == TRUE)) + DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); + + my_sync(meta_file, MYF(MY_WME)); + + DBUG_RETURN(0); +} + +/* + This method writes out the header of a meta file and returns whether or not it was successful. + By setting dirty you say whether or not the file represents the actual state of the data file. + Upon ::open() we set to dirty, and upon ::close() we set to clean. If we determine during + a read that the file was dirty we will force a rebuild of this file. +*/ +int ha_archive::write_meta_file(File meta_file, ulonglong rows, bool dirty) +{ + uchar meta_buffer[META_BUFFER_SIZE]; + ulonglong check_point= 0; //Reserved for the future + + DBUG_ENTER("ha_archive::write_meta_file"); + + meta_buffer[0]= (uchar)ARCHIVE_CHECK_HEADER; + meta_buffer[1]= (uchar)ARCHIVE_VERSION; + int8store(meta_buffer + 2, rows); + int8store(meta_buffer + 10, check_point); + *(meta_buffer + 18)= (uchar)dirty; + DBUG_PRINT("ha_archive::write_meta_file", ("Check %d", (uint)ARCHIVE_CHECK_HEADER)); + DBUG_PRINT("ha_archive::write_meta_file", ("Version %d", (uint)ARCHIVE_VERSION)); + DBUG_PRINT("ha_archive::write_meta_file", ("Rows %llu", rows)); + DBUG_PRINT("ha_archive::write_meta_file", ("Checkpoint %llu", check_point)); + DBUG_PRINT("ha_archive::write_meta_file", ("Dirty %d", (uint)dirty)); + + VOID(my_seek(meta_file, 0, MY_SEEK_SET, MYF(0))); + if (my_write(meta_file, (byte *)meta_buffer, META_BUFFER_SIZE, 0) != META_BUFFER_SIZE) + DBUG_RETURN(-1); + + my_sync(meta_file, MYF(MY_WME)); + + DBUG_RETURN(0); +} + + +/* + We create the shared memory space that we will use for the open table. + See ha_example.cc for a longer description. +*/ +ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table) { ARCHIVE_SHARE *share; + char meta_file_name[FN_REFLEN]; uint length; char *tmp_name; @@ -117,7 +276,7 @@ static ARCHIVE_SHARE *get_share(const char *table_name, TABLE *table) if (!archive_init) { VOID(pthread_mutex_init(&archive_mutex,MY_MUTEX_INIT_FAST)); - if (!hash_init(&archive_open_tables,system_charset_info,32,0,0, + if (hash_init(&archive_open_tables,system_charset_info,32,0,0, (hash_get_key) archive_get_key,0,0)) { pthread_mutex_unlock(&LOCK_mysql_create_db); @@ -143,33 +302,62 @@ static ARCHIVE_SHARE *get_share(const char *table_name, TABLE *table) return NULL; } - share->use_count=0; - share->table_name_length=length; - share->table_name=tmp_name; + share->use_count= 0; + share->table_name_length= length; + share->table_name= tmp_name; fn_format(share->data_file_name,table_name,"",ARZ,MY_REPLACE_EXT|MY_UNPACK_FILENAME); + fn_format(meta_file_name,table_name,"",ARM,MY_REPLACE_EXT|MY_UNPACK_FILENAME); strmov(share->table_name,table_name); + /* + We will use this lock for rows. + */ + VOID(pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST)); + if ((share->meta_file= my_open(meta_file_name, O_RDWR, MYF(0))) == -1) + goto error; + + if (read_meta_file(share->meta_file, &share->rows_recorded)) + { + /* + The problem here is that for some reason, probably a crash, the meta + file has been corrupted. So what do we do? Well we try to rebuild it + ourself. Once that happens, we reread it, but if that fails we just + call it quits and return an error. + */ + if (rebuild_meta_file(share->table_name, share->meta_file)) + goto error; + if (read_meta_file(share->meta_file, &share->rows_recorded)) + goto error; + } + /* + After we read, we set the file to dirty. When we close, we will do the + opposite. + */ + (void)write_meta_file(share->meta_file, share->rows_recorded, TRUE); /* It is expensive to open and close the data files and since you can't have a gzip file that can be both read and written we keep a writer open that is shared amoung all open tables. */ if ((share->archive_write= gzopen(share->data_file_name, "ab")) == NULL) - goto error; + goto error2; if (my_hash_insert(&archive_open_tables, (byte*) share)) - goto error; + goto error2; thr_lock_init(&share->lock); if (pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST)) - goto error2; + goto error3; } share->use_count++; pthread_mutex_unlock(&archive_mutex); return share; -error2: +error3: + VOID(pthread_mutex_destroy(&share->mutex)); thr_lock_delete(&share->lock); /* We close, but ignore errors since we already have errors */ (void)gzclose(share->archive_write); +error2: + my_close(share->meta_file,MYF(0)); error: pthread_mutex_unlock(&archive_mutex); my_free((gptr) share, MYF(0)); @@ -179,10 +367,10 @@ error: /* - Free lock controls. + Free the share. See ha_example.cc for a description. */ -static int free_share(ARCHIVE_SHARE *share) +int ha_archive::free_share(ARCHIVE_SHARE *share) { int rc= 0; pthread_mutex_lock(&archive_mutex); @@ -190,7 +378,8 @@ static int free_share(ARCHIVE_SHARE *share) { hash_delete(&archive_open_tables, (byte*) share); thr_lock_delete(&share->lock); - pthread_mutex_destroy(&share->mutex); + VOID(pthread_mutex_destroy(&share->mutex)); + (void)write_meta_file(share->meta_file, share->rows_recorded, FALSE); if (gzclose(share->archive_write) == Z_ERRNO) rc= 1; my_free((gptr) share, MYF(0)); @@ -205,7 +394,7 @@ static int free_share(ARCHIVE_SHARE *share) We just implement one additional file extension. */ const char **ha_archive::bas_ext() const -{ static const char *ext[]= { ARZ, NullS }; return ext; } +{ static const char *ext[]= { ARZ, ARN, ARM, NullS }; return ext; } /* @@ -213,7 +402,6 @@ const char **ha_archive::bas_ext() const Create/get our shared structure. Init out lock. We open the file we will read from. - Set the size of ref_length. */ int ha_archive::open(const char *name, int mode, uint test_if_locked) { @@ -266,54 +454,63 @@ int ha_archive::close(void) /* - We create our data file here. The format is pretty simple. The first - bytes in any file are the version number. Currently we do nothing - with this, but in the future this gives us the ability to figure out - version if we change the format at all. After the version we - starting writing our rows. Unlike other storage engines we do not - "pack" our data. Since we are about to do a general compression, - packing would just be a waste of CPU time. If the table has blobs - they are written after the row in the order of creation. - - So to read a row we: - Read the version - Read the record and copy it into buf - Loop through any blobs and read them + We create our data file here. The format is pretty simple. + You can read about the format of the data file above. + Unlike other storage engines we do not "pack" our data. Since we + are about to do a general compression, packing would just be a waste of + CPU time. If the table has blobs they are written after the row in the order + of creation. */ int ha_archive::create(const char *name, TABLE *table_arg, HA_CREATE_INFO *create_info) { - File create_file; + File create_file; // We use to create the datafile and the metafile char name_buff[FN_REFLEN]; - size_t written; int error; DBUG_ENTER("ha_archive::create"); + if ((create_file= my_create(fn_format(name_buff,name,"",ARM, + MY_REPLACE_EXT|MY_UNPACK_FILENAME),0, + O_RDWR | O_TRUNC,MYF(MY_WME))) < 0) + { + error= my_errno; + goto error; + } + write_meta_file(create_file, 0, FALSE); + my_close(create_file,MYF(0)); + + /* + We reuse name_buff since it is available. + */ if ((create_file= my_create(fn_format(name_buff,name,"",ARZ, MY_REPLACE_EXT|MY_UNPACK_FILENAME),0, O_RDWR | O_TRUNC,MYF(MY_WME))) < 0) { error= my_errno; - goto err; + goto error; } if ((archive= gzdopen(create_file, "ab")) == NULL) { error= errno; delete_table(name); - goto err; + goto error; } - version= ARCHIVE_VERSION; - written= gzwrite(archive, &version, sizeof(version)); - if (gzclose(archive) || written != sizeof(version)) + if (write_data_header(archive)) { - error= errno; - delete_table(name); - goto err; + gzclose(archive); + goto error2; } + + if (gzclose(archive)) + goto error2; + DBUG_RETURN(0); -err: +error2: + error= errno; + delete_table(name); +error: /* Return error number, if we got one */ DBUG_RETURN(error ? error : -1); } @@ -322,33 +519,50 @@ err: /* Look at ha_archive::open() for an explanation of the row format. Here we just write out the row. + + Wondering about start_bulk_insert()? We don't implement it for + archive since it optimizes for lots of writes. The only save + for implementing start_bulk_insert() is that we could skip + setting dirty to true each time. */ int ha_archive::write_row(byte * buf) { - char *pos; z_off_t written; DBUG_ENTER("ha_archive::write_row"); statistic_increment(ha_write_count,&LOCK_status); - if (table->timestamp_default_now) - update_timestamp(buf+table->timestamp_default_now-1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) + table->timestamp_field->set_time(); + pthread_mutex_lock(&share->mutex); written= gzwrite(share->archive_write, buf, table->reclength); + DBUG_PRINT("ha_archive::get_row", ("Wrote %d bytes expected %d", written, table->reclength)); share->dirty= TRUE; if (written != table->reclength) - DBUG_RETURN(errno ? errno : -1); - + goto error; + /* + We should probably mark the table as damagaged if the record is written + but the blob fails. + */ for (Field_blob **field=table->blob_field ; *field ; field++) { char *ptr; uint32 size= (*field)->get_length(); - (*field)->get_ptr(&ptr); - written= gzwrite(share->archive_write, ptr, (unsigned)size); - if (written != size) - DBUG_RETURN(errno ? errno : -1); + if (size) + { + (*field)->get_ptr(&ptr); + written= gzwrite(share->archive_write, ptr, (unsigned)size); + if (written != size) + goto error; + } } + share->rows_recorded++; + pthread_mutex_unlock(&share->mutex); DBUG_RETURN(0); +error: + pthread_mutex_unlock(&share->mutex); + DBUG_RETURN(errno ? errno : -1); } @@ -364,47 +578,28 @@ int ha_archive::rnd_init(bool scan) int read; // gzread() returns int, and we use this to check the header /* We rewind the file so that we can read from the beginning if scan */ - if(scan) + if (scan) { + scan_rows= share->rows_recorded; records= 0; - if (gzrewind(archive)) - DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); - } - /* - If dirty, we lock, and then reset/flush the data. - I found that just calling gzflush() doesn't always work. - */ - if (share->dirty == TRUE) - { - pthread_mutex_lock(&share->mutex); + /* + If dirty, we lock, and then reset/flush the data. + I found that just calling gzflush() doesn't always work. + */ if (share->dirty == TRUE) { -/* I was having problems with OSX, but it worked for 10.3 so I am wrapping this with and ifdef */ -#ifdef BROKEN_GZFLUSH - gzclose(share->archive_write); - if ((share->archive_write= gzopen(share->data_file_name, "ab")) == NULL) + pthread_mutex_lock(&share->mutex); + if (share->dirty == TRUE) { - pthread_mutex_unlock(&share->mutex); - DBUG_RETURN(errno ? errno : -1); + gzflush(share->archive_write, Z_SYNC_FLUSH); + share->dirty= FALSE; } -#else - gzflush(share->archive_write, Z_SYNC_FLUSH); -#endif - share->dirty= FALSE; + pthread_mutex_unlock(&share->mutex); } - pthread_mutex_unlock(&share->mutex); - } - - /* - At the moment we just check the size of version to make sure the header is - intact. - */ - if (scan) - { - read= gzread(archive, &version, sizeof(version)); - if (read != sizeof(version)) - DBUG_RETURN(errno ? errno : -1); + + if (read_data_header(archive)) + DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); } DBUG_RETURN(0); @@ -415,14 +610,19 @@ int ha_archive::rnd_init(bool scan) This is the method that is used to read a row. It assumes that the row is positioned where you want it. */ -int ha_archive::get_row(byte *buf) +int ha_archive::get_row(gzFile file_to_read, byte *buf) { int read; // Bytes read, gzread() returns int char *last; size_t total_blob_length= 0; + Field_blob **field; DBUG_ENTER("ha_archive::get_row"); - read= gzread(archive, buf, table->reclength); + read= gzread(file_to_read, buf, table->reclength); + DBUG_PRINT("ha_archive::get_row", ("Read %d bytes expected %d", read, table->reclength)); + + if (read == Z_STREAM_ERROR) + DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); /* If we read nothing we are at the end of the file */ if (read == 0) @@ -433,7 +633,7 @@ int ha_archive::get_row(byte *buf) DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); /* Calculate blob length, we use this for our buffer */ - for (Field_blob **field=table->blob_field; *field ; field++) + for (field=table->blob_field; *field ; field++) total_blob_length += (*field)->get_length(); /* Adjust our row buffer if we need be */ @@ -441,14 +641,17 @@ int ha_archive::get_row(byte *buf) last= (char *)buffer.ptr(); /* Loop through our blobs and read them */ - for (Field_blob **field=table->blob_field; *field ; field++) + for (field=table->blob_field; *field ; field++) { size_t size= (*field)->get_length(); - read= gzread(archive, last, size); - if ((size_t) read != size) - DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); - (*field)->set_ptr(size, last); - last += size; + if (size) + { + read= gzread(file_to_read, last, size); + if ((size_t) read != size) + DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); + (*field)->set_ptr(size, last); + last += size; + } } DBUG_RETURN(0); } @@ -464,9 +667,15 @@ int ha_archive::rnd_next(byte *buf) int rc; DBUG_ENTER("ha_archive::rnd_next"); + if (!scan_rows) + DBUG_RETURN(HA_ERR_END_OF_FILE); + scan_rows--; + statistic_increment(ha_read_rnd_next_count,&LOCK_status); current_position= gztell(archive); - rc= get_row(buf); + rc= get_row(archive, buf); + + if (rc != HA_ERR_END_OF_FILE) records++; @@ -479,6 +688,7 @@ int ha_archive::rnd_next(byte *buf) each call to ha_archive::rnd_next() if an ordering of the rows is needed. */ + void ha_archive::position(const byte *record) { DBUG_ENTER("ha_archive::position"); @@ -501,9 +711,166 @@ int ha_archive::rnd_pos(byte * buf, byte *pos) current_position= ha_get_ptr(pos, ref_length); z_off_t seek= gzseek(archive, current_position, SEEK_SET); - DBUG_RETURN(get_row(buf)); + DBUG_RETURN(get_row(archive, buf)); } +/* + This method rebuilds the meta file. It does this by walking the datafile and + rewriting the meta file. +*/ +int ha_archive::rebuild_meta_file(char *table_name, File meta_file) +{ + int rc; + byte *buf; + ulonglong rows_recorded= 0; + gzFile rebuild_file; /* Archive file we are working with */ + char data_file_name[FN_REFLEN]; + DBUG_ENTER("ha_archive::rebuild_meta_file"); + + /* + Open up the meta file to recreate it. + */ + fn_format(data_file_name, table_name, "", ARZ, + MY_REPLACE_EXT|MY_UNPACK_FILENAME); + if ((rebuild_file= gzopen(data_file_name, "rb")) == NULL) + DBUG_RETURN(errno ? errno : -1); + + if (rc= read_data_header(rebuild_file)) + goto error; + + /* + We malloc up the buffer we will use for counting the rows. + I know, this malloc'ing memory but this should be a very + rare event. + */ + if (!(buf= (byte*) my_malloc(table->rec_buff_length > sizeof(ulonglong) +1 ? + table->rec_buff_length : sizeof(ulonglong) +1 , + MYF(MY_WME)))) + { + rc= HA_ERR_CRASHED_ON_USAGE; + goto error; + } + + while (!(rc= get_row(rebuild_file, buf))) + rows_recorded++; + + /* + Only if we reach the end of the file do we assume we can rewrite. + At this point we reset rc to a non-message state. + */ + if (rc == HA_ERR_END_OF_FILE) + { + (void)write_meta_file(meta_file, rows_recorded, FALSE); + rc= 0; + } + + my_free((gptr) buf, MYF(0)); +error: + gzclose(rebuild_file); + + DBUG_RETURN(rc); +} + +/* + The table can become fragmented if data was inserted, read, and then + inserted again. What we do is open up the file and recompress it completely. +*/ +int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt) +{ + DBUG_ENTER("ha_archive::optimize"); + int read; // Bytes read, gzread() returns int + gzFile reader, writer; + char block[IO_SIZE]; + char writer_filename[FN_REFLEN]; + + /* Lets create a file to contain the new data */ + fn_format(writer_filename, share->table_name, "", ARN, + MY_REPLACE_EXT|MY_UNPACK_FILENAME); + + /* Closing will cause all data waiting to be flushed, to be flushed */ + gzclose(share->archive_write); + + if ((reader= gzopen(share->data_file_name, "rb")) == NULL) + DBUG_RETURN(-1); + + if ((writer= gzopen(writer_filename, "wb")) == NULL) + { + gzclose(reader); + DBUG_RETURN(-1); + } + + while (read= gzread(reader, block, IO_SIZE)) + gzwrite(writer, block, read); + + gzclose(reader); + gzclose(writer); + + my_rename(writer_filename,share->data_file_name,MYF(0)); + + /* + We reopen the file in case some IO is waiting to go through. + In theory the table is closed right after this operation, + but it is possible for IO to still happen. + I may be being a bit too paranoid right here. + */ + if ((share->archive_write= gzopen(share->data_file_name, "ab")) == NULL) + DBUG_RETURN(errno ? errno : -1); + share->dirty= FALSE; + + DBUG_RETURN(0); +} + + +/* + No transactions yet, so this is pretty dull. +*/ +int ha_archive::external_lock(THD *thd, int lock_type) +{ + DBUG_ENTER("ha_archive::external_lock"); + DBUG_RETURN(0); +} + +/* + Below is an example of how to setup row level locking. +*/ +THR_LOCK_DATA **ha_archive::store_lock(THD *thd, + THR_LOCK_DATA **to, + enum thr_lock_type lock_type) +{ + if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) + { + /* + Here is where we get into the guts of a row level lock. + If TL_UNLOCK is set + If we are not doing a LOCK TABLE or DISCARD/IMPORT + TABLESPACE, then allow multiple writers + */ + + if ((lock_type >= TL_WRITE_CONCURRENT_INSERT && + lock_type <= TL_WRITE) && !thd->in_lock_tables + && !thd->tablespace_op) + lock_type = TL_WRITE_ALLOW_WRITE; + + /* + In queries of type INSERT INTO t1 SELECT ... FROM t2 ... + MySQL would use the lock TL_READ_NO_INSERT on t2, and that + would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts + to t2. Convert the lock to a normal read lock to allow + concurrent inserts to t2. + */ + + if (lock_type == TL_READ_NO_INSERT && !thd->in_lock_tables) + lock_type = TL_READ; + + lock.type=lock_type; + } + + *to++= &lock; + + return to; +} + + /****************************************************************************** Everything below here is default, please look at ha_example.cc for @@ -573,8 +940,8 @@ void ha_archive::info(uint flag) DBUG_ENTER("ha_archive::info"); /* This is a lie, but you don't want the optimizer to see zero or 1 */ - if (records < 2) - records= 2; + records= share->rows_recorded; + deleted= 0; DBUG_VOID_RETURN; } @@ -591,23 +958,6 @@ int ha_archive::reset(void) DBUG_RETURN(0); } - -int ha_archive::external_lock(THD *thd, int lock_type) -{ - DBUG_ENTER("ha_archive::external_lock"); - DBUG_RETURN(0); -} - -THR_LOCK_DATA **ha_archive::store_lock(THD *thd, - THR_LOCK_DATA **to, - enum thr_lock_type lock_type) -{ - if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) - lock.type=lock_type; - *to++= &lock; - return to; -} - ha_rows ha_archive::records_in_range(uint inx, key_range *min_key, key_range *max_key) { diff --git a/sql/examples/ha_archive.h b/sql/examples/ha_archive.h index f08353a5d6c..b619de5f6c1 100644 --- a/sql/examples/ha_archive.h +++ b/sql/examples/ha_archive.h @@ -32,8 +32,10 @@ typedef struct st_archive_share { uint table_name_length,use_count; pthread_mutex_t mutex; THR_LOCK lock; + File meta_file; /* Meta file we use */ gzFile archive_write; /* Archive file we are working with */ bool dirty; /* Flag for if a flush should occur */ + ulonglong rows_recorded; /* Number of rows in tables */ } ARCHIVE_SHARE; /* @@ -50,7 +52,7 @@ class ha_archive: public handler z_off_t current_position; /* The position of the row we just read */ byte byte_buffer[IO_SIZE]; /* Initial buffer for our string */ String buffer; /* Buffer used for blob storage */ - unsigned int version; /* Used for recording version */ + ulonglong scan_rows; /* Number of rows left in scan */ public: ha_archive(TABLE *table): handler(table) @@ -104,7 +106,14 @@ public: int rnd_init(bool scan=1); int rnd_next(byte *buf); int rnd_pos(byte * buf, byte *pos); - int get_row(byte *buf); + int get_row(gzFile file_to_read, byte *buf); + int read_meta_file(File meta_file, ulonglong *rows); + int write_meta_file(File meta_file, ulonglong rows, bool dirty); + ARCHIVE_SHARE *get_share(const char *table_name, TABLE *table); + int free_share(ARCHIVE_SHARE *share); + int rebuild_meta_file(char *table_name, File meta_file); + int read_data_header(gzFile file_to_read); + int write_data_header(gzFile file_to_write); void position(const byte *record); void info(uint); int extra(enum ha_extra_function operation); @@ -112,7 +121,7 @@ public: int external_lock(THD *thd, int lock_type); ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key); int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info); - + int optimize(THD* thd, HA_CHECK_OPT* check_opt); THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type); }; diff --git a/sql/examples/ha_example.cc b/sql/examples/ha_example.cc index 097abd48e05..cb0780ea74d 100644 --- a/sql/examples/ha_example.cc +++ b/sql/examples/ha_example.cc @@ -263,8 +263,8 @@ int ha_example::write_row(byte * buf) clause was used. Consecutive ordering is not guarenteed. Currently new_data will not have an updated auto_increament record, or and updated timestamp field. You can do these for example by doing these: - if (table->timestamp_on_update_now) - update_timestamp(new_row+table->timestamp_on_update_now-1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) + table->timestamp_field->set_time(); if (table->next_number_field && record == table->record[0]) update_auto_increment(); diff --git a/sql/examples/ha_tina.cc b/sql/examples/ha_tina.cc index 06a19e478ae..0345a170c3c 100644 --- a/sql/examples/ha_tina.cc +++ b/sql/examples/ha_tina.cc @@ -428,8 +428,8 @@ int ha_tina::write_row(byte * buf) statistic_increment(ha_write_count,&LOCK_status); - if (table->timestamp_default_now) - update_timestamp(buf+table->timestamp_default_now-1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) + table->timestamp_field->set_time(); size= encode_quote(buf); @@ -464,8 +464,8 @@ int ha_tina::update_row(const byte * old_data, byte * new_data) statistic_increment(ha_update_count,&LOCK_status); - if (table->timestamp_default_now) - update_timestamp(new_data+table->timestamp_default_now-1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) + table->timestamp_field->set_time(); size= encode_quote(new_data); diff --git a/sql/examples/ha_tina.h b/sql/examples/ha_tina.h index 67a907fddb6..22193c01013 100644 --- a/sql/examples/ha_tina.h +++ b/sql/examples/ha_tina.h @@ -90,6 +90,12 @@ class ha_tina: public handler /* The next method will never be called */ virtual double read_time(ha_rows rows) { DBUG_ASSERT(0); return((double) rows / 20.0+1); } virtual bool fast_key_read() { return 1;} + /* + TODO: return actual upper bound of number of records in the table. + (e.g. save number of records seen on full table scan and/or use file size + as upper bound) + */ + ha_rows estimate_rows_upper_bound() { return HA_POS_ERROR; } int open(const char *name, int mode, uint test_if_locked); int close(void); diff --git a/sql/field.cc b/sql/field.cc index 1cadf186a4c..7a8f59c25fe 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -188,42 +188,51 @@ static bool test_if_real(const char *str,int length, CHARSET_INFO *cs) FIELD_CAST_STOP */ static Field::field_cast_enum field_cast_decimal[]= -{Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, +{Field::FIELD_CAST_DECIMAL, + Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_tiny[]= -{Field::FIELD_CAST_SHORT, Field::FIELD_CAST_MEDIUM, Field::FIELD_CAST_LONG, +{Field::FIELD_CAST_TINY, + Field::FIELD_CAST_SHORT, Field::FIELD_CAST_MEDIUM, Field::FIELD_CAST_LONG, Field::FIELD_CAST_LONGLONG, Field::FIELD_CAST_FLOAT, Field::FIELD_CAST_DOUBLE, Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_short[]= -{Field::FIELD_CAST_MEDIUM, Field::FIELD_CAST_LONG, Field::FIELD_CAST_LONGLONG, +{Field::FIELD_CAST_SHORT, + Field::FIELD_CAST_MEDIUM, Field::FIELD_CAST_LONG, Field::FIELD_CAST_LONGLONG, Field::FIELD_CAST_FLOAT, Field::FIELD_CAST_DOUBLE, Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_medium[]= -{Field::FIELD_CAST_LONG, Field::FIELD_CAST_LONGLONG, +{Field::FIELD_CAST_MEDIUM, + Field::FIELD_CAST_LONG, Field::FIELD_CAST_LONGLONG, Field::FIELD_CAST_DOUBLE, Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_long[]= -{Field::FIELD_CAST_LONGLONG, +{Field::FIELD_CAST_LONG, + Field::FIELD_CAST_LONGLONG, Field::FIELD_CAST_DOUBLE, Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_longlong[]= -{Field::FIELD_CAST_DOUBLE, +{Field::FIELD_CAST_LONGLONG, + Field::FIELD_CAST_DOUBLE, Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_float[]= -{Field::FIELD_CAST_DOUBLE, +{Field::FIELD_CAST_FLOAT, + Field::FIELD_CAST_DOUBLE, Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_double[]= -{Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, +{Field::FIELD_CAST_DOUBLE, + Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_null[]= -{Field::FIELD_CAST_DECIMAL, Field::FIELD_CAST_TINY, Field::FIELD_CAST_SHORT, +{Field::FIELD_CAST_NULL, + Field::FIELD_CAST_DECIMAL, Field::FIELD_CAST_TINY, Field::FIELD_CAST_SHORT, Field::FIELD_CAST_MEDIUM, Field::FIELD_CAST_LONG, Field::FIELD_CAST_LONGLONG, Field::FIELD_CAST_FLOAT, Field::FIELD_CAST_DOUBLE, Field::FIELD_CAST_TIMESTAMP, Field::FIELD_CAST_YEAR, @@ -234,44 +243,54 @@ static Field::field_cast_enum field_cast_null[]= Field::FIELD_CAST_GEOM, Field::FIELD_CAST_ENUM, Field::FIELD_CAST_SET, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_timestamp[]= -{Field::FIELD_CAST_DATETIME, +{Field::FIELD_CAST_TIMESTAMP, + Field::FIELD_CAST_DATETIME, Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_year[]= -{Field::FIELD_CAST_SHORT, Field::FIELD_CAST_MEDIUM, Field::FIELD_CAST_LONG, +{Field::FIELD_CAST_YEAR, + Field::FIELD_CAST_SHORT, Field::FIELD_CAST_MEDIUM, Field::FIELD_CAST_LONG, Field::FIELD_CAST_LONGLONG, Field::FIELD_CAST_FLOAT, Field::FIELD_CAST_DOUBLE, Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_date[]= -{Field::FIELD_CAST_DATETIME, +{Field::FIELD_CAST_DATE, + Field::FIELD_CAST_DATETIME, Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_newdate[]= -{Field::FIELD_CAST_DATETIME, +{Field::FIELD_CAST_NEWDATE, + Field::FIELD_CAST_DATETIME, Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_time[]= -{Field::FIELD_CAST_DATETIME, +{Field::FIELD_CAST_TIME, + Field::FIELD_CAST_DATETIME, Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_datetime[]= -{Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, +{Field::FIELD_CAST_DATETIME, + Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_string[]= -{Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; +{Field::FIELD_CAST_STRING, + Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_varstring[]= -{Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; +{Field::FIELD_CAST_VARSTRING, + Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_blob[]= -{Field::FIELD_CAST_STOP}; +{Field::FIELD_CAST_BLOB, + Field::FIELD_CAST_STOP}; +/* + Geometrical, enum and set fields can be casted only to expressions +*/ static Field::field_cast_enum field_cast_geom[]= {Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_enum[]= -{Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, - Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; +{Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_set[]= -{Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, - Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; +{Field::FIELD_CAST_STOP}; // Array of pointers on conversion table for all fields types casting static Field::field_cast_enum *field_cast_array[]= {0, //FIELD_CAST_STOP @@ -286,18 +305,26 @@ static Field::field_cast_enum *field_cast_array[]= }; +/* + Check if field of given type can store a value of this field. + + SYNOPSIS + type type for test + + RETURN + 1 can + 0 can not +*/ + bool Field::field_cast_compatible(Field::field_cast_enum type) { DBUG_ASSERT(type != FIELD_CAST_STOP); Field::field_cast_enum *array= field_cast_array[field_cast_type()]; - uint i= 0; - Field::field_cast_enum tp; - do + while (*array != FIELD_CAST_STOP) { - tp= array[i++]; - if (tp == type) + if (*(array++) == type) return 1; - } while (tp != FIELD_CAST_STOP); + } return 0; } @@ -441,11 +468,9 @@ bool Field::get_time(TIME *ltime) void Field::store_time(TIME *ltime,timestamp_type type) { - char buff[MAX_DATE_REP_LENGTH]; - String tmp; - tmp.set(buff, sizeof(buff), &my_charset_bin); - TIME_to_string(ltime, &tmp); - store(buff, tmp.length(), &my_charset_bin); + char buff[MAX_DATE_STRING_REP_LENGTH]; + uint length= (uint) my_TIME_to_str(ltime, buff); + store(buff, length, &my_charset_bin); } @@ -2905,11 +2930,12 @@ void Field_double::sql_type(String &res) const */ Field_timestamp::Field_timestamp(char *ptr_arg, uint32 len_arg, + uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, CHARSET_INFO *cs) - :Field_str(ptr_arg, 19, (uchar*) 0,0, + :Field_str(ptr_arg, 19, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg, cs) { /* For 4.0 MYD and 4.0 InnoDB compatibility */ @@ -2925,23 +2951,41 @@ Field_timestamp::Field_timestamp(char *ptr_arg, uint32 len_arg, /* - Sets TABLE::timestamp_default_now and TABLE::timestamp_on_update_now - members according to unireg type of this TIMESTAMP field. - + Get auto-set type for TIMESTAMP field. + SYNOPSIS - Field_timestamp::set_timestamp_offsets() - + get_auto_set_type() + + DESCRIPTION + Returns value indicating during which operations this TIMESTAMP field + should be auto-set to current timestamp. */ -void Field_timestamp::set_timestamp_offsets() +timestamp_auto_set_type Field_timestamp::get_auto_set_type() const { - ulong timestamp= (ulong) (ptr - (char*) table->record[0]) + 1; - - DBUG_ASSERT(table->timestamp_field == this && unireg_check != NONE); - - table->timestamp_default_now= - (unireg_check == TIMESTAMP_UN_FIELD)? 0 : timestamp; - table->timestamp_on_update_now= - (unireg_check == TIMESTAMP_DN_FIELD)? 0 : timestamp; + switch (unireg_check) + { + case TIMESTAMP_DN_FIELD: + return TIMESTAMP_AUTO_SET_ON_INSERT; + case TIMESTAMP_UN_FIELD: + return TIMESTAMP_AUTO_SET_ON_UPDATE; + case TIMESTAMP_OLD_FIELD: + /* + Altough we can have several such columns in legacy tables this + function should be called only for first of them (i.e. the one + having auto-set property). + */ + DBUG_ASSERT(table->timestamp_field == this); + /* Fall-through */ + case TIMESTAMP_DNUN_FIELD: + return TIMESTAMP_AUTO_SET_ON_BOTH; + default: + /* + Normally this function should not be called for TIMESTAMPs without + auto-set property. + */ + DBUG_ASSERT(0); + return TIMESTAMP_NO_AUTO_SET; + } } @@ -3249,6 +3293,7 @@ void Field_timestamp::sql_type(String &res) const void Field_timestamp::set_time() { long tmp= (long) table->in_use->query_start(); + set_notnull(); #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -3475,8 +3520,8 @@ bool Field_time::send_binary(Protocol *protocol) { TIME tm; Field_time::get_time(&tm); - tm.day= tm.hour/3600; // Move hours to days - tm.hour-= tm.day*3600; + tm.day= tm.hour/24; // Move hours to days + tm.hour-= tm.day*24; return protocol->store_time(&tm); } @@ -4258,9 +4303,12 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) /* Convert character set if nesessary */ if (String::needs_conversion(length, cs, field_charset, ¬_used)) { - tmpstr.copy(from, length, cs, field_charset); + uint conv_errors; + tmpstr.copy(from, length, cs, field_charset, &conv_errors); from= tmpstr.ptr(); length= tmpstr.length(); + if (conv_errors) + error= 1; } /* @@ -4283,11 +4331,11 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) from+= field_charset->cset->scan(field_charset, from, end, MY_SEQ_SPACES); if (from != end) - { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); - error=1; - } + error= 1; } + if (error) + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); + return error; } @@ -4309,8 +4357,9 @@ int Field_str::store(double nr) uint length; bool use_scientific_notation= TRUE; use_scientific_notation= TRUE; -if (field_length < 32 && fabs(nr) < log_10[field_length]-1) + if (field_length < 32 && fabs(nr) < log_10[field_length]-1) use_scientific_notation= FALSE; + length= (uint) my_sprintf(buff, (buff, "%-.*g", (use_scientific_notation ? max(0, (int)field_length-5) : @@ -4325,7 +4374,7 @@ if (field_length < 32 && fabs(nr) < log_10[field_length]-1) like inserting 500.0 in char(1) */ DBUG_ASSERT(field_length < 5 || length <= field_length+1); - return store((const char *)buff, min(length, field_length), charset()); + return store((const char *) buff, length, charset()); } @@ -4407,21 +4456,8 @@ void Field_string::sql_type(String &res) const res.length(length); } - char *Field_string::pack(char *to, const char *from, uint max_length) { - const char *end=from+min(field_length,max_length); - uchar length; - while (end > from && end[-1] == ' ') - end--; - *to= length=(uchar) (end-from); - memcpy(to+1, from, (int) length); - return to+1+length; -} - - -char *Field_string::pack_key(char *to, const char *from, uint max_length) -{ uint length= min(field_length,max_length); uint char_length= max_length/field_charset->mbmaxlen; if (length > char_length) @@ -4429,15 +4465,24 @@ char *Field_string::pack_key(char *to, const char *from, uint max_length) set_if_smaller(length, char_length); while (length && from[length-1] == ' ') length--; - *to= (uchar)length; - memcpy(to+1, from, length); - return to+1+length; + *to++= (char) (uchar) length; + if (field_length > 255) + *to++= (char) (uchar) (length >> 8); + memcpy(to, from, length); + return to+length; } const char *Field_string::unpack(char *to, const char *from) { - uint length= (uint) (uchar) *from++; + uint length; + if (field_length > 255) + { + length= uint2korr(from); + from+= 2; + } + else + length= (uint) (uchar) *from++; memcpy(to, from, (int) length); bfill(to+length, field_length - length, ' '); return from+length; @@ -4446,8 +4491,19 @@ const char *Field_string::unpack(char *to, const char *from) int Field_string::pack_cmp(const char *a, const char *b, uint length) { - uint a_length= (uint) (uchar) *a++; - uint b_length= (uint) (uchar) *b++; + uint a_length, b_length; + if (field_length > 255) + { + a_length= uint2korr(a); + b_length= uint2korr(b); + a+= 2; + b+= 2; + } + else + { + a_length= (uint) (uchar) *a++; + b_length= (uint) (uchar) *b++; + } return my_strnncoll(field_charset, (const uchar*)a,a_length, (const uchar*)b,b_length); @@ -4456,7 +4512,14 @@ int Field_string::pack_cmp(const char *a, const char *b, uint length) int Field_string::pack_cmp(const char *b, uint length) { - uint b_length= (uint) (uchar) *b++; + uint b_length; + if (field_length > 255) + { + b_length= uint2korr(b); + b+= 2; + } + else + b_length= (uint) (uchar) *b++; char *end= ptr + field_length; while (end > ptr && end[-1] == ' ') end--; @@ -4496,16 +4559,20 @@ int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs) /* Convert character set if nesessary */ if (String::needs_conversion(length, cs, field_charset, ¬_used)) { - tmpstr.copy(from, length, cs, field_charset); + uint conv_errors; + tmpstr.copy(from, length, cs, field_charset, &conv_errors); from= tmpstr.ptr(); length= tmpstr.length(); + if (conv_errors) + error= 1; } if (length > field_length) { length=field_length; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); error= 1; } + if (error) + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); memcpy(ptr+HA_KEY_BLOB_LENGTH,from,length); int2store(ptr, length); return error; @@ -4833,6 +4900,7 @@ void Field_blob::put_length(char *pos, uint32 length) int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) { + int error= 0; if (!length) { bzero(ptr,Field_blob::pack_length()); @@ -4849,9 +4917,12 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) if ((was_conversion= String::needs_conversion(length, cs, field_charset, ¬_used))) { - tmpstr.copy(from, length, cs, field_charset); + uint conv_errors; + tmpstr.copy(from, length, cs, field_charset, &conv_errors); from= tmpstr.ptr(); length= tmpstr.length(); + if (conv_errors) + error= 1; } copy_length= max_data_length(); @@ -4865,8 +4936,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) min(length, copy_length), copy_length); if (copy_length < length) - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); - + error= 1; Field_blob::store_length(copy_length); if (was_conversion || table->copy_blobs || copy_length <= MAX_FIELD_WIDTH) { // Must make a copy @@ -4878,6 +4948,8 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) } bmove(ptr+packlength,(char*) &from,sizeof(char*)); } + if (error) + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); return 0; } @@ -4939,10 +5011,10 @@ String *Field_blob::val_str(String *val_buffer __attribute__((unused)), int Field_blob::cmp(const char *a,uint32 a_length, const char *b, uint32 b_length) { - int diff=my_strnncoll(field_charset, - (const uchar*)a,min(a_length,b_length), - (const uchar*)b,min(a_length,b_length)); - return diff ? diff : (int) (a_length - b_length); + return field_charset->coll->strnncoll(field_charset, + (const uchar*)a, a_length, + (const uchar*)b, b_length, + 0); } @@ -5024,6 +5096,11 @@ void Field_blob::get_key_image(char *buff,uint length, } #endif /*HAVE_SPATIAL*/ + get_ptr(&blob); + uint char_length= length / cs->mbmaxlen; + char_length= my_charpos(cs, blob, blob + blob_length, char_length); + set_if_smaller(blob_length, char_length); + if ((uint32) length > blob_length) { /* @@ -5034,7 +5111,6 @@ void Field_blob::get_key_image(char *buff,uint length, length=(uint) blob_length; } int2store(buff,length); - get_ptr(&blob); memcpy(buff+HA_KEY_BLOB_LENGTH, blob, length); } @@ -5050,6 +5126,10 @@ int Field_blob::key_cmp(const byte *key_ptr, uint max_key_length) char *blob1; uint blob_length=get_length(ptr); memcpy_fixed(&blob1,ptr+packlength,sizeof(char*)); + CHARSET_INFO *cs= charset(); + uint char_length= max_key_length / cs->mbmaxlen; + char_length= my_charpos(cs, blob1, blob1+blob_length, char_length); + set_if_smaller(blob_length, char_length); return Field_blob::cmp(blob1,min(blob_length, max_key_length), (char*) key_ptr+HA_KEY_BLOB_LENGTH, uint2korr(key_ptr)); @@ -5458,7 +5538,7 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) /* Remove end space */ while (length > 0 && my_isspace(system_charset_info,from[length-1])) length--; - uint tmp=find_type(typelib, from, length, 0); + uint tmp=find_type2(typelib, from, length, field_charset); if (!tmp) { if (length < 6) // Can't be more than 99999 enums @@ -5636,8 +5716,8 @@ int Field_set::store(const char *from,uint length,CHARSET_INFO *cs) from= tmpstr.ptr(); length= tmpstr.length(); } - ulonglong tmp= find_set(typelib, from, length, ¬_used, ¬_used2, - &got_warning); + ulonglong tmp= find_set(typelib, from, length, field_charset, + ¬_used, ¬_used2, &got_warning); if (!tmp && length && length < 22) { /* This is for reading numbers with LOAD DATA INFILE */ @@ -5944,8 +6024,9 @@ Field *make_field(char *ptr, uint32 field_length, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); case FIELD_TYPE_TIMESTAMP: - return new Field_timestamp(ptr,field_length, - unireg_check, field_name, table, field_charset); + return new Field_timestamp(ptr,field_length, null_pos, null_bit, + unireg_check, field_name, table, + field_charset); case FIELD_TYPE_YEAR: return new Field_year(ptr,field_length,null_pos,null_bit, unireg_check, field_name, table); diff --git a/sql/field.h b/sql/field.h index 725e962ca07..50ea1450085 100644 --- a/sql/field.h +++ b/sql/field.h @@ -179,7 +179,14 @@ public: virtual void make_field(Send_field *)=0; virtual void sort_string(char *buff,uint length)=0; virtual bool optimize_range(uint idx, uint part); - virtual bool store_for_compare() { return 0; } + /* + This should be true for fields which, when compared with constant + items, can be casted to longlong. In this case we will at 'fix_fields' + stage cast the constant items to longlongs and at the execution stage + use field->val_int() for comparison. Used to optimize clauses like + 'a_column BETWEEN date_const, date_const'. + */ + virtual bool can_be_compared_as_longlong() const { return FALSE; } virtual void free() {} Field *new_field(MEM_ROOT *root, struct st_table *new_table) { @@ -568,7 +575,7 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return 8; } void sql_type(String &str) const; - bool store_for_compare() { return 1; } + bool can_be_compared_as_longlong() const { return TRUE; } uint32 max_length() { return 20; } field_cast_enum field_cast_type() { return FIELD_CAST_LONGLONG; } }; @@ -678,6 +685,7 @@ public: class Field_timestamp :public Field_str { public: Field_timestamp(char *ptr_arg, uint32 len_arg, + uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, CHARSET_INFO *cs); @@ -696,7 +704,7 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return 4; } void sql_type(String &str) const; - bool store_for_compare() { return 1; } + bool can_be_compared_as_longlong() const { return TRUE; } bool zero_pack() const { return 0; } void set_time(); virtual void set_default() @@ -707,8 +715,11 @@ public: else Field::set_default(); } - inline long get_timestamp() + /* Get TIMESTAMP field value as seconds since begging of Unix Epoch */ + inline long get_timestamp(my_bool *null_value) { + if ((*null_value= is_null())) + return 0; #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) return sint4korr(ptr); @@ -720,7 +731,7 @@ public: bool get_date(TIME *ltime,uint fuzzydate); bool get_time(TIME *ltime); field_cast_enum field_cast_type() { return FIELD_CAST_TIMESTAMP; } - void set_timestamp_offsets(); + timestamp_auto_set_type get_auto_set_type() const; }; @@ -742,7 +753,7 @@ public: String *val_str(String*,String *); bool send_binary(Protocol *protocol); void sql_type(String &str) const; - bool store_for_compare() { return 1; } + bool can_be_compared_as_longlong() const { return TRUE; } field_cast_enum field_cast_type() { return FIELD_CAST_YEAR; } }; @@ -774,7 +785,7 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return 4; } void sql_type(String &str) const; - bool store_for_compare() { return 1; } + bool can_be_compared_as_longlong() const { return TRUE; } bool zero_pack() const { return 1; } field_cast_enum field_cast_type() { return FIELD_CAST_DATE; } }; @@ -804,7 +815,7 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return 3; } void sql_type(String &str) const; - bool store_for_compare() { return 1; } + bool can_be_compared_as_longlong() const { return TRUE; } bool zero_pack() const { return 1; } bool get_date(TIME *ltime,uint fuzzydate); bool get_time(TIME *ltime); @@ -841,7 +852,7 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return 3; } void sql_type(String &str) const; - bool store_for_compare() { return 1; } + bool can_be_compared_as_longlong() const { return TRUE; } bool zero_pack() const { return 1; } field_cast_enum field_cast_type() { return FIELD_CAST_TIME; } }; @@ -877,7 +888,7 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return 8; } void sql_type(String &str) const; - bool store_for_compare() { return 1; } + bool can_be_compared_as_longlong() const { return TRUE; } bool zero_pack() const { return 1; } bool get_date(TIME *ltime,uint fuzzydate); bool get_time(TIME *ltime); @@ -918,7 +929,6 @@ public: void sort_string(char *buff,uint length); void sql_type(String &str) const; char *pack(char *to, const char *from, uint max_length=~(uint) 0); - char *pack_key(char *to, const char *from, uint max_length); const char *unpack(char* to, const char *from); int pack_cmp(const char *a,const char *b,uint key_length); int pack_cmp(const char *b,uint key_length); diff --git a/sql/field_conv.cc b/sql/field_conv.cc index d7993939092..890687fc925 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -164,7 +164,8 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions) /* Check if this is a special type, which will get a special walue - when set to NULL + when set to NULL (TIMESTAMP fields which allow setting to NULL + are handled by first check). */ if (field->type() == FIELD_TYPE_TIMESTAMP) { @@ -251,7 +252,8 @@ static void do_copy_timestamp(Copy_field *copy) { if (*copy->from_null_ptr & copy->from_bit) { - ((Field_timestamp*) copy->to_field)->set_time();// Same as set_field_to_null + /* Same as in set_field_to_null_with_conversions() */ + ((Field_timestamp*) copy->to_field)->set_time(); } else (copy->do_copy2)(copy); @@ -261,7 +263,11 @@ static void do_copy_timestamp(Copy_field *copy) static void do_copy_next_number(Copy_field *copy) { if (*copy->from_null_ptr & copy->from_bit) - copy->to_field->reset(); // Same as set_field_to_null + { + /* Same as in set_field_to_null_with_conversions() */ + copy->to_field->table->auto_increment_field_not_null= FALSE; + copy->to_field->reset(); + } else (copy->do_copy2)(copy); } @@ -436,18 +442,20 @@ void Copy_field::set(Field *to,Field *from,bool save) } } else - do_copy=do_copy_not_null; + { + if (to_field->type() == FIELD_TYPE_TIMESTAMP) + do_copy= do_copy_timestamp; // Automatic timestamp + else if (to_field == to_field->table->next_number_field) + do_copy= do_copy_next_number; + else + do_copy= do_copy_not_null; + } } else if (to_field->real_maybe_null()) { to_null_ptr= to->null_ptr; to_bit= to->null_bit; - if (to_field->type() == FIELD_TYPE_TIMESTAMP) - do_copy=do_copy_timestamp; // Automatic timestamp - else if (to_field == to_field->table->next_number_field) - do_copy=do_copy_next_number; - else - do_copy=do_copy_maybe_null; + do_copy= do_copy_maybe_null; } else do_copy=0; diff --git a/sql/filesort.cc b/sql/filesort.cc index ef8148616e5..3e56acefea9 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -179,7 +179,13 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, else #endif { - records=table->file->estimate_number_of_rows(); + records= table->file->estimate_rows_upper_bound(); + /* + If number of records is not known, use as much of sort buffer + as possible. + */ + if (records == HA_POS_ERROR) + records--; // we use 'records+1' below. selected_records_file= 0; } @@ -327,7 +333,7 @@ static char **make_char_array(register uint fields, uint length, myf my_flag) } /* make_char_array */ - /* Read all buffer pointers into memory */ +/* Read 'count' number of buffer pointers into memory */ static BUFFPEK *read_buffpek_from_file(IO_CACHE *buffpek_pointers, uint count) { @@ -348,8 +354,40 @@ static BUFFPEK *read_buffpek_from_file(IO_CACHE *buffpek_pointers, uint count) } - - /* Search after sort_keys and place them in a temp. file */ +/* + Search after sort_keys and write them into tempfile. + SYNOPSIS + find_all_keys() + param Sorting parameter + select Use this to get source data + sort_keys Array of pointers to sort key + addon buffers. + buffpek_pointers File to write BUFFPEKs describing sorted segments + in tempfile. + tempfile File to write sorted sequences of sortkeys to. + indexfile If !NULL, use it for source data (contains rowids) + + NOTE + Basic idea: + while (get_next_sortkey()) + { + if (no free space in sort_keys buffers) + { + sort sort_keys buffer; + dump sorted sequence to 'tempfile'; + dump BUFFPEK describing sequence location into 'buffpek_pointers'; + } + put sort key into 'sort_keys'; + } + if (sort_keys has some elements && dumped at least once) + sort-dump-dump as above; + else + don't sort, leave sort_keys array to be sorted by caller. + + All produced sequences are guaranteed to be non-empty. + RETURN + Number of records written on success. + HA_POS_ERROR on error. +*/ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, uchar **sort_keys, @@ -489,7 +527,25 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, } /* find_all_keys */ - /* Skriver en buffert med nycklar till filen */ +/* + Sort the buffer and write: + 1) the sorted sequence to tempfile + 2) a BUFFPEK describing the sorted sequence position to buffpek_pointers + (was: Skriver en buffert med nycklar till filen) + SYNOPSIS + write_keys() + param Sort parameters + sort_keys Array of pointers to keys to sort + count Number of elements in sort_keys array + buffpek_pointers One 'BUFFPEK' struct will be written into this file. + The BUFFPEK::{file_pos, count} will indicate where + the sorted data was stored. + tempfile The sorted sequence will be written into this file. + + RETURN + 0 OK + 1 Error +*/ static int write_keys(SORTPARAM *param, register uchar **sort_keys, uint count, @@ -854,7 +910,21 @@ void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length) /* - Merge buffers to one buffer + Merge buffers to one buffer + SYNOPSIS + merge_buffers() + param Sort parameter + from_file File with source data (BUFFPEKs point to this file) + to_file File to write the sorted result data. + sort_buffer Buffer for data to store up to MERGEBUFF2 sort keys. + lastbuff OUT Store here BUFFPEK describing data written to to_file + Fb First element in source BUFFPEKs array + Tb Last element in source BUFFPEKs array + flag + + RETURN + 0 - OK + other - error */ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, @@ -893,6 +963,9 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, strpos= (uchar*) sort_buffer; org_max_rows=max_rows= param->max_rows; + /* The following will fire if there is not enough space in sort_buffer */ + DBUG_ASSERT(maxcount!=0); + if (init_queue(&queue, (uint) (Tb-Fb)+1, offsetof(BUFFPEK,key), 0, (queue_compare) (cmp= get_ptr_compare(sort_length)), (void*) &sort_length)) diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc index bbe73ee8674..a5d0023b875 100644 --- a/sql/ha_berkeley.cc +++ b/sql/ha_berkeley.cc @@ -25,7 +25,7 @@ We will need an updated Berkeley DB version for this. - Killing threads that has got a 'deadlock' - SHOW TABLE STATUS should give more information about the table. - - Get a more accurate count of the number of rows (estimate_number_of_rows()). + - Get a more accurate count of the number of rows (estimate_rows_upper_bound()). We could store the found number of rows when the table is scanned and then increment the counter for each attempted write. - We will need to extend the manager thread to makes checkpoints at @@ -63,7 +63,7 @@ #define HA_BERKELEY_ROWS_IN_TABLE 10000 /* to get optimization right */ #define HA_BERKELEY_RANGE_COUNT 100 #define HA_BERKELEY_MAX_ROWS 10000000 /* Max rows in table */ -/* extra rows for estimate_number_of_rows() */ +/* extra rows for estimate_rows_upper_bound() */ #define HA_BERKELEY_EXTRA_ROWS 100 /* Bits for share->status */ @@ -90,7 +90,7 @@ const char *berkeley_lock_names[] = u_int32_t berkeley_lock_types[]= { DB_LOCK_DEFAULT, DB_LOCK_OLDEST, DB_LOCK_RANDOM }; TYPELIB berkeley_lock_typelib= {array_elements(berkeley_lock_names)-1,"", - berkeley_lock_names}; + berkeley_lock_names, NULL}; static void berkeley_print_error(const char *db_errpfx, char *buffer); static byte* bdb_get_key(BDB_SHARE *share,uint *length, @@ -856,8 +856,8 @@ int ha_berkeley::write_row(byte * record) DBUG_ENTER("write_row"); statistic_increment(table->in_use->status_var.ha_write_count, &LOCK_status); - if (table->timestamp_default_now) - update_timestamp(record+table->timestamp_default_now-1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) + table->timestamp_field->set_time(); if (table->next_number_field && record == table->record[0]) update_auto_increment(); if ((error=pack_row(&row, record,1))) @@ -1104,8 +1104,8 @@ int ha_berkeley::update_row(const byte * old_row, byte * new_row) LINT_INIT(error); statistic_increment(table->in_use->status_var.ha_update_count,&LOCK_status); - if (table->timestamp_on_update_now) - update_timestamp(new_row+table->timestamp_on_update_now-1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) + table->timestamp_field->set_time(); if (hidden_primary_key) { @@ -2566,7 +2566,7 @@ end: Used when sorting to allocate buffers and by the optimizer. */ -ha_rows ha_berkeley::estimate_number_of_rows() +ha_rows ha_berkeley::estimate_rows_upper_bound() { return share->rows + HA_BERKELEY_EXTRA_ROWS; } diff --git a/sql/ha_berkeley.h b/sql/ha_berkeley.h index d4fb4ca5fbb..e485b12bdb4 100644 --- a/sql/ha_berkeley.h +++ b/sql/ha_berkeley.h @@ -100,7 +100,7 @@ class ha_berkeley: public handler ulong table_flags(void) const { return int_table_flags; } uint max_supported_keys() const { return MAX_KEY-1; } uint extra_rec_buf_length() { return BDB_HIDDEN_PRIMARY_KEY_LENGTH; } - ha_rows estimate_number_of_rows(); + ha_rows estimate_rows_upper_bound(); const key_map *keys_to_use_for_scanning() { return &key_map_full; } bool has_transactions() { return 1;} diff --git a/sql/ha_heap.cc b/sql/ha_heap.cc index 6dc5e85e1d4..3bb8383e488 100644 --- a/sql/ha_heap.cc +++ b/sql/ha_heap.cc @@ -87,8 +87,8 @@ void ha_heap::set_keys_for_scanning(void) int ha_heap::write_row(byte * buf) { statistic_increment(current_thd->status_var.ha_write_count,&LOCK_status); - if (table->timestamp_default_now) - update_timestamp(buf+table->timestamp_default_now-1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) + table->timestamp_field->set_time(); if (table->next_number_field && buf == table->record[0]) update_auto_increment(); return heap_write(file,buf); @@ -97,8 +97,8 @@ int ha_heap::write_row(byte * buf) int ha_heap::update_row(const byte * old_data, byte * new_data) { statistic_increment(current_thd->status_var.ha_update_count,&LOCK_status); - if (table->timestamp_on_update_now) - update_timestamp(new_data+table->timestamp_on_update_now-1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) + table->timestamp_field->set_time(); return heap_update(file,old_data,new_data); } @@ -380,7 +380,8 @@ ha_rows ha_heap::records_in_range(uint inx, key_range *min_key, if (key->algorithm == HA_KEY_ALG_BTREE) return hp_rb_records_in_range(file, inx, min_key, max_key); - if (min_key->length != max_key->length || + if (!min_key || !max_key || + min_key->length != max_key->length || min_key->length != key->key_length || min_key->flag != HA_READ_KEY_EXACT || max_key->flag != HA_READ_AFTER_KEY) @@ -430,12 +431,8 @@ int ha_heap::create(const char *name, TABLE *table_arg, seg->type= field->key_type(); else { - if (!f_is_packed(flag) && - f_packtype(flag) == (int) FIELD_TYPE_DECIMAL && - !(field->charset() == &my_charset_bin)) - seg->type= (int) HA_KEYTYPE_TEXT; - else - seg->type= (int) HA_KEYTYPE_BINARY; + if ((seg->type = field->key_type()) != (int) HA_KEYTYPE_TEXT) + seg->type= HA_KEYTYPE_BINARY; } seg->start= (uint) key_part->offset; seg->length= (uint) key_part->length; diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 222e2b4ce8a..efd74a543c2 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -20,10 +20,9 @@ NOTE: You can only use noninlined InnoDB functions in this file, because we have disables the InnoDB inlining in this file. */ /* TODO list for the InnoDB handler in 4.1: - - Check if the query_id is now right also in prepared and executed stats - in build_template() - - Add multi-language char set support to CREATE TABLE and the comparison - of strings + - Remove the flag innodb_active_trans from thd and replace it with a + function call innodb_active_trans(thd), which looks at the InnoDB + trx struct state field - Find out what kind of problems the OS X case-insensitivity causes to table and database names; should we 'normalize' the names like we do in Windows? @@ -41,6 +40,7 @@ have disables the InnoDB inlining in this file. */ #include <hash.h> #include <myisampack.h> #include <mysys_err.h> +#include <my_sys.h> #define MAX_ULONG_BIT ((ulong) 1 << (sizeof(ulong)*8-1)) @@ -117,6 +117,9 @@ uint innobase_flush_log_at_trx_commit = 1; my_bool innobase_log_archive = FALSE;/* unused */ my_bool innobase_use_native_aio = FALSE; my_bool innobase_fast_shutdown = TRUE; +my_bool innobase_very_fast_shutdown = FALSE; /* this can be set to + 1 just prior calling + innobase_end() */ my_bool innobase_file_per_table = FALSE; my_bool innobase_locks_unsafe_for_binlog = FALSE; my_bool innobase_create_status_file = FALSE; @@ -299,7 +302,7 @@ convert_error_code_to_mysql( } else if (error == (int) DB_CANNOT_DROP_CONSTRAINT) { - return(HA_ERR_CANNOT_ADD_FOREIGN); /* TODO: This is a bit + return(HA_ERR_ROW_IS_REFERENCED); /* TODO: This is a bit misleading, a new MySQL error code should be introduced */ } else if (error == (int) DB_COL_APPEARS_TWICE_IN_INDEX) { @@ -426,6 +429,36 @@ innobase_mysql_print_thd( putc('\n', f); } +/********************************************************************** +Compares NUL-terminated UTF-8 strings case insensitively. + +NOTE that the exact prototype of this function has to be in +/innobase/dict/dict0dict.c! */ +extern "C" +int +innobase_strcasecmp( +/*================*/ + /* out: 0 if a=b, <0 if a<b, >1 if a>b */ + const char* a, /* in: first string to compare */ + const char* b) /* in: second string to compare */ +{ + return(my_strcasecmp(system_charset_info, a, b)); +} + +/********************************************************************** +Makes all characters in a NUL-terminated UTF-8 string lower case. + +NOTE that the exact prototype of this function has to be in +/innobase/dict/dict0dict.c! */ +extern "C" +void +innobase_casedn_str( +/*================*/ + char* a) /* in/out: string to put in lower case */ +{ + my_casedn_str(system_charset_info, a); +} + /************************************************************************* Creates a temporary file. */ extern "C" @@ -684,14 +717,7 @@ innobase_query_caching_of_table_permitted( separator between db and table */ norm_name[full_name_len] = '\0'; #ifdef __WIN__ - /* Put to lower case */ - - char* ptr = norm_name; - - while (*ptr != '\0') { - *ptr = tolower(*ptr); - ptr++; - } + innobase_casedn_str(norm_name); #endif /* The call of row_search_.. will start a new transaction if it is not yet started */ @@ -736,15 +762,35 @@ innobase_invalidate_query_cache( } /********************************************************************* -Get the quote character to be used in SQL identifiers. */ +Get the quote character to be used in SQL identifiers. +This definition must match the one in innobase/ut/ut0ut.c! */ extern "C" -char -mysql_get_identifier_quote_char(void) -/*=================================*/ +int +mysql_get_identifier_quote_char( +/*============================*/ /* out: quote character to be - used in SQL identifiers */ + used in SQL identifiers; EOF if none */ + trx_t* trx, /* in: transaction */ + const char* name, /* in: name to print */ + ulint namelen)/* in: length of name */ +{ + if (!trx || !trx->mysql_thd) { + return(EOF); + } + return(get_quote_char_for_identifier((THD*) trx->mysql_thd, + name, namelen)); +} + +/************************************************************************** +Obtain a pointer to the MySQL THD object, as in current_thd(). This +definition must match the one in sql/ha_innodb.cc! */ +extern "C" +void* +innobase_current_thd(void) +/*======================*/ + /* out: MySQL THD object */ { - return '`'; + return(current_thd); } /********************************************************************* @@ -782,6 +828,10 @@ ha_innobase::init_table_handle_for_HANDLER(void) trx_assign_read_view(prebuilt->trx); + /* Set the MySQL flag to mark that there is an active transaction */ + + current_thd->transaction.all.innodb_active_trans = 1; + /* We did the necessary inits in this function, no need to repeat them in row_search_for_mysql */ @@ -791,6 +841,7 @@ ha_innobase::init_table_handle_for_HANDLER(void) if the trx isolation level would have been specified as SERIALIZABLE */ prebuilt->select_lock_type = LOCK_NONE; + prebuilt->stored_select_lock_type = LOCK_NONE; /* Always fetch all columns in the index record */ @@ -829,7 +880,7 @@ innobase_init(void) Note that when using the embedded server, the datadirectory is not necessarily the current directory of this program. */ - if (mysql_embedded) { + if (mysqld_embedded) { default_path = mysql_real_data_home; fil_path_to_mysql_datadir = mysql_real_data_home; } else { @@ -967,7 +1018,7 @@ innobase_init(void) srv_max_n_open_files = (ulint) innobase_open_files; srv_innodb_status = (ibool) innobase_create_status_file; - srv_print_verbose_log = mysql_embedded ? 0 : 1; + srv_print_verbose_log = mysqld_embedded ? 0 : 1; /* Store the default charset-collation number of this MySQL installation */ @@ -1043,6 +1094,15 @@ innobase_end(void) #endif if (innodb_inited) { + if (innobase_very_fast_shutdown) { + srv_very_fast_shutdown = TRUE; + fprintf(stderr, +"InnoDB: MySQL has requested a very fast shutdown without flushing\n" +"InnoDB: the InnoDB buffer pool to data files. At the next mysqld startup\n" +"InnoDB: InnoDB will do a crash recovery!\n"); + + } + innodb_inited= 0; if (innobase_shutdown_for_mysql() != DB_SUCCESS) err= 1; @@ -1100,6 +1160,48 @@ innobase_commit_low( } /********************************************************************* +Creates an InnoDB transaction struct for the thd if it does not yet have one. +Starts a new InnoDB transaction if a transaction is not yet started. And +assigns a new snapshot for a consistent read if the transaction does not yet +have one. */ + +int +innobase_start_trx_and_assign_read_view( +/*====================================*/ + /* out: 0 */ + THD* thd) /* in: MySQL thread handle of the user for whom + the transaction should be committed */ +{ + trx_t* trx; + + DBUG_ENTER("innobase_start_trx_and_assign_read_view"); + + /* Create a new trx struct for thd, if it does not yet have one */ + + trx = check_trx_exists(thd); + + /* This is just to play safe: release a possible FIFO ticket and + search latch. Since we will reserve the kernel mutex, we have to + release the search system latch first to obey the latching order. */ + + innobase_release_stat_resources(trx); + + /* If the transaction is not started yet, start it */ + + trx_start_if_not_started_noninline(trx); + + /* Assign a read view if the transaction does not have it yet */ + + trx_assign_read_view(trx); + + /* Set the MySQL flag to mark that there is an active transaction */ + + current_thd->transaction.all.innodb_active_trans = 1; + + DBUG_RETURN(0); +} + +/********************************************************************* Commits a transaction in an InnoDB database or marks an SQL statement ended. */ @@ -1130,8 +1232,10 @@ innobase_commit( 1. ::external_lock(), 2. ::start_stmt(), - 3. innobase_query_caching_of_table_permitted(), and + 3. innobase_query_caching_of_table_permitted(), 4. innobase_savepoint(), + 5. ::init_table_handle_for_HANDLER(), + 6. innobase_start_trx_and_assign_read_view() and it is only set to 0 in a commit or a rollback. If it is 0 we know there cannot be resources to be freed and we could return immediately. @@ -1460,14 +1564,7 @@ normalize_table_name( norm_name[name_ptr - db_ptr - 1] = '/'; #ifdef __WIN__ - /* Put to lower case */ - - ptr = norm_name; - - while (*ptr != '\0') { - *ptr = tolower(*ptr); - ptr++; - } + innobase_casedn_str(norm_name); #endif } @@ -1485,12 +1582,14 @@ ha_innobase::open( { dict_table_t* ib_table; char norm_name[1000]; + THD* thd; DBUG_ENTER("ha_innobase::open"); UT_NOT_USED(mode); UT_NOT_USED(test_if_locked); + thd = current_thd; normalize_table_name(norm_name, name); user_thd = NULL; @@ -1540,7 +1639,7 @@ ha_innobase::open( DBUG_RETURN(1); } - if (ib_table->ibd_file_missing && !current_thd->tablespace_op) { + if (ib_table->ibd_file_missing && !thd->tablespace_op) { ut_print_timestamp(stderr); fprintf(stderr, " InnoDB error:\n" "MySQL is trying to open a table handle but the .ibd file for\n" @@ -2106,11 +2205,6 @@ build_template( templ = prebuilt->mysql_template + n_requested_fields; field = table->field[i]; - /* TODO: Check if the query_id is now right also in prepared - and executed SQL statements. Previously, MySQL-4.1 failed to - update field->query_id so that the formula - thd->query_id == field->query_id did not work. */ - ibool index_contains_field= dict_index_contains_col_or_prefix(index, i); @@ -2221,8 +2315,8 @@ ha_innobase::write_row( statistic_increment(current_thd->status_var.ha_write_count, &LOCK_status); - if (table->timestamp_default_now) - update_timestamp(record + table->timestamp_default_now - 1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) + table->timestamp_field->set_time(); if (last_query_id != user_thd->query_id) { prebuilt->sql_stat_start = TRUE; @@ -2504,8 +2598,8 @@ ha_innobase::update_row( ut_ad(prebuilt->trx == (trx_t*) current_thd->transaction.all.innobase_tid); - if (table->timestamp_on_update_now) - update_timestamp(new_row + table->timestamp_on_update_now - 1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) + table->timestamp_field->set_time(); if (last_query_id != user_thd->query_id) { prebuilt->sql_stat_start = TRUE; @@ -2778,7 +2872,7 @@ ha_innobase::index_read( (ulint)upd_and_key_val_buff_len, index, (byte*) key_ptr, - (ulint) key_len); + (ulint) key_len, prebuilt->trx); } else { /* We position the cursor to the last or the first entry in the index */ @@ -3068,7 +3162,7 @@ ha_innobase::index_last( { int error; - DBUG_ENTER("index_first"); + DBUG_ENTER("index_last"); statistic_increment(current_thd->status_var.ha_read_last_count, &LOCK_status); @@ -3261,7 +3355,15 @@ create_table_def( trx_t* trx, /* in: InnoDB transaction handle */ TABLE* form, /* in: information on table columns and indexes */ - const char* table_name) /* in: table name */ + const char* table_name, /* in: table name */ + const char* path_of_temp_table)/* in: if this is a table explicitly + created by the user with the + TEMPORARY keyword, then this + parameter is the dir path where the + table should be placed if we create + an .ibd file for it (no .ibd extension + in the path, though); otherwise this + is NULL */ { Field* field; dict_table_t* table; @@ -3284,6 +3386,11 @@ create_table_def( table = dict_mem_table_create((char*) table_name, 0, n_cols); + if (path_of_temp_table) { + table->dir_path_of_temp_table = + mem_heap_strdup(table->heap, path_of_temp_table); + } + for (i = 0; i < n_cols; i++) { field = form->field[i]; @@ -3364,8 +3471,7 @@ create_index( ind_type = 0; - if (key_num == form->primary_key) - { + if (key_num == form->primary_key) { ind_type = ind_type | DICT_CLUSTERED; } @@ -3392,9 +3498,9 @@ create_index( field = form->field[j]; - if (0 == ut_cmp_in_lower_case( - (char*)field->field_name, - (char*)key_part->field->field_name)) { + if (0 == innobase_strcasecmp( + field->field_name, + key_part->field->field_name)) { /* Found the corresponding column */ break; @@ -3425,10 +3531,6 @@ create_index( prefix_len = 0; } - if (prefix_len >= DICT_MAX_COL_PREFIX_LEN) { - DBUG_RETURN(-1); - } - /* We assume all fields should be sorted in ascending order, hence the '0': */ @@ -3534,7 +3636,7 @@ ha_innobase::create( srv_lower_case_table_names = FALSE; } - fn_format(name2, name, "", "",2); // Remove the .frm extension + fn_format(name2, name, "", "", 2); // Remove the .frm extension normalize_table_name(norm_name, name2); @@ -3546,8 +3648,13 @@ ha_innobase::create( /* Create the table definition in InnoDB */ - error = create_table_def(trx, form, norm_name); - + if (create_info->options & HA_LEX_CREATE_TMP_TABLE) { + + error = create_table_def(trx, form, norm_name, name2); + } else { + error = create_table_def(trx, form, norm_name, NULL); + } + if (error) { innobase_commit_low(trx); @@ -3622,8 +3729,8 @@ ha_innobase::create( } if (current_thd->query != NULL) { - LEX_STRING q; + if (thd->convert_string(&q, system_charset_info, current_thd->query, current_thd->query_length, @@ -3835,7 +3942,7 @@ innobase_drop_database( namebuf[len] = '/'; namebuf[len + 1] = '\0'; #ifdef __WIN__ - my_casedn_str(system_charset_info, namebuf); + innobase_casedn_str(namebuf); #endif trx = trx_allocate_for_mysql(); trx->mysql_thd = current_thd; @@ -3997,14 +4104,16 @@ ha_innobase::records_in_range( index, (byte*) (min_key ? min_key->key : (const mysql_byte*) 0), - (ulint) (min_key ? min_key->length : 0)); + (ulint) (min_key ? min_key->length : 0), + prebuilt->trx); row_sel_convert_mysql_key_to_innobase( range_end, (byte*) key_val_buff2, buff2_len, index, (byte*) (max_key ? max_key->key : (const mysql_byte*) 0), - (ulint) (max_key ? max_key->length : 0)); + (ulint) (max_key ? max_key->length : 0), + prebuilt->trx); mode1 = convert_search_mode_to_innobase(min_key ? min_key->flag : HA_READ_KEY_EXACT); @@ -4038,7 +4147,7 @@ Gives an UPPER BOUND to the number of rows in a table. This is used in filesort.cc. */ ha_rows -ha_innobase::estimate_number_of_rows(void) +ha_innobase::estimate_rows_upper_bound(void) /*======================================*/ /* out: upper bound of rows */ { @@ -4047,7 +4156,7 @@ ha_innobase::estimate_number_of_rows(void) ulonglong estimate; ulonglong local_data_file_length; - DBUG_ENTER("estimate_number_of_rows"); + DBUG_ENTER("estimate_rows_upper_bound"); /* We do not know if MySQL can call this function before calling external_lock(). To be safe, update the thd of the current table @@ -4127,7 +4236,7 @@ ha_innobase::read_time( time_for_scan = scan_time(); - if ((total_rows = estimate_number_of_rows()) < rows) + if ((total_rows = estimate_rows_upper_bound()) < rows) return time_for_scan; return (ranges + (double) rows / (double) total_rows * time_for_scan); @@ -4148,6 +4257,8 @@ ha_innobase::info( ha_rows rec_per_key; ulong j; ulong i; + char path[FN_REFLEN]; + os_file_stat_t stat_info; DBUG_ENTER("info"); @@ -4157,7 +4268,7 @@ ha_innobase::info( if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) { - return; + DBUG_VOID_RETURN; } /* We do not know if MySQL can call this function before calling @@ -4185,6 +4296,26 @@ ha_innobase::info( prebuilt->trx->op_info = (char*) "returning various info to MySQL"; + + if (ib_table->space != 0) { + my_snprintf(path, sizeof(path), "%s/%s%s", + mysql_data_home, ib_table->name, + ".ibd"); + unpack_filename(path,path); + } else { + my_snprintf(path, sizeof(path), "%s/%s%s", + mysql_data_home, ib_table->name, + reg_ext); + + unpack_filename(path,path); + } + + /* Note that we do not know the access time of the table, + nor the CHECK TABLE time, nor the UPDATE or INSERT time. */ + + if (os_file_get_status(path,&stat_info)) { + create_time = stat_info.ctime; + } } if (flag & HA_STATUS_VARIABLE) { @@ -4391,7 +4522,8 @@ ha_innobase::update_table_comment( (ulong) fsp_get_available_space_in_free_extents( prebuilt->table->space)); - dict_print_info_on_foreign_keys(FALSE, file, prebuilt->table); + dict_print_info_on_foreign_keys(FALSE, file, + prebuilt->trx, prebuilt->table); flen = ftell(file); if(length + flen + 3 > 64000) { flen = 64000 - 3 - length; @@ -4457,7 +4589,8 @@ ha_innobase::get_foreign_key_create_info(void) trx_search_latch_release_if_reserved(prebuilt->trx); /* output the data to a temporary file */ - dict_print_info_on_foreign_keys(TRUE, file, prebuilt->table); + dict_print_info_on_foreign_keys(TRUE, file, + prebuilt->trx, prebuilt->table); prebuilt->trx->op_info = (char*)""; flen = ftell(file); @@ -4632,23 +4765,41 @@ ha_innobase::start_stmt( prepared for an update of a row */ prebuilt->select_lock_type = LOCK_X; - } else { - if (thd->lex->sql_command == SQLCOM_SELECT - && thd->lex->lock_option == TL_READ) { - - /* For other than temporary tables, we obtain - no lock for consistent read (plain SELECT) */ - - prebuilt->select_lock_type = LOCK_NONE; - } else { - /* Not a consistent read: use LOCK_X as the - select_lock_type value (TODO: how could we know - whether it should be LOCK_S, LOCK_X, or LOCK_NONE?) */ - - prebuilt->select_lock_type = LOCK_X; - } - } + } else { + if (trx->isolation_level != TRX_ISO_SERIALIZABLE + && thd->lex->sql_command == SQLCOM_SELECT + && thd->lex->lock_option == TL_READ) { + /* For other than temporary tables, we obtain + no lock for consistent read (plain SELECT). */ + + prebuilt->select_lock_type = LOCK_NONE; + } else { + /* Not a consistent read: restore the + select_lock_type value. The value of + stored_select_lock_type was decided in: + 1) ::store_lock(), + 2) ::external_lock(), and + 3) ::init_table_handle_for_HANDLER(). */ + + prebuilt->select_lock_type = + prebuilt->stored_select_lock_type; + } + + if (prebuilt->stored_select_lock_type != LOCK_S + && prebuilt->stored_select_lock_type != LOCK_X) { + fprintf(stderr, +"InnoDB: Error: stored_select_lock_type is %lu inside ::start_stmt()!\n", + prebuilt->stored_select_lock_type); + + /* Set the value to LOCK_X: this is just fault + tolerance, we do not know what the correct value + should be! */ + + prebuilt->select_lock_type = LOCK_X; + } + } + /* Set the MySQL flag to mark that there is an active transaction */ thd->transaction.all.innodb_active_trans = 1; @@ -4723,6 +4874,7 @@ ha_innobase::external_lock( /* If this is a SELECT, then it is in UPDATE TABLE ... or SELECT ... FOR UPDATE */ prebuilt->select_lock_type = LOCK_X; + prebuilt->stored_select_lock_type = LOCK_X; } if (lock_type != F_UNLCK) { @@ -4758,7 +4910,8 @@ ha_innobase::external_lock( } if (prebuilt->select_lock_type != LOCK_NONE) { - if (thd->in_lock_tables) { + if (thd->in_lock_tables && + thd->variables.innodb_table_locks) { ulint error; error = row_lock_table_for_mysql(prebuilt); @@ -4975,14 +5128,22 @@ ha_innobase::store_lock( { row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; - if (lock_type == TL_READ_WITH_SHARED_LOCKS || + if ((lock_type == TL_READ && thd->in_lock_tables) || + (lock_type == TL_READ_HIGH_PRIORITY && thd->in_lock_tables) || + lock_type == TL_READ_WITH_SHARED_LOCKS || lock_type == TL_READ_NO_INSERT) { - /* This is a SELECT ... IN SHARE MODE, or - we are doing a complex SQL statement like + /* The OR cases above are in this order: + 1) MySQL is doing LOCK TABLES ... READ LOCAL, or + 2) (we do not know when TL_READ_HIGH_PRIORITY is used), or + 3) this is a SELECT ... IN SHARE MODE, or + 4) we are doing a complex SQL statement like INSERT INTO ... SELECT ... and the logical logging (MySQL - binlog) requires the use of a locking read */ + binlog) requires the use of a locking read, or + MySQL is doing LOCK TABLES ... READ. */ prebuilt->select_lock_type = LOCK_S; + prebuilt->stored_select_lock_type = LOCK_S; + } else if (lock_type != TL_IGNORE) { /* In ha_berkeley.cc there is a comment that MySQL @@ -4993,6 +5154,7 @@ ha_innobase::store_lock( here even if this would be SELECT ... FOR UPDATE */ prebuilt->select_lock_type = LOCK_NONE; + prebuilt->stored_select_lock_type = LOCK_NONE; } if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) { @@ -5155,14 +5317,13 @@ ha_innobase::get_auto_increment() } /*********************************************************************** -This function stores binlog offset and flushes logs */ +This function stores the binlog offset and flushes logs. */ void innobase_store_binlog_offset_and_flush_log( -/*=============================*/ +/*=======================================*/ char *binlog_name, /* in: binlog name */ - longlong offset /* in: binlog offset */ -) + longlong offset) /* in: binlog offset */ { mtr_t mtr; @@ -5172,7 +5333,7 @@ innobase_store_binlog_offset_and_flush_log( mtr_start_noninline(&mtr); /* Update the latest MySQL binlog name and offset info - in trx sys header */ + in trx sys header */ trx_sys_update_mysql_binlog_offset( binlog_name, @@ -5235,18 +5396,121 @@ ha_innobase::cmp_ref( return 0; } -char *ha_innobase::get_mysql_bin_log_name() +char* +ha_innobase::get_mysql_bin_log_name() { - return trx_sys_mysql_bin_log_name; + return(trx_sys_mysql_bin_log_name); } -ulonglong ha_innobase::get_mysql_bin_log_pos() +ulonglong +ha_innobase::get_mysql_bin_log_pos() { - /* - trx... is ib_longlong, which is a typedef for a 64-bit integer (__int64 or - longlong) so it's ok to cast it to ulonglong. - */ - return trx_sys_mysql_bin_log_pos; + /* trx... is ib_longlong, which is a typedef for a 64-bit integer + (__int64 or longlong) so it's ok to cast it to ulonglong. */ + + return(trx_sys_mysql_bin_log_pos); +} + +extern "C" { +/********************************************************************** +This function is used to find the storage length in bytes of the first n +characters for prefix indexes using a multibyte character set. The function +finds charset information and returns length of prefix_len characters in the +index field in bytes. + +NOTE: the prototype of this function is copied to data0type.c! If you change +this function, you MUST change also data0type.c! */ + +ulint +innobase_get_at_most_n_mbchars( +/*===========================*/ + /* out: number of bytes occupied by the first + n characters */ + ulint charset_id, /* in: character set id */ + ulint prefix_len, /* in: prefix length in bytes of the index + (this has to be divided by mbmaxlen to get the + number of CHARACTERS n in the prefix) */ + ulint data_len, /* in: length of the string in bytes */ + const char* str) /* in: character string */ +{ + ulint char_length; /* character length in bytes */ + ulint n_chars; /* number of characters in prefix */ + CHARSET_INFO* charset; /* charset used in the field */ + + charset = get_charset(charset_id, MYF(MY_WME)); + + ut_ad(charset); + ut_ad(charset->mbmaxlen); + + /* Calculate how many characters at most the prefix index contains */ + + n_chars = prefix_len / charset->mbmaxlen; + + /* If the charset is multi-byte, then we must find the length of the + first at most n chars in the string. If the string contains less + characters than n, then we return the length to the end of the last + character. */ + + if (charset->mbmaxlen > 1) { + /* my_charpos() returns the byte length of the first n_chars + characters, or a value bigger than the length of str, if + there were not enough full characters in str. + + Why does the code below work: + Suppose that we are looking for n UTF-8 characters. + + 1) If the string is long enough, then the prefix contains at + least n complete UTF-8 characters + maybe some extra + characters + an incomplete UTF-8 character. No problem in + this case. The function returns the pointer to the + end of the nth character. + + 2) If the string is not long enough, then the string contains + the complete value of a column, that is, only complete UTF-8 + characters, and we can store in the column prefix index the + whole string. */ + + char_length = my_charpos(charset, str, + str + data_len, n_chars); + if (char_length > data_len) { + char_length = data_len; + } + } else { + if (data_len < prefix_len) { + char_length = data_len; + } else { + char_length = prefix_len; + } + } + + return(char_length); +} +} + +extern "C" { +/********************************************************************** +This function returns true if SQL-query in the current thread +is either REPLACE or LOAD DATA INFILE REPLACE. +NOTE that /mysql/innobase/row/row0ins.c must contain the +prototype for this function ! */ + +ibool +innobase_query_is_replace(void) +/*===========================*/ +{ + THD* thd; + + thd = (THD *)innobase_current_thd(); + + if ( thd->lex->sql_command == SQLCOM_REPLACE || + thd->lex->sql_command == SQLCOM_REPLACE_SELECT || + ( thd->lex->sql_command == SQLCOM_LOAD && + thd->lex->duplicates == DUP_REPLACE )) { + return true; + } else { + return false; + } +} } #endif /* HAVE_INNOBASE_DB */ diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index fa3e9f012e8..57e136a8fba 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -150,7 +150,7 @@ class ha_innobase: public handler void position(byte *record); ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key); - ha_rows estimate_number_of_rows(); + ha_rows estimate_rows_upper_bound(); int create(const char *name, register TABLE *form, HA_CREATE_INFO *create_info); @@ -192,8 +192,16 @@ extern my_bool innobase_log_archive, innobase_use_native_aio, innobase_fast_shutdown, innobase_file_per_table, innobase_locks_unsafe_for_binlog, innobase_create_status_file; +extern my_bool innobase_very_fast_shutdown; /* set this to 1 just before + calling innobase_end() if you want + InnoDB to shut down without + flushing the buffer pool: this + is equivalent to a 'crash' */ extern "C" { extern ulong srv_max_buf_pool_modified_pct; +extern ulong srv_max_purge_lag; +extern ulong srv_auto_extend_increment; +extern ulong srv_max_purge_lag; } extern TYPELIB innobase_lock_typelib; @@ -229,3 +237,5 @@ my_bool innobase_query_caching_of_table_permitted(THD* thd, char* full_name, void innobase_release_temporary_latches(void* innobase_tid); void innobase_store_binlog_offset_and_flush_log(char *binlog_name,longlong offset); + +int innobase_start_trx_and_assign_read_view(THD* thd); diff --git a/sql/ha_isam.cc b/sql/ha_isam.cc index 201230c8aab..d703df7d2e3 100644 --- a/sql/ha_isam.cc +++ b/sql/ha_isam.cc @@ -70,8 +70,8 @@ uint ha_isam::min_record_length(uint options) const int ha_isam::write_row(byte * buf) { statistic_increment(current_thd->status_var.ha_write_count, &LOCK_status); - if (table->timestamp_default_now) - update_timestamp(buf+table->timestamp_default_now-1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) + table->timestamp_field->set_time(); if (table->next_number_field && buf == table->record[0]) update_auto_increment(); return !nisam_write(file,buf) ? 0 : my_errno ? my_errno : -1; @@ -80,8 +80,8 @@ int ha_isam::write_row(byte * buf) int ha_isam::update_row(const byte * old_data, byte * new_data) { statistic_increment(current_thd->status_var.ha_update_count, &LOCK_status); - if (table->timestamp_on_update_now) - update_timestamp(new_data+table->timestamp_on_update_now-1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) + table->timestamp_field->set_time(); return !nisam_update(file,old_data,new_data) ? 0 : my_errno ? my_errno : -1; } diff --git a/sql/ha_isammrg.cc b/sql/ha_isammrg.cc index 19983e4b257..5a070087724 100644 --- a/sql/ha_isammrg.cc +++ b/sql/ha_isammrg.cc @@ -78,8 +78,8 @@ int ha_isammrg::write_row(byte * buf) int ha_isammrg::update_row(const byte * old_data, byte * new_data) { statistic_increment(current_thd->status_var.ha_update_count, &LOCK_status); - if (table->timestamp_on_update_now) - update_timestamp(new_data+table->timestamp_on_update_now-1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) + table->timestamp_field->set_time(); return !mrg_update(file,old_data,new_data) ? 0 : my_errno ? my_errno : -1; } diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index a738e535f14..7482c6d5fa8 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -37,7 +37,7 @@ ulong myisam_recover_options= HA_RECOVER_NONE; const char *myisam_recover_names[] = { "DEFAULT", "BACKUP", "FORCE", "QUICK", NullS}; TYPELIB myisam_recover_typelib= {array_elements(myisam_recover_names)-1,"", - myisam_recover_names}; + myisam_recover_names, NULL}; /***************************************************************************** @@ -252,8 +252,8 @@ int ha_myisam::write_row(byte * buf) statistic_increment(current_thd->status_var.ha_write_count,&LOCK_status); /* If we have a timestamp column, update it to the current time */ - if (table->timestamp_default_now) - update_timestamp(buf+table->timestamp_default_now-1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) + table->timestamp_field->set_time(); /* If we have an auto_increment column and we are writing a changed row @@ -510,16 +510,16 @@ int ha_myisam::repair(THD* thd, HA_CHECK_OPT *check_opt) (uint) (T_RETRY_WITHOUT_QUICK | T_QUICK))) { param.testflag&= ~T_RETRY_WITHOUT_QUICK; - sql_print_error("Note: Retrying repair of: '%s' without quick", - table->path); + sql_print_information("Retrying repair of: '%s' without quick", + table->path); continue; } param.testflag&= ~T_QUICK; if ((param.testflag & T_REP_BY_SORT)) { param.testflag= (param.testflag & ~T_REP_BY_SORT) | T_REP; - sql_print_error("Note: Retrying repair of: '%s' with keycache", - table->path); + sql_print_information("Retrying repair of: '%s' with keycache", + table->path); continue; } break; @@ -528,16 +528,17 @@ int ha_myisam::repair(THD* thd, HA_CHECK_OPT *check_opt) !(check_opt->flags & T_VERY_SILENT)) { char llbuff[22],llbuff2[22]; - sql_print_error("Note: Found %s of %s rows when repairing '%s'", - llstr(file->state->records, llbuff), - llstr(start_records, llbuff2), - table->path); + sql_print_information("Found %s of %s rows when repairing '%s'", + llstr(file->state->records, llbuff), + llstr(start_records, llbuff2), + table->path); } return error; } int ha_myisam::optimize(THD* thd, HA_CHECK_OPT *check_opt) { + int error; if (!file) return HA_ADMIN_INTERNAL_ERROR; MI_CHECK param; @@ -547,7 +548,14 @@ int ha_myisam::optimize(THD* thd, HA_CHECK_OPT *check_opt) param.testflag = (check_opt->flags | T_SILENT | T_FORCE_CREATE | T_REP_BY_SORT | T_STATISTICS | T_SORT_INDEX); param.sort_buffer_length= check_opt->sort_buffer_size; - return repair(thd,param,1); + if ((error= repair(thd,param,1)) && param.retry_repair) + { + sql_print_warning("Warning: Optimize table got errno %d, retrying", + my_errno); + param.testflag&= ~T_REP_BY_SORT; + error= repair(thd,param,1); + } + return error; } @@ -914,7 +922,13 @@ int ha_myisam::enable_indexes(uint mode) param.myf_rw&= ~MY_WAIT_IF_FULL; param.sort_buffer_length= thd->variables.myisam_sort_buff_size; param.tmpdir=&mysql_tmpdir_list; - error=repair(thd,param,0) != HA_ADMIN_OK; + if ((error= (repair(thd,param,0) != HA_ADMIN_OK)) && param.retry_repair) + { + sql_print_warning("Warning: Enabling keys got errno %d, retrying", + my_errno); + param.testflag&= ~(T_REP_BY_SORT | T_QUICK); + error= (repair(thd,param,0) != HA_ADMIN_OK); + } info(HA_STATUS_CONST); thd->proc_info=save_proc_info; } @@ -1035,7 +1049,7 @@ bool ha_myisam::check_and_repair(THD *thd) // Don't use quick if deleted rows if (!file->state->del && (myisam_recover_options & HA_RECOVER_QUICK)) check_opt.flags|=T_QUICK; - sql_print_error("Warning: Checking table: '%s'",table->path); + sql_print_warning("Checking table: '%s'",table->path); old_query= thd->query; old_query_length= thd->query_length; @@ -1046,7 +1060,7 @@ bool ha_myisam::check_and_repair(THD *thd) if ((marked_crashed= mi_is_crashed(file)) || check(thd, &check_opt)) { - sql_print_error("Warning: Recovering table: '%s'",table->path); + sql_print_warning("Recovering table: '%s'",table->path); check_opt.flags= ((myisam_recover_options & HA_RECOVER_BACKUP ? T_BACKUP_DATA : 0) | (marked_crashed ? 0 : T_QUICK) | @@ -1071,8 +1085,8 @@ bool ha_myisam::is_crashed() const int ha_myisam::update_row(const byte * old_data, byte * new_data) { statistic_increment(current_thd->status_var.ha_update_count,&LOCK_status); - if (table->timestamp_on_update_now) - update_timestamp(new_data+table->timestamp_on_update_now-1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) + table->timestamp_field->set_time(); return mi_update(file,old_data,new_data); } @@ -1283,6 +1297,7 @@ int ha_myisam::delete_table(const char *name) return mi_delete_table(name); } + int ha_myisam::external_lock(THD *thd, int lock_type) { return mi_lock_database(file, !table->tmp_table ? @@ -1290,7 +1305,6 @@ int ha_myisam::external_lock(THD *thd, int lock_type) F_UNLCK : F_EXTRA_LCK)); } - THR_LOCK_DATA **ha_myisam::store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type) diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index 29a31571380..903dd9dec2f 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -82,8 +82,8 @@ int ha_myisammrg::close(void) int ha_myisammrg::write_row(byte * buf) { statistic_increment(current_thd->status_var.ha_write_count,&LOCK_status); - if (table->timestamp_default_now) - update_timestamp(buf+table->timestamp_default_now-1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) + table->timestamp_field->set_time(); if (table->next_number_field && buf == table->record[0]) update_auto_increment(); return myrg_write(file,buf); @@ -92,8 +92,8 @@ int ha_myisammrg::write_row(byte * buf) int ha_myisammrg::update_row(const byte * old_data, byte * new_data) { statistic_increment(current_thd->status_var.ha_update_count,&LOCK_status); - if (table->timestamp_on_update_now) - update_timestamp(new_data+table->timestamp_on_update_now); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) + table->timestamp_field->set_time(); return myrg_update(file,old_data,new_data); } diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 1837500d8f7..7878684718c 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -20,7 +20,6 @@ NDB Cluster */ - #ifdef __GNUC__ #pragma implementation // gcc: Class implementation #endif @@ -33,9 +32,6 @@ #include <ndbapi/NdbApi.hpp> #include <ndbapi/NdbScanFilter.hpp> -#define USE_DISCOVER_ON_STARTUP -//#define USE_NDB_POOL - // Default value for parallelism static const int parallelism= 240; @@ -49,11 +45,13 @@ static const ha_rows autoincrement_prefetch= 32; // connectstring to cluster if given by mysqld const char *ndbcluster_connectstring= 0; +static const char *ha_ndb_ext=".ndb"; + #define NDB_HIDDEN_PRIMARY_KEY_LENGTH 8 #define ERR_PRINT(err) \ - DBUG_PRINT("error", ("Error: %d message: %s", err.code, err.message)) + DBUG_PRINT("error", ("%d message: %s", err.code, err.message)) #define ERR_RETURN(err) \ { \ @@ -63,13 +61,14 @@ const char *ndbcluster_connectstring= 0; // Typedefs for long names typedef NdbDictionary::Column NDBCOL; -typedef NdbDictionary::Table NDBTAB; +typedef NdbDictionary::Table NDBTAB; typedef NdbDictionary::Index NDBINDEX; typedef NdbDictionary::Dictionary NDBDICT; -bool ndbcluster_inited= false; +bool ndbcluster_inited= FALSE; static Ndb* g_ndb= NULL; +static Ndb_cluster_connection* g_ndb_cluster_connection= NULL; // Handler synchronization pthread_mutex_t ndbcluster_mutex; @@ -86,6 +85,16 @@ static int packfrm(const void *data, uint len, const void **pack_data, uint *pac static int unpackfrm(const void **data, uint *len, const void* pack_data); +static int ndb_get_table_statistics(Ndb*, const char *, + Uint64* rows, Uint64* commits); + + +/* + Dummy buffer to read zero pack_length fields + which are mapped to 1 char +*/ +static byte dummy_buf[1]; + /* Error handling functions */ @@ -103,7 +112,9 @@ static const err_code_mapping err_map[]= { 893, HA_ERR_FOUND_DUPP_UNIQUE }, { 721, HA_ERR_TABLE_EXIST }, { 4244, HA_ERR_TABLE_EXIST }, - { 241, HA_ERR_OLD_METADATA }, + + { 709, HA_ERR_NO_SUCH_TABLE }, + { 284, HA_ERR_NO_SUCH_TABLE }, { 266, HA_ERR_LOCK_WAIT_TIMEOUT }, { 274, HA_ERR_LOCK_WAIT_TIMEOUT }, @@ -118,6 +129,8 @@ static const err_code_mapping err_map[]= { 827, HA_ERR_RECORD_FILE_FULL }, { 832, HA_ERR_RECORD_FILE_FULL }, + { 0, 1 }, + { -1, -1 } }; @@ -127,13 +140,158 @@ static int ndb_to_mysql_error(const NdbError *err) uint i; for (i=0 ; err_map[i].ndb_err != err->code ; i++) { - if (err_map[i].my_err == -1) + if (err_map[i].my_err == -1){ + // Push the NDB error message as warning + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_GET_ERRMSG, ER(ER_GET_ERRMSG), + err->code, err->message, "NDB"); return err->code; + } } return err_map[i].my_err; } + +inline +int execute_no_commit(ha_ndbcluster *h, NdbConnection *trans) +{ + int m_batch_execute= 0; +#ifdef NOT_USED + if (m_batch_execute) + return 0; +#endif + return trans->execute(NoCommit,AbortOnError,1); +} + +inline +int execute_commit(ha_ndbcluster *h, NdbConnection *trans) +{ + int m_batch_execute= 0; +#ifdef NOT_USED + if (m_batch_execute) + return 0; +#endif + return trans->execute(Commit,AbortOnError,1); +} + +inline +int execute_no_commit_ie(ha_ndbcluster *h, NdbConnection *trans) +{ + int m_batch_execute= 0; +#ifdef NOT_USED + if (m_batch_execute) + return 0; +#endif + return trans->execute(NoCommit,IgnoreError,1); +} + +/* + Place holder for ha_ndbcluster thread specific data +*/ + +Thd_ndb::Thd_ndb() +{ + ndb= new Ndb(g_ndb_cluster_connection, ""); + lock_count= 0; + count= 0; + error= 0; +} + +Thd_ndb::~Thd_ndb() +{ + if (ndb) + delete ndb; +} + +/* + * manage uncommitted insert/deletes during transactio to get records correct + */ + +struct Ndb_table_local_info { + int no_uncommitted_rows_count; + ulong last_count; + ha_rows records; +}; + +void ha_ndbcluster::set_rec_per_key() +{ + DBUG_ENTER("ha_ndbcluster::get_status_const"); + for (uint i=0 ; i < table->keys ; i++) + { + table->key_info[i].rec_per_key[table->key_info[i].key_parts-1]= 1; + } + DBUG_VOID_RETURN; +} + +void ha_ndbcluster::records_update() +{ + DBUG_ENTER("ha_ndbcluster::records_update"); + struct Ndb_table_local_info *info= (struct Ndb_table_local_info *)m_table_info; + DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d", + ((const NDBTAB *)m_table)->getTableId(), + info->no_uncommitted_rows_count)); + // if (info->records == ~(ha_rows)0) + { + Uint64 rows; + if(ndb_get_table_statistics(m_ndb, m_tabname, &rows, 0) == 0){ + info->records= rows; + } + } + { + THD *thd= current_thd; + if (((Thd_ndb*)(thd->transaction.thd_ndb))->error) + info->no_uncommitted_rows_count= 0; + } + records= info->records+ info->no_uncommitted_rows_count; + DBUG_VOID_RETURN; +} + +void ha_ndbcluster::no_uncommitted_rows_execute_failure() +{ + DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_execute_failure"); + THD *thd= current_thd; + ((Thd_ndb*)(thd->transaction.thd_ndb))->error= 1; + DBUG_VOID_RETURN; +} + +void ha_ndbcluster::no_uncommitted_rows_init(THD *thd) +{ + DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_init"); + struct Ndb_table_local_info *info= (struct Ndb_table_local_info *)m_table_info; + Thd_ndb *thd_ndb= (Thd_ndb *)thd->transaction.thd_ndb; + if (info->last_count != thd_ndb->count) + { + info->last_count = thd_ndb->count; + info->no_uncommitted_rows_count= 0; + info->records= ~(ha_rows)0; + DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d", + ((const NDBTAB *)m_table)->getTableId(), + info->no_uncommitted_rows_count)); + } + DBUG_VOID_RETURN; +} + +void ha_ndbcluster::no_uncommitted_rows_update(int c) +{ + DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_update"); + struct Ndb_table_local_info *info= + (struct Ndb_table_local_info *)m_table_info; + info->no_uncommitted_rows_count+= c; + DBUG_PRINT("info", ("id=%d, no_uncommitted_rows_count=%d", + ((const NDBTAB *)m_table)->getTableId(), + info->no_uncommitted_rows_count)); + DBUG_VOID_RETURN; +} + +void ha_ndbcluster::no_uncommitted_rows_reset(THD *thd) +{ + DBUG_ENTER("ha_ndbcluster::no_uncommitted_rows_reset"); + ((Thd_ndb*)(thd->transaction.thd_ndb))->count++; + ((Thd_ndb*)(thd->transaction.thd_ndb))->error= 0; + DBUG_VOID_RETURN; +} + /* Take care of the error that occured in NDB @@ -142,12 +300,11 @@ static int ndb_to_mysql_error(const NdbError *err) # The mapped error code */ + int ha_ndbcluster::ndb_err(NdbConnection *trans) { int res; const NdbError err= trans->getNdbError(); - if (!err.code) - return 0; // Don't log things to DBUG log if no error DBUG_ENTER("ndb_err"); ERR_PRINT(err); @@ -157,6 +314,7 @@ int ha_ndbcluster::ndb_err(NdbConnection *trans) NDBDICT *dict= m_ndb->getDictionary(); DBUG_PRINT("info", ("invalidateTable %s", m_tabname)); dict->invalidateTable(m_tabname); + table->version=0L; /* Free when thread is ready */ break; } default: @@ -183,10 +341,11 @@ bool ha_ndbcluster::get_error_message(int error, DBUG_ENTER("ha_ndbcluster::get_error_message"); DBUG_PRINT("enter", ("error: %d", error)); - if (!m_ndb) - DBUG_RETURN(false); + Ndb *ndb= ((Thd_ndb*)current_thd->transaction.thd_ndb)->ndb; + if (!ndb) + DBUG_RETURN(FALSE); - const NdbError err= m_ndb->getNdbError(error); + const NdbError err= ndb->getNdbError(error); bool temporary= err.status==NdbError::TemporaryError; buf->set(err.message, strlen(err.message), &my_charset_bin); DBUG_PRINT("exit", ("message: %s, temporary: %d", buf->ptr(), temporary)); @@ -196,7 +355,8 @@ bool ha_ndbcluster::get_error_message(int error, /* Check if type is supported by NDB. - TODO Use this once, not in every operation + TODO Use this once in open(), not in every operation + */ static inline bool ndb_supported_type(enum_field_types type) @@ -224,12 +384,12 @@ static inline bool ndb_supported_type(enum_field_types type) case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_ENUM: case MYSQL_TYPE_SET: - return true; + return TRUE; case MYSQL_TYPE_NULL: case MYSQL_TYPE_GEOMETRY: break; } - return false; + return FALSE; } @@ -277,7 +437,7 @@ int ha_ndbcluster::set_ndb_key(NdbOperation *ndb_op, Field *field, */ int ha_ndbcluster::set_ndb_value(NdbOperation *ndb_op, Field *field, - uint fieldnr) + uint fieldnr, bool *set_blob_value) { const byte* field_ptr= field->ptr; uint32 pack_len= field->pack_length(); @@ -289,6 +449,13 @@ int ha_ndbcluster::set_ndb_value(NdbOperation *ndb_op, Field *field, if (ndb_supported_type(field->type())) { + // ndb currently does not support size 0 + const byte *empty_field= ""; + if (pack_len == 0) + { + pack_len= 1; + field_ptr= empty_field; + } if (! (field->flags & BLOB_FLAG)) { if (field->is_null()) @@ -312,14 +479,18 @@ int ha_ndbcluster::set_ndb_value(NdbOperation *ndb_op, Field *field, char* blob_ptr= NULL; field_blob->get_ptr(&blob_ptr); - // Looks like NULL blob can also be signaled in this way - if (blob_ptr == NULL) - DBUG_RETURN(ndb_blob->setNull() != 0); + // Looks like NULL ptr signals length 0 blob + if (blob_ptr == NULL) { + DBUG_ASSERT(blob_len == 0); + blob_ptr= (char*)""; + } DBUG_PRINT("value", ("set blob ptr=%x len=%u", (unsigned)blob_ptr, blob_len)); DBUG_DUMP("value", (char*)blob_ptr, min(blob_len, 26)); + if (set_blob_value) + *set_blob_value= TRUE; // No callback needed to write value DBUG_RETURN(ndb_blob->setValue(blob_ptr, blob_len) != 0); } @@ -414,7 +585,7 @@ int ha_ndbcluster::get_ndb_blobs_value(NdbBlob *last_ndb_blob) */ int ha_ndbcluster::get_ndb_value(NdbOperation *ndb_op, Field *field, - uint fieldnr) + uint fieldnr, byte* buf) { DBUG_ENTER("get_ndb_value"); DBUG_PRINT("enter", ("fieldnr: %d flags: %o", fieldnr, @@ -422,12 +593,19 @@ int ha_ndbcluster::get_ndb_value(NdbOperation *ndb_op, Field *field, if (field != NULL) { + DBUG_ASSERT(buf); if (ndb_supported_type(field->type())) { DBUG_ASSERT(field->ptr != NULL); if (! (field->flags & BLOB_FLAG)) - { - m_value[fieldnr].rec= ndb_op->getValue(fieldnr, field->ptr); + { + byte *field_buf; + if (field->pack_length() != 0) + field_buf= buf + (field->ptr - table->record[0]); + else + field_buf= dummy_buf; + m_value[fieldnr].rec= ndb_op->getValue(fieldnr, + field_buf); DBUG_RETURN(m_value[fieldnr].rec == NULL); } @@ -459,24 +637,24 @@ int ha_ndbcluster::get_ndb_value(NdbOperation *ndb_op, Field *field, bool ha_ndbcluster::uses_blob_value(bool all_fields) { if (table->blob_fields == 0) - return false; + return FALSE; if (all_fields) - return true; + return TRUE; { uint no_fields= table->fields; int i; - THD *thd= current_thd; + THD *thd= table->in_use; // They always put blobs at the end.. for (i= no_fields - 1; i >= 0; i--) { Field *field= table->field[i]; if (thd->query_id == field->query_id) { - return true; + return TRUE; } } } - return false; + return FALSE; } @@ -494,75 +672,77 @@ int ha_ndbcluster::get_metadata(const char *path) { NDBDICT *dict= m_ndb->getDictionary(); const NDBTAB *tab; - const void *data, *pack_data; - const char **key_name; - uint ndb_columns, mysql_columns, length, pack_length; int error; + bool invalidating_ndb_table= FALSE; + DBUG_ENTER("get_metadata"); DBUG_PRINT("enter", ("m_tabname: %s, path: %s", m_tabname, path)); - if (!(tab= dict->getTable(m_tabname))) - ERR_RETURN(dict->getNdbError()); - DBUG_PRINT("info", ("Table schema version: %d", tab->getObjectVersion())); - - /* - This is the place to check that the table we got from NDB - is equal to the one on local disk - */ - ndb_columns= (uint) tab->getNoOfColumns(); - mysql_columns= table->fields; - if (table->primary_key == MAX_KEY) - ndb_columns--; - if (ndb_columns != mysql_columns) - { - DBUG_PRINT("error", - ("Wrong number of columns, ndb: %d mysql: %d", - ndb_columns, mysql_columns)); - DBUG_RETURN(HA_ERR_OLD_METADATA); - } - - /* - Compare FrmData in NDB with frm file from disk. - */ - error= 0; - if (readfrm(path, &data, &length) || - packfrm(data, length, &pack_data, &pack_length)) - { - my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR)); - my_free((char*)pack_data, MYF(MY_ALLOW_ZERO_PTR)); - DBUG_RETURN(1); - } + do { + const void *data, *pack_data; + uint length, pack_length; + + if (!(tab= dict->getTable(m_tabname))) + ERR_RETURN(dict->getNdbError()); + DBUG_PRINT("info", ("Table schema version: %d", tab->getObjectVersion())); + /* + Compare FrmData in NDB with frm file from disk. + */ + error= 0; + if (readfrm(path, &data, &length) || + packfrm(data, length, &pack_data, &pack_length)) + { + my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR)); + my_free((char*)pack_data, MYF(MY_ALLOW_ZERO_PTR)); + DBUG_RETURN(1); + } - if ((pack_length != tab->getFrmLength()) || - (memcmp(pack_data, tab->getFrmData(), pack_length))) - { - DBUG_PRINT("error", - ("metadata, pack_length: %d getFrmLength: %d memcmp: %d", - pack_length, tab->getFrmLength(), - memcmp(pack_data, tab->getFrmData(), pack_length))); - DBUG_DUMP("pack_data", (char*)pack_data, pack_length); - DBUG_DUMP("frm", (char*)tab->getFrmData(), tab->getFrmLength()); - error= HA_ERR_OLD_METADATA; - } - my_free((char*)data, MYF(0)); - my_free((char*)pack_data, MYF(0)); + if ((pack_length != tab->getFrmLength()) || + (memcmp(pack_data, tab->getFrmData(), pack_length))) + { + if (!invalidating_ndb_table) + { + DBUG_PRINT("info", ("Invalidating table")); + dict->invalidateTable(m_tabname); + invalidating_ndb_table= TRUE; + } + else + { + DBUG_PRINT("error", + ("metadata, pack_length: %d getFrmLength: %d memcmp: %d", + pack_length, tab->getFrmLength(), + memcmp(pack_data, tab->getFrmData(), pack_length))); + DBUG_DUMP("pack_data", (char*)pack_data, pack_length); + DBUG_DUMP("frm", (char*)tab->getFrmData(), tab->getFrmLength()); + error= 3; + invalidating_ndb_table= FALSE; + } + } + else + { + invalidating_ndb_table= FALSE; + } + my_free((char*)data, MYF(0)); + my_free((char*)pack_data, MYF(0)); + } while (invalidating_ndb_table); + if (error) DBUG_RETURN(error); - // All checks OK, lets use the table - m_table= (void*)tab; - + m_table= NULL; + m_table_info= NULL; + DBUG_RETURN(build_index_list(table, ILBP_OPEN)); } int ha_ndbcluster::build_index_list(TABLE *tab, enum ILBP phase) { + uint i; int error= 0; - char *name; - const char *index_name; + const char *name, *index_name; + char unique_index_name[FN_LEN]; static const char* unique_suffix= "$unique"; - uint i, name_len; KEY* key_info= tab->key_info; const char **key_name= tab->keynames.type_names; NdbDictionary::Dictionary *dict= m_ndb->getDictionary(); @@ -576,21 +756,15 @@ int ha_ndbcluster::build_index_list(TABLE *tab, enum ILBP phase) m_index[i].type= idx_type; if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX) { - name_len= strlen(index_name)+strlen(unique_suffix)+1; - // Create name for unique index by appending "$unique"; - if (!(name= my_malloc(name_len, MYF(MY_WME)))) - DBUG_RETURN(2); - strxnmov(name, name_len, index_name, unique_suffix, NullS); - m_index[i].unique_name= name; - DBUG_PRINT("info", ("Created unique index name: %s for index %d", - name, i)); + strxnmov(unique_index_name, FN_LEN, index_name, unique_suffix, NullS); + DBUG_PRINT("info", ("Created unique index name \'%s\' for index %d", + unique_index_name, i)); } // Create secondary indexes if in create phase if (phase == ILBP_CREATE) { - DBUG_PRINT("info", ("Creating index %u: %s", i, index_name)); - - switch (m_index[i].type){ + DBUG_PRINT("info", ("Creating index %u: %s", i, index_name)); + switch (idx_type){ case PRIMARY_KEY_INDEX: // Do nothing, already created @@ -600,16 +774,16 @@ int ha_ndbcluster::build_index_list(TABLE *tab, enum ILBP phase) break; case UNIQUE_ORDERED_INDEX: if (!(error= create_ordered_index(index_name, key_info))) - error= create_unique_index(get_unique_index_name(i), key_info); + error= create_unique_index(unique_index_name, key_info); break; case UNIQUE_INDEX: - error= create_unique_index(get_unique_index_name(i), key_info); + error= create_unique_index(unique_index_name, key_info); break; case ORDERED_INDEX: error= create_ordered_index(index_name, key_info); break; default: - DBUG_ASSERT(false); + DBUG_ASSERT(FALSE); break; } if (error) @@ -620,21 +794,20 @@ int ha_ndbcluster::build_index_list(TABLE *tab, enum ILBP phase) } } // Add handles to index objects - DBUG_PRINT("info", ("Trying to add handle to index %s", index_name)); - if ((m_index[i].type != PRIMARY_KEY_INDEX) && - (m_index[i].type != UNIQUE_INDEX)) + if (idx_type != PRIMARY_KEY_INDEX && idx_type != UNIQUE_INDEX) { + DBUG_PRINT("info", ("Get handle to index %s", index_name)); const NDBINDEX *index= dict->getIndex(index_name, m_tabname); if (!index) DBUG_RETURN(1); m_index[i].index= (void *) index; } - if (m_index[i].unique_name) + if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX) { - const NDBINDEX *index= dict->getIndex(m_index[i].unique_name, m_tabname); + DBUG_PRINT("info", ("Get handle to unique_index %s", unique_index_name)); + const NDBINDEX *index= dict->getIndex(unique_index_name, m_tabname); if (!index) DBUG_RETURN(1); m_index[i].unique_index= (void *) index; } - DBUG_PRINT("info", ("Added handle to index %s", index_name)); } DBUG_RETURN(error); @@ -665,13 +838,11 @@ void ha_ndbcluster::release_metadata() DBUG_PRINT("enter", ("m_tabname: %s", m_tabname)); m_table= NULL; + m_table_info= NULL; // Release index list for (i= 0; i < MAX_KEY; i++) { - if (m_index[i].unique_name) - my_free((char*)m_index[i].unique_name, MYF(0)); - m_index[i].unique_name= NULL; m_index[i].unique_index= NULL; m_index[i].index= NULL; } @@ -681,17 +852,12 @@ void ha_ndbcluster::release_metadata() int ha_ndbcluster::get_ndb_lock_type(enum thr_lock_type type) { - int lm; - if (type == TL_WRITE_ALLOW_WRITE) - lm= NdbScanOperation::LM_Exclusive; + if (type >= TL_WRITE_ALLOW_WRITE) + return NdbOperation::LM_Exclusive; else if (uses_blob_value(retrieve_all_fields)) - /* - TODO use a new scan mode to read + lock + keyinfo - */ - lm= NdbScanOperation::LM_Exclusive; + return NdbOperation::LM_Read; else - lm= NdbScanOperation::LM_CommittedRead; - return lm; + return NdbOperation::LM_CommittedRead; } static const ulong index_type_flags[]= @@ -709,33 +875,26 @@ static const ulong index_type_flags[]= through the index. */ // HA_KEYREAD_ONLY | - HA_READ_NEXT | - HA_READ_RANGE, + HA_READ_NEXT | + HA_READ_RANGE | + HA_READ_ORDER, /* UNIQUE_INDEX */ HA_ONLY_WHOLE_INDEX, /* UNIQUE_ORDERED_INDEX */ - HA_READ_NEXT | - HA_READ_RANGE, + HA_READ_NEXT | + HA_READ_RANGE | + HA_READ_ORDER, /* ORDERED_INDEX */ - HA_READ_NEXT | - HA_READ_RANGE, + HA_READ_NEXT | + HA_READ_RANGE | + HA_READ_ORDER }; static const int index_flags_size= sizeof(index_type_flags)/sizeof(ulong); -inline const char* ha_ndbcluster::get_index_name(uint idx_no) const -{ - return table->keynames.type_names[idx_no]; -} - -inline const char* ha_ndbcluster::get_unique_index_name(uint idx_no) const -{ - return m_index[idx_no].unique_name; -} - inline NDB_INDEX_TYPE ha_ndbcluster::get_index_type(uint idx_no) const { DBUG_ASSERT(idx_no < MAX_KEY); @@ -829,8 +988,10 @@ int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf) DBUG_PRINT("enter", ("key_len: %u", key_len)); DBUG_DUMP("key", (char*)key, key_len); - if (!(op= trans->getNdbOperation((NDBTAB *) m_table)) || - op->readTuple() != 0) + NdbOperation::LockMode lm= + (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type); + if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || + op->readTuple(lm) != 0) ERR_RETURN(trans->getNdbError()); if (table->primary_key == MAX_KEY) @@ -842,7 +1003,7 @@ int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf) ERR_RETURN(trans->getNdbError()); // Read key at the same time, for future reference - if (get_ndb_value(op, NULL, no_fields)) + if (get_ndb_value(op, NULL, no_fields, NULL)) ERR_RETURN(trans->getNdbError()); } else @@ -859,7 +1020,7 @@ int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf) if ((thd->query_id == field->query_id) || retrieve_all_fields) { - if (get_ndb_value(op, field, i)) + if (get_ndb_value(op, field, i, buf)) ERR_RETURN(trans->getNdbError()); } else @@ -869,7 +1030,7 @@ int ha_ndbcluster::pk_read(const byte *key, uint key_len, byte *buf) } } - if (trans->execute(NoCommit, IgnoreError) != 0) + if (execute_no_commit_ie(this,trans) != 0) { table->status= STATUS_NOT_FOUND; DBUG_RETURN(ndb_err(trans)); @@ -898,8 +1059,10 @@ int ha_ndbcluster::complemented_pk_read(const byte *old_data, byte *new_data) // We have allready retrieved all fields, nothing to complement DBUG_RETURN(0); - if (!(op= trans->getNdbOperation((NDBTAB *) m_table)) || - op->readTuple() != 0) + NdbOperation::LockMode lm= + (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type); + if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || + op->readTuple(lm) != 0) ERR_RETURN(trans->getNdbError()); int res; @@ -913,12 +1076,12 @@ int ha_ndbcluster::complemented_pk_read(const byte *old_data, byte *new_data) if (!(field->flags & PRI_KEY_FLAG) && (thd->query_id != field->query_id)) { - if (get_ndb_value(op, field, i)) + if (get_ndb_value(op, field, i, new_data)) ERR_RETURN(trans->getNdbError()); } } - if (trans->execute(NoCommit) != 0) + if (execute_no_commit(this,trans) != 0) { table->status= STATUS_NOT_FOUND; DBUG_RETURN(ndb_err(trans)); @@ -948,12 +1111,13 @@ int ha_ndbcluster::unique_index_read(const byte *key, DBUG_ENTER("unique_index_read"); DBUG_PRINT("enter", ("key_len: %u, index: %u", key_len, active_index)); DBUG_DUMP("key", (char*)key, key_len); - DBUG_PRINT("enter", ("name: %s", get_unique_index_name(active_index))); + NdbOperation::LockMode lm= + (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type); if (!(op= trans->getNdbIndexOperation((NDBINDEX *) m_index[active_index].unique_index, - (NDBTAB *) m_table)) || - op->readTuple() != 0) + (const NDBTAB *) m_table)) || + op->readTuple(lm) != 0) ERR_RETURN(trans->getNdbError()); // Set secondary index key(s) @@ -976,7 +1140,7 @@ int ha_ndbcluster::unique_index_read(const byte *key, if ((thd->query_id == field->query_id) || (field->flags & PRI_KEY_FLAG)) { - if (get_ndb_value(op, field, i)) + if (get_ndb_value(op, field, i, buf)) ERR_RETURN(op->getNdbError()); } else @@ -986,7 +1150,7 @@ int ha_ndbcluster::unique_index_read(const byte *key, } } - if (trans->execute(NoCommit, IgnoreError) != 0) + if (execute_no_commit_ie(this,trans) != 0) { table->status= STATUS_NOT_FOUND; DBUG_RETURN(ndb_err(trans)); @@ -1022,7 +1186,7 @@ inline int ha_ndbcluster::next_result(byte *buf) If this an update or delete, call nextResult with false to process any records already cached in NdbApi */ - bool contact_ndb= m_lock.type != TL_WRITE_ALLOW_WRITE; + bool contact_ndb= m_lock.type < TL_WRITE_ALLOW_WRITE; do { DBUG_PRINT("info", ("Call nextResult, contact_ndb: %d", contact_ndb)); /* @@ -1030,10 +1194,10 @@ inline int ha_ndbcluster::next_result(byte *buf) */ if (ops_pending && blobs_pending) { - if (trans->execute(NoCommit) != 0) - DBUG_RETURN(ndb_err(trans)); + if (execute_no_commit(this,trans) != 0) + DBUG_RETURN(ndb_err(trans)); ops_pending= 0; - blobs_pending= false; + blobs_pending= FALSE; } check= cursor->nextResult(contact_ndb); if (check == 0) @@ -1056,9 +1220,22 @@ inline int ha_ndbcluster::next_result(byte *buf) be sent to NDB */ DBUG_PRINT("info", ("ops_pending: %d", ops_pending)); - if (ops_pending && (trans->execute(NoCommit) != 0)) - DBUG_RETURN(ndb_err(trans)); - ops_pending= 0; + if (ops_pending) + { + if (current_thd->transaction.on) + { + if (execute_no_commit(this,trans) != 0) + DBUG_RETURN(ndb_err(trans)); + } + else + { + if (execute_commit(this,trans) != 0) + DBUG_RETURN(ndb_err(trans)); + int res= trans->restart(); + DBUG_ASSERT(res == 0); + } + ops_pending= 0; + } contact_ndb= (check == 2); } @@ -1073,76 +1250,204 @@ inline int ha_ndbcluster::next_result(byte *buf) DBUG_RETURN(HA_ERR_END_OF_FILE); } - /* - Set bounds for a ordered index scan, use key_range + Set bounds for ordered index scan. */ int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op, - const key_range *key, - int bound) + const key_range *keys[2]) { - uint key_len, key_store_len, tot_len, key_tot_len; - byte *key_ptr; - KEY* key_info= table->key_info + active_index; - KEY_PART_INFO* key_part= key_info->key_part; - KEY_PART_INFO* end= key_part+key_info->key_parts; - Field* field; - bool key_nullable, key_null; + const KEY *const key_info= table->key_info + active_index; + const uint key_parts= key_info->key_parts; + uint key_tot_len[2]; + uint tot_len; + uint i, j; DBUG_ENTER("set_bounds"); - DBUG_PRINT("enter", ("bound: %d", bound)); - DBUG_PRINT("enter", ("key_parts: %d", key_info->key_parts)); - DBUG_PRINT("enter", ("key->length: %d", key->length)); - DBUG_PRINT("enter", ("key->flag: %d", key->flag)); + DBUG_PRINT("info", ("key_parts=%d", key_parts)); - // Set bounds using key data + for (j= 0; j <= 1; j++) + { + const key_range *key= keys[j]; + if (key != NULL) + { + // for key->flag see ha_rkey_function + DBUG_PRINT("info", ("key %d length=%d flag=%d", + j, key->length, key->flag)); + key_tot_len[j]= key->length; + } + else + { + DBUG_PRINT("info", ("key %d not present", j)); + key_tot_len[j]= 0; + } + } tot_len= 0; - key_ptr= (byte *) key->key; - key_tot_len= key->length; - for (; key_part != end; key_part++) - { - field= key_part->field; - key_len= key_part->length; - key_store_len= key_part->store_length; - key_nullable= (bool) key_part->null_bit; - key_null= (field->maybe_null() && *key_ptr); - tot_len+= key_store_len; - - const char* bounds[]= {"LE", "LT", "GE", "GT", "EQ"}; - DBUG_ASSERT(bound >= 0 && bound <= 4); - DBUG_PRINT("info", ("Set Bound%s on %s %s %s %s", - bounds[bound], - field->field_name, - key_nullable ? "NULLABLE" : "", - key_null ? "NULL":"")); - DBUG_PRINT("info", ("Total length %ds", tot_len)); - - DBUG_DUMP("key", (char*) key_ptr, key_store_len); - - if (op->setBound(field->field_name, - bound, - key_null ? 0 : (key_nullable ? key_ptr + 1 : key_ptr), - key_null ? 0 : key_len) != 0) - ERR_RETURN(op->getNdbError()); - - key_ptr+= key_store_len; - if (tot_len >= key_tot_len) - break; + for (i= 0; i < key_parts; i++) + { + KEY_PART_INFO *key_part= &key_info->key_part[i]; + Field *field= key_part->field; + uint part_len= key_part->length; + uint part_store_len= key_part->store_length; + bool part_nullable= (bool) key_part->null_bit; + // Info about each key part + struct part_st { + bool part_last; + const key_range *key; + const byte *part_ptr; + bool part_null; + int bound_type; + const char* bound_ptr; + }; + struct part_st part[2]; + + for (j= 0; j <= 1; j++) + { + struct part_st &p = part[j]; + p.key= NULL; + p.bound_type= -1; + if (tot_len < key_tot_len[j]) + { + p.part_last= (tot_len + part_store_len >= key_tot_len[j]); + p.key= keys[j]; + p.part_ptr= &p.key->key[tot_len]; + p.part_null= (field->maybe_null() && *p.part_ptr); + p.bound_ptr= (const char *) + p.part_null ? 0 : part_nullable ? p.part_ptr + 1 : p.part_ptr; + + if (j == 0) + { + switch (p.key->flag) + { + case HA_READ_KEY_EXACT: + p.bound_type= NdbIndexScanOperation::BoundEQ; + break; + case HA_READ_KEY_OR_NEXT: + p.bound_type= NdbIndexScanOperation::BoundLE; + break; + case HA_READ_AFTER_KEY: + if (! p.part_last) + p.bound_type= NdbIndexScanOperation::BoundLE; + else + p.bound_type= NdbIndexScanOperation::BoundLT; + break; + default: + break; + } + } + if (j == 1) { + switch (p.key->flag) + { + case HA_READ_BEFORE_KEY: + if (! p.part_last) + p.bound_type= NdbIndexScanOperation::BoundGE; + else + p.bound_type= NdbIndexScanOperation::BoundGT; + break; + case HA_READ_AFTER_KEY: // weird + p.bound_type= NdbIndexScanOperation::BoundGE; + break; + default: + break; + } + } - /* - Only one bound which is not EQ can be set - so if this bound was not EQ, bail out and make - a best effort attempt - */ - if (bound != NdbIndexScanOperation::BoundEQ) - break; - } + if (p.bound_type == -1) + { + DBUG_PRINT("error", ("key %d unknown flag %d", j, p.key->flag)); + DBUG_ASSERT(false); + // Stop setting bounds but continue with what we have + DBUG_RETURN(0); + } + } + } + + // Seen with e.g. b = 1 and c > 1 + if (part[0].bound_type == NdbIndexScanOperation::BoundLE && + part[1].bound_type == NdbIndexScanOperation::BoundGE && + memcmp(part[0].part_ptr, part[1].part_ptr, part_store_len) == 0) + { + DBUG_PRINT("info", ("replace LE/GE pair by EQ")); + part[0].bound_type= NdbIndexScanOperation::BoundEQ; + part[1].bound_type= -1; + } + // Not seen but was in previous version + if (part[0].bound_type == NdbIndexScanOperation::BoundEQ && + part[1].bound_type == NdbIndexScanOperation::BoundGE && + memcmp(part[0].part_ptr, part[1].part_ptr, part_store_len) == 0) + { + DBUG_PRINT("info", ("remove GE from EQ/GE pair")); + part[1].bound_type= -1; + } + for (j= 0; j <= 1; j++) + { + struct part_st &p = part[j]; + // Set bound if not done with this key + if (p.key != NULL) + { + DBUG_PRINT("info", ("key %d:%d offset=%d length=%d last=%d bound=%d", + j, i, tot_len, part_len, p.part_last, p.bound_type)); + DBUG_DUMP("info", (const char*)p.part_ptr, part_store_len); + + // Set bound if not cancelled via type -1 + if (p.bound_type != -1) + if (op->setBound(field->field_name, p.bound_type, p.bound_ptr)) + ERR_RETURN(op->getNdbError()); + } + } + + tot_len+= part_store_len; + } DBUG_RETURN(0); } +inline +int ha_ndbcluster::define_read_attrs(byte* buf, NdbOperation* op) +{ + uint i; + THD *thd= current_thd; + NdbConnection *trans= m_active_trans; + + DBUG_ENTER("define_read_attrs"); + + // Define attributes to read + for (i= 0; i < table->fields; i++) + { + Field *field= table->field[i]; + if ((thd->query_id == field->query_id) || + (field->flags & PRI_KEY_FLAG) || + retrieve_all_fields) + { + if (get_ndb_value(op, field, i, buf)) + ERR_RETURN(op->getNdbError()); + } + else + { + m_value[i].ptr= NULL; + } + } + + if (table->primary_key == MAX_KEY) + { + DBUG_PRINT("info", ("Getting hidden key")); + // Scanning table with no primary key + int hidden_no= table->fields; +#ifndef DBUG_OFF + const NDBTAB *tab= (const NDBTAB *) m_table; + if (!tab->getColumn(hidden_no)) + DBUG_RETURN(1); +#endif + if (get_ndb_value(op, NULL, hidden_no, NULL)) + ERR_RETURN(op->getNdbError()); + } + + if (execute_no_commit(this,trans) != 0) + DBUG_RETURN(ndb_err(trans)); + DBUG_PRINT("exit", ("Scan started successfully")); + DBUG_RETURN(next_result(buf)); +} /* Start ordered index scan in NDB @@ -1152,52 +1457,60 @@ int ha_ndbcluster::ordered_index_scan(const key_range *start_key, const key_range *end_key, bool sorted, byte* buf) { + bool restart; NdbConnection *trans= m_active_trans; NdbResultSet *cursor; NdbIndexScanOperation *op; - const char *index_name; DBUG_ENTER("ordered_index_scan"); DBUG_PRINT("enter", ("index: %u, sorted: %d", active_index, sorted)); DBUG_PRINT("enter", ("Starting new ordered scan on %s", m_tabname)); - - index_name= get_index_name(active_index); - if (!(op= trans->getNdbIndexScanOperation((NDBINDEX *) - m_index[active_index].index, - (NDBTAB *) m_table))) - ERR_RETURN(trans->getNdbError()); - NdbScanOperation::LockMode lm= (NdbScanOperation::LockMode) - get_ndb_lock_type(m_lock.type); - if (!(cursor= op->readTuples(lm, 0, parallelism, sorted))) - ERR_RETURN(trans->getNdbError()); - m_active_cursor= cursor; + // Check that sorted seems to be initialised + DBUG_ASSERT(sorted == 0 || sorted == 1); + + if (m_active_cursor == 0) + { + restart= false; + NdbOperation::LockMode lm= + (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type); + if (!(op= trans->getNdbIndexScanOperation((NDBINDEX *) + m_index[active_index].index, + (const NDBTAB *) m_table)) || + !(cursor= op->readTuples(lm, 0, parallelism, sorted))) + ERR_RETURN(trans->getNdbError()); + m_active_cursor= cursor; + } else { + restart= true; + op= (NdbIndexScanOperation*)m_active_cursor->getOperation(); + + DBUG_ASSERT(op->getSorted() == sorted); + DBUG_ASSERT(op->getLockMode() == + (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type)); + if(op->reset_bounds()) + DBUG_RETURN(ndb_err(m_active_trans)); + } - if (start_key && - set_bounds(op, start_key, - (start_key->flag == HA_READ_KEY_EXACT) ? - NdbIndexScanOperation::BoundEQ : - (start_key->flag == HA_READ_AFTER_KEY) ? - NdbIndexScanOperation::BoundLT : - NdbIndexScanOperation::BoundLE)) - DBUG_RETURN(1); + { + const key_range *keys[2]= { start_key, end_key }; + int ret= set_bounds(op, keys); + if (ret) + DBUG_RETURN(ret); + } - if (end_key) + if (!restart) { - if (start_key && start_key->flag == HA_READ_KEY_EXACT) - { - DBUG_PRINT("info", ("start_key is HA_READ_KEY_EXACT ignoring end_key")); - } - else if (set_bounds(op, end_key, - (end_key->flag == HA_READ_AFTER_KEY) ? - NdbIndexScanOperation::BoundGE : - NdbIndexScanOperation::BoundGT)) - DBUG_RETURN(1); + DBUG_RETURN(define_read_attrs(buf, op)); + } + else + { + if (execute_no_commit(this,trans) != 0) + DBUG_RETURN(ndb_err(trans)); + + DBUG_RETURN(next_result(buf)); } - DBUG_RETURN(define_read_attrs(buf, op)); } - /* Start a filtered scan in NDB. @@ -1227,11 +1540,10 @@ int ha_ndbcluster::filtered_scan(const byte *key, uint key_len, DBUG_PRINT("info", ("Starting a new filtered scan on %s", m_tabname)); - if (!(op= trans->getNdbScanOperation((NDBTAB *) m_table))) - ERR_RETURN(trans->getNdbError()); - NdbScanOperation::LockMode lm= (NdbScanOperation::LockMode) - get_ndb_lock_type(m_lock.type); - if (!(cursor= op->readTuples(lm, 0, parallelism))) + NdbOperation::LockMode lm= + (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type); + if (!(op= trans->getNdbScanOperation((const NDBTAB *) m_table)) || + !(cursor= op->readTuples(lm, 0, parallelism))) ERR_RETURN(trans->getNdbError()); m_active_cursor= cursor; @@ -1298,68 +1610,18 @@ int ha_ndbcluster::full_table_scan(byte *buf) DBUG_ENTER("full_table_scan"); DBUG_PRINT("enter", ("Starting new scan on %s", m_tabname)); - if (!(op=trans->getNdbScanOperation((NDBTAB *) m_table))) - ERR_RETURN(trans->getNdbError()); - NdbScanOperation::LockMode lm= (NdbScanOperation::LockMode) - get_ndb_lock_type(m_lock.type); - if (!(cursor= op->readTuples(lm, 0, parallelism))) + NdbOperation::LockMode lm= + (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type); + if (!(op=trans->getNdbScanOperation((const NDBTAB *) m_table)) || + !(cursor= op->readTuples(lm, 0, parallelism))) ERR_RETURN(trans->getNdbError()); m_active_cursor= cursor; DBUG_RETURN(define_read_attrs(buf, op)); } - -inline -int ha_ndbcluster::define_read_attrs(byte* buf, NdbOperation* op) -{ - uint i; - THD *thd= current_thd; - NdbConnection *trans= m_active_trans; - - DBUG_ENTER("define_read_attrs"); - - // Define attributes to read - for (i= 0; i < table->fields; i++) - { - Field *field= table->field[i]; - if ((thd->query_id == field->query_id) || - (field->flags & PRI_KEY_FLAG) || - retrieve_all_fields) - { - if (get_ndb_value(op, field, i)) - ERR_RETURN(op->getNdbError()); - } - else - { - m_value[i].ptr= NULL; - } - } - - if (table->primary_key == MAX_KEY) - { - DBUG_PRINT("info", ("Getting hidden key")); - // Scanning table with no primary key - int hidden_no= table->fields; -#ifndef DBUG_OFF - const NDBTAB *tab= (NDBTAB *) m_table; - if (!tab->getColumn(hidden_no)) - DBUG_RETURN(1); -#endif - if (get_ndb_value(op, NULL, hidden_no)) - ERR_RETURN(op->getNdbError()); - } - - if (trans->execute(NoCommit) != 0) - DBUG_RETURN(ndb_err(trans)); - DBUG_PRINT("exit", ("Scan started successfully")); - DBUG_RETURN(next_result(buf)); -} - - /* Insert one record into NDB */ - int ha_ndbcluster::write_row(byte *record) { bool has_auto_increment; @@ -1370,14 +1632,18 @@ int ha_ndbcluster::write_row(byte *record) THD *thd= current_thd; DBUG_ENTER("write_row"); + + if(m_ignore_dup_key_not_supported) + { + DBUG_RETURN(HA_ERR_WRONG_COMMAND); + } statistic_increment(thd->status_var.ha_write_count, &LOCK_status); - if (table->timestamp_default_now) - update_timestamp(record+table->timestamp_default_now-1); + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) + table->timestamp_field->set_time(); has_auto_increment= (table->next_number_field && record == table->record[0]); - skip_auto_increment= table->auto_increment_field_not_null; - if (!(op= trans->getNdbOperation((NDBTAB *) m_table))) + if (!(op= trans->getNdbOperation((const NDBTAB *) m_table))) ERR_RETURN(trans->getNdbError()); res= (m_use_write) ? op->writeTuple() :op->insertTuple(); @@ -1387,7 +1653,7 @@ int ha_ndbcluster::write_row(byte *record) if (table->primary_key == MAX_KEY) { // Table has hidden primary key - Uint64 auto_value= m_ndb->getAutoIncrementValue((NDBTAB *) m_table); + Uint64 auto_value= m_ndb->getAutoIncrementValue((const NDBTAB *) m_table); if (set_hidden_key(op, table->fields, (const byte*)&auto_value)) ERR_RETURN(op->getNdbError()); } @@ -1395,21 +1661,26 @@ int ha_ndbcluster::write_row(byte *record) { int res; - if ((has_auto_increment) && (!skip_auto_increment)) + if (has_auto_increment) + { + skip_auto_increment= FALSE; update_auto_increment(); + skip_auto_increment= !auto_increment_column_changed; + } if ((res= set_primary_key(op))) return res; } // Set non-key attribute(s) + bool set_blob_value= FALSE; for (i= 0; i < table->fields; i++) { Field *field= table->field[i]; if (!(field->flags & PRI_KEY_FLAG) && - set_ndb_value(op, field, i)) + set_ndb_value(op, field, i, &set_blob_value)) { - skip_auto_increment= true; + skip_auto_increment= TRUE; ERR_RETURN(op->getNdbError()); } } @@ -1422,20 +1693,38 @@ int ha_ndbcluster::write_row(byte *record) Find out how this is detected! */ rows_inserted++; - bulk_insert_not_flushed= true; + no_uncommitted_rows_update(1); + bulk_insert_not_flushed= TRUE; if ((rows_to_insert == 1) || ((rows_inserted % bulk_insert_rows) == 0) || - uses_blob_value(false) != 0) + set_blob_value) { + THD *thd= current_thd; // Send rows to NDB DBUG_PRINT("info", ("Sending inserts to NDB, "\ "rows_inserted:%d, bulk_insert_rows: %d", - (int)rows_inserted, (int)bulk_insert_rows)); - bulk_insert_not_flushed= false; - if (trans->execute(NoCommit) != 0) + (int)rows_inserted, (int)bulk_insert_rows)); + + bulk_insert_not_flushed= FALSE; + if (thd->transaction.on) { - skip_auto_increment= true; - DBUG_RETURN(ndb_err(trans)); + if (execute_no_commit(this,trans) != 0) + { + skip_auto_increment= TRUE; + no_uncommitted_rows_execute_failure(); + DBUG_RETURN(ndb_err(trans)); + } + } + else + { + if (execute_commit(this,trans) != 0) + { + skip_auto_increment= TRUE; + no_uncommitted_rows_execute_failure(); + DBUG_RETURN(ndb_err(trans)); + } + int res= trans->restart(); + DBUG_ASSERT(res == 0); } } if ((has_auto_increment) && (skip_auto_increment)) @@ -1444,11 +1733,11 @@ int ha_ndbcluster::write_row(byte *record) DBUG_PRINT("info", ("Trying to set next auto increment value to %lu", (ulong) next_val)); - if (m_ndb->setAutoIncrementValue((NDBTAB *) m_table, next_val, true)) + if (m_ndb->setAutoIncrementValue((const NDBTAB *) m_table, next_val, TRUE)) DBUG_PRINT("info", ("Setting next auto increment value to %u", next_val)); } - skip_auto_increment= true; + skip_auto_increment= TRUE; DBUG_RETURN(0); } @@ -1502,9 +1791,9 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data) DBUG_ENTER("update_row"); statistic_increment(thd->status_var.ha_update_count, &LOCK_status); - if (table->timestamp_on_update_now) - update_timestamp(new_data+table->timestamp_on_update_now-1); - + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) + table->timestamp_field->set_time(); + /* Check for update of primary key for special handling */ if ((table->primary_key != MAX_KEY) && (key_cmp(table->primary_key, old_data, new_data))) @@ -1552,12 +1841,12 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data) if (!(op= cursor->updateTuple())) ERR_RETURN(trans->getNdbError()); ops_pending++; - if (uses_blob_value(false)) - blobs_pending= true; + if (uses_blob_value(FALSE)) + blobs_pending= TRUE; } else { - if (!(op= trans->getNdbOperation((NDBTAB *) m_table)) || + if (!(op= trans->getNdbOperation((const NDBTAB *) m_table)) || op->updateTuple() != 0) ERR_RETURN(trans->getNdbError()); @@ -1595,8 +1884,10 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data) } // Execute update operation - if (!cursor && trans->execute(NoCommit) != 0) + if (!cursor && execute_no_commit(this,trans) != 0) { + no_uncommitted_rows_execute_failure(); DBUG_RETURN(ndb_err(trans)); + } DBUG_RETURN(0); } @@ -1630,16 +1921,20 @@ int ha_ndbcluster::delete_row(const byte *record) ERR_RETURN(trans->getNdbError()); ops_pending++; + no_uncommitted_rows_update(-1); + // If deleting from cursor, NoCommit will be handled in next_result DBUG_RETURN(0); } else { - if (!(op=trans->getNdbOperation((NDBTAB *) m_table)) || + if (!(op=trans->getNdbOperation((const NDBTAB *) m_table)) || op->deleteTuple() != 0) ERR_RETURN(trans->getNdbError()); + no_uncommitted_rows_update(-1); + if (table->primary_key == MAX_KEY) { // This table has no primary key, use "hidden" primary key @@ -1660,8 +1955,10 @@ int ha_ndbcluster::delete_row(const byte *record) } // Execute delete operation - if (trans->execute(NoCommit) != 0) + if (execute_no_commit(this,trans) != 0) { + no_uncommitted_rows_execute_failure(); DBUG_RETURN(ndb_err(trans)); + } DBUG_RETURN(0); } @@ -1702,7 +1999,7 @@ void ha_ndbcluster::unpack_record(byte* buf) else { NdbBlob* ndb_blob= (*value).blob; - bool isNull= true; + bool isNull= TRUE; int ret= ndb_blob->getNull(isNull); DBUG_ASSERT(ret == 0); if (isNull) @@ -1717,7 +2014,7 @@ void ha_ndbcluster::unpack_record(byte* buf) { // Table with hidden primary key int hidden_no= table->fields; - const NDBTAB *tab= (NDBTAB *) m_table; + const NDBTAB *tab= (const NDBTAB *) m_table; const NDBCOL *hidden_col= tab->getColumn(hidden_no); NdbRecAttr* rec= m_value[hidden_no].rec; DBUG_ASSERT(rec); @@ -1735,7 +2032,7 @@ void ha_ndbcluster::unpack_record(byte* buf) void ha_ndbcluster::print_results() { - const NDBTAB *tab= (NDBTAB*) m_table; + const NDBTAB *tab= (const NDBTAB*) m_table; DBUG_ENTER("print_results"); #ifndef DBUG_OFF @@ -1770,7 +2067,7 @@ void ha_ndbcluster::print_results() else { ndb_blob= value.blob; - bool isNull= true; + bool isNull= TRUE; ndb_blob->getNull(isNull); if (isNull) { fprintf(DBUG_FILE, "NULL\n"); @@ -1910,18 +2207,54 @@ int ha_ndbcluster::index_end() int ha_ndbcluster::index_read(byte *buf, - const byte *key, uint key_len, - enum ha_rkey_function find_flag) + const byte *key, uint key_len, + enum ha_rkey_function find_flag) { DBUG_ENTER("index_read"); DBUG_PRINT("enter", ("active_index: %u, key_len: %u, find_flag: %d", active_index, key_len, find_flag)); + int error; + ndb_index_type type = get_index_type(active_index); + const KEY* key_info = table->key_info+active_index; + switch (type){ + case PRIMARY_KEY_ORDERED_INDEX: + case PRIMARY_KEY_INDEX: + if (find_flag == HA_READ_KEY_EXACT && key_info->key_length == key_len) + { + DBUG_RETURN(pk_read(key, key_len, buf)); + } + else if (type == PRIMARY_KEY_INDEX) + { + DBUG_RETURN(1); + } + break; + case UNIQUE_ORDERED_INDEX: + case UNIQUE_INDEX: + if (find_flag == HA_READ_KEY_EXACT && key_info->key_length == key_len) + { + DBUG_RETURN(unique_index_read(key, key_len, buf)); + } + else if (type == UNIQUE_INDEX) + { + DBUG_RETURN(1); + } + break; + case ORDERED_INDEX: + break; + default: + case UNDEFINED_INDEX: + DBUG_ASSERT(FALSE); + DBUG_RETURN(1); + break; + } + key_range start_key; - start_key.key= key; - start_key.length= key_len; - start_key.flag= find_flag; - DBUG_RETURN(read_range_first(&start_key, NULL, false, true)); + start_key.key = key; + start_key.length = key_len; + start_key.flag = find_flag; + error= ordered_index_scan(&start_key, 0, TRUE, buf); + DBUG_RETURN(error == HA_ERR_END_OF_FILE ? HA_ERR_KEY_NOT_FOUND : error); } @@ -1962,7 +2295,10 @@ int ha_ndbcluster::index_first(byte *buf) DBUG_ENTER("index_first"); statistic_increment(current_thd->status_var.ha_read_first_count, &LOCK_status); - DBUG_RETURN(1); + // Start the ordered index scan and fetch the first row + + // Only HA_READ_ORDER indexes get called by index_first + DBUG_RETURN(ordered_index_scan(0, 0, TRUE, buf)); } @@ -1970,23 +2306,31 @@ int ha_ndbcluster::index_last(byte *buf) { DBUG_ENTER("index_last"); statistic_increment(current_thd->status_var.ha_read_last_count,&LOCK_status); + int res; + if((res= ordered_index_scan(0, 0, TRUE, buf)) == 0){ + NdbResultSet *cursor= m_active_cursor; + while((res= cursor->nextResult(TRUE)) == 0); + if(res == 1){ + unpack_record(buf); + table->status= 0; + DBUG_RETURN(0); + } + } DBUG_RETURN(1); } -int ha_ndbcluster::read_range_first(const key_range *start_key, - const key_range *end_key, - bool eq_range, bool sorted) +inline +int ha_ndbcluster::read_range_first_to_buf(const key_range *start_key, + const key_range *end_key, + bool eq_range, bool sorted, + byte* buf) { KEY* key_info; int error= 1; - byte* buf= table->record[0]; - DBUG_ENTER("ha_ndbcluster::read_range_first"); + DBUG_ENTER("ha_ndbcluster::read_range_first_to_buf"); DBUG_PRINT("info", ("eq_range: %d, sorted: %d", eq_range, sorted)); - if (m_active_cursor) - close_scan(); - switch (get_index_type(active_index)){ case PRIMARY_KEY_ORDERED_INDEX: case PRIMARY_KEY_INDEX: @@ -2014,15 +2358,26 @@ int ha_ndbcluster::read_range_first(const key_range *start_key, break; } - // Start the ordered index scan and fetch the first row - error= ordered_index_scan(start_key, end_key, sorted, - buf); - + error= ordered_index_scan(start_key, end_key, sorted, buf); DBUG_RETURN(error); } +int ha_ndbcluster::read_range_first(const key_range *start_key, + const key_range *end_key, + bool eq_range, bool sorted) +{ + byte* buf= table->record[0]; + DBUG_ENTER("ha_ndbcluster::read_range_first"); + + DBUG_RETURN(read_range_first_to_buf(start_key, + end_key, + eq_range, + sorted, + buf)); +} + int ha_ndbcluster::read_range_next() { DBUG_ENTER("ha_ndbcluster::read_range_next"); @@ -2040,7 +2395,8 @@ int ha_ndbcluster::rnd_init(bool scan) { if (!scan) DBUG_RETURN(1); - cursor->restart(); + int res= cursor->restart(); + DBUG_ASSERT(res == 0); } index_init(table->primary_key); DBUG_RETURN(0); @@ -2063,8 +2419,10 @@ int ha_ndbcluster::close_scan() deleteing/updating transaction before closing the scan */ DBUG_PRINT("info", ("ops_pending: %d", ops_pending)); - if (trans->execute(NoCommit) != 0) + if (execute_no_commit(this,trans) != 0) { + no_uncommitted_rows_execute_failure(); DBUG_RETURN(ndb_err(trans)); + } ops_pending= 0; } @@ -2152,7 +2510,7 @@ void ha_ndbcluster::position(const byte *record) DBUG_PRINT("info", ("Getting hidden key")); int hidden_no= table->fields; NdbRecAttr* rec= m_value[hidden_no].rec; - const NDBTAB *tab= (NDBTAB *) m_table; + const NDBTAB *tab= (const NDBTAB *) m_table; const NDBCOL *hidden_col= tab->getColumn(hidden_no); DBUG_ASSERT(hidden_col->getPrimaryKey() && hidden_col->getAutoIncrement() && @@ -2177,10 +2535,26 @@ void ha_ndbcluster::info(uint flag) DBUG_PRINT("info", ("HA_STATUS_NO_LOCK")); if (flag & HA_STATUS_TIME) DBUG_PRINT("info", ("HA_STATUS_TIME")); - if (flag & HA_STATUS_CONST) - DBUG_PRINT("info", ("HA_STATUS_CONST")); if (flag & HA_STATUS_VARIABLE) + { DBUG_PRINT("info", ("HA_STATUS_VARIABLE")); + if (m_table_info) + { + records_update(); + } + else + { + Uint64 rows; + if(ndb_get_table_statistics(m_ndb, m_tabname, &rows, 0) == 0){ + records= rows; + } + } + } + if (flag & HA_STATUS_CONST) + { + DBUG_PRINT("info", ("HA_STATUS_CONST")); + set_rec_per_key(); + } if (flag & HA_STATUS_ERRKEY) { DBUG_PRINT("info", ("HA_STATUS_ERRKEY")); @@ -2273,14 +2647,20 @@ int ha_ndbcluster::extra(enum ha_extra_function operation) break; case HA_EXTRA_IGNORE_DUP_KEY: /* Dup keys don't rollback everything*/ DBUG_PRINT("info", ("HA_EXTRA_IGNORE_DUP_KEY")); - - DBUG_PRINT("info", ("Turning ON use of write instead of insert")); - m_use_write= TRUE; + if (current_thd->lex->sql_command == SQLCOM_REPLACE) + { + DBUG_PRINT("info", ("Turning ON use of write instead of insert")); + m_use_write= TRUE; + } else + { + m_ignore_dup_key_not_supported= TRUE; + } break; case HA_EXTRA_NO_IGNORE_DUP_KEY: DBUG_PRINT("info", ("HA_EXTRA_NO_IGNORE_DUP_KEY")); DBUG_PRINT("info", ("Turning OFF use of write instead of insert")); - m_use_write= false; + m_use_write= FALSE; + m_ignore_dup_key_not_supported= FALSE; break; case HA_EXTRA_RETRIEVE_ALL_COLS: /* Retrieve all columns, not just those where field->query_id is the same as @@ -2327,7 +2707,7 @@ int ha_ndbcluster::extra(enum ha_extra_function operation) void ha_ndbcluster::start_bulk_insert(ha_rows rows) { int bytes, batch; - const NDBTAB *tab= (NDBTAB *) m_table; + const NDBTAB *tab= (const NDBTAB *) m_table; DBUG_ENTER("start_bulk_insert"); DBUG_PRINT("enter", ("rows: %d", (int)rows)); @@ -2368,9 +2748,11 @@ int ha_ndbcluster::end_bulk_insert() DBUG_PRINT("info", ("Sending inserts to NDB, "\ "rows_inserted:%d, bulk_insert_rows: %d", rows_inserted, bulk_insert_rows)); - bulk_insert_not_flushed= false; - if (trans->execute(NoCommit) != 0) - error= ndb_err(trans); + bulk_insert_not_flushed= FALSE; + if (execute_no_commit(this,trans) != 0) { + no_uncommitted_rows_execute_failure(); + my_errno= error= ndb_err(trans); + } } rows_inserted= 0; @@ -2396,7 +2778,7 @@ int ha_ndbcluster::reset() const char **ha_ndbcluster::bas_ext() const -{ static const char *ext[1]= { NullS }; return ext; } +{ static const char *ext[]= { ".ndb", NullS }; return ext; } /* @@ -2407,7 +2789,11 @@ const char **ha_ndbcluster::bas_ext() const double ha_ndbcluster::scan_time() { - return rows2double(records*1000); + DBUG_ENTER("ha_ndbcluster::scan_time()"); + double res= rows2double(records*1000); + DBUG_PRINT("exit", ("table: %s value: %f", + m_tabname, res)); + DBUG_RETURN(res); } @@ -2416,14 +2802,16 @@ THR_LOCK_DATA **ha_ndbcluster::store_lock(THD *thd, enum thr_lock_type lock_type) { DBUG_ENTER("store_lock"); - if (lock_type != TL_IGNORE && m_lock.type == TL_UNLOCK) { - + /* If we are not doing a LOCK TABLE, then allow multiple writers */ - if ((lock_type >= TL_WRITE_CONCURRENT_INSERT && + /* Since NDB does not currently have table locks + this is treated as a ordinary lock */ + + if ((lock_type >= TL_WRITE_ALLOW_WRITE && lock_type <= TL_WRITE) && !thd->in_lock_tables) lock_type= TL_WRITE_ALLOW_WRITE; @@ -2477,9 +2865,6 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) NdbConnection* trans= NULL; DBUG_ENTER("external_lock"); - DBUG_PRINT("enter", ("transaction.ndb_lock_count: %d", - thd->transaction.ndb_lock_count)); - /* Check that this handler instance has a connection set up to the Ndb object of thd @@ -2487,10 +2872,15 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) if (check_ndb_connection()) DBUG_RETURN(1); + Thd_ndb *thd_ndb= (Thd_ndb*)thd->transaction.thd_ndb; + + DBUG_PRINT("enter", ("transaction.thd_ndb->lock_count: %d", + thd_ndb->lock_count)); + if (lock_type != F_UNLCK) { DBUG_PRINT("info", ("lock_type != F_UNLCK")); - if (!thd->transaction.ndb_lock_count++) + if (!thd_ndb->lock_count++) { PRINT_OPTION_FLAGS(thd); @@ -2502,10 +2892,8 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) trans= m_ndb->startTransaction(); if (trans == NULL) - { - thd->transaction.ndb_lock_count--; // We didn't get the lock ERR_RETURN(m_ndb->getNdbError()); - } + no_uncommitted_rows_reset(thd); thd->transaction.stmt.ndb_tid= trans; } else @@ -2518,10 +2906,8 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) trans= m_ndb->startTransaction(); if (trans == NULL) - { - thd->transaction.ndb_lock_count--; // We didn't get the lock ERR_RETURN(m_ndb->getNdbError()); - } + no_uncommitted_rows_reset(thd); /* If this is the start of a LOCK TABLE, a table look @@ -2555,15 +2941,25 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) (NdbConnection*)thd->transaction.all.ndb_tid: (NdbConnection*)thd->transaction.stmt.ndb_tid; DBUG_ASSERT(m_active_trans); - // Start of transaction retrieve_all_fields= FALSE; ops_pending= 0; + { + NDBDICT *dict= m_ndb->getDictionary(); + const NDBTAB *tab; + void *tab_info; + if (!(tab= dict->getTable(m_tabname, &tab_info))) + ERR_RETURN(dict->getNdbError()); + DBUG_PRINT("info", ("Table schema version: %d", tab->getObjectVersion())); + m_table= (void *)tab; + m_table_info= tab_info; + } + no_uncommitted_rows_init(thd); } else { DBUG_PRINT("info", ("lock_type == F_UNLCK")); - if (!--thd->transaction.ndb_lock_count) + if (!--thd_ndb->lock_count) { DBUG_PRINT("trans", ("Last external_lock")); PRINT_OPTION_FLAGS(thd); @@ -2580,7 +2976,28 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) thd->transaction.stmt.ndb_tid= 0; } } - m_active_trans= NULL; + m_table= NULL; + m_table_info= NULL; + /* + This is the place to make sure this handler instance + no longer are connected to the active transaction. + + And since the handler is no longer part of the transaction + it can't have open cursors, ops or blobs pending. + */ + m_active_trans= NULL; + + if (m_active_cursor) + DBUG_PRINT("warning", ("m_active_cursor != NULL")); + m_active_cursor= NULL; + + if (blobs_pending) + DBUG_PRINT("warning", ("blobs_pending != 0")); + blobs_pending= 0; + + if (ops_pending) + DBUG_PRINT("warning", ("ops_pending != 0L")); + ops_pending= 0; } DBUG_RETURN(error); } @@ -2589,6 +3006,8 @@ 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. */ int ha_ndbcluster::start_stmt(THD *thd) @@ -2604,9 +3023,12 @@ int ha_ndbcluster::start_stmt(THD *thd) NdbConnection *tablock_trans= (NdbConnection*)thd->transaction.all.ndb_tid; DBUG_PRINT("info", ("tablock_trans: %x", (uint)tablock_trans)); - DBUG_ASSERT(tablock_trans); trans= m_ndb->hupp(tablock_trans); + DBUG_ASSERT(tablock_trans); +// trans= m_ndb->hupp(tablock_trans); + trans= m_ndb->startTransaction(); if (trans == NULL) ERR_RETURN(m_ndb->getNdbError()); + no_uncommitted_rows_reset(thd); thd->transaction.stmt.ndb_tid= trans; } m_active_trans= trans; @@ -2626,7 +3048,7 @@ int ha_ndbcluster::start_stmt(THD *thd) int ndbcluster_commit(THD *thd, void *ndb_transaction) { int res= 0; - Ndb *ndb= (Ndb*)thd->transaction.ndb; + Ndb *ndb= ((Thd_ndb*)thd->transaction.thd_ndb)->ndb; NdbConnection *trans= (NdbConnection*)ndb_transaction; DBUG_ENTER("ndbcluster_commit"); @@ -2635,7 +3057,7 @@ int ndbcluster_commit(THD *thd, void *ndb_transaction) "stmt" : "all")); DBUG_ASSERT(ndb && trans); - if (trans->execute(Commit) != 0) + if (execute_commit(0,trans) != 0) { const NdbError err= trans->getNdbError(); const NdbOperation *error_op= trans->getNdbErrorOperation(); @@ -2644,7 +3066,7 @@ int ndbcluster_commit(THD *thd, void *ndb_transaction) if (res != -1) ndbcluster_print_error(res, error_op); } - ndb->closeTransaction(trans); + ndb->closeTransaction(trans); DBUG_RETURN(res); } @@ -2656,7 +3078,7 @@ int ndbcluster_commit(THD *thd, void *ndb_transaction) int ndbcluster_rollback(THD *thd, void *ndb_transaction) { int res= 0; - Ndb *ndb= (Ndb*)thd->transaction.ndb; + Ndb *ndb= ((Thd_ndb*)thd->transaction.thd_ndb)->ndb; NdbConnection *trans= (NdbConnection*)ndb_transaction; DBUG_ENTER("ndbcluster_rollback"); @@ -2691,6 +3113,8 @@ static int create_ndb_column(NDBCOL &col, { // Set name col.setName(field->field_name); + // Get char set + CHARSET_INFO *cs= field->charset(); // Set type and sizes const enum enum_field_types mysql_type= field->real_type(); switch (mysql_type) { @@ -2762,15 +3186,22 @@ static int create_ndb_column(NDBCOL &col, case MYSQL_TYPE_STRING: if (field->flags & BINARY_FLAG) col.setType(NDBCOL::Binary); - else + else { col.setType(NDBCOL::Char); - col.setLength(field->pack_length()); + col.setCharset(cs); + } + if (field->pack_length() == 0) + col.setLength(1); // currently ndb does not support size 0 + else + col.setLength(field->pack_length()); break; case MYSQL_TYPE_VAR_STRING: if (field->flags & BINARY_FLAG) col.setType(NDBCOL::Varbinary); - else + else { col.setType(NDBCOL::Varchar); + col.setCharset(cs); + } col.setLength(field->pack_length()); break; // Blob types (all come in as MYSQL_TYPE_BLOB) @@ -2778,8 +3209,10 @@ static int create_ndb_column(NDBCOL &col, case MYSQL_TYPE_TINY_BLOB: if (field->flags & BINARY_FLAG) col.setType(NDBCOL::Blob); - else + else { col.setType(NDBCOL::Text); + col.setCharset(cs); + } col.setInlineSize(256); // No parts col.setPartSize(0); @@ -2789,8 +3222,10 @@ static int create_ndb_column(NDBCOL &col, case MYSQL_TYPE_BLOB: if (field->flags & BINARY_FLAG) col.setType(NDBCOL::Blob); - else + else { col.setType(NDBCOL::Text); + col.setCharset(cs); + } // Use "<=" even if "<" is the exact condition if (field->max_length() <= (1 << 8)) goto mysql_type_tiny_blob; @@ -2809,8 +3244,10 @@ static int create_ndb_column(NDBCOL &col, case MYSQL_TYPE_MEDIUM_BLOB: if (field->flags & BINARY_FLAG) col.setType(NDBCOL::Blob); - else + else { col.setType(NDBCOL::Text); + col.setCharset(cs); + } col.setInlineSize(256); col.setPartSize(4000); col.setStripeSize(8); @@ -2819,8 +3256,10 @@ static int create_ndb_column(NDBCOL &col, case MYSQL_TYPE_LONG_BLOB: if (field->flags & BINARY_FLAG) col.setType(NDBCOL::Blob); - else + else { col.setType(NDBCOL::Text); + col.setCharset(cs); + } col.setInlineSize(256); col.setPartSize(8000); col.setStripeSize(4); @@ -2849,12 +3288,12 @@ static int create_ndb_column(NDBCOL &col, { col.setAutoIncrement(TRUE); ulonglong value= info->auto_increment_value ? - info->auto_increment_value -1 : (ulonglong) 0; + info->auto_increment_value : (ulonglong) 1; DBUG_PRINT("info", ("Autoincrement key, initial: %llu", value)); col.setAutoIncrementInitialValue(value); } else - col.setAutoIncrement(false); + col.setAutoIncrement(FALSE); return 0; } @@ -2872,12 +3311,24 @@ int ha_ndbcluster::create(const char *name, const void *data, *pack_data; const char **key_names= form->keynames.type_names; char name2[FN_HEADLEN]; + bool create_from_engine= (info->table_options & HA_CREATE_FROM_ENGINE); DBUG_ENTER("create"); DBUG_PRINT("enter", ("name: %s", name)); fn_format(name2, name, "", "",2); // Remove the .frm extension set_dbname(name2); - set_tabname(name2); + set_tabname(name2); + + if (create_from_engine) + { + /* + Table alreay exists in NDB and frm file has been created by + caller. + Do Ndb specific stuff, such as create a .ndb file + */ + my_errno= write_ndb_file(); + DBUG_RETURN(my_errno); + } DBUG_PRINT("table", ("name: %s", m_tabname)); tab.setName(m_tabname); @@ -2912,22 +3363,18 @@ int ha_ndbcluster::create(const char *name, col.setName("$PK"); col.setType(NdbDictionary::Column::Bigunsigned); col.setLength(1); - col.setNullable(false); + col.setNullable(FALSE); col.setPrimaryKey(TRUE); col.setAutoIncrement(TRUE); tab.addColumn(col); } - my_errno= 0; - if (check_ndb_connection()) - { - my_errno= HA_ERR_NO_CONNECTION; + if ((my_errno= check_ndb_connection())) DBUG_RETURN(my_errno); - } // Create the table in NDB NDBDICT *dict= m_ndb->getDictionary(); - if (dict->createTable(tab)) + if (dict->createTable(tab) != 0) { const NdbError err= dict->getNdbError(); ERR_PRINT(err); @@ -2940,6 +3387,9 @@ int ha_ndbcluster::create(const char *name, // Create secondary indexes my_errno= build_index_list(form, ILBP_CREATE); + if (!my_errno) + my_errno= write_ndb_file(); + DBUG_RETURN(my_errno); } @@ -2948,7 +3398,7 @@ int ha_ndbcluster::create_ordered_index(const char *name, KEY *key_info) { DBUG_ENTER("create_ordered_index"); - DBUG_RETURN(create_index(name, key_info, false)); + DBUG_RETURN(create_index(name, key_info, FALSE)); } int ha_ndbcluster::create_unique_index(const char *name, @@ -2956,7 +3406,7 @@ int ha_ndbcluster::create_unique_index(const char *name, { DBUG_ENTER("create_unique_index"); - DBUG_RETURN(create_index(name, key_info, true)); + DBUG_RETURN(create_index(name, key_info, TRUE)); } @@ -2975,7 +3425,6 @@ int ha_ndbcluster::create_index(const char *name, DBUG_ENTER("create_index"); DBUG_PRINT("enter", ("name: %s ", name)); - // NdbDictionary::Index ndb_index(name); NdbDictionary::Index ndb_index(name); if (unique) ndb_index.setType(NdbDictionary::Index::UniqueHashIndex); @@ -2983,7 +3432,7 @@ int ha_ndbcluster::create_index(const char *name, { ndb_index.setType(NdbDictionary::Index::OrderedIndex); // TODO Only temporary ordered indexes supported - ndb_index.setLogging(false); + ndb_index.setLogging(FALSE); } ndb_index.setTable(m_tabname); @@ -3016,14 +3465,16 @@ int ha_ndbcluster::rename_table(const char *from, const char *to) set_tabname(from); set_tabname(to, new_tabname); - if (check_ndb_connection()) { - my_errno= HA_ERR_NO_CONNECTION; - DBUG_RETURN(my_errno); - } + if (check_ndb_connection()) + DBUG_RETURN(my_errno= HA_ERR_NO_CONNECTION); + int result= alter_table_name(m_tabname, new_tabname); if (result == 0) + { set_tabname(to); + handler::rename_table(from, to); + } DBUG_RETURN(result); } @@ -3049,6 +3500,7 @@ int ha_ndbcluster::alter_table_name(const char *from, const char *to) ERR_RETURN(dict->getNdbError()); m_table= NULL; + m_table_info= NULL; DBUG_RETURN(0); } @@ -3067,6 +3519,8 @@ int ha_ndbcluster::delete_table(const char *name) if (check_ndb_connection()) DBUG_RETURN(HA_ERR_NO_CONNECTION); + + handler::delete_table(name); DBUG_RETURN(drop_table()); } @@ -3113,11 +3567,12 @@ ulonglong ha_ndbcluster::get_auto_increment() Uint64 auto_value; DBUG_ENTER("get_auto_increment"); DBUG_PRINT("enter", ("m_tabname: %s", m_tabname)); - cache_size= ((rows_to_insert > autoincrement_prefetch) ? - rows_to_insert : autoincrement_prefetch); + cache_size= ((rows_to_insert - rows_inserted < autoincrement_prefetch) ? + rows_to_insert - rows_inserted : + max(rows_to_insert, autoincrement_prefetch)); auto_value= ((skip_auto_increment) ? - m_ndb->readAutoIncrementValue((NDBTAB *) m_table) : - m_ndb->getAutoIncrementValue((NDBTAB *) m_table, cache_size)); + m_ndb->readAutoIncrementValue((const NDBTAB *) m_table) : + m_ndb->getAutoIncrementValue((const NDBTAB *) m_table, cache_size)); DBUG_RETURN((ulonglong) auto_value); } @@ -3132,18 +3587,22 @@ ha_ndbcluster::ha_ndbcluster(TABLE *table_arg): m_active_cursor(NULL), m_ndb(NULL), m_table(NULL), + m_table_info(NULL), m_table_flags(HA_REC_NOT_IN_SEQ | HA_NULL_IN_KEY | - HA_NOT_EXACT_COUNT | - HA_NO_PREFIX_CHAR_KEYS), - m_use_write(false), + HA_AUTO_PART_KEY | + HA_NO_PREFIX_CHAR_KEYS), + m_share(0), + m_use_write(FALSE), + m_ignore_dup_key_not_supported(FALSE), retrieve_all_fields(FALSE), rows_to_insert(1), rows_inserted(0), bulk_insert_rows(1024), - bulk_insert_not_flushed(false), + bulk_insert_not_flushed(FALSE), ops_pending(0), - skip_auto_increment(true), + skip_auto_increment(TRUE), + blobs_pending(0), blobs_buffer(0), blobs_buffer_size(0), dupkey((uint) -1) @@ -3155,15 +3614,12 @@ ha_ndbcluster::ha_ndbcluster(TABLE *table_arg): m_tabname[0]= '\0'; m_dbname[0]= '\0'; - // TODO Adjust number of records and other parameters for proper - // selection of scan/pk access - records= 100; + records= ~(ha_rows)0; // uninitialized block_size= 1024; for (i= 0; i < MAX_KEY; i++) { m_index[i].type= UNDEFINED_INDEX; - m_index[i].unique_name= NULL; m_index[i].unique_index= NULL; m_index[i].index= NULL; } @@ -3180,12 +3636,18 @@ ha_ndbcluster::~ha_ndbcluster() { DBUG_ENTER("~ha_ndbcluster"); + if (m_share) + free_share(m_share); release_metadata(); my_free(blobs_buffer, MYF(MY_ALLOW_ZERO_PTR)); blobs_buffer= 0; // Check for open cursor/transaction + if (m_active_cursor) { + } DBUG_ASSERT(m_active_cursor == NULL); + if (m_active_trans) { + } DBUG_ASSERT(m_active_trans == NULL); DBUG_VOID_RETURN; @@ -3200,6 +3662,7 @@ ha_ndbcluster::~ha_ndbcluster() int ha_ndbcluster::open(const char *name, int mode, uint test_if_locked) { + int res; KEY *key; DBUG_ENTER("open"); DBUG_PRINT("enter", ("name: %s mode: %d test_if_locked: %d", @@ -3222,10 +3685,16 @@ int ha_ndbcluster::open(const char *name, int mode, uint test_if_locked) set_dbname(name); set_tabname(name); - if (check_ndb_connection()) + if (check_ndb_connection()) { + free_share(m_share); m_share= 0; DBUG_RETURN(HA_ERR_NO_CONNECTION); + } + + res= get_metadata(name); + if (!res) + info(HA_STATUS_VARIABLE | HA_STATUS_CONST); - DBUG_RETURN(get_metadata(name)); + DBUG_RETURN(res); } @@ -3237,89 +3706,87 @@ int ha_ndbcluster::open(const char *name, int mode, uint test_if_locked) int ha_ndbcluster::close(void) { DBUG_ENTER("close"); - free_share(m_share); + free_share(m_share); m_share= 0; release_metadata(); m_ndb= NULL; DBUG_RETURN(0); } -Ndb* ha_ndbcluster::seize_ndb() +Thd_ndb* ha_ndbcluster::seize_thd_ndb() { - Ndb* ndb; - DBUG_ENTER("seize_ndb"); + Thd_ndb *thd_ndb; + DBUG_ENTER("seize_thd_ndb"); -#ifdef USE_NDB_POOL - // Seize from pool - ndb= Ndb::seize(); -#else - ndb= new Ndb(""); -#endif - if (ndb->init(max_transactions) != 0) + thd_ndb= new Thd_ndb(); + thd_ndb->ndb->getDictionary()->set_local_table_data_size(sizeof(Ndb_table_local_info)); + if (thd_ndb->ndb->init(max_transactions) != 0) { - ERR_PRINT(ndb->getNdbError()); + ERR_PRINT(thd_ndb->ndb->getNdbError()); /* TODO Alt.1 If init fails because to many allocated Ndb wait on condition for a Ndb object to be released. Alt.2 Seize/release from pool, wait until next release */ - delete ndb; - ndb= NULL; + delete thd_ndb; + thd_ndb= NULL; } - DBUG_RETURN(ndb); + DBUG_RETURN(thd_ndb); } -void ha_ndbcluster::release_ndb(Ndb* ndb) +void ha_ndbcluster::release_thd_ndb(Thd_ndb* thd_ndb) { - DBUG_ENTER("release_ndb"); -#ifdef USE_NDB_POOL - // Release to pool - Ndb::release(ndb); -#else - delete ndb; -#endif + DBUG_ENTER("release_thd_ndb"); + delete thd_ndb; DBUG_VOID_RETURN; } /* - If this thread already has a Ndb object allocated + If this thread already has a Thd_ndb object allocated in current THD, reuse it. Otherwise - seize a Ndb object, assign it to current THD and use it. - - Having a Ndb object also means that a connection to - NDB cluster has been opened. The connection is - checked. + seize a Thd_ndb object, assign it to current THD and use it. */ +Ndb* check_ndb_in_thd(THD* thd) +{ + DBUG_ENTER("check_ndb_in_thd"); + Thd_ndb *thd_ndb= (Thd_ndb*)thd->transaction.thd_ndb; + + if (!thd_ndb) + { + if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb())) + DBUG_RETURN(NULL); + thd->transaction.thd_ndb= thd_ndb; + } + DBUG_RETURN(thd_ndb->ndb); +} + + int ha_ndbcluster::check_ndb_connection() { THD* thd= current_thd; - Ndb* ndb; DBUG_ENTER("check_ndb_connection"); - if (!thd->transaction.ndb) - { - ndb= seize_ndb(); - if (!ndb) - DBUG_RETURN(2); - thd->transaction.ndb= ndb; - } - m_ndb= (Ndb*)thd->transaction.ndb; + if (!(m_ndb= check_ndb_in_thd(thd))) + DBUG_RETURN(HA_ERR_NO_CONNECTION); m_ndb->setDatabaseName(m_dbname); DBUG_RETURN(0); } + void ndbcluster_close_connection(THD *thd) { - Ndb* ndb; + Thd_ndb *thd_ndb= (Thd_ndb*)thd->transaction.thd_ndb; DBUG_ENTER("ndbcluster_close_connection"); - ndb= (Ndb*)thd->transaction.ndb; - ha_ndbcluster::release_ndb(ndb); - thd->transaction.ndb= NULL; + if (thd_ndb) + { + ha_ndbcluster::release_thd_ndb(thd_ndb); + thd->transaction.thd_ndb= NULL; + } DBUG_VOID_RETURN; } @@ -3328,23 +3795,29 @@ void ndbcluster_close_connection(THD *thd) Try to discover one table from NDB */ -int ndbcluster_discover(const char *dbname, const char *name, +int ndbcluster_discover(THD* thd, const char *db, const char *name, const void** frmblob, uint* frmlen) { uint len; const void* data; const NDBTAB* tab; + Ndb* ndb; DBUG_ENTER("ndbcluster_discover"); - DBUG_PRINT("enter", ("db: %s, name: %s", dbname, name)); + DBUG_PRINT("enter", ("db: %s, name: %s", db, name)); - Ndb ndb(dbname); - if ((ndb.init() != 0) && (ndb.waitUntilReady() != 0)) - ERR_RETURN(ndb.getNdbError()); - - if (!(tab= ndb.getDictionary()->getTable(name))) - { - DBUG_PRINT("info", ("Table %s not found", name)); - DBUG_RETURN(1); + if (!(ndb= check_ndb_in_thd(thd))) + DBUG_RETURN(HA_ERR_NO_CONNECTION); + ndb->setDatabaseName(db); + + NDBDICT* dict= ndb->getDictionary(); + dict->set_local_table_data_size(sizeof(Ndb_table_local_info)); + dict->invalidateTable(name); + if (!(tab= dict->getTable(name))) + { + const NdbError err= dict->getNdbError(); + if (err.code == 709) + DBUG_RETURN(1); + ERR_RETURN(err); } DBUG_PRINT("info", ("Found table %s", tab->getName())); @@ -3366,41 +3839,203 @@ int ndbcluster_discover(const char *dbname, const char *name, DBUG_RETURN(0); } - -#ifdef USE_DISCOVER_ON_STARTUP /* - Dicover tables from NDB Cluster - - fetch a list of tables from NDB - - store the frm file for each table on disk - - if the table has an attached frm file - - if the database of the table exists -*/ + Check if a table exists in NDB + + */ -int ndb_discover_tables() +int ndbcluster_table_exists(THD* thd, const char *db, const char *name) { + uint len; + const void* data; + const NDBTAB* tab; + Ndb* ndb; + DBUG_ENTER("ndbcluster_table_exists"); + DBUG_PRINT("enter", ("db: %s, name: %s", db, name)); + + if (!(ndb= check_ndb_in_thd(thd))) + DBUG_RETURN(HA_ERR_NO_CONNECTION); + ndb->setDatabaseName(db); + + NDBDICT* dict= ndb->getDictionary(); + dict->set_local_table_data_size(sizeof(Ndb_table_local_info)); + dict->invalidateTable(name); + if (!(tab= dict->getTable(name))) + { + const NdbError err= dict->getNdbError(); + if (err.code == 709) + DBUG_RETURN(0); + ERR_RETURN(err); + } + + DBUG_PRINT("info", ("Found table %s", tab->getName())); + DBUG_RETURN(1); +} + + + +extern "C" byte* tables_get_key(const char *entry, uint *length, + my_bool not_used __attribute__((unused))) +{ + *length= strlen(entry); + return (byte*) entry; +} + + +int ndbcluster_find_files(THD *thd,const char *db,const char *path, + const char *wild, bool dir, List<char> *files) +{ + DBUG_ENTER("ndbcluster_find_files"); + DBUG_PRINT("enter", ("db: %s", db)); + { // extra bracket to avoid gcc 2.95.3 warning uint i; + Ndb* ndb; + char name[FN_REFLEN]; + HASH ndb_tables, ok_tables; NdbDictionary::Dictionary::List list; - NdbDictionary::Dictionary* dict; - char path[FN_REFLEN]; - DBUG_ENTER("ndb_discover_tables"); - - /* List tables in NDB Cluster kernel */ - dict= g_ndb->getDictionary(); + + if (!(ndb= check_ndb_in_thd(thd))) + DBUG_RETURN(HA_ERR_NO_CONNECTION); + + if (dir) + DBUG_RETURN(0); // Discover of databases not yet supported + + // List tables in NDB + NDBDICT *dict= ndb->getDictionary(); if (dict->listObjects(list, NdbDictionary::Object::UserTable) != 0) - ERR_RETURN(g_ndb->getNdbError()); - + ERR_RETURN(dict->getNdbError()); + + if (hash_init(&ndb_tables, system_charset_info,list.count,0,0, + (hash_get_key)tables_get_key,0,0)) + { + DBUG_PRINT("error", ("Failed to init HASH ndb_tables")); + DBUG_RETURN(-1); + } + + if (hash_init(&ok_tables, system_charset_info,32,0,0, + (hash_get_key)tables_get_key,0,0)) + { + DBUG_PRINT("error", ("Failed to init HASH ok_tables")); + hash_free(&ndb_tables); + DBUG_RETURN(-1); + } + for (i= 0 ; i < list.count ; i++) { NdbDictionary::Dictionary::List::Element& t= list.elements[i]; + DBUG_PRINT("info", ("Found %s/%s in NDB", t.database, t.name)); + + // Add only tables that belongs to db + if (my_strcasecmp(system_charset_info, t.database, db)) + continue; - DBUG_PRINT("discover", ("%d: %s/%s", t.id, t.database, t.name)); - if (create_table_from_handler(t.database, t.name, true)) - DBUG_PRINT("info", ("Could not discover %s/%s", t.database, t.name)); + // Apply wildcard to list of tables in NDB + if (wild) + { + if (lower_case_table_names) + { + if (wild_case_compare(files_charset_info, t.name, wild)) + continue; + } + else if (wild_compare(t.name,wild,0)) + continue; + } + DBUG_PRINT("info", ("Inserting %s into ndb_tables hash", t.name)); + my_hash_insert(&ndb_tables, (byte*)thd->strdup(t.name)); } - DBUG_RETURN(0); + + char *file_name; + List_iterator<char> it(*files); + List<char> delete_list; + while ((file_name=it++)) + { + DBUG_PRINT("info", ("%s", file_name)); + if (hash_search(&ndb_tables, file_name, strlen(file_name))) + { + DBUG_PRINT("info", ("%s existed in NDB _and_ on disk ", file_name)); + // File existed in NDB and as frm file, put in ok_tables list + my_hash_insert(&ok_tables, (byte*)file_name); + continue; + } + + // File is not in NDB, check for .ndb file with this name + (void)strxnmov(name, FN_REFLEN, + mysql_data_home,"/",db,"/",file_name,ha_ndb_ext,NullS); + DBUG_PRINT("info", ("Check access for %s", name)); + if (access(name, F_OK)) + { + DBUG_PRINT("info", ("%s did not exist on disk", name)); + // .ndb file did not exist on disk, another table type + continue; + } + + DBUG_PRINT("info", ("%s existed on disk", name)); + // The .ndb file exists on disk, but it's not in list of tables in ndb + // Verify that handler agrees table is gone. + if (ndbcluster_table_exists(thd, db, file_name) == 0) + { + DBUG_PRINT("info", ("NDB says %s does not exists", file_name)); + it.remove(); + // Put in list of tables to remove from disk + delete_list.push_back(thd->strdup(file_name)); + } + } + + // Check for new files to discover + DBUG_PRINT("info", ("Checking for new files to discover")); + List<char> create_list; + for (i= 0 ; i < ndb_tables.records ; i++) + { + file_name= hash_element(&ndb_tables, i); + if (!hash_search(&ok_tables, file_name, strlen(file_name))) + { + DBUG_PRINT("info", ("%s must be discovered", file_name)); + // File is in list of ndb tables and not in ok_tables + // This table need to be created + create_list.push_back(thd->strdup(file_name)); + } + } + + // Lock mutex before deleting and creating frm files + pthread_mutex_lock(&LOCK_open); + + if (!global_read_lock) + { + // Delete old files + List_iterator_fast<char> it3(delete_list); + while ((file_name=it3++)) + { + DBUG_PRINT("info", ("Remove table %s/%s",db, file_name )); + // Delete the table and all related files + TABLE_LIST table_list; + bzero((char*) &table_list,sizeof(table_list)); + table_list.db= (char*) db; + table_list.alias=table_list.real_name=(char*)file_name; + (void)mysql_rm_table_part2(thd, &table_list, + /* if_exists */ TRUE, + /* drop_temporary */ FALSE, + /* drop_view */ FALSE, + /* dont_log_query*/ TRUE); + } + } + + // Create new files + List_iterator_fast<char> it2(create_list); + while ((file_name=it2++)) + { + DBUG_PRINT("info", ("Table %s need discovery", name)); + if (ha_create_table_from_engine(thd, db, file_name, TRUE) == 0) + files->push_back(thd->strdup(file_name)); + } + + pthread_mutex_unlock(&LOCK_open); + + hash_free(&ok_tables); + hash_free(&ndb_tables); + } // extra bracket to avoid gcc 2.95.3 warning + DBUG_RETURN(0); } -#endif /* @@ -3410,34 +4045,55 @@ int ndb_discover_tables() bool ndbcluster_init() { + int res; DBUG_ENTER("ndbcluster_init"); // Set connectstring if specified if (ndbcluster_connectstring != 0) - { DBUG_PRINT("connectstring", ("%s", ndbcluster_connectstring)); - Ndb::setConnectString(ndbcluster_connectstring); + if ((g_ndb_cluster_connection= + new Ndb_cluster_connection(ndbcluster_connectstring)) == 0) + { + DBUG_PRINT("error",("Ndb_cluster_connection(%s)",ndbcluster_connectstring)); + DBUG_RETURN(TRUE); } + // Create a Ndb object to open the connection to NDB - g_ndb= new Ndb("sys"); + g_ndb= new Ndb(g_ndb_cluster_connection, "sys"); + g_ndb->getDictionary()->set_local_table_data_size(sizeof(Ndb_table_local_info)); if (g_ndb->init() != 0) { ERR_PRINT (g_ndb->getNdbError()); DBUG_RETURN(TRUE); } - if (g_ndb->waitUntilReady() != 0) + + if ((res= g_ndb_cluster_connection->connect(1)) == 0) { - ERR_PRINT (g_ndb->getNdbError()); - DBUG_RETURN(TRUE); + g_ndb->waitUntilReady(10); + } + else if(res == 1) + { + if (g_ndb_cluster_connection->start_connect_thread()) { + DBUG_PRINT("error", ("g_ndb_cluster_connection->start_connect_thread()")); + DBUG_RETURN(TRUE); + } } + else + { + DBUG_ASSERT(res == -1); + DBUG_PRINT("error", ("permanent error")); + DBUG_RETURN(TRUE); + } + (void) hash_init(&ndbcluster_open_tables,system_charset_info,32,0,0, (hash_get_key) ndbcluster_get_key,0,0); pthread_mutex_init(&ndbcluster_mutex,MY_MUTEX_INIT_FAST); + ndbcluster_inited= 1; #ifdef USE_DISCOVER_ON_STARTUP if (ndb_discover_tables() != 0) DBUG_RETURN(TRUE); #endif - DBUG_RETURN(false); + DBUG_RETURN(FALSE); } @@ -3450,15 +4106,15 @@ bool ndbcluster_init() bool ndbcluster_end() { DBUG_ENTER("ndbcluster_end"); - - delete g_ndb; + if(g_ndb) + delete g_ndb; g_ndb= NULL; + if (g_ndb_cluster_connection) + delete g_ndb_cluster_connection; + g_ndb_cluster_connection= NULL; if (!ndbcluster_inited) DBUG_RETURN(0); hash_free(&ndbcluster_open_tables); -#ifdef USE_NDB_POOL - ndb_pool_release(); -#endif pthread_mutex_destroy(&ndbcluster_mutex); ndbcluster_inited= 0; DBUG_RETURN(0); @@ -3584,8 +4240,6 @@ ha_ndbcluster::records_in_range(uint inx, key_range *min_key, NDB_INDEX_TYPE idx_type= get_index_type(inx); DBUG_ENTER("records_in_range"); - DBUG_PRINT("enter", ("inx: %u", inx)); - // Prevent partial read of hash indexes by returning HA_POS_ERROR if ((idx_type == UNIQUE_INDEX || idx_type == PRIMARY_KEY_INDEX) && ((min_key && min_key->length < key_length) || @@ -3760,4 +4414,89 @@ static int unpackfrm(const void **unpack_data, uint *unpack_len, DBUG_RETURN(0); } + +static +int +ndb_get_table_statistics(Ndb* ndb, const char * table, + Uint64* row_count, Uint64* commit_count) +{ + DBUG_ENTER("ndb_get_table_statistics"); + DBUG_PRINT("enter", ("table: %s", table)); + + do + { + NdbConnection* pTrans= ndb->startTransaction(); + if (pTrans == NULL) + break; + + NdbScanOperation* pOp= pTrans->getNdbScanOperation(table); + if (pOp == NULL) + break; + + NdbResultSet* rs= pOp->readTuples(NdbOperation::LM_CommittedRead); + if (rs == 0) + break; + + int check= pOp->interpret_exit_last_row(); + if (check == -1) + break; + + Uint64 rows, commits; + pOp->getValue(NdbDictionary::Column::ROW_COUNT, (char*)&rows); + pOp->getValue(NdbDictionary::Column::COMMIT_COUNT, (char*)&commits); + + check= pTrans->execute(NoCommit); + if (check == -1) + break; + + Uint64 sum_rows= 0; + Uint64 sum_commits= 0; + while((check= rs->nextResult(TRUE)) == 0) + { + sum_rows+= rows; + sum_commits+= commits; + } + + if (check == -1) + break; + + ndb->closeTransaction(pTrans); + if(row_count) + * row_count= sum_rows; + if(commit_count) + * commit_count= sum_commits; + DBUG_PRINT("exit", ("records: %u commits: %u", sum_rows, sum_commits)); + DBUG_RETURN(0); + } while(0); + + DBUG_PRINT("exit", ("failed")); + DBUG_RETURN(-1); +} + +/* + Create a .ndb file to serve as a placeholder indicating + that the table with this name is a ndb table +*/ + +int ha_ndbcluster::write_ndb_file() +{ + File file; + bool error=1; + char path[FN_REFLEN]; + + DBUG_ENTER("write_ndb_file"); + DBUG_PRINT("enter", ("db: %s, name: %s", m_dbname, m_tabname)); + + (void)strxnmov(path, FN_REFLEN, + mysql_data_home,"/",m_dbname,"/",m_tabname,ha_ndb_ext,NullS); + + if ((file=my_create(path, CREATE_MODE,O_RDWR | O_TRUNC,MYF(MY_WME))) >= 0) + { + // It's an empty file + error=0; + my_close(file,MYF(0)); + } + DBUG_RETURN(error); +} + #endif /* HAVE_NDBCLUSTER_DB */ diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index 777e234f935..245d906c5ae 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -52,7 +52,6 @@ typedef enum ndb_index_type { typedef struct ndb_index_data { NDB_INDEX_TYPE type; void *index; - const char * unique_name; void *unique_index; } NDB_INDEX_DATA; @@ -63,6 +62,20 @@ typedef struct st_ndbcluster_share { uint table_name_length,use_count; } NDB_SHARE; +/* + Place holder for ha_ndbcluster thread specific data +*/ + +class Thd_ndb { + public: + Thd_ndb(); + ~Thd_ndb(); + Ndb *ndb; + ulong count; + uint lock_count; + int error; +}; + class ha_ndbcluster: public handler { public: @@ -93,6 +106,10 @@ class ha_ndbcluster: public handler int read_range_first(const key_range *start_key, const key_range *end_key, bool eq_range, bool sorted); + int read_range_first_to_buf(const key_range *start_key, + const key_range *end_key, + bool eq_range, bool sorted, + byte* buf); int read_range_next(); bool get_error_message(int error, String *buf); @@ -122,12 +139,12 @@ class ha_ndbcluster: public handler bool low_byte_first() const { #ifdef WORDS_BIGENDIAN - return false; + return FALSE; #else - return true; + return TRUE; #endif } - bool has_transactions() { return true; } + bool has_transactions() { return TRUE; } const char* index_type(uint key_number) { switch (get_index_type(key_number)) { @@ -147,8 +164,8 @@ class ha_ndbcluster: public handler void start_bulk_insert(ha_rows rows); int end_bulk_insert(); - static Ndb* seize_ndb(); - static void release_ndb(Ndb* ndb); + static Thd_ndb* seize_thd_ndb(); + static void release_thd_ndb(Thd_ndb* thd_ndb); uint8 table_cache_type() { return HA_CACHE_TBL_NOCACHE; } private: @@ -162,8 +179,6 @@ class ha_ndbcluster: public handler int build_index_list(TABLE *tab, enum ILBP phase); int get_metadata(const char* path); void release_metadata(); - const char* get_index_name(uint idx_no) const; - const char* get_unique_index_name(uint idx_no) const; NDB_INDEX_TYPE get_index_type(uint idx_no) const; NDB_INDEX_TYPE get_index_type_from_table(uint index_no) const; @@ -192,15 +207,14 @@ class ha_ndbcluster: public handler uint fieldnr, const byte* field_ptr); int set_ndb_key(NdbOperation*, Field *field, uint fieldnr, const byte* field_ptr); - int set_ndb_value(NdbOperation*, Field *field, uint fieldnr); - int get_ndb_value(NdbOperation*, Field *field, uint fieldnr); + int set_ndb_value(NdbOperation*, Field *field, uint fieldnr, bool *set_blob_value= 0); + int get_ndb_value(NdbOperation*, Field *field, uint fieldnr, byte*); friend int g_get_ndb_blobs_value(NdbBlob *ndb_blob, void *arg); int get_ndb_blobs_value(NdbBlob *last_ndb_blob); int set_primary_key(NdbOperation *op, const byte *key); int set_primary_key(NdbOperation *op); int set_primary_key_from_old_data(NdbOperation *op, const byte *old_data); - int set_bounds(NdbIndexScanOperation *ndb_op, const key_range *key, - int bound); + int set_bounds(NdbIndexScanOperation *ndb_op, const key_range *keys[2]); int key_cmp(uint keynr, const byte * old_row, const byte * new_row); void print_results(); @@ -208,13 +222,16 @@ class ha_ndbcluster: public handler int ndb_err(NdbConnection*); bool uses_blob_value(bool all_fields); + int write_ndb_file(); + private: int check_ndb_connection(); NdbConnection *m_active_trans; NdbResultSet *m_active_cursor; Ndb *m_ndb; - void *m_table; + void *m_table; + void *m_table_info; char m_dbname[FN_HEADLEN]; //char m_schemaname[FN_HEADLEN]; char m_tabname[FN_HEADLEN]; @@ -226,6 +243,7 @@ class ha_ndbcluster: public handler typedef union { NdbRecAttr *rec; NdbBlob *blob; void *ptr; } NdbValue; NdbValue m_value[NDB_MAX_ATTRIBUTES_IN_TABLE]; bool m_use_write; + bool m_ignore_dup_key_not_supported; bool retrieve_all_fields; ha_rows rows_to_insert; ha_rows rows_inserted; @@ -238,6 +256,15 @@ class ha_ndbcluster: public handler char *blobs_buffer; uint32 blobs_buffer_size; uint dupkey; + + void set_rec_per_key(); + void records_update(); + void no_uncommitted_rows_execute_failure(); + void no_uncommitted_rows_update(int); + void no_uncommitted_rows_init(THD *); + void no_uncommitted_rows_reset(THD *); + + friend int execute_no_commit(ha_ndbcluster*, NdbConnection*); }; bool ndbcluster_init(void); @@ -248,8 +275,11 @@ int ndbcluster_rollback(THD *thd, void* ndb_transaction); void ndbcluster_close_connection(THD *thd); -int ndbcluster_discover(const char* dbname, const char* name, +int ndbcluster_discover(THD* thd, const char* dbname, const char* name, const void** frmblob, uint* frmlen); +int ndbcluster_find_files(THD *thd,const char *db,const char *path, + const char *wild, bool dir, List<char> *files); +int ndbcluster_table_exists(THD* thd, const char *db, const char *name); int ndbcluster_drop_database(const char* path); void ndbcluster_print_error(int error, const NdbOperation *error_op); diff --git a/sql/handler.cc b/sql/handler.cc index 5185e7f8921..bb5e980f7bf 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -103,7 +103,7 @@ const char *tx_isolation_names[] = { "READ-UNCOMMITTED", "READ-COMMITTED", "REPEATABLE-READ", "SERIALIZABLE", NullS}; TYPELIB tx_isolation_typelib= {array_elements(tx_isolation_names)-1,"", - tx_isolation_names}; + tx_isolation_names, NULL}; enum db_type ha_resolve_by_name(const char *name, uint namelen) { @@ -157,12 +157,11 @@ enum db_type ha_checktype(enum db_type database_type) break; } - return - DB_TYPE_UNKNOWN != (enum db_type) thd->variables.table_type ? - (enum db_type) thd->variables.table_type : - DB_TYPE_UNKNOWN != (enum db_type) global_system_variables.table_type ? - (enum db_type) global_system_variables.table_type : - DB_TYPE_MYISAM; + return ((enum db_type) thd->variables.table_type != DB_TYPE_UNKNOWN ? + (enum db_type) thd->variables.table_type : + (enum db_type) global_system_variables.table_type != + DB_TYPE_UNKNOWN ? + (enum db_type) global_system_variables.table_type : DB_TYPE_MYISAM); } /* ha_checktype */ @@ -555,7 +554,7 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) query_cache.invalidate(thd->transaction.changed_tables); #endif /*HAVE_QUERY_CACHE*/ if (error && trans == &thd->transaction.all && mysql_bin_log.is_open()) - sql_print_error("Error: Got error during commit; Binlog is not up to date!"); + sql_print_error("Got error during commit; Binlog is not up to date!"); thd->variables.tx_isolation=thd->session_tx_isolation; if (operation_done) { @@ -784,10 +783,14 @@ bool ha_flush_logs() int ha_delete_table(enum db_type table_type, const char *path) { + handler *file; char tmp_path[FN_REFLEN]; - handler *file=get_new_handler((TABLE*) 0, table_type); - if (!file) + + /* DB_TYPE_UNKNOWN is used in ALTER TABLE when renaming only .frm files */ + if (table_type == DB_TYPE_UNKNOWN || + ! (file=get_new_handler((TABLE*) 0, table_type))) return ENOENT; + if (lower_case_table_names == 2 && !(file->table_flags() & HA_FILE_BASED)) { /* Ensure that table handler get path in lower case */ @@ -939,23 +942,6 @@ int handler::read_first_row(byte * buf, uint primary_key) } -/* Set a timestamp in record */ - -void handler::update_timestamp(byte *record) -{ - long skr= (long) table->in_use->query_start(); -#ifdef WORDS_BIGENDIAN - if (table->db_low_byte_first) - { - int4store(record,skr); - } - else -#endif - longstore(record,skr); - return; -} - - /* Generate the next auto-increment number based on increment and offset @@ -1240,6 +1226,21 @@ void handler::print_error(int error, myf errflag) case HA_ERR_NO_REFERENCED_ROW: textno=ER_NO_REFERENCED_ROW; break; + case HA_ERR_NO_SUCH_TABLE: + { + /* + We have to use path to find database name instead of using + table->table_cache_key because if the table didn't exist, then + table_cache_key was not set up + */ + char *db; + char buff[FN_REFLEN]; + uint length=dirname_part(buff,table->path); + buff[length-1]=0; + db=buff+dirname_length(buff); + my_error(ER_NO_SUCH_TABLE,MYF(0),db,table->table_name); + break; + } default: { /* The error was "unknown" to this function. @@ -1320,14 +1321,15 @@ int handler::rename_table(const char * from, const char * to) } /* - Tell the handler to turn on or off logging to the handler's recovery log + Tell the handler to turn on or off transaction in the handler */ -int ha_recovery_logging(THD *thd, bool on) +int ha_enable_transaction(THD *thd, bool on) { int error=0; - DBUG_ENTER("ha_recovery_logging"); + DBUG_ENTER("ha_enable_transaction"); + thd->transaction.on= on; DBUG_RETURN(error); } @@ -1385,6 +1387,71 @@ int ha_create_table(const char *name, HA_CREATE_INFO *create_info, DBUG_RETURN(error != 0); } +/* + Try to discover table from engine and + if found, write the frm file to disk. + + RETURN VALUES: + 0 : Table existed in engine and created + on disk if so requested + 1 : Table does not exist + >1 : error + +*/ + +int ha_create_table_from_engine(THD* thd, + const char *db, + const char *name, + bool create_if_found) +{ + int error; + const void *frmblob; + uint frmlen; + char path[FN_REFLEN]; + HA_CREATE_INFO create_info; + TABLE table; + DBUG_ENTER("ha_create_table_from_engine"); + DBUG_PRINT("enter", ("name '%s'.'%s' create_if_found: %d", + db, name, create_if_found)); + + bzero((char*) &create_info,sizeof(create_info)); + + if ((error= ha_discover(thd, db, name, &frmblob, &frmlen))) + DBUG_RETURN(error); + /* + Table exists in handler + frmblob and frmlen are set + */ + + if (create_if_found) + { + (void)strxnmov(path,FN_REFLEN,mysql_data_home,"/",db,"/",name,NullS); + // Save the frm file + if ((error = writefrm(path, frmblob, frmlen))) + goto err_end; + + if (openfrm(thd, path,"",0,(uint) READ_ALL, 0, &table)) + DBUG_RETURN(1); + + update_create_info_from_table(&create_info, &table); + create_info.table_options|= HA_CREATE_FROM_ENGINE; + + if (lower_case_table_names == 2 && + !(table.file->table_flags() & HA_FILE_BASED)) + { + /* Ensure that handler gets name in lower case */ + my_casedn_str(files_charset_info, path); + } + + error=table.file->create(path,&table,&create_info); + VOID(closefrm(&table)); + } + +err_end: + my_free((char*) frmblob, MYF(MY_ALLOW_ZERO_PTR)); + DBUG_RETURN(error); +} + static int NEAR_F delete_file(const char *name,const char *ext,int extflag) { char buff[FN_REFLEN]; @@ -1490,17 +1557,21 @@ int ha_change_key_cache(KEY_CACHE *old_key_cache, /* Try to discover one table from handler(s) + + RETURN + 0 ok. In this case *frmblob and *frmlen are set + 1 error. frmblob and frmlen may not be set */ -int ha_discover(const char* dbname, const char* name, - const void** frmblob, uint* frmlen) +int ha_discover(THD *thd, const char *db, const char *name, + const void **frmblob, uint *frmlen) { int error= 1; // Table does not exist in any handler DBUG_ENTER("ha_discover"); - DBUG_PRINT("enter", ("db: %s, name: %s", dbname, name)); + DBUG_PRINT("enter", ("db: %s, name: %s", db, name)); #ifdef HAVE_NDBCLUSTER_DB if (have_ndbcluster == SHOW_OPTION_YES) - error= ndbcluster_discover(dbname, name, frmblob, frmlen); + error= ndbcluster_discover(thd, db, name, frmblob, frmlen); #endif if (!error) statistic_increment(ha_discover_count,&LOCK_status); @@ -1509,6 +1580,55 @@ int ha_discover(const char* dbname, const char* name, /* + Call this function in order to give the handler the possiblity + to ask engine if there are any new tables that should be written to disk + or any dropped tables that need to be removed from disk +*/ + +int +ha_find_files(THD *thd,const char *db,const char *path, + const char *wild, bool dir, List<char> *files) +{ + int error= 0; + DBUG_ENTER("ha_find_files"); + DBUG_PRINT("enter", ("db: %s, path: %s, wild: %s, dir: %d", + db, path, wild, dir)); +#ifdef HAVE_NDBCLUSTER_DB + if (have_ndbcluster == SHOW_OPTION_YES) + error= ndbcluster_find_files(thd, db, path, wild, dir, files); +#endif + DBUG_RETURN(error); + + +} + +#ifdef NOT_YET_USED + +/* + Ask handler if the table exists in engine + + RETURN + 0 Table does not exist + 1 Table exists + # Error code + + */ +int ha_table_exists(THD* thd, const char* db, const char* name) +{ + int error= 2; + DBUG_ENTER("ha_table_exists"); + DBUG_PRINT("enter", ("db: %s, name: %s", db, name)); +#ifdef HAVE_NDBCLUSTER_DB + if (have_ndbcluster == SHOW_OPTION_YES) + error= ndbcluster_table_exists(thd, db, name); +#endif + DBUG_RETURN(error); +} + +#endif + + +/* Read first row between two ranges. Store ranges for future calls to read_range_next @@ -1554,9 +1674,9 @@ int handler::read_range_first(const key_range *start_key, start_key->length, start_key->flag); if (result) - DBUG_RETURN((result == HA_ERR_KEY_NOT_FOUND || - result == HA_ERR_END_OF_FILE) ? HA_ERR_END_OF_FILE : - result); + DBUG_RETURN((result == HA_ERR_KEY_NOT_FOUND) + ? HA_ERR_END_OF_FILE + : result); DBUG_RETURN (compare_key(end_range) <= 0 ? 0 : HA_ERR_END_OF_FILE); } diff --git a/sql/handler.h b/sql/handler.h index 9a08b8ed78c..c408425ed60 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -139,16 +139,16 @@ #define HA_CACHE_TBL_TRANSACT 4 -enum db_type -{ +enum db_type +{ DB_TYPE_UNKNOWN=0,DB_TYPE_DIAB_ISAM=1, DB_TYPE_HASH,DB_TYPE_MISAM,DB_TYPE_PISAM, DB_TYPE_RMS_ISAM, DB_TYPE_HEAP, DB_TYPE_ISAM, DB_TYPE_MRG_ISAM, DB_TYPE_MYISAM, DB_TYPE_MRG_MYISAM, - DB_TYPE_BERKELEY_DB, DB_TYPE_INNODB, + DB_TYPE_BERKELEY_DB, DB_TYPE_INNODB, DB_TYPE_GEMINI, DB_TYPE_NDBCLUSTER, DB_TYPE_EXAMPLE_DB, DB_TYPE_ARCHIVE_DB, DB_TYPE_CSV_DB, - + DB_TYPE_DEFAULT // Must be last }; @@ -165,16 +165,24 @@ enum row_type { ROW_TYPE_NOT_USED=-1, ROW_TYPE_DEFAULT, ROW_TYPE_FIXED, /* struct to hold information about the table that should be created */ /* Bits in used_fields */ -#define HA_CREATE_USED_AUTO 1 -#define HA_CREATE_USED_RAID 2 -#define HA_CREATE_USED_UNION 4 -#define HA_CREATE_USED_INSERT_METHOD 8 -#define HA_CREATE_USED_MIN_ROWS 16 -#define HA_CREATE_USED_MAX_ROWS 32 -#define HA_CREATE_USED_AVG_ROW_LENGTH 64 -#define HA_CREATE_USED_PACK_KEYS 128 -#define HA_CREATE_USED_CHARSET 256 -#define HA_CREATE_USED_DEFAULT_CHARSET 512 +#define HA_CREATE_USED_AUTO (1L << 0) +#define HA_CREATE_USED_RAID (1L << 1) +#define HA_CREATE_USED_UNION (1L << 2) +#define HA_CREATE_USED_INSERT_METHOD (1L << 3) +#define HA_CREATE_USED_MIN_ROWS (1L << 4) +#define HA_CREATE_USED_MAX_ROWS (1L << 5) +#define HA_CREATE_USED_AVG_ROW_LENGTH (1L << 6) +#define HA_CREATE_USED_PACK_KEYS (1L << 7) +#define HA_CREATE_USED_CHARSET (1L << 8) +#define HA_CREATE_USED_DEFAULT_CHARSET (1L << 9) +#define HA_CREATE_USED_DATADIR (1L << 10) +#define HA_CREATE_USED_INDEXDIR (1L << 11) +#define HA_CREATE_USED_ENGINE (1L << 12) +#define HA_CREATE_USED_CHECKSUM (1L << 13) +#define HA_CREATE_USED_DELAY_KEY_WRITE (1L << 14) +#define HA_CREATE_USED_ROW_FORMAT (1L << 15) +#define HA_CREATE_USED_COMMENT (1L << 16) +#define HA_CREATE_USED_PASSWORD (1L << 17) typedef struct st_thd_trans { void *bdb_tid; @@ -205,6 +213,7 @@ typedef struct st_ha_create_information uint raid_type,raid_chunks; uint merge_insert_method; bool table_existed; /* 1 in create if table existed */ + bool frm_only; /* 1 if no ha_create_table() */ } HA_CREATE_INFO; @@ -287,7 +296,6 @@ public: {} virtual ~handler(void) { /* TODO: DBUG_ASSERT(inited == NONE); */ } int ha_open(const char *name, int mode, int test_if_locked); - void update_timestamp(byte *record); void update_auto_increment(); virtual void print_error(int error, myf errflag); virtual bool get_error_message(int error, String *buf); @@ -300,7 +308,15 @@ public: virtual const key_map *keys_to_use_for_scanning() { return &key_map_empty; } virtual bool has_transactions(){ return 0;} virtual uint extra_rec_buf_length() { return 0; } - virtual ha_rows estimate_number_of_rows() { return records+EXTRA_RECORDS; } + + /* + Return upper bound of current number of records in the table + (max. of how many records one will retrieve when doing a full table scan) + If upper bound is not known, HA_POS_ERROR should be returned as a max + possible upper bound. + */ + virtual ha_rows estimate_rows_upper_bound() + { return records+EXTRA_RECORDS; } virtual const char *index_type(uint key_number) { DBUG_ASSERT(0); return "";} @@ -546,6 +562,8 @@ void ha_close_connection(THD* thd); enum db_type ha_checktype(enum db_type database_type); int ha_create_table(const char *name, HA_CREATE_INFO *create_info, bool update_create_info); +int ha_create_table_from_engine(THD* thd, const char *db, const char *name, + bool create_if_found); int ha_delete_table(enum db_type db_type, const char *path); void ha_drop_database(char* path); int ha_init_key_cache(const char *name, KEY_CACHE *key_cache); @@ -564,8 +582,13 @@ int ha_savepoint(THD *thd, char *savepoint_name); int ha_autocommit_or_rollback(THD *thd, int error); void ha_set_spin_retries(uint retries); bool ha_flush_logs(void); -int ha_recovery_logging(THD *thd, bool on); +int ha_enable_transaction(THD *thd, bool on); int ha_change_key_cache(KEY_CACHE *old_key_cache, KEY_CACHE *new_key_cache); -int ha_discover(const char* dbname, const char* name, +int ha_discover(THD* thd, const char* dbname, const char* name, const void** frmblob, uint* frmlen); +int ha_find_files(THD *thd,const char *db,const char *path, + const char *wild, bool dir,List<char>* files); +int ha_table_exists(THD* thd, const char* db, const char* name); + + diff --git a/sql/item.cc b/sql/item.cc index 134b04d5540..6730b0dd4a2 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -45,11 +45,11 @@ void item_init(void) } Item::Item(): - name_length(0), fixed(0) + name_length(0), fixed(0), + collation(default_charset(), DERIVATION_COERCIBLE) { marker= 0; maybe_null=null_value=with_sum_func=unsigned_flag=0; - collation.set(default_charset(), DERIVATION_COERCIBLE); name= 0; decimals= 0; max_length= 0; @@ -113,13 +113,14 @@ void Item::cleanup() DBUG_PRINT("info", ("Item: 0x%lx", this)); DBUG_PRINT("info", ("Type: %d", (int)type())); fixed=0; + marker= 0; DBUG_VOID_RETURN; } Item_ident::Item_ident(const char *db_name_par,const char *table_name_par, const char *field_name_par) :orig_db_name(db_name_par), orig_table_name(table_name_par), - orig_field_name(field_name_par), changed_during_fix_field(0), + orig_field_name(field_name_par), db_name(db_name_par), table_name(table_name_par), field_name(field_name_par), cached_field_index(NO_CACHED_FIELD_INDEX), cached_table(0), depended_from(0) @@ -133,7 +134,6 @@ Item_ident::Item_ident(THD *thd, Item_ident *item) orig_db_name(item->orig_db_name), orig_table_name(item->orig_table_name), orig_field_name(item->orig_field_name), - changed_during_fix_field(0), db_name(item->db_name), table_name(item->table_name), field_name(item->field_name), @@ -150,11 +150,6 @@ void Item_ident::cleanup() table_name, orig_table_name, field_name, orig_field_name)); Item::cleanup(); - if (changed_during_fix_field) - { - *changed_during_fix_field= this; - changed_during_fix_field= 0; - } db_name= orig_db_name; table_name= orig_table_name; field_name= orig_field_name; @@ -451,19 +446,47 @@ Item_field::Item_field(Field *f) have_privileges(0), any_privileges(0) { set_field(f); - collation.set(DERIVATION_IMPLICIT); - fixed= 1; + /* + field_name and talbe_name should not point to garbage + if this item is to be reused + */ + orig_table_name= orig_field_name= ""; } Item_field::Item_field(THD *thd, Field *f) - :Item_ident(NullS, thd->strdup(f->table_name), - thd->strdup(f->field_name)), + :Item_ident(f->table->table_cache_key, f->table_name, f->field_name), item_equal(0), no_const_subst(0), have_privileges(0), any_privileges(0) { + /* + We always need to provide Item_field with a fully qualified field + name to avoid ambiguity when executing prepared statements like + SELECT * from d1.t1, d2.t1; (assuming d1.t1 and d2.t1 have columns + with same names). + This is because prepared statements never deal with wildcards in + select list ('*') and always fix fields using fully specified path + (i.e. db.table.column). + No check for OOM: if db_name is NULL, we'll just get + "Field not found" error. + We need to copy db_name, table_name and field_name because they must + be allocated in the statement memory, not in table memory (the table + structure can go away and pop up again between subsequent executions + of a prepared statement). + */ + if (thd->current_arena->is_stmt_prepare()) + { + if (db_name) + orig_db_name= thd->strdup(db_name); + orig_table_name= thd->strdup(table_name); + orig_field_name= thd->strdup(field_name); + /* + We don't restore 'name' in cleanup because it's not changed + during execution. Still we need it to point to persistent + memory if this item is to be reused. + */ + name= (char*) orig_field_name; + } set_field(f); - collation.set(DERIVATION_IMPLICIT); - fixed= 1; } // Constructor need to process subselect with temporary tables (see Item) @@ -490,6 +513,21 @@ void Item_field::set_field(Field *field_par) db_name=field_par->table->table_cache_key; unsigned_flag=test(field_par->flags & UNSIGNED_FLAG); collation.set(field_par->charset(), DERIVATION_IMPLICIT); + fixed= 1; +} + + +/* + Reset this item to point to a field from the new temporary table. + This is used when we create a new temporary table for each execution + of prepared statement. +*/ + +void Item_field::reset_field(Field *f) +{ + set_field(f); + /* 'name' is pointing at field->field_name of old field */ + name= (char*) f->field_name; } const char *Item_ident::full_name() const @@ -1042,7 +1080,7 @@ int Item_param::save_in_field(Field *field, bool no_conversions) return field->store(str_value.ptr(), str_value.length(), str_value.charset()); case NULL_VALUE: - return set_field_to_null(field); + return set_field_to_null_with_conversions(field, no_conversions); case NO_VALUE: default: DBUG_ASSERT(0); @@ -1145,9 +1183,10 @@ String *Item_param::val_str(String* str) return str; case TIME_VALUE: { - if (str->reserve(MAX_DATE_REP_LENGTH)) + if (str->reserve(MAX_DATE_STRING_REP_LENGTH)) break; - TIME_to_string(&value.time, str); + str->length((uint) my_TIME_to_str(&value.time, (char*) str->ptr())); + str->set_charset(&my_charset_bin); return str; } case NULL_VALUE: @@ -1177,24 +1216,19 @@ const String *Item_param::query_val_str(String* str) const case TIME_VALUE: { char *buf, *ptr; - String tmp; str->length(0); /* TODO: in case of error we need to notify replication that binary log contains wrong statement */ - if (str->reserve(MAX_DATE_REP_LENGTH+3)) + if (str->reserve(MAX_DATE_STRING_REP_LENGTH+3)) break; /* Create date string inplace */ buf= str->c_ptr_quick(); ptr= buf; *ptr++= '\''; - tmp.set(ptr, MAX_DATE_REP_LENGTH, &my_charset_bin); - tmp.length(0); - TIME_to_string(&value.time, &tmp); - - ptr+= tmp.length(); + ptr+= (uint) my_TIME_to_str(&value.time, ptr); *ptr++= '\''; str->length((uint32) (ptr - buf)); break; @@ -1372,7 +1406,7 @@ bool Item_ref_null_helper::get_date(TIME *ltime, uint fuzzydate) static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, Item_ident *item) { - // store pointer on SELECT_LEX from wich item is dependent + // store pointer on SELECT_LEX from which item is dependent item->depended_from= last; current->mark_as_dependent(last); if (thd->lex->describe & DESCRIBE_EXTENDED) @@ -1416,6 +1450,7 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) TABLE_LIST *table_list; Item **refer= (Item **)not_found_item; uint counter; + bool not_used; // Prevent using outer fields in subselects, that is not supported now SELECT_LEX *cursel= (SELECT_LEX *) thd->lex->current_select; if (cursel->master_unit()->first_select()->linkage != DERIVED_TABLE_TYPE) @@ -1440,11 +1475,9 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) enum_parsing_place place= prev_subselect_item->parsing_place; /* check table fields only if subquery used somewhere out of HAVING - or SELECT list or outer SELECT do not use groupping (i.e. tables - are accessable) + or outer SELECT do not use groupping (i.e. tables are accessable) */ - if (((place != IN_HAVING && - place != SELECT_LIST) || + if ((place != IN_HAVING || (sl->with_sum_func == 0 && sl->group_list.elements == 0)) && (tmp= find_field_in_tables(thd, this, table_list, ref, @@ -1469,7 +1502,8 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) } if (sl->resolve_mode == SELECT_LEX::SELECT_MODE && (refer= find_item_in_list(this, sl->item_list, &counter, - REPORT_EXCEPT_NOT_FOUND)) != + REPORT_EXCEPT_NOT_FOUND, + ¬_used)) != (Item **) not_found_item) { if (*refer && (*refer)->fixed) // Avoid crash in case of error @@ -1517,14 +1551,11 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) return -1; } - Item_ref *rf; - *ref= rf= new Item_ref(last->ref_pointer_array + counter, - ref, - (char *)table_name, - (char *)field_name); - register_item_tree_changing(ref); + Item_ref *rf= new Item_ref(last->ref_pointer_array + counter, + (char *)table_name, (char *)field_name); if (!rf) return 1; + thd->change_item_tree(ref, rf); /* rf is Item_ref => never substitute other items (in this case) during fix_fields() => we can use rf after fix_fields() @@ -1541,12 +1572,11 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) if (last->having_fix_field) { Item_ref *rf; - *ref= rf= new Item_ref(ref, *ref, - (cached_table->db[0]?cached_table->db:0), - (char *)cached_table->alias, - (char *)field_name); + rf= new Item_ref((cached_table->db[0] ? cached_table->db : 0), + (char*) cached_table->alias, (char*) field_name); if (!rf) return 1; + thd->change_item_tree(ref, rf); /* rf is Item_ref => never substitute other items (in this case) during fix_fields() => we can use rf after fix_fields() @@ -1625,6 +1655,7 @@ void Item_field::cleanup() I.e. we can drop 'field'. */ field= result_field= 0; + DBUG_VOID_RETURN; } /* @@ -1669,7 +1700,8 @@ Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal) /* - Set a pointer to the multiple equality the field reference belongs to (if any) + Set a pointer to the multiple equality the field reference belongs to + (if any) SYNOPSIS equal_fields_propagator() @@ -1708,7 +1740,21 @@ Item *Item_field::equal_fields_propagator(byte *arg) /* - Set a pointer to the multiple equality the field reference belongs to (if any) + Mark the item to not be part of substitution if it's not a binary item + See comments in Arg_comparator::set_compare_func() for details +*/ + +Item *Item_field::set_no_const_sub(byte *arg) +{ + if (field->charset() != &my_charset_bin) + no_const_subst=1; + return this; +} + + +/* + Set a pointer to the multiple equality the field reference belongs to + (if any) SYNOPSIS replace_equal_field_processor() @@ -1744,6 +1790,7 @@ bool Item_field::replace_equal_field_processor(byte *arg) return 0; } + void Item::init_make_field(Send_field *tmp_field, enum enum_field_types field_type) { @@ -1754,7 +1801,9 @@ void Item::init_make_field(Send_field *tmp_field, tmp_field->table_name= empty_name; tmp_field->col_name= name; tmp_field->charsetnr= collation.collation->number; - tmp_field->flags=maybe_null ? 0 : NOT_NULL_FLAG; + tmp_field->flags= (maybe_null ? 0 : NOT_NULL_FLAG) | + (my_binary_compare(collation.collation) ? + BINARY_FLAG : 0); tmp_field->type=field_type; tmp_field->length=max_length; tmp_field->decimals=decimals; @@ -2261,15 +2310,17 @@ bool Item_field::send(Protocol *protocol, String *buffer) return protocol->store(result_field); } + /* This is used for HAVING clause Find field in select list having the same name - */ +*/ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) { DBUG_ASSERT(fixed == 0); uint counter; + bool not_used; if (!ref) { TABLE_LIST *table_list; @@ -2289,13 +2340,14 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) first_select()->linkage != DERIVED_TABLE_TYPE) ? REPORT_EXCEPT_NOT_FOUND : - REPORT_ALL_ERRORS))) == + REPORT_ALL_ERRORS), ¬_used)) == (Item **)not_found_item) { - upward_lookup= 1; Field *tmp= (Field*) not_found_field; + SELECT_LEX *last= 0; + upward_lookup= 1; /* - We can't find table field in table list of current select, + We can't find table field in select list of current select, consequently we have to find it in outer subselect(s). We can't join lists of outer & current select, because of scope of view rules. For example if both tables (outer & current) have @@ -2303,15 +2355,14 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) mention of table name, but if we join tables in one list it will cause error ER_NON_UNIQ_ERROR in find_item_in_list. */ - SELECT_LEX *last=0; for ( ; sl ; sl= (prev_unit= sl->master_unit())->outer_select()) { last= sl; Item_subselect *prev_subselect_item= prev_unit->item; if (sl->resolve_mode == SELECT_LEX::SELECT_MODE && (ref= find_item_in_list(this, sl->item_list, - &counter, - REPORT_EXCEPT_NOT_FOUND)) != + &counter, REPORT_EXCEPT_NOT_FOUND, + ¬_used)) != (Item **)not_found_item) { if (*ref && (*ref)->fixed) // Avoid crash in case of error @@ -2333,8 +2384,7 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) or SELECT list or outer SELECT do not use groupping (i.e. tables are accessable) */ - if (((place != IN_HAVING && - place != SELECT_LIST) || + if ((place != IN_HAVING || (sl->with_sum_func == 0 && sl->group_list.elements == 0)) && (tmp= find_field_in_tables(thd, this, table_list, reference, @@ -2369,9 +2419,9 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) if (!ref) return 1; - else if (!tmp) + if (!tmp) return -1; - else if (ref == (Item **)not_found_item && tmp == not_found_field) + if (ref == (Item **)not_found_item && tmp == not_found_field) { if (upward_lookup) { @@ -2384,22 +2434,26 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) // Call to report error find_item_in_list(this, *(thd->lex->current_select->get_item_list()), - &counter, - REPORT_ALL_ERRORS); + &counter, REPORT_ALL_ERRORS, ¬_used); } - ref= 0; + ref= 0; // Safety return 1; } - else if (tmp != not_found_field) + if (tmp != not_found_field) { - ref= 0; // To prevent "delete *ref;" on ~Item_erf() of this item + /* + Set ref to 0 as we are replacing this item with the found item + and this will ensure we get an error if this item would be + used elsewhere + */ + ref= 0; // Safety if (tmp != view_ref_found) { Item_field* fld; - if (!((*reference)= fld= new Item_field(tmp))) + if (!(fld= new Item_field(tmp))) return 1; + thd->change_item_tree(reference, fld); mark_as_dependent(thd, last, thd->lex->current_select, fld); - register_item_tree_changing(reference); return 0; } /* @@ -2416,15 +2470,15 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) } else { - if (!(*ref)->fixed) - { - my_error(ER_ILLEGAL_REFERENCE, MYF(0), name, - "forward reference in item list"); - return -1; - } - mark_as_dependent(thd, last, thd->lex->current_select, - this); - ref= last->ref_pointer_array + counter; + if (!(*ref)->fixed) + { + my_error(ER_ILLEGAL_REFERENCE, MYF(0), name, + "forward reference in item list"); + return -1; + } + mark_as_dependent(thd, last, thd->lex->current_select, + this); + ref= last->ref_pointer_array + counter; } } else if (!ref) @@ -2478,8 +2532,6 @@ void Item_ref::cleanup() DBUG_ENTER("Item_ref::cleanup"); Item_ident::cleanup(); result_field= 0; - if (hook_ptr) - *hook_ptr= orig_item; DBUG_VOID_RETURN; } @@ -2602,7 +2654,6 @@ bool Item_default_value::fix_fields(THD *thd, def_field->move_field(def_field->table->default_values - def_field->table->record[0]); set_field(def_field); - fixed= 1; return 0; } @@ -2660,7 +2711,6 @@ bool Item_insert_value::fix_fields(THD *thd, set_field(new Field_null(0, 0, Field::NONE, tmp_field->field_name, tmp_field->table, &my_charset_bin)); } - fixed= 1; return 0; } @@ -2779,10 +2829,12 @@ Item_result item_cmp_type(Item_result a,Item_result b) } -Item *resolve_const_item(Item *item,Item *comp_item) +void resolve_const_item(THD *thd, Item **ref, Item *comp_item) { + Item *item= *ref; + Item *new_item; if (item->basic_const_item()) - return item; // Can't be better + return; // Can't be better Item_result res_type=item_cmp_type(comp_item->result_type(), item->result_type()); char *name=item->name; // Alloced by sql_alloc @@ -2793,27 +2845,32 @@ Item *resolve_const_item(Item *item,Item *comp_item) String tmp(buff,sizeof(buff),&my_charset_bin),*result; result=item->val_str(&tmp); if (item->null_value) - return new Item_null(name); - uint length=result->length(); - char *tmp_str=sql_strmake(result->ptr(),length); - return new Item_string(name,tmp_str,length,result->charset()); + new_item= new Item_null(name); + else + { + uint length= result->length(); + char *tmp_str= sql_strmake(result->ptr(), length); + new_item= new Item_string(name, tmp_str, length, result->charset()); + } } - if (res_type == INT_RESULT) + else if (res_type == INT_RESULT) { longlong result=item->val_int(); uint length=item->max_length; bool null_value=item->null_value; - return (null_value ? (Item*) new Item_null(name) : - (Item*) new Item_int(name,result,length)); + new_item= (null_value ? (Item*) new Item_null(name) : + (Item*) new Item_int(name, result, length)); } else { // It must REAL_RESULT double result=item->val(); uint length=item->max_length,decimals=item->decimals; bool null_value=item->null_value; - return (null_value ? (Item*) new Item_null(name) : - (Item*) new Item_real(name,result,decimals,length)); + new_item= (null_value ? (Item*) new Item_null(name) : (Item*) + new Item_real(name, result, decimals, length)); } + if (new_item) + thd->change_item_tree(ref, new_item); } /* @@ -3040,6 +3097,7 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item) else field_example= 0; max_length= real_length(item); + maybe_null= item->maybe_null; collation.set(item->collation); } @@ -3057,62 +3115,90 @@ static Item_result type_convertor[4][4]= {STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT}, {ROW_RESULT, ROW_RESULT, ROW_RESULT, ROW_RESULT}}; + +/* + Values of 'from' field can be stored in 'to' field. + + SYNOPSIS + is_attr_compatible() + from Item which values should be saved + to Item where values should be saved + + RETURN + 1 can be saved + 0 can not be saved +*/ + +inline bool is_attr_compatible(Item *from, Item *to) +{ + return ((to->max_length >= from->max_length) && + (to->maybe_null || !from->maybe_null) && + (to->result_type() != STRING_RESULT || + from->result_type() != STRING_RESULT || + my_charset_same(from->collation.collation, + to->collation.collation))); +} + + bool Item_type_holder::join_types(THD *thd, Item *item) { uint32 new_length= real_length(item); - bool change_field= 0, skip_store_field= 0; - Item_result new_type= type_convertor[item_type][item->result_type()]; + bool use_new_field= 0, use_expression_type= 0; + Item_result new_result_type= type_convertor[item_type][item->result_type()]; + bool item_is_a_field= item->type() == Item::FIELD_ITEM; - // we have both fields - if (field_example && item->type() == Item::FIELD_ITEM) + /* + Check if both items point to fields: in this case we + can adjust column types of result table in the union smartly. + */ + if (field_example && item_is_a_field) { Field *field= ((Item_field *)item)->field; - if (field_example->field_cast_type() != field->field_cast_type()) + /* Can 'field_example' field store data of the column? */ + if ((use_new_field= + (!field->field_cast_compatible(field_example->field_cast_type()) || + !is_attr_compatible(item, this)))) { - if (!(change_field= - field_example->field_cast_compatible(field->field_cast_type()))) - { - /* - if old field can't store value of 'worse' new field we will make - decision about result field type based only on Item result type - */ - if (!field->field_cast_compatible(field_example->field_cast_type())) - skip_store_field= 1; - } + /* + The old field can't store value of the new field. + Check if the new field can store value of the old one. + */ + use_expression_type|= + (!field_example->field_cast_compatible(field->field_cast_type()) || + !is_attr_compatible(this, item)); } } - - // size/type should be changed - if (change_field || - (new_type != item_type) || - (max_length < new_length) || - ((new_type == INT_RESULT) && - (decimals < item->decimals)) || - (!maybe_null && item->maybe_null) || - (item_type == STRING_RESULT && new_type == STRING_RESULT && - !my_charset_same(collation.collation, item->collation.collation))) - { - // new field has some parameters worse then current - skip_store_field|= (change_field && - (max_length > new_length) || - ((new_type == INT_RESULT) && - (decimals > item->decimals)) || - (maybe_null && !item->maybe_null) || - (item_type == STRING_RESULT && - new_type == STRING_RESULT && - !my_charset_same(collation.collation, - item->collation.collation))); + else if (field_example || item_is_a_field) + { /* - It is safe assign pointer on field, because it will be used just after - all JOIN::prepare calls and before any SELECT execution + Expression types can't be mixed with field types, we have to use + expression types. */ - if (skip_store_field || item->type() != Item::FIELD_ITEM) + use_new_field= 1; // make next if test easier + use_expression_type= 1; + } + + /* Check whether size/type of the result item should be changed */ + if (use_new_field || + (new_result_type != item_type) || (new_length > max_length) || + (!maybe_null && item->maybe_null) || + (item_type == STRING_RESULT && + collation.collation != item->collation.collation)) + { + const char *old_cs,*old_derivation; + if (use_expression_type || !item_is_a_field) field_example= 0; else + { + /* + It is safe to assign a pointer to field here, because it will be used + before any table is closed. + */ field_example= ((Item_field*) item)->field; + } - const char *old_cs= collation.collation->name, - *old_derivation= collation.derivation_name(); + old_cs= collation.collation->name; + old_derivation= collation.derivation_name(); if (item_type == STRING_RESULT && collation.aggregate(item->collation)) { my_error(ER_CANT_AGGREGATE_2COLLATIONS, MYF(0), @@ -3126,18 +3212,18 @@ bool Item_type_holder::join_types(THD *thd, Item *item) max_length= max(max_length, new_length); decimals= max(decimals, item->decimals); maybe_null|= item->maybe_null; - item_type= new_type; + item_type= new_result_type; } DBUG_ASSERT(item_type != ROW_RESULT); return 0; } + uint32 Item_type_holder::real_length(Item *item) { if (item->type() == Item::FIELD_ITEM) - { return ((Item_field *)item)->max_disp_length(); - } + switch (item->result_type()) { case STRING_RESULT: @@ -3173,6 +3259,14 @@ String *Item_type_holder::val_str(String*) return 0; } +void Item_result_field::cleanup() +{ + DBUG_ENTER("Item_result_field::cleanup()"); + Item::cleanup(); + result_field= 0; + DBUG_VOID_RETURN; +} + /***************************************************************************** ** Instantiate templates *****************************************************************************/ diff --git a/sql/item.h b/sql/item.h index f8d04b6c851..d3e53af1523 100644 --- a/sql/item.h +++ b/sql/item.h @@ -99,7 +99,8 @@ public: static void *operator new(size_t size) {return (void*) sql_alloc((uint) size); } static void *operator new(size_t size, MEM_ROOT *mem_root) { return (void*) alloc_root(mem_root, (uint) size); } - static void operator delete(void *ptr,size_t size) {} /*lint -e715 */ + static void operator delete(void *ptr,size_t size) {} + static void operator delete(void *ptr,size_t size, MEM_ROOT *mem_root) {} enum Type {FIELD_ITEM, FUNC_ITEM, SUM_FUNC_ITEM, STRING_ITEM, INT_ITEM, REAL_ITEM, NULL_ITEM, VARBIN_ITEM, @@ -219,7 +220,8 @@ public: a constant expression */ virtual bool basic_const_item() const { return 0; } - virtual Item *new_item() { return 0; } /* Only for const items */ + /* cloning of constant items (0 if it is not const) */ + virtual Item *new_item() { return 0; } virtual cond_result eq_cmp_result() const { return COND_OK; } inline uint float_length(uint decimals_par) const { return decimals != NOT_FIXED_DEC ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;} @@ -237,17 +239,29 @@ public: virtual void print(String *str_arg) { str_arg->append(full_name()); } void print_item_w_name(String *); virtual void update_used_tables() {} - virtual void split_sum_func(Item **ref_pointer_array, List<Item> &fields) {} + virtual void split_sum_func(THD *thd, Item **ref_pointer_array, + List<Item> &fields) {} virtual bool get_date(TIME *ltime,uint fuzzydate); virtual bool get_time(TIME *ltime); virtual bool get_date_result(TIME *ltime,uint fuzzydate) { return get_date(ltime,fuzzydate); } virtual bool is_null() { return 0; } + /* + it is "top level" item of WHERE clause and we do not need correct NULL + handling + */ virtual void top_level_item() {} + /* + set field of temporary table for Item which can be switched on temporary + table during query processing (groupping and so on) + */ virtual void set_result_field(Field *field) {} virtual bool is_result_field() { return 0; } virtual bool is_bool_func() { return 0; } virtual void save_in_result_field(bool no_conversions) {} + /* + set value of aggegate function in case of no rows for groupping were found + */ virtual void no_rows_in_result() {} virtual Item *copy_or_same(THD *thd) { return this; } virtual Item *copy_andor_structure(THD *thd) { return this; } @@ -271,6 +285,7 @@ public: virtual bool remove_fixed(byte * arg) { fixed= 0; return 0; } virtual bool collect_item_field_processor(byte * arg) { return 0; } virtual Item *equal_fields_propagator(byte * arg) { return this; } + virtual Item *set_no_const_sub(byte *arg) { return this; } virtual bool replace_equal_field_processor(byte * arg) { return 0; } virtual Item *this_item() { return this; } /* For SPs mostly. */ @@ -407,6 +422,7 @@ public: class st_select_lex; class Item_ident :public Item { +protected: /* We have to store initial values of db_name, table_name and field_name to be able to restore them during cleanup() because they can be @@ -416,7 +432,6 @@ class Item_ident :public Item const char *orig_db_name; const char *orig_table_name; const char *orig_field_name; - Item **changed_during_fix_field; public: const char *db_name; const char *table_name; @@ -439,8 +454,6 @@ public: Item_ident(THD *thd, Item_ident *item); const char *full_name() const; void cleanup(); - void register_item_tree_changing(Item **ref) - { changed_during_fix_field= ref; } bool remove_dependence_processor(byte * arg); void print(String *str); @@ -474,13 +487,21 @@ public: field(0), result_field(0), item_equal(0), no_const_subst(0), have_privileges(0), any_privileges(0) { collation.set(DERIVATION_IMPLICIT); } - // Constructor need to process subselect with temporary tables (see Item) + /* + Constructor needed to process subselect with temporary tables (see Item) + */ Item_field(THD *thd, Item_field *item); /* - Constructor used inside setup_wild(), ensures that field and table - names will live as long as Item_field (important in prep. stmt.) + Constructor used inside setup_wild(), ensures that field, table, + and database names will live as long as Item_field (this is important + in prepared statements). */ Item_field(THD *thd, Field *field); + /* + If this constructor is used, fix_fields() won't work, because + db_name, table_name and column_name are unknown. It's necessary to call + reset_field() before fix_fields() for all fields created this way. + */ Item_field(Field *field); enum Type type() const { return FIELD_ITEM; } bool eq(const Item *item, bool binary_cmp) const; @@ -491,6 +512,7 @@ public: longlong val_int_result(); String *str_result(String* tmp); bool send(Protocol *protocol, String *str_arg); + void reset_field(Field *f); bool fix_fields(THD *, struct st_table_list *, Item **); void make_field(Send_field *tmp_field); int save_in_field(Field *field,bool no_conversions); @@ -515,6 +537,7 @@ public: void cleanup(); Item_equal *find_item_equal(COND_EQUAL *cond_equal); Item *equal_fields_propagator(byte *arg); + Item *set_no_const_sub(byte *arg); bool replace_equal_field_processor(byte *arg); inline uint32 max_disp_length() { return field->max_length(); } Item_field *filed_for_view_update() { return this; } @@ -873,8 +896,8 @@ public: class Item_empty_string :public Item_string { public: - Item_empty_string(const char *header,uint length) :Item_string("",0, - &my_charset_bin) + Item_empty_string(const char *header,uint length, CHARSET_INFO *cs= NULL) : + Item_string("",0, cs ? cs : &my_charset_bin) { name=(char*) header; max_length=length;} void make_field(Send_field *field); }; @@ -931,6 +954,7 @@ public: { save_in_field(result_field, no_conversions); } + void cleanup(); }; @@ -939,20 +963,13 @@ class Item_ref :public Item_ident public: Field *result_field; /* Save result here */ Item **ref; - Item **hook_ptr; /* These two to restore */ - Item *orig_item; /* things in 'cleanup()' */ - Item_ref(Item **hook, Item *original,const char *db_par, - const char *table_name_par, const char *field_name_par) - :Item_ident(db_par, table_name_par, field_name_par), result_field(0), - ref(0), hook_ptr(hook), orig_item(original) {} - Item_ref(Item **item, Item **hook, - const char *table_name_par, const char *field_name_par) - :Item_ident(NullS, table_name_par, field_name_par), result_field(0), - ref(item), hook_ptr(hook), orig_item(hook ? *hook:0) {} - // Constructor need to process subselect with temporary tables (see Item) - Item_ref(THD *thd, Item_ref *item, Item **hook) - :Item_ident(thd, item), result_field(item->result_field), ref(item->ref), - hook_ptr(hook), orig_item(hook ? *hook : 0) {} + Item_ref(const char *db_par, const char *table_name_par, + const char *field_name_par) + :Item_ident(db_par, table_name_par, field_name_par), result_field(0), ref(0) {} + Item_ref(Item **item, const char *table_name_par, const char *field_name_par) + :Item_ident(NullS, table_name_par, field_name_par), result_field(0), ref(item) {} + /* Constructor need to process subselect with temporary tables (see Item) */ + Item_ref(THD *thd, Item_ref *item) :Item_ident(thd, item), result_field(item->result_field), ref(item->ref) {} enum Type type() const { return REF_ITEM; } bool eq(const Item *item, bool binary_cmp) const { return ref && (*ref)->eq(item, binary_cmp); } @@ -1020,7 +1037,7 @@ protected: public: Item_ref_null_helper(Item_in_subselect* master, Item **item, const char *table_name_par, const char *field_name_par): - Item_ref(item, NULL, table_name_par, field_name_par), owner(master) {} + Item_ref(item, table_name_par, field_name_par), owner(master) {} double val(); longlong val_int(); String* val_str(String* s); @@ -1458,5 +1475,5 @@ public: extern Item_buff *new_Item_buff(Item *item); extern Item_result item_cmp_type(Item_result a,Item_result b); -extern Item *resolve_const_item(Item *item,Item *cmp_item); +extern void resolve_const_item(THD *thd, Item **ref, Item *cmp_item); extern bool field_is_equal_to_item(Field *field,Item *item); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 66af96f671f..467cd8313a4 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -145,7 +145,7 @@ void Item_func_not_all::print(String *str) 1 Item was replaced with an integer version of the item */ -static bool convert_constant_item(Field *field, Item **item) +static bool convert_constant_item(THD *thd, Field *field, Item **item) { if ((*item)->const_item()) { @@ -153,7 +153,7 @@ static bool convert_constant_item(Field *field, Item **item) { Item *tmp=new Item_int_with_ref(field->val_int(), *item); if (tmp) - *item=tmp; + thd->change_item_tree(item, tmp); return 1; // Item was replaced } } @@ -164,6 +164,7 @@ static bool convert_constant_item(Field *field, Item **item) void Item_bool_func2::fix_length_and_dec() { max_length= 1; // Function returns 0 or 1 + THD *thd= current_thd; /* As some compare functions are generated after sql_yacc, @@ -188,17 +189,26 @@ void Item_bool_func2::fix_length_and_dec() { uint strong= 0; uint weak= 0; + uint32 dummy_offset; DTCollation coll; if (args[0]->result_type() == STRING_RESULT && args[1]->result_type() == STRING_RESULT && - !my_charset_same(args[0]->collation.collation, - args[1]->collation.collation) && + String::needs_conversion(0, args[0]->collation.collation, + args[1]->collation.collation, + &dummy_offset) && !coll.set(args[0]->collation, args[1]->collation, TRUE)) { Item* conv= 0; + Item_arena *arena= thd->current_arena, backup; strong= coll.strong; weak= strong ? 0 : 1; + /* + In case we're in statement prepare, create conversion item + in its memory: it will be reused on each execute. + */ + if (arena->is_stmt_prepare()) + thd->set_n_backup_item_arena(arena, &backup); if (args[weak]->type() == STRING_ITEM) { String tmp, cstr; @@ -211,26 +221,13 @@ void Item_bool_func2::fix_length_and_dec() } else { - THD *thd= current_thd; - /* - In case we're in prepared statement, create conversion - item in its memory: it will be reused on each execute. - (and don't juggle with mem_root's if it is ordinary statement). - We come here only during first fix_fields() because after creating - conversion item we will have arguments with compatible collations. - */ - Item_arena *arena= thd->current_arena, backup; - if (arena->is_conventional()) - arena= 0; - else - thd->set_n_backup_item_arena(arena, &backup); conv= new Item_func_conv_charset(args[weak], args[strong]->collation.collation); - if (arena) - thd->restore_backup_item_arena(arena, &backup); conv->collation.set(args[weak]->collation.derivation); conv->fix_fields(thd, 0, &conv); } + if (arena->is_stmt_prepare()) + thd->restore_backup_item_arena(arena, &backup); if (args[weak]->type() == FIELD_ITEM) ((Item_field *)args[weak])->no_const_subst= 1; args[weak]= conv ? conv : args[weak]; @@ -248,9 +245,9 @@ void Item_bool_func2::fix_length_and_dec() if (args[0]->type() == FIELD_ITEM) { Field *field=((Item_field*) args[0])->field; - if (field->store_for_compare()) + if (field->can_be_compared_as_longlong()) { - if (convert_constant_item(field,&args[1])) + if (convert_constant_item(thd, field,&args[1])) { cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, INT_RESULT); // Works for all types. @@ -261,9 +258,9 @@ void Item_bool_func2::fix_length_and_dec() if (args[1]->type() == FIELD_ITEM /* && !args[1]->const_item() */) { Field *field=((Item_field*) args[1])->field; - if (field->store_for_compare()) + if (field->can_be_compared_as_longlong()) { - if (convert_constant_item(field,&args[0])) + if (convert_constant_item(thd, field,&args[0])) { cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, INT_RESULT); // Works for all types. @@ -323,6 +320,17 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) func= &Arg_comparator::compare_binary_string; else if (func == &Arg_comparator::compare_e_string) func= &Arg_comparator::compare_e_binary_string; + + /* + As this is binary comparsion, mark all fields that they can't be + transformed. Otherwise we would get into trouble with comparisons + like: + WHERE col= 'j' AND col LIKE BINARY 'j' + which would be transformed to: + WHERE col= 'j' + */ + (*a)->transform(&Item::set_no_const_sub, (byte*) 0); + (*b)->transform(&Item::set_no_const_sub, (byte*) 0); } } else if (type == INT_RESULT) @@ -841,6 +849,7 @@ longlong Item_func_interval::val_int() void Item_func_between::fix_length_and_dec() { max_length= 1; + THD *thd= current_thd; /* As some compare functions are generated after sql_yacc, @@ -861,11 +870,15 @@ void Item_func_between::fix_length_and_dec() if (args[0]->type() == FIELD_ITEM) { Field *field=((Item_field*) args[0])->field; - if (field->store_for_compare()) + if (field->can_be_compared_as_longlong()) { - if (convert_constant_item(field,&args[1])) + /* + The following can't be recoded with || as convert_constant_item + changes the argument + */ + if (convert_constant_item(thd, field,&args[1])) cmp_type=INT_RESULT; // Works for all types. - if (convert_constant_item(field,&args[2])) + if (convert_constant_item(thd, field,&args[2])) cmp_type=INT_RESULT; // Works for all types. } } @@ -1167,6 +1180,15 @@ Item_func_nullif::val_str(String *str) return res; } + +bool +Item_func_nullif::is_null() +{ + if (!cmp.compare()) + return (null_value=1); + return 0; +} + /* CASE expression Return the matching ITEM or NULL if all compares (including else) failed @@ -1751,6 +1773,7 @@ void Item_func_in::fix_length_and_dec() { Item **arg, **arg_end; uint const_itm= 1; + THD *thd= current_thd; agg_cmp_type(&cmp_type, args, arg_count); @@ -1788,6 +1811,9 @@ void Item_func_in::fix_length_and_dec() Conversion is possible: All IN arguments are constants. */ + Item_arena *arena= thd->current_arena, backup; + if (arena->is_stmt_prepare()) + thd->set_n_backup_item_arena(arena, &backup); for (arg= args+1, arg_end= args+arg_count; arg < arg_end; arg++) { if (!my_charset_same(cmp_collation.collation, @@ -1803,6 +1829,8 @@ void Item_func_in::fix_length_and_dec() arg[0]= conv; } } + if (arena->is_stmt_prepare()) + thd->restore_backup_item_arena(arena, &backup); } } @@ -1830,7 +1858,7 @@ void Item_func_in::fix_length_and_dec() DBUG_ASSERT(0); return; } - if (array && !(current_thd->is_fatal_error)) // If not EOM + if (array && !(thd->is_fatal_error)) // If not EOM { uint j=0; for (uint i=1 ; i < arg_count ; i++) @@ -2048,7 +2076,8 @@ Item *Item_cond::transform(Item_transformer transformer, byte *arg) } -void Item_cond::split_sum_func(Item **ref_pointer_array, List<Item> &fields) +void Item_cond::split_sum_func(THD *thd, Item **ref_pointer_array, + List<Item> &fields) { List_iterator<Item> li(list); Item *item; @@ -2057,13 +2086,15 @@ void Item_cond::split_sum_func(Item **ref_pointer_array, List<Item> &fields) while ((item=li++)) { if (item->with_sum_func && item->type() != SUM_FUNC_ITEM) - item->split_sum_func(ref_pointer_array, fields); + item->split_sum_func(thd, ref_pointer_array, fields); else if (item->used_tables() || item->type() == SUM_FUNC_ITEM) { + Item **ref= li.ref(); uint el= fields.elements; + Item *new_item= new Item_ref(ref_pointer_array + el, 0, item->name); fields.push_front(item); ref_pointer_array[el]= item; - li.replace(new Item_ref(ref_pointer_array + el, li.ref(), 0, item->name)); + thd->change_item_tree(ref, new_item); } item->update_used_tables(); used_tables_cache|=item->used_tables(); diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 8d52459c0e5..754ebe08b77 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -215,21 +215,11 @@ public: class Item_bool_rowready_func2 :public Item_bool_func2 { - Item *orig_a, *orig_b; /* propagate_const can change parameters */ public: - Item_bool_rowready_func2(Item *a,Item *b) :Item_bool_func2(a,b), - orig_a(a), orig_b(b) + Item_bool_rowready_func2(Item *a, Item *b) :Item_bool_func2(a, b) { allowed_arg_cols= a->cols(); } - void cleanup() - { - DBUG_ENTER("Item_bool_rowready_func2::cleanup"); - Item_bool_func2::cleanup(); - tmp_arg[0]= orig_a; - tmp_arg[1]= orig_b; - DBUG_VOID_RETURN; - } Item *neg_transformer(THD *thd); virtual Item *negated_item(); }; @@ -483,6 +473,7 @@ public: const char *func_name() const { return "nullif"; } void print(String *str) { Item_func::print(str); } table_map not_null_tables() const { return 0; } + bool is_null(); }; @@ -893,7 +884,7 @@ public: char escape; Item_func_like(Item *a,Item *b, Item *escape_arg) - :Item_bool_func2(a,b), canDoTurboBM(false), pattern(0), pattern_len(0), + :Item_bool_func2(a,b), canDoTurboBM(FALSE), pattern(0), pattern_len(0), bmGs(0), bmBc(0), escape_item(escape_arg) {} longlong val_int(); enum Functype functype() const { return LIKE_FUNC; } @@ -970,7 +961,7 @@ public: table_map used_tables() const; void update_used_tables(); void print(String *str); - void split_sum_func(Item **ref_pointer_array, List<Item> &fields); + void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields); friend int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds); void top_level_item() { abort_on_null=1; } void copy_andor_arguments(THD *thd, Item_cond *item); diff --git a/sql/item_func.cc b/sql/item_func.cc index fe5112cd75b..3cc0fd077a4 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -292,27 +292,30 @@ Item *Item_func::transform(Item_transformer transformer, byte *argument) Item *new_item= (*arg)->transform(transformer, argument); if (!new_item) return 0; - *arg= new_item; + if (*arg != new_item) + current_thd->change_item_tree(arg, new_item); } } return (this->*transformer)(argument); } -void Item_func::split_sum_func(Item **ref_pointer_array, List<Item> &fields) +void Item_func::split_sum_func(THD *thd, Item **ref_pointer_array, + List<Item> &fields) { Item **arg, **arg_end; for (arg= args, arg_end= args+arg_count; arg != arg_end ; arg++) { Item *item=* arg; if (item->with_sum_func && item->type() != SUM_FUNC_ITEM) - item->split_sum_func(ref_pointer_array, fields); + item->split_sum_func(thd, ref_pointer_array, fields); else if (item->used_tables() || item->type() == SUM_FUNC_ITEM) { uint el= fields.elements; + Item *new_item= new Item_ref(ref_pointer_array + el, 0, item->name); fields.push_front(item); ref_pointer_array[el]= item; - *arg= new Item_ref(ref_pointer_array + el, arg, 0, item->name); + thd->change_item_tree(arg, new_item); } } } @@ -718,8 +721,8 @@ void Item_func_int_div::fix_length_and_dec() double Item_func_mod::val() { DBUG_ASSERT(fixed == 1); - double value= floor(args[0]->val()+0.5); - double val2=floor(args[1]->val()+0.5); + double value= args[0]->val(); + double val2= args[1]->val(); if ((null_value= args[0]->null_value || args[1]->null_value)) return 0.0; /* purecov: inspected */ if (val2 == 0.0) @@ -747,10 +750,7 @@ longlong Item_func_mod::val_int() void Item_func_mod::fix_length_and_dec() { - max_length=args[1]->max_length; - decimals=0; - maybe_null=1; - find_num_type(); + Item_num_op::fix_length_and_dec(); } @@ -1087,21 +1087,38 @@ double Item_func_round::val() } -void Item_func_rand::fix_length_and_dec() +bool Item_func_rand::fix_fields(THD *thd, struct st_table_list *tables, + Item **ref) { - decimals=NOT_FIXED_DEC; - max_length=float_length(decimals); + Item_real_func::fix_fields(thd, tables, ref); used_tables_cache|= RAND_TABLE_BIT; if (arg_count) { // Only use argument once in query - uint32 tmp= (uint32) (args[0]->val_int()); - if ((rand= (struct rand_struct*) sql_alloc(sizeof(*rand)))) - randominit(rand,(uint32) (tmp*0x10001L+55555555L), - (uint32) (tmp*0x10000001L)); + /* + Allocate rand structure once: we must use thd->current_arena + to create rand in proper mem_root if it's a prepared statement or + stored procedure. + */ + if (!rand && !(rand= (struct rand_struct*) + thd->current_arena->alloc(sizeof(*rand)))) + return TRUE; + /* + PARAM_ITEM is returned if we're in statement prepare and consequently + no placeholder value is set yet. + */ + if (args[0]->type() != PARAM_ITEM) + { + /* + TODO: do not do reinit 'rand' for every execute of PS/SP if + args[0] is a constant. + */ + uint32 tmp= (uint32) args[0]->val_int(); + randominit(rand, (uint32) (tmp*0x10001L+55555555L), + (uint32) (tmp*0x10000001L)); + } } else { - THD *thd= current_thd; /* No need to send a Rand log event if seed was given eg: RAND(seed), as it will be replicated in the query as such. @@ -1115,6 +1132,7 @@ void Item_func_rand::fix_length_and_dec() thd->rand_saved_seed2=thd->rand.seed2; rand= &thd->rand; } + return FALSE; } void Item_func_rand::update_used_tables() @@ -1534,10 +1552,11 @@ longlong Item_func_find_in_set::val_int() { const char *substr_end= str_end + symbol_len; bool is_last_item= (substr_end == real_end); - if (wc == (my_wc_t) separator || is_last_item) + bool is_separator= (wc == (my_wc_t) separator); + if (is_separator || is_last_item) { position++; - if (is_last_item) + if (is_last_item && !is_separator) str_end= substr_end; if (!my_strnncoll(cs, (const uchar *) str_begin, str_end - str_begin, @@ -2820,7 +2839,21 @@ void Item_func_get_user_var::fix_length_and_dec() error= get_var_with_binlog(thd, name, &var_entry); if (var_entry) + { collation.set(var_entry->collation); + switch (var_entry->type) { + case REAL_RESULT: + max_length= DBL_DIG + 8; + case INT_RESULT: + max_length= MAX_BIGINT_WIDTH; + break; + case STRING_RESULT: + max_length= MAX_BLOB_WIDTH; + break; + case ROW_RESULT: // Keep compiler happy + break; + } + } else null_value= 1; @@ -2937,10 +2970,10 @@ void Item_func_match::init_search(bool no_order) if (key == NO_SUCH_KEY) { List<Item> fields; + fields.push_back(new Item_string(" ",1, cmp_collation.collation)); for (uint i=1; i < arg_count; i++) fields.push_back(args[i]); - concat=new Item_func_concat_ws(new Item_string(" ",1, - cmp_collation.collation), fields); + concat=new Item_func_concat_ws(fields); /* Above function used only to get value and do not need fix_fields for it: Item_string - basic constant diff --git a/sql/item_func.h b/sql/item_func.h index a5448f54693..3b941c4a40c 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -125,7 +125,7 @@ public: void set_arguments(List<Item> &list); inline uint argument_count() const { return arg_count; } inline void remove_arguments() { arg_count=0; } - virtual void split_sum_func(Item **ref_pointer_array, List<Item> &fields); + void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields); void print(String *str); void print_op(String *str); void print_args(String *str, uint from); @@ -220,6 +220,7 @@ class Item_func_signed :public Item_int_func { public: Item_func_signed(Item *a) :Item_int_func(a) {} + const char *func_name() const { return "cast_as_signed"; } double val() { double tmp= args[0]->val(); @@ -242,6 +243,7 @@ class Item_func_unsigned :public Item_func_signed { public: Item_func_unsigned(Item *a) :Item_func_signed(a) {} + const char *func_name() const { return "cast_as_unsigned"; } void fix_length_and_dec() { max_length=args[0]->max_length; unsigned_flag=1; } void print(String *str); @@ -352,6 +354,7 @@ class Item_dec_func :public Item_real_func #ifndef HAVE_FINITE return value; #else + /* The following should be safe, even if we compare doubles */ if (finite(value) && value != POSTFIX_ERROR) return value; null_value=1; @@ -516,13 +519,13 @@ class Item_func_rand :public Item_real_func { struct rand_struct *rand; public: - Item_func_rand(Item *a) :Item_real_func(a) {} - Item_func_rand() :Item_real_func() {} + Item_func_rand(Item *a) :Item_real_func(a), rand(0) {} + Item_func_rand() :Item_real_func() {} double val(); const char *func_name() const { return "rand"; } bool const_item() const { return 0; } void update_used_tables(); - void fix_length_and_dec(); + bool fix_fields(THD *thd, struct st_table_list *tables, Item **ref); }; @@ -1039,6 +1042,7 @@ public: table_map not_null_tables() const { return 0; } bool fix_fields(THD *thd, struct st_table_list *tlist, Item **ref); bool eq(const Item *, bool binary_cmp) const; + /* The following should be safe, even if we compare doubles */ longlong val_int() { DBUG_ASSERT(fixed == 1); return val()!=0.0; } double val(); void print(String *str); diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 9d58cc37c2a..935925c1e83 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -27,6 +27,13 @@ #include "sql_acl.h" #include <m_ctype.h> +void Item_geometry_func::fix_length_and_dec() +{ + collation.set(&my_charset_bin); + decimals=0; + max_length=MAX_BLOB_WIDTH; +} + String *Item_func_geometry_from_text::val_str(String *str) { @@ -44,6 +51,7 @@ String *Item_func_geometry_from_text::val_str(String *str) if ((arg_count == 2) && !args[1]->null_value) srid= (uint32)args[1]->val_int(); + str->set_charset(&my_charset_bin); if (str->reserve(SRID_SIZE, 512)) return 0; str->length(0); @@ -54,12 +62,6 @@ String *Item_func_geometry_from_text::val_str(String *str) } -void Item_func_geometry_from_text::fix_length_and_dec() -{ - max_length=MAX_BLOB_WIDTH; -} - - String *Item_func_geometry_from_wkb::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -71,6 +73,7 @@ String *Item_func_geometry_from_wkb::val_str(String *str) if ((arg_count == 2) && !args[1]->null_value) srid= (uint32)args[1]->val_int(); + str->set_charset(&my_charset_bin); if (str->reserve(SRID_SIZE, 512)) return 0; str->length(0); @@ -84,12 +87,6 @@ String *Item_func_geometry_from_wkb::val_str(String *str) } -void Item_func_geometry_from_wkb::fix_length_and_dec() -{ - max_length=MAX_BLOB_WIDTH; -} - - String *Item_func_as_wkt::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -138,12 +135,6 @@ String *Item_func_as_wkb::val_str(String *str) } -void Item_func_as_wkb::fix_length_and_dec() -{ - max_length= MAX_BLOB_WIDTH; -} - - String *Item_func_geometry_type::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -180,6 +171,7 @@ String *Item_func_envelope::val_str(String *str) return 0; srid= uint4korr(swkb->ptr()); + str->set_charset(&my_charset_bin); str->length(0); if (str->reserve(SRID_SIZE, 512)) return 0; @@ -202,6 +194,7 @@ String *Item_func_centroid::val_str(String *str) swkb->length() - SRID_SIZE)))) return 0; + str->set_charset(&my_charset_bin); if (str->reserve(SRID_SIZE, 512)) return 0; str->length(0); @@ -232,6 +225,7 @@ String *Item_func_spatial_decomp::val_str(String *str) return 0; srid= uint4korr(swkb->ptr()); + str->set_charset(&my_charset_bin); if (str->reserve(SRID_SIZE, 512)) goto err; str->length(0); @@ -279,6 +273,7 @@ String *Item_func_spatial_decomp_n::val_str(String *str) swkb->length() - SRID_SIZE))))) return 0; + str->set_charset(&my_charset_bin); if (str->reserve(SRID_SIZE, 512)) goto err; srid= uint4korr(swkb->ptr()); @@ -333,6 +328,7 @@ String *Item_func_point::val_str(String *str) str->realloc(1 + 4 + SIZEOF_STORED_DOUBLE*2)))) return 0; + str->set_charset(&my_charset_bin); str->length(0); str->q_append((char)Geometry::wkb_ndr); str->q_append((uint32)Geometry::wkb_point); @@ -358,6 +354,7 @@ String *Item_func_spatial_collection::val_str(String *str) String arg_value; uint i; + str->set_charset(&my_charset_bin); str->length(0); if (str->reserve(1 + 4 + 4, 512)) goto err; diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h index a1f36130152..79e4f804a04 100644 --- a/sql/item_geofunc.h +++ b/sql/item_geofunc.h @@ -23,24 +23,33 @@ #pragma interface /* gcc class implementation */ #endif -class Item_func_geometry_from_text: public Item_str_func +class Item_geometry_func: public Item_str_func { public: - Item_func_geometry_from_text(Item *a) :Item_str_func(a) {} - Item_func_geometry_from_text(Item *a, Item *srid) :Item_str_func(a, srid) {} + Item_geometry_func() :Item_str_func() {} + Item_geometry_func(Item *a) :Item_str_func(a) {} + Item_geometry_func(Item *a,Item *b) :Item_str_func(a,b) {} + Item_geometry_func(Item *a,Item *b,Item *c) :Item_str_func(a,b,c) {} + Item_geometry_func(List<Item> &list) :Item_str_func(list) {} + void fix_length_and_dec(); +}; + +class Item_func_geometry_from_text: public Item_geometry_func +{ +public: + Item_func_geometry_from_text(Item *a) :Item_geometry_func(a) {} + Item_func_geometry_from_text(Item *a, Item *srid) :Item_geometry_func(a, srid) {} const char *func_name() const { return "geometryfromtext"; } String *val_str(String *); - void fix_length_and_dec(); }; -class Item_func_geometry_from_wkb: public Item_str_func +class Item_func_geometry_from_wkb: public Item_geometry_func { public: - Item_func_geometry_from_wkb(Item *a): Item_str_func(a) {} - Item_func_geometry_from_wkb(Item *a, Item *srid): Item_str_func(a, srid) {} + Item_func_geometry_from_wkb(Item *a): Item_geometry_func(a) {} + Item_func_geometry_from_wkb(Item *a, Item *srid): Item_geometry_func(a, srid) {} const char *func_name() const { return "geometryfromwkb"; } String *val_str(String *); - void fix_length_and_dec(); }; class Item_func_as_wkt: public Item_str_func @@ -52,13 +61,12 @@ public: void fix_length_and_dec(); }; -class Item_func_as_wkb: public Item_str_func +class Item_func_as_wkb: public Item_geometry_func { public: - Item_func_as_wkb(Item *a): Item_str_func(a) {} + Item_func_as_wkb(Item *a): Item_geometry_func(a) {} const char *func_name() const { return "aswkb"; } String *val_str(String *); - void fix_length_and_dec(); }; class Item_func_geometry_type: public Item_str_func @@ -73,40 +81,37 @@ public: }; }; -class Item_func_centroid: public Item_str_func +class Item_func_centroid: public Item_geometry_func { public: - Item_func_centroid(Item *a): Item_str_func(a) {} + Item_func_centroid(Item *a): Item_geometry_func(a) {} const char *func_name() const { return "centroid"; } String *val_str(String *); - void fix_length_and_dec(){max_length=MAX_BLOB_WIDTH;} }; -class Item_func_envelope: public Item_str_func +class Item_func_envelope: public Item_geometry_func { public: - Item_func_envelope(Item *a): Item_str_func(a) {} + Item_func_envelope(Item *a): Item_geometry_func(a) {} const char *func_name() const { return "envelope"; } String *val_str(String *); - void fix_length_and_dec(){max_length=MAX_BLOB_WIDTH;} }; -class Item_func_point: public Item_str_func +class Item_func_point: public Item_geometry_func { public: - Item_func_point(Item *a, Item *b): Item_str_func(a, b) {} - Item_func_point(Item *a, Item *b, Item *srid): Item_str_func(a, b, srid) {} + Item_func_point(Item *a, Item *b): Item_geometry_func(a, b) {} + Item_func_point(Item *a, Item *b, Item *srid): Item_geometry_func(a, b, srid) {} const char *func_name() const { return "point"; } String *val_str(String *); - void fix_length_and_dec(){max_length=MAX_BLOB_WIDTH;} }; -class Item_func_spatial_decomp: public Item_str_func +class Item_func_spatial_decomp: public Item_geometry_func { enum Functype decomp_func; public: Item_func_spatial_decomp(Item *a, Item_func::Functype ft) : - Item_str_func(a) { decomp_func = ft; } + Item_geometry_func(a) { decomp_func = ft; } const char *func_name() const { switch (decomp_func) @@ -123,15 +128,14 @@ public: } } String *val_str(String *); - void fix_length_and_dec(){max_length=MAX_BLOB_WIDTH;} }; -class Item_func_spatial_decomp_n: public Item_str_func +class Item_func_spatial_decomp_n: public Item_geometry_func { enum Functype decomp_func_n; public: Item_func_spatial_decomp_n(Item *a, Item *b, Item_func::Functype ft): - Item_str_func(a, b) { decomp_func_n = ft; } + Item_geometry_func(a, b) { decomp_func_n = ft; } const char *func_name() const { switch (decomp_func_n) @@ -148,10 +152,9 @@ public: } } String *val_str(String *); - void fix_length_and_dec(){max_length=MAX_BLOB_WIDTH;} }; -class Item_func_spatial_collection: public Item_str_func +class Item_func_spatial_collection: public Item_geometry_func { String tmp_value; enum Geometry::wkbType coll_type; @@ -159,13 +162,12 @@ class Item_func_spatial_collection: public Item_str_func public: Item_func_spatial_collection( List<Item> &list, enum Geometry::wkbType ct, enum Geometry::wkbType it): - Item_str_func(list) + Item_geometry_func(list) { coll_type=ct; item_type=it; } String *val_str(String *); - void fix_length_and_dec(){max_length=MAX_BLOB_WIDTH;} const char *func_name() const { return "multipoint"; } }; diff --git a/sql/item_row.cc b/sql/item_row.cc index 8bf0d5061a7..8a020861fef 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -84,19 +84,21 @@ bool Item_row::fix_fields(THD *thd, TABLE_LIST *tabl, Item **ref) return 0; } -void Item_row::split_sum_func(Item **ref_pointer_array, List<Item> &fields) +void Item_row::split_sum_func(THD *thd, Item **ref_pointer_array, + List<Item> &fields) { Item **arg, **arg_end; for (arg= items, arg_end= items+arg_count; arg != arg_end ; arg++) { if ((*arg)->with_sum_func && (*arg)->type() != SUM_FUNC_ITEM) - (*arg)->split_sum_func(ref_pointer_array, fields); + (*arg)->split_sum_func(thd, ref_pointer_array, fields); else if ((*arg)->used_tables() || (*arg)->type() == SUM_FUNC_ITEM) { uint el= fields.elements; + Item *new_item= new Item_ref(ref_pointer_array + el, 0, (*arg)->name); fields.push_front(*arg); ref_pointer_array[el]= *arg; - *arg= new Item_ref(ref_pointer_array + el, arg, 0, (*arg)->name); + thd->change_item_tree(arg, new_item); } } } diff --git a/sql/item_row.h b/sql/item_row.h index ec5f0f1fc95..ef5856dcdd3 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -57,7 +57,7 @@ public: return 0; }; bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref); - void split_sum_func(Item **ref_pointer_array, List<Item> &fields); + void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields); table_map used_tables() const { return used_tables_cache; }; bool const_item() const { return const_item_cache; }; enum Item_result result_type() const { return ROW_RESULT; } diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index b3665edad39..d015ca36eac 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -289,7 +289,8 @@ String *Item_func_concat::val_str(String *str) str->copy(*res); str->append(*res2); } - res=str; + res= str; + use_as_buff= &tmp_value; } else if (res == &tmp_value) { @@ -531,7 +532,7 @@ String *Item_func_concat_ws::val_str(String *str) uint i; null_value=0; - if (!(sep_str= separator->val_str(&tmp_sep_str))) + if (!(sep_str= args[0]->val_str(&tmp_sep_str))) goto null; use_as_buff= &tmp_value; @@ -540,7 +541,7 @@ String *Item_func_concat_ws::val_str(String *str) // Skip until non-null argument is found. // If not, return the empty string - for (i=0; i < arg_count; i++) + for (i=1; i < arg_count; i++) if ((res= args[i]->val_str(str))) break; if (i == arg_count) @@ -634,67 +635,25 @@ null: return 0; } -void Item_func_concat_ws::split_sum_func(Item **ref_pointer_array, - List<Item> &fields) -{ - if (separator->with_sum_func && separator->type() != SUM_FUNC_ITEM) - separator->split_sum_func(ref_pointer_array, fields); - else if (separator->used_tables() || separator->type() == SUM_FUNC_ITEM) - { - uint el= fields.elements; - fields.push_front(separator); - ref_pointer_array[el]= separator; - separator= new Item_ref(ref_pointer_array + el, - &separator, 0, separator->name); - } - Item_str_func::split_sum_func(ref_pointer_array, fields); -} void Item_func_concat_ws::fix_length_and_dec() { - collation.set(separator->collation); - max_length=separator->max_length*(arg_count-1); - for (uint i=0 ; i < arg_count ; i++) - { - DTCollation tmp(collation.collation, collation.derivation); + max_length=0; + + if (agg_arg_collations(collation, args, arg_count)) + return; + + max_length= arg_count > 1 ? args[0]->max_length * (arg_count - 2) : 0; + for (uint i=1 ; i < arg_count ; i++) max_length+=args[i]->max_length; - if (collation.aggregate(args[i]->collation)) - { - collation.set(tmp); // Restore the previous value - my_coll_agg_error(collation, args[i]->collation, func_name()); - break; - } - } + if (max_length > MAX_BLOB_WIDTH) { max_length=MAX_BLOB_WIDTH; maybe_null=1; } - used_tables_cache|= separator->used_tables(); - not_null_tables_cache&= separator->not_null_tables(); - const_item_cache&= separator->const_item(); - with_sum_func= with_sum_func || separator->with_sum_func; } -void Item_func_concat_ws::update_used_tables() -{ - Item_func::update_used_tables(); - separator->update_used_tables(); - used_tables_cache|=separator->used_tables(); - const_item_cache&=separator->const_item(); -} - -void Item_func_concat_ws::print(String *str) -{ - str->append("concat_ws(", 10); - separator->print(str); - if (arg_count) - { - str->append(','); - print_args(str, 0); - } - str->append(')'); -} String *Item_func_reverse::val_str(String *str) { @@ -1244,7 +1203,7 @@ String *Item_func_ltrim::val_str(String *str) { const char *r_ptr=remove_str->ptr(); end-=remove_length; - while (ptr < end && !memcmp(ptr,r_ptr,remove_length)) + while (ptr <= end && !memcmp(ptr, r_ptr, remove_length)) ptr+=remove_length; end+=remove_length; } @@ -1316,8 +1275,8 @@ String *Item_func_rtrim::val_str(String *str) else #endif /* USE_MB */ { - while (ptr + remove_length < end && - !memcmp(end-remove_length,r_ptr,remove_length)) + while (ptr + remove_length <= end && + !memcmp(end-remove_length, r_ptr, remove_length)) end-=remove_length; } } @@ -1770,19 +1729,20 @@ String *Item_func_elt::val_str(String *str) } -void Item_func_make_set::split_sum_func(Item **ref_pointer_array, +void Item_func_make_set::split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields) { if (item->with_sum_func && item->type() != SUM_FUNC_ITEM) - item->split_sum_func(ref_pointer_array, fields); + item->split_sum_func(thd, ref_pointer_array, fields); else if (item->used_tables() || item->type() == SUM_FUNC_ITEM) { uint el= fields.elements; + Item *new_item= new Item_ref(ref_pointer_array + el, 0, item->name); fields.push_front(item); ref_pointer_array[el]= item; - item= new Item_ref(ref_pointer_array + el, &item, 0, item->name); + thd->change_item_tree(&item, new_item); } - Item_str_func::split_sum_func(ref_pointer_array, fields); + Item_str_func::split_sum_func(thd, ref_pointer_array, fields); } @@ -2424,14 +2384,22 @@ String *Item_load_file::val_str(String *str) String *file_name; File file; MY_STAT stat_info; + char path[FN_REFLEN]; DBUG_ENTER("load_file"); - if (!(file_name= args[0]->val_str(str)) || + if (!(file_name= args[0]->val_str(str)) #ifndef NO_EMBEDDED_ACCESS_CHECKS - !(current_thd->master_access & FILE_ACL) || + || !(current_thd->master_access & FILE_ACL) #endif - !my_stat(file_name->c_ptr(), &stat_info, MYF(MY_WME))) + ) goto err; + + (void) fn_format(path, file_name->c_ptr(), mysql_real_data_home, "", + MY_RELATIVE_PATH | MY_UNPACK_FILENAME); + + if (!my_stat(path, &stat_info, MYF(MY_WME))) + goto err; + if (!(stat_info.st_mode & S_IROTH)) { /* my_error(ER_TEXTFILE_NOT_READABLE, MYF(0), file_name->c_ptr()); */ @@ -2711,35 +2679,40 @@ longlong Item_func_crc32::val_int() String *Item_func_compress::val_str(String *str) { + int err= Z_OK, code; + ulong new_size; + String *res; + Byte *body; + char *tmp, *last_char; DBUG_ASSERT(fixed == 1); - String *res= args[0]->val_str(str); - if (!res) + + if (!(res= args[0]->val_str(str))) { null_value= 1; return 0; } if (res->is_empty()) return res; - int err= Z_OK; - int code; - /* - citation from zlib.h (comment for compress function): + Citation from zlib.h (comment for compress function): Compresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total - size of the destination buffer, which must be at least 0.1% larger than - sourceLen plus 12 bytes. - - Proportion 120/100 founded by Sinisa with help of procedure - compress(compress(compress(...))) - I.e. zlib give number 'at least'.. + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least 0.1% larger than + sourceLen plus 12 bytes. + We assume here that the buffer can't grow more than .25 %. */ - ulong new_size= (ulong)((res->length()*120)/100)+12; + new_size= res->length() + res->length() / 5 + 12; - buffer.realloc((uint32)new_size + 4 + 1); - Byte *body= ((Byte*)buffer.ptr()) + 4; + // Check new_size overflow: new_size <= res->length() + if (((uint32) (new_size+5) <= res->length()) || + buffer.realloc((uint32) new_size + 4 + 1)) + { + null_value= 1; + return 0; + } + body= ((Byte*)buffer.ptr()) + 4; // As far as we have checked res->is_empty() we can use ptr() if ((err= compress(body, &new_size, @@ -2751,11 +2724,11 @@ String *Item_func_compress::val_str(String *str) return 0; } - char *tmp= (char*)buffer.ptr(); // int4store is a macro; avoid side effects + tmp= (char*)buffer.ptr(); // int4store is a macro; avoid side effects int4store(tmp, res->length() & 0x3FFFFFFF); - /* This is for the stupid char fields which trim ' ': */ - char *last_char= ((char*)body)+new_size-1; + /* This is to ensure that things works for CHAR fields, which trim ' ': */ + last_char= ((char*)body)+new_size-1; if (*last_char == ' ') { *++last_char= '.'; diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 49429a55cac..afc4b9da0a1 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -89,30 +89,12 @@ public: class Item_func_concat_ws :public Item_str_func { - Item *separator; String tmp_value; - public: - Item_func_concat_ws(Item *a,List<Item> &list) - :Item_str_func(list),separator(a) {} + Item_func_concat_ws(List<Item> &list) :Item_str_func(list) {} String *val_str(String *); void fix_length_and_dec(); - void update_used_tables(); - bool fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref) - { - DBUG_ASSERT(fixed == 0); - return (separator->fix_fields(thd, tlist, &separator) || - separator->check_cols(1) || - Item_func::fix_fields(thd, tlist, ref)); - } - void split_sum_func(Item **ref_pointer_array, List<Item> &fields); const char *func_name() const { return "concat_ws"; } - bool walk(Item_processor processor, byte *arg) - { - return separator->walk(processor, arg) || - Item_str_func::walk(processor, arg); - } - void print(String *str); }; class Item_func_reverse :public Item_str_func @@ -418,7 +400,7 @@ public: item->check_cols(1) || Item_func::fix_fields(thd, tlist, ref)); } - void split_sum_func(Item **ref_pointer_array, List<Item> &fields); + void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields); void fix_length_and_dec(); void update_used_tables(); const char *func_name() const { return "make_set"; } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 25bb2701101..bafca8acf0f 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -164,12 +164,7 @@ bool Item_subselect::fix_fields(THD *thd_param, TABLE_LIST *tables, Item **ref) thd->where= "checking transformed subquery"; if (!(*ref)->fixed) ret= (*ref)->fix_fields(thd, tables, ref); - // We can't substitute aggregate functions like "SELECT (max(i))" - if (substype() == SINGLEROW_SUBS && (*ref)->with_sum_func) - { - my_error(ER_INVALID_GROUP_FUNC_USE, MYF(0)); - return 1; - } + thd->where= save_where; return ret; } // Is it one field subselect? @@ -321,18 +316,12 @@ Item_singlerow_subselect::select_transformer(JOIN *join) return RES_OK; SELECT_LEX *select_lex= join->select_lex; - - /* Juggle with current arena only if we're in prepared statement prepare */ - DBUG_PRINT("TANSF:", ("thd %p, select_lex->join->thd: %s", - thd, select_lex->join->thd)); Item_arena *arena= thd->current_arena; - Item_arena backup; - if (arena->is_conventional()) - arena= 0; // For easier test - + if (!select_lex->master_unit()->first_select()->next_select() && !select_lex->table_list.elements && select_lex->item_list.elements == 1 && + !select_lex->item_list.head()->with_sum_func && /* We cant change name of Item_field or Item_ref, because it will prevent it's correct resolving, but we should save name of @@ -341,7 +330,13 @@ Item_singlerow_subselect::select_transformer(JOIN *join) TODO: solve above problem */ !(select_lex->item_list.head()->type() == FIELD_ITEM || - select_lex->item_list.head()->type() == REF_ITEM) + select_lex->item_list.head()->type() == REF_ITEM) && + /* + switch off this optimisation for prepare statement, + because we do not rollback this changes + TODO: make rollback for it, or special name resolving mode in 5.0. + */ + !arena->is_stmt_prepare() ) { @@ -363,9 +358,6 @@ Item_singlerow_subselect::select_transformer(JOIN *join) if (join->conds || join->having) { Item *cond; - if (arena) - thd->set_n_backup_item_arena(arena, &backup); - if (!join->having) cond= join->conds; else if (!join->conds) @@ -376,19 +368,16 @@ Item_singlerow_subselect::select_transformer(JOIN *join) if (!(substitution= new Item_func_if(cond, substitution, new Item_null()))) goto err; - if (arena) - thd->restore_backup_item_arena(arena, &backup); } return RES_REDUCE; } return RES_OK; err: - if (arena) - thd->restore_backup_item_arena(arena, &backup); return RES_ERROR; } + void Item_singlerow_subselect::store(uint i, Item *item) { row[i]->store(item); @@ -412,7 +401,13 @@ void Item_singlerow_subselect::fix_length_and_dec() engine->fix_length_and_dec(row); value= *row; } - maybe_null= engine->may_be_null(); + /* + If there are not tables in subquery then ability to have NULL value + depends on SELECT list (if single row subquery have tables then it + always can be NULL if there are not records fetched). + */ + if (engine->no_tables()) + maybe_null= engine->may_be_null(); } uint Item_singlerow_subselect::cols() @@ -650,10 +645,13 @@ String *Item_in_subselect::val_str(String *str) } +/* Rewrite a single-column IN/ALL/ANY subselect. */ + Item_subselect::trans_res Item_in_subselect::single_value_transformer(JOIN *join, Comp_creator *func) { + const char *save_where= thd->where; Item_subselect::trans_res result= RES_ERROR; DBUG_ENTER("Item_in_subselect::single_value_transformer"); @@ -672,12 +670,27 @@ Item_in_subselect::single_value_transformer(JOIN *join, else thd->set_n_backup_item_arena(arena, &backup); + /* + Check that the right part of the subselect contains no more than one + column. E.g. in SELECT 1 IN (SELECT * ..) the right part is (SELECT * ...) + */ if (select_lex->item_list.elements > 1) { my_error(ER_OPERAND_COLUMNS, MYF(0), 1); goto err; } + /* + If this is an ALL/ANY single-value subselect, try to rewrite it with + a MIN/MAX subselect. We can do that if a possible NULL result of the + subselect can be ignored. + E.g. SELECT * FROM t1 WHERE b > ANY (SELECT a FROM t2) can be rewritten + with SELECT * FROM t1 WHERE b > (SELECT MAX(a) FROM t2). + We can't check that this optimization is safe if it's not a top-level + item of the WHERE clause (e.g. because the WHERE clause can contain IS + NULL/IS NOT NULL functions). If so, we rewrite ALL/ANY with NOT EXISTS + later in this method. + */ if ((abort_on_null || (upper_not && upper_not->top_level())) && !select_lex->master_unit()->uncacheable && !func->eqne_op()) { @@ -769,7 +782,6 @@ Item_in_subselect::single_value_transformer(JOIN *join, we can use same item for all selects. */ expr= new Item_ref((Item**)optimizer->get_cache(), - NULL, (char *)"<no matter>", (char *)in_left_expr_name); @@ -777,7 +789,13 @@ Item_in_subselect::single_value_transformer(JOIN *join, } select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; - + /* + Add the left part of a subselect to a WHERE or HAVING clause of + the right part, e.g. SELECT 1 IN (SELECT a FROM t1) => + SELECT Item_in_optimizer(1, SELECT a FROM t1 WHERE a=1) + HAVING is used only if the right part contains a SUM function, a GROUP + BY or a HAVING clause. + */ if (join->having || select_lex->with_sum_func || select_lex->group_list.elements) { @@ -886,6 +904,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, } ok: + thd->where= save_where; result= RES_OK; err: @@ -898,6 +917,7 @@ err: Item_subselect::trans_res Item_in_subselect::row_value_transformer(JOIN *join) { + const char *save_where= thd->where; Item *item= 0; SELECT_LEX *select_lex= join->select_lex; DBUG_ENTER("Item_in_subselect::row_value_transformer"); @@ -943,9 +963,6 @@ Item_in_subselect::row_value_transformer(JOIN *join) } select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; - select_lex->setup_ref_array(thd, - select_lex->order_list.elements + - select_lex->group_list.elements); { uint n= left_expr->cols(); List_iterator_fast<Item> li(select_lex->item_list); @@ -956,9 +973,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) (char *) "<no matter>", (char *) "<list ref>"); func= - eq_creator.create(new Item_ref((*optimizer->get_cache())-> - addr(i), - NULL, + eq_creator.create(new Item_ref((*optimizer->get_cache())->addr(i), (char *)"<no matter>", (char *)in_left_expr_name), func); @@ -994,6 +1009,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) if (join->conds->fix_fields(thd, join->tables_list, 0)) goto err; } + thd->where= save_where; if (arena) thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(RES_OK); @@ -1271,7 +1287,8 @@ int subselect_uniquesubquery_engine::exec() error= table->file->index_read(table->record[0], tab->ref.key_buff, tab->ref.key_length,HA_READ_KEY_EXACT); - if (error && error != HA_ERR_KEY_NOT_FOUND) + if (error && + error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) error= report_error(table, error); else { @@ -1323,7 +1340,8 @@ int subselect_indexsubquery_engine::exec() error= table->file->index_read(table->record[0], tab->ref.key_buff, tab->ref.key_length,HA_READ_KEY_EXACT); - if (error && error != HA_ERR_KEY_NOT_FOUND) + if (error && + error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) error= report_error(table, error); else { @@ -1547,3 +1565,58 @@ int subselect_uniquesubquery_engine::change_item(Item_subselect *si, DBUG_ASSERT(0); return -1; } + + +/* + Report about presence of tables in subquery + + SINOPSYS + subselect_single_select_engine::no_tables() + + RETURN + TRUE there are not tables used in subquery + FALSE there are some tables in subquery +*/ +bool subselect_single_select_engine::no_tables() +{ + return(select_lex->table_list.elements == 0); +} + + +/* + Report about presence of tables in subquery + + SINOPSYS + subselect_union_engine::no_tables() + + RETURN + TRUE there are not tables used in subquery + FALSE there are some tables in subquery +*/ +bool subselect_union_engine::no_tables() +{ + for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) + { + if (sl->table_list.elements) + return FALSE; + } + return TRUE; +} + + +/* + Report about presence of tables in subquery + + SINOPSYS + subselect_uniquesubquery_engine::no_tables() + + RETURN + TRUE there are not tables used in subquery + FALSE there are some tables in subquery +*/ + +bool subselect_uniquesubquery_engine::no_tables() +{ + /* returning value is correct, but this method should never be called */ + return 0; +} diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 4a325c4b224..6efb9052115 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -291,6 +291,7 @@ public: static table_map calc_const_tables(TABLE_LIST *); virtual void print(String *str)= 0; virtual int change_item(Item_subselect *si, select_subselect *result)= 0; + virtual bool no_tables()= 0; }; @@ -315,6 +316,7 @@ public: table_map upper_select_const_tables(); void print (String *str); int change_item(Item_subselect *si, select_subselect *result); + bool no_tables(); }; @@ -335,6 +337,7 @@ public: table_map upper_select_const_tables(); void print (String *str); int change_item(Item_subselect *si, select_subselect *result); + bool no_tables(); }; @@ -364,6 +367,7 @@ public: table_map upper_select_const_tables() { return 0; } void print (String *str); int change_item(Item_subselect *si, select_subselect *result); + bool no_tables(); }; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 85e27991ac6..743b3eee58c 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -24,7 +24,7 @@ #include "mysql_priv.h" Item_sum::Item_sum(List<Item> &list) - :args_copy(0), arg_count(list.elements) + :arg_count(list.elements) { if ((args=(Item**) sql_alloc(sizeof(Item*)*arg_count))) { @@ -56,39 +56,6 @@ Item_sum::Item_sum(THD *thd, Item_sum *item): if (!(args= (Item**) thd->alloc(sizeof(Item*)*arg_count))) return; memcpy(args, item->args, sizeof(Item*)*arg_count); - if (item->args_copy != 0) - save_args(thd); - else - args_copy= 0; -} - - -/* - Save copy of arguments if we are preparing a prepared statement - (arguments can be rewritten in get_tmp_table_item()) - - SYNOPSIS - Item_sum::save_args_for_prepared_statement() - thd - thread handler - - RETURN - 0 - OK - 1 - Error -*/ -bool Item_sum::save_args_for_prepared_statement(THD *thd) -{ - if (!thd->current_arena->is_conventional() && args_copy == 0) - return save_args(thd->current_arena); - return 0; -} - - -bool Item_sum::save_args(Item_arena* arena) -{ - if (!(args_copy= (Item**) arena->alloc(sizeof(Item*)*arg_count))) - return 1; - memcpy(args_copy, args, sizeof(Item*)*arg_count); - return 0; } @@ -99,17 +66,6 @@ void Item_sum::mark_as_sum_func() } -void Item_sum::cleanup() -{ - DBUG_ENTER("Item_sum::cleanup"); - Item_result_field::cleanup(); - if (args_copy != 0) - memcpy(args, args_copy, sizeof(Item*)*arg_count); - result_field=0; - DBUG_VOID_RETURN; -} - - void Item_sum::make_field(Send_field *tmp_field) { if (args[0]->type() == Item::FIELD_ITEM && keep_field_type()) @@ -215,9 +171,6 @@ Item_sum_num::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { DBUG_ASSERT(fixed == 0); - if (save_args_for_prepared_statement(thd)) - return 1; - if (!thd->allow_sum_func) { my_error(ER_INVALID_GROUP_FUNC_USE,MYF(0)); @@ -249,9 +202,6 @@ Item_sum_hybrid::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { DBUG_ASSERT(fixed == 0); - if (save_args_for_prepared_statement(thd)) - return 1; - Item *item= args[0]; if (!thd->allow_sum_func) { @@ -1855,8 +1805,7 @@ Item_func_group_concat::Item_func_group_concat(bool is_distinct, SQL_LIST *is_order, String *is_separator) :Item_sum(), tmp_table_param(0), max_elements_in_tree(0), warning(0), - warning_available(0), key_length(0), - tree_mode(0), distinct(is_distinct), warning_for_row(0), + key_length(0), tree_mode(0), distinct(is_distinct), warning_for_row(0), separator(is_separator), tree(&tree_base), table(0), order(0), tables_list(0), arg_count_order(0), arg_count_field(0), @@ -1914,7 +1863,6 @@ Item_func_group_concat::Item_func_group_concat(THD *thd, tmp_table_param(item->tmp_table_param), max_elements_in_tree(item->max_elements_in_tree), warning(item->warning), - warning_available(item->warning_available), key_length(item->key_length), tree_mode(item->tree_mode), distinct(item->distinct), @@ -1941,10 +1889,6 @@ void Item_func_group_concat::cleanup() DBUG_ENTER("Item_func_group_concat::cleanup"); Item_sum::cleanup(); - /* fix order list */ - for (uint i= 0; i < arg_count_order ; i++) - order[i]->item= &order[i]->item_ptr; - /* Free table and tree if they belong to this item (if item have not pointer to original item from which was made copy => it own its objects ) @@ -1964,6 +1908,13 @@ void Item_func_group_concat::cleanup() tree_mode= 0; delete_tree(tree); } + if (warning) + { + char warn_buff[MYSQL_ERRMSG_SIZE]; + sprintf(warn_buff, ER(ER_CUT_VALUE_GROUP_CONCAT), count_cut_values); + warning->set_msg(thd, warn_buff); + warning= 0; + } } DBUG_VOID_RETURN; } @@ -1971,19 +1922,6 @@ void Item_func_group_concat::cleanup() Item_func_group_concat::~Item_func_group_concat() { - /* - Free table and tree if they belong to this item (if item have not pointer - to original item from which was made copy => it own its objects ) - */ - if (!original) - { - if (warning_available) - { - char warn_buff[MYSQL_ERRMSG_SIZE]; - sprintf(warn_buff, ER(ER_CUT_VALUE_GROUP_CONCAT), count_cut_values); - warning->set_msg(current_thd, warn_buff); - } - } } @@ -2033,16 +1971,20 @@ bool Item_func_group_concat::add() } null_value= FALSE; + + TREE_ELEMENT *el= 0; // Only for safety if (tree_mode) - { - if (!tree_insert(tree, table->record[0], 0, tree->custom_arg)) - return 1; - } - else - { - if (result.length() <= group_concat_max_len && !warning_for_row) - dump_leaf_key(table->record[0], 1, this); - } + el= tree_insert(tree, table->record[0], 0, tree->custom_arg); + /* + If the row is not a duplicate (el->count == 1) + we can dump the row here in case of GROUP_CONCAT(DISTINCT...) + instead of doing tree traverse later. + */ + if (result.length() <= group_concat_max_len && + !warning_for_row && + (!tree_mode || (el->count == 1 && distinct && !arg_count_order))) + dump_leaf_key(table->record[0], 1, this); + return 0; } @@ -2060,9 +2002,6 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) uint i; /* for loop variable */ DBUG_ASSERT(fixed == 0); - if (save_args_for_prepared_statement(thd)) - return 1; - if (!thd->allow_sum_func) { my_error(ER_INVALID_GROUP_FUNC_USE,MYF(0)); @@ -2091,6 +2030,8 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) thd->allow_sum_func= 1; if (!(tmp_table_param= new TMP_TABLE_PARAM)) return 1; + /* We'll convert all blobs to varchar fields in the temporary table */ + tmp_table_param->convert_blob_length= group_concat_max_len; tables_list= tables; fixed= 1; return 0; @@ -2186,9 +2127,7 @@ bool Item_func_group_concat::setup(THD *thd) } else { - compare_key= NULL; - if (distinct) - compare_key= (qsort_cmp2) group_concat_key_cmp_with_distinct; + compare_key= (qsort_cmp2) group_concat_key_cmp_with_distinct; } /* Create a tree of sort. Tree is used for a sort and a remove double @@ -2231,17 +2170,17 @@ String* Item_func_group_concat::val_str(String* str) DBUG_ASSERT(fixed == 1); if (null_value) return 0; + if (count_cut_values && !warning) + warning= push_warning(item_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_CUT_VALUE_GROUP_CONCAT, + ER(ER_CUT_VALUE_GROUP_CONCAT)); + if (result.length()) + return &result; if (tree_mode) { tree_walk(tree, (tree_walk_action)&dump_leaf_key, (void*)this, left_root_right); } - if (count_cut_values && !warning_available) - { - warning_available= TRUE; - warning= push_warning(item_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_CUT_VALUE_GROUP_CONCAT, NULL); - } return &result; } diff --git a/sql/item_sum.h b/sql/item_sum.h index 9046a215c86..cede5a1e02e 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -35,23 +35,22 @@ public: }; Item **args, *tmp_args[2]; - Item **args_copy; /* copy of arguments for PS */ uint arg_count; bool quick_group; /* If incremental update of fields */ void mark_as_sum_func(); - Item_sum() :args_copy(0), arg_count(0), quick_group(1) + Item_sum() :arg_count(0), quick_group(1) { mark_as_sum_func(); } Item_sum(Item *a) - :args(tmp_args), args_copy(0), arg_count(1), quick_group(1) + :args(tmp_args), arg_count(1), quick_group(1) { args[0]=a; mark_as_sum_func(); } Item_sum( Item *a, Item *b ) - :args(tmp_args), args_copy(0), arg_count(2), quick_group(1) + :args(tmp_args), arg_count(2), quick_group(1) { args[0]=a; args[1]=b; mark_as_sum_func(); @@ -59,7 +58,6 @@ public: Item_sum(List<Item> &list); //Copy constructor, need to perform subselects with temporary tables Item_sum(THD *thd, Item_sum *item); - void cleanup(); enum Type type() const { return SUM_FUNC_ITEM; } virtual enum Sumfunctype sum_func () const=0; inline bool reset() { clear(); return add(); }; @@ -94,8 +92,6 @@ public: virtual bool setup(THD *thd) {return 0;} virtual void make_unique() {} Item *get_tmp_table_item(THD *thd); - bool save_args_for_prepared_statement(THD *); - bool save_args(Item_arena *arena); bool walk (Item_processor processor, byte *argument); }; @@ -713,7 +709,6 @@ class Item_func_group_concat : public Item_sum TMP_TABLE_PARAM *tmp_table_param; uint max_elements_in_tree; MYSQL_ERROR *warning; - bool warning_available; uint key_length; bool tree_mode; bool distinct; diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 988529c845c..3a25921d84b 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -36,7 +36,7 @@ static const char *month_names[]= }; TYPELIB month_names_typelib= -{ array_elements(month_names)-1,"", month_names }; +{ array_elements(month_names)-1,"", month_names, NULL }; static const char *day_names[]= { @@ -45,7 +45,7 @@ static const char *day_names[]= }; TYPELIB day_names_typelib= -{ array_elements(day_names)-1,"", day_names}; +{ array_elements(day_names)-1,"", day_names, NULL}; /* @@ -989,7 +989,7 @@ longlong Item_func_unix_timestamp::val_int() { // Optimize timestamp field Field *field=((Item_field*) args[0])->field; if (field->type() == FIELD_TYPE_TIMESTAMP) - return ((Field_timestamp*) field)->get_timestamp(); + return ((Field_timestamp*) field)->get_timestamp(&null_value); } if (get_arg0_date(<ime, 0)) @@ -1298,14 +1298,13 @@ String *Item_func_curtime::val_str(String *str) void Item_func_curtime::fix_length_and_dec() { TIME ltime; - String tmp((char*) buff,sizeof(buff), &my_charset_bin); decimals=0; collation.set(&my_charset_bin); store_now_in_TIME(<ime); value= TIME_to_ulonglong_time(<ime); - make_time((DATE_TIME_FORMAT *) 0, <ime, &tmp); - max_length= buff_length= tmp.length(); + buff_length= (uint) my_time_to_str(<ime, buff); + max_length= buff_length; } @@ -1347,16 +1346,14 @@ String *Item_func_now::val_str(String *str) void Item_func_now::fix_length_and_dec() { - String tmp((char*) buff,sizeof(buff),&my_charset_bin); - decimals=0; collation.set(&my_charset_bin); store_now_in_TIME(<ime); value= (longlong) TIME_to_ulonglong_datetime(<ime); - make_datetime((DATE_TIME_FORMAT *) 0, <ime, &tmp); - max_length= buff_length= tmp.length(); + buff_length= (uint) my_datetime_to_str(<ime, buff); + max_length= buff_length; } @@ -2078,6 +2075,24 @@ bool Item_extract::eq(const Item *item, bool binary_cmp) const } +bool Item_char_typecast::eq(const Item *item, bool binary_cmp) const +{ + if (this == item) + return 1; + if (item->type() != FUNC_ITEM || + func_name() != ((Item_func*)item)->func_name()) + return 0; + + Item_char_typecast *cast= (Item_char_typecast*)item; + if (cast_length != cast->cast_length || + cast_cs != cast->cast_cs) + return 0; + + if (!args[0]->eq(cast->args[0], binary_cmp)) + return 0; + return 1; +} + void Item_typecast::print(String *str) { str->append("cast(", 5); diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 5f71045ef27..694f2dc583c 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -683,6 +683,8 @@ class Item_char_typecast :public Item_typecast public: Item_char_typecast(Item *a, int length_arg, CHARSET_INFO *cs_arg) :Item_typecast(a), cast_length(length_arg), cast_cs(cs_arg) {} + bool eq(const Item *item, bool binary_cmp) const; + const char *func_name() const { return "cast_as_char"; } const char* cast_type() const { return "char"; }; String *val_str(String *a); void fix_length_and_dec(); @@ -694,6 +696,7 @@ class Item_date_typecast :public Item_typecast_maybe_null { public: Item_date_typecast(Item *a) :Item_typecast_maybe_null(a) {} + const char *func_name() const { return "cast_as_date"; } String *val_str(String *str); bool get_date(TIME *ltime, uint fuzzy_date); const char *cast_type() const { return "date"; } @@ -709,6 +712,7 @@ class Item_time_typecast :public Item_typecast_maybe_null { public: Item_time_typecast(Item *a) :Item_typecast_maybe_null(a) {} + const char *func_name() const { return "cast_as_time"; } String *val_str(String *str); bool get_time(TIME *ltime); const char *cast_type() const { return "time"; } @@ -724,6 +728,7 @@ class Item_datetime_typecast :public Item_typecast_maybe_null { public: Item_datetime_typecast(Item *a) :Item_typecast_maybe_null(a) {} + const char *func_name() const { return "cast_as_datetime"; } String *val_str(String *str); const char *cast_type() const { return "datetime"; } enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } diff --git a/sql/lock.cc b/sql/lock.cc index debfb900c23..6d5ca5bf6b4 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -794,9 +794,15 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh, { if (thd->global_read_lock) // This thread had the read locks { - my_error(ER_CANT_UPDATE_WITH_READLOCK,MYF(0)); + if (is_not_commit) + my_error(ER_CANT_UPDATE_WITH_READLOCK,MYF(0)); (void) pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(1); + /* + We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does. + This allowance is needed to not break existing versions of innobackup + which do a BEGIN; INSERT; FLUSH TABLES WITH READ LOCK; COMMIT. + */ + DBUG_RETURN(is_not_commit); } old_message=thd->enter_cond(&COND_refresh, &LOCK_open, "Waiting for release of readlock"); diff --git a/sql/log.cc b/sql/log.cc index ab080366f95..ef538c3b03f 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1929,19 +1929,6 @@ void MYSQL_LOG::set_max_size(ulong max_size_arg) } -Disable_binlog::Disable_binlog(THD *thd_arg) : - thd(thd_arg), save_options(thd_arg->options) -{ - thd_arg->options&= ~OPTION_BIN_LOG; -} - - -Disable_binlog::~Disable_binlog() -{ - thd->options= save_options; -} - - /* Check if a string is a valid number @@ -2005,15 +1992,15 @@ void print_buffer_to_file(enum loglevel level, const char *buffer) skr=time(NULL); localtime_r(&skr, &tm_tmp); start=&tm_tmp; - fprintf(stderr, "%02d%02d%02d %2d:%02d:%02d [%s] %s\n", - start->tm_year % 100, - start->tm_mon+1, - start->tm_mday, - start->tm_hour, - start->tm_min, - start->tm_sec, + fprintf(stderr, "%02d%02d%02d %2d:%02d:%02d [%s] %s\n", + start->tm_year % 100, + start->tm_mon+1, + start->tm_mday, + start->tm_hour, + start->tm_min, + start->tm_sec, (level == ERROR_LEVEL ? "ERROR" : level == WARNING_LEVEL ? - "WARNING" : "INFORMATION"), + "Warning" : "Note"), buffer); fflush(stderr); diff --git a/sql/log_event.cc b/sql/log_event.cc index c2a684ffe03..ee5af6ed1c0 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1362,7 +1362,7 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) Thank you. */ thd->catalog= (char*) catalog; - thd->db= (char*) rewrite_db(db); + thd->db= (char*) rewrite_db(db); // thd->db_length is set later if needed thd->variables.auto_increment_increment= auto_increment_increment; thd->variables.auto_increment_offset= auto_increment_offset; @@ -1384,6 +1384,11 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) if (db_ok(thd->db, replicate_do_db, replicate_ignore_db)) { thd->set_time((time_t)when); + /* + We cannot use db_len from event to fill thd->db_length, because + rewrite_db() may have changed db. + */ + thd->db_length= thd->db ? strlen(thd->db) : 0; thd->query_length= q_len; thd->query = (char*)query; VOID(pthread_mutex_lock(&LOCK_thread_count)); @@ -1527,11 +1532,19 @@ end: */ thd->db= thd->catalog= 0; // prevent db from being freed thd->query= 0; // just to be sure - thd->query_length= 0; + thd->query_length= thd->db_length =0; VOID(pthread_mutex_unlock(&LOCK_thread_count)); close_thread_tables(thd); free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC)); - return (thd->query_error ? thd->query_error : Log_event::exec_event(rli)); + /* + If there was an error we stop. Otherwise we increment positions. Note that + we will not increment group* positions if we are just after a SET + ONE_SHOT, because SET ONE_SHOT should not be separated from its following + updating query. + */ + return (thd->query_error ? thd->query_error : + (thd->one_shot_set ? (rli->inc_event_relay_log_pos(),0) : + Log_event::exec_event(rli))); } #endif @@ -2422,7 +2435,7 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, bool use_rli_only_for_errors) { char *load_data_query= 0; - thd->db= (char*) rewrite_db(db); + thd->db= (char*) rewrite_db(db); // thd->db_length is set later if needed DBUG_ASSERT(thd->query == 0); thd->query_length= 0; // Should not be needed thd->query_error= 0; @@ -2454,6 +2467,7 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, if (db_ok(thd->db, replicate_do_db, replicate_ignore_db)) { thd->set_time((time_t)when); + thd->db_length= thd->db ? strlen(thd->db) : 0; VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_id = query_id++; VOID(pthread_mutex_unlock(&LOCK_thread_count)); @@ -2580,7 +2594,7 @@ Slave: load data infile on table '%s' at log position %s in log \ VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->db= thd->catalog= 0; thd->query= 0; - thd->query_length= 0; + thd->query_length= thd->db_length= 0; VOID(pthread_mutex_unlock(&LOCK_thread_count)); close_thread_tables(thd); if (load_data_query) diff --git a/sql/log_event.h b/sql/log_event.h index a9d18c65f0c..a1c02b0e3c0 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -755,7 +755,7 @@ public: { fname= afname; fname_len= alen; - local_fname= true; + local_fname= TRUE; } /* fname doesn't point to memory inside Log_event::temp_buf */ int check_fname_outside_temp_buf() diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index d8916149b77..960f054b3ac 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -280,6 +280,7 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset; #define MODE_INVALID_DATES (MODE_NO_ZERO_DATE*2) #define MODE_ERROR_FOR_DIVISION_BY_ZERO (MODE_INVALID_DATES*2) #define MODE_TRADITIONAL (MODE_ERROR_FOR_DIVISION_BY_ZERO*2) +#define MODE_NO_AUTO_CREATE_USER (MODE_TRADITIONAL*2) #define RAID_BLOCK_SIZE 1024 @@ -331,14 +332,6 @@ void debug_sync_point(const char* lock_name, uint lock_timeout); #define WEEK_MONDAY_FIRST 1 #define WEEK_YEAR 2 #define WEEK_FIRST_WEEKDAY 4 -/* - Required buffer length for make_date, make_time, make_datetime - and TIME_to_string functions. Note, that the caller is still - responsible to check that given TIME structure has values - in valid ranges, otherwise size of the buffer could be not - enough. -*/ -#define MAX_DATE_REP_LENGTH 30 enum enum_parsing_place { @@ -426,7 +419,7 @@ int mysql_insert_select_prepare(THD *thd); int insert_select_precheck(THD *thd, TABLE_LIST *tables); int update_precheck(THD *thd, TABLE_LIST *tables); int delete_precheck(THD *thd, TABLE_LIST *tables); -int insert_precheck(THD *thd, TABLE_LIST *tables, bool update); +int insert_precheck(THD *thd, TABLE_LIST *tables); int create_table_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *create_table); Item *negate_expression(THD *thd, Item *expr); @@ -442,6 +435,9 @@ struct Query_cache_query_flags uint collation_connection_num; ha_rows limit; Time_zone *time_zone; + ulong sql_mode; + ulong max_sort_length; + ulong group_concat_max_len; }; #define QUERY_CACHE_FLAGS_SIZE sizeof(Query_cache_query_flags) #include "sql_cache.h" @@ -496,7 +492,8 @@ bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length); bool is_update_query(enum enum_sql_command command); bool alloc_query(THD *thd, char *packet, ulong packet_length); void mysql_init_select(LEX *lex); -void mysql_init_query(THD *thd, uchar *buf, uint length, bool lexonly=0); +void mysql_reset_thd_for_next_command(THD *thd); +void mysql_init_query(THD *thd, uchar *buf, uint length); bool mysql_new_select(LEX *lex, bool move_down); void create_select_for_variable(const char *var_name); void mysql_init_multi_delete(LEX *lex); @@ -571,7 +568,7 @@ int mysql_union(THD *thd, LEX *lex, select_result *result, int mysql_handle_derived(LEX *lex); Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, Item ***copy_func, Field **from_field, - bool group,bool modify_item); + bool group, bool modify_item, uint convert_blob_length); int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, List<create_field> &fields, List<Key> &keys, uint &db_options, @@ -697,6 +694,7 @@ int mysqld_show_keys(THD *thd, TABLE_LIST *table); int mysqld_show_logs(THD *thd); void append_identifier(THD *thd, String *packet, const char *name, uint length); +int get_quote_char_for_identifier(THD *thd, const char *name, uint length); void mysqld_list_fields(THD *thd,TABLE_LIST *table, const char *wild); int mysqld_dump_create_info(THD *thd, TABLE *table, int fd = -1); int mysqld_show_create(THD *thd, TABLE_LIST *table_list); @@ -739,14 +737,18 @@ void mysql_reset_errors(THD *thd); my_bool mysqld_show_warnings(THD *thd, ulong levels_to_show); /* sql_handler.cc */ -int mysql_ha_open(THD *thd, TABLE_LIST *tables); -int mysql_ha_close(THD *thd, TABLE_LIST *tables, - bool dont_send_ok=0, bool dont_lock=0, bool no_alias=0); -int mysql_ha_close_list(THD *thd, TABLE_LIST *tables, bool flushed=0); +int mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen= 0); +int mysql_ha_close(THD *thd, TABLE_LIST *tables); int mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *, List<Item> *,enum ha_rkey_function,Item *,ha_rows,ha_rows); +int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags); +/* mysql_ha_flush mode_flags bits */ +#define MYSQL_HA_CLOSE_FINAL 0x00 +#define MYSQL_HA_REOPEN_ON_USAGE 0x01 +#define MYSQL_HA_FLUSH_ALL 0x02 /* sql_base.cc */ +#define TMP_TABLE_KEY_EXTRA 8 void set_item_name(Item *item,char *pos,uint length); bool add_field_to_list(THD *thd, char *field_name, enum enum_field_types type, char *length, char *decimal, @@ -769,7 +771,8 @@ enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND, IGNORE_ERRORS}; extern const Item **not_found_item; Item ** find_item_in_list(Item *item, List<Item> &items, uint *counter, - find_item_error_report_type report_error); + find_item_error_report_type report_error, + bool *unaliased); bool get_key_map_from_key_list(key_map *map, TABLE *table, List<String> *index_list); bool insert_fields(THD *thd,TABLE_LIST *tables, @@ -789,6 +792,7 @@ void wait_for_refresh(THD *thd); int open_tables(THD *thd, TABLE_LIST *tables, uint *counter); int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables); int open_and_lock_tables(THD *thd,TABLE_LIST *tables); +void relink_tables_for_derived(THD *thd); int lock_tables(THD *thd, TABLE_LIST *tables, uint counter); TABLE *open_temporary_table(THD *thd, const char *path, const char *db, const char *table_name, bool link_in_list); @@ -896,9 +900,10 @@ extern void yyerror(const char*); extern bool check_reserved_words(LEX_STRING *name); /* strfunc.cc */ -ulonglong find_set(TYPELIB *typelib,const char *x, uint length, +ulonglong find_set(TYPELIB *lib, const char *x, uint length, CHARSET_INFO *cs, char **err_pos, uint *err_len, bool *set_warning); uint find_type(TYPELIB *lib, const char *find, uint length, bool part_match); +uint find_type2(TYPELIB *lib, const char *find, uint length, CHARSET_INFO *cs); uint check_word(TYPELIB *lib, const char *val, const char *end, const char **end_of_word); @@ -969,7 +974,7 @@ extern uint test_flags,select_errors,ha_open_options; extern uint protocol_version, mysqld_port, dropping_tables; extern uint delay_key_write_options, lower_case_table_names; extern bool opt_endinfo, using_udf_functions, locked_in_memory; -extern bool opt_using_transactions, mysql_embedded; +extern bool opt_using_transactions, mysqld_embedded; extern bool using_update_log, opt_large_files, server_id_supplied; extern bool opt_log, opt_update_log, opt_bin_log, opt_slow_log, opt_error_log; extern bool opt_disable_networking, opt_skip_show_db; @@ -1085,8 +1090,6 @@ int openfrm(THD *thd, const char *name,const char *alias,uint filestat, uint prgflag, uint ha_open_flags, TABLE *outparam); int readfrm(const char *name, const void** data, uint* length); int writefrm(const char* name, const void* data, uint len); -int create_table_from_handler(const char *db, const char *name, - bool create_if_found); int closefrm(TABLE *table); db_type get_table_type(const char *name); int read_string(File file, gptr *to, uint length); @@ -1123,7 +1126,6 @@ void make_date(const DATE_TIME_FORMAT *format, const TIME *l_time, String *str); void make_time(const DATE_TIME_FORMAT *format, const TIME *l_time, String *str); -void TIME_to_string(const TIME *time, String *str); ulonglong TIME_to_ulonglong_datetime(const TIME *time); ulonglong TIME_to_ulonglong_date(const TIME *time); ulonglong TIME_to_ulonglong_time(const TIME *time); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index db81b14c9c9..3d7ab14f13d 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -221,11 +221,11 @@ const char *sql_mode_names[] = "NO_TABLE_OPTIONS", "NO_FIELD_OPTIONS", "MYSQL323", "MYSQL40", "ANSI", "NO_AUTO_VALUE_ON_ZERO", "NO_BACKSLASH_ESCAPES", "STRICT_TRANS_TABLES", "STRICT_ALL_TABLES", "NO_ZERO_IN_DATE", "NO_ZERO_DATE", "ALLOW_INVALID_DATES", "ERROR_FOR_DIVISION_BY_ZERO", - "TRADITIONAL", + "TRADITIONAL", "NO_AUTO_CREATE_USER", NullS }; TYPELIB sql_mode_typelib= { array_elements(sql_mode_names)-1,"", - sql_mode_names }; + sql_mode_names, NULL }; const char *first_keyword= "first", *binary_keyword= "BINARY"; const char *my_localhost= "localhost", *delayed_user= "DELAYED"; #if SIZEOF_OFF_T > 4 && defined(BIG_TABLES) @@ -327,6 +327,7 @@ const char *opt_date_time_formats[3]; char *language_ptr, *default_collation_name, *default_character_set_name; char mysql_data_home_buff[2], *mysql_data_home=mysql_real_data_home; +struct passwd *user_info; char server_version[SERVER_VERSION_LENGTH]; char *mysqld_unix_port, *opt_mysql_tmpdir; char *my_bind_addr_str; @@ -449,9 +450,9 @@ pthread_cond_t eventShutdown; #endif #ifndef EMBEDDED_LIBRARY -bool mysql_embedded=0; +bool mysqld_embedded=0; #else -bool mysql_embedded=1; +bool mysqld_embedded=1; #endif #ifndef DBUG_OFF @@ -965,7 +966,7 @@ void clean_up(bool print_message) if (print_message && errmesg) sql_print_information(ER(ER_SHUTDOWN_COMPLETE),my_progname); -#if !defined(__WIN__) && !defined(EMBEDDED_LIBRARY) +#if !defined(EMBEDDED_LIBRARY) if (!opt_bootstrap) (void) my_delete(pidfile_name,MYF(0)); // This may not always exist #endif @@ -1050,65 +1051,72 @@ static void set_ports() #ifndef EMBEDDED_LIBRARY /* Change to run as another user if started with --user */ -static void set_user(const char *user) +static struct passwd *check_user(const char *user) { #if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) - struct passwd *ent; + struct passwd *user_info; uid_t user_id= geteuid(); - // don't bother if we aren't superuser + // Don't bother if we aren't superuser if (user_id) { if (user) { - /* Don't give a warning, if real user is same as given with --user */ - struct passwd *user_info= getpwnam(user); + // Don't give a warning, if real user is same as given with --user + user_info= getpwnam(user); if ((!user_info || user_id != user_info->pw_uid) && global_system_variables.log_warnings) sql_print_warning( "One can only use the --user switch if running as root\n"); } - return; + return NULL; } if (!user) { if (!opt_bootstrap) { - fprintf(stderr,"Fatal error: Please read \"Security\" section of the manual to find out how to run mysqld as root!\n"); + sql_print_error("Fatal error: Please read \"Security\" section of the manual to find out how to run mysqld as root!\n"); unireg_abort(1); } - return; + return NULL; } if (!strcmp(user,"root")) - return; // Avoid problem with dynamic libraries + return NULL; // Avoid problem with dynamic libraries - uid_t uid; - if (!(ent = getpwnam(user))) + if (!(user_info= getpwnam(user))) { - // allow a numeric uid to be used + // Allow a numeric uid to be used const char *pos; - for (pos=user; my_isdigit(mysqld_charset,*pos); pos++) ; - if (*pos) // Not numeric id - { - fprintf(stderr,"Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n",user); - unireg_abort(1); - } - uid=atoi(user); // Use numberic uid + for (pos= user; my_isdigit(mysqld_charset,*pos); pos++) ; + if (*pos) // Not numeric id + goto err; + if (!(user_info= getpwuid(atoi(user)))) + goto err; + else + return user_info; } else - { + return user_info; + +err: + sql_print_error("Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n",user); +#endif + return NULL; +} + +static void set_user(const char *user, struct passwd *user_info) +{ +#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) + DBUG_ASSERT(user_info); #ifdef HAVE_INITGROUPS - initgroups((char*) user,ent->pw_gid); + initgroups((char*) user,user_info->pw_gid); #endif - if (setgid(ent->pw_gid) == -1) - { - sql_perror("setgid"); - unireg_abort(1); - } - uid=ent->pw_uid; + if (setgid(user_info->pw_gid) == -1) + { + sql_perror("setgid"); + unireg_abort(1); } - - if (setuid(uid) == -1) + if (setuid(user_info->pw_uid) == -1) { sql_perror("setuid"); unireg_abort(1); @@ -1116,6 +1124,25 @@ static void set_user(const char *user) #endif } + +static void set_effective_user(struct passwd *user_info) +{ +#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) + DBUG_ASSERT(user_info); + if (setregid((gid_t)-1, user_info->pw_gid) == -1) + { + sql_perror("setregid"); + unireg_abort(1); + } + if (setreuid((uid_t)-1, user_info->pw_uid) == -1) + { + sql_perror("setreuid"); + unireg_abort(1); + } +#endif +} + + /* Change root user if started with --chroot */ static void set_root(const char *path) @@ -1191,7 +1218,16 @@ static void server_init(void) unireg_abort(1); } } - set_user(mysqld_user); // Works also with mysqld_user==NULL + + if ((user_info= check_user(mysqld_user))) + { +#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) + if (locked_in_memory) // getuid() == 0 here + set_effective_user(user_info); + else +#endif + set_user(mysqld_user, user_info); + } #ifdef __NT__ /* create named pipe */ @@ -1467,7 +1503,11 @@ static void init_signals(void) } static void start_signal_handler(void) -{} +{ + // Save vm id of this process + if (!opt_bootstrap) + create_pid_file(); +} static void check_data_home(const char *path) {} @@ -2085,8 +2125,7 @@ static void check_data_home(const char *path) /* ARGSUSED */ -extern "C" int my_message_sql(uint error, const char *str, - myf MyFlags __attribute__((unused))) +extern "C" int my_message_sql(uint error, const char *str, myf MyFlags) { THD *thd; DBUG_ENTER("my_message_sql"); @@ -2105,7 +2144,11 @@ extern "C" int my_message_sql(uint error, const char *str, if (thd->lex->current_select && thd->lex->current_select->no_error && !thd->is_fatal_error) { - DBUG_PRINT("error", ("above error converted to warning")); + DBUG_PRINT("error", ("Error converted to warning: current_select: no_error %d fatal_error: %d", + (thd->lex->current_select ? + thd->lex->current_select->no_error : 0), + (int) thd->is_fatal_error)); + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, error, str); } else @@ -2119,7 +2162,7 @@ extern "C" int my_message_sql(uint error, const char *str, } } } - else + if (!thd || MyFlags & ME_NOREFRESH) sql_print_error("%s: %s",my_progname,str); /* purecov: inspected */ DBUG_RETURN(0); } @@ -2672,19 +2715,26 @@ server."); /* We must set dflt_key_cache in case we are using ISAM tables */ dflt_key_cache= sql_key_cache; -#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) - if (locked_in_memory && !geteuid()) +#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) && !defined(EMBEDDED_LIBRARY) + if (locked_in_memory && !getuid()) { + if (setreuid((uid_t)-1, 0) == -1) + { // this should never happen + sql_perror("setreuid"); + unireg_abort(1); + } if (mlockall(MCL_CURRENT)) { if (global_system_variables.log_warnings) sql_print_warning("Failed to lock memory. Errno: %d\n",errno); locked_in_memory= 0; } + if (user_info) + set_user(mysqld_user, user_info); } -#else - locked_in_memory=0; + else #endif + locked_in_memory=0; ft_init_stopwords(); @@ -2945,10 +2995,10 @@ we force server id to 2, but this MySQL server will not act as a slave."); #ifndef __NETWARE__ (void) pthread_kill(signal_thread, MYSQL_KILL_SIGNAL); #endif /* __NETWARE__ */ -#ifndef __WIN__ + if (!opt_bootstrap) (void) my_delete(pidfile_name,MYF(MY_WME)); // Not needed anymore -#endif + if (unix_sock != INVALID_SOCKET) unlink(mysqld_unix_port); exit(1); @@ -4036,13 +4086,16 @@ enum options_mysqld OPT_INNODB_BUFFER_POOL_SIZE, OPT_INNODB_BUFFER_POOL_AWE_MEM_MB, OPT_INNODB_ADDITIONAL_MEM_POOL_SIZE, + OPT_INNODB_MAX_PURGE_LAG, OPT_INNODB_FILE_IO_THREADS, OPT_INNODB_LOCK_WAIT_TIMEOUT, OPT_INNODB_THREAD_CONCURRENCY, OPT_INNODB_FORCE_RECOVERY, OPT_INNODB_STATUS_FILE, OPT_INNODB_MAX_DIRTY_PAGES_PCT, + OPT_INNODB_TABLE_LOCKS, OPT_INNODB_OPEN_FILES, + OPT_INNODB_AUTOEXTEND_INCREMENT, OPT_BDB_CACHE_SIZE, OPT_BDB_LOG_BUFFER_SIZE, OPT_BDB_MAX_LOCK, @@ -4284,10 +4337,20 @@ Disable with --skip-innodb (will save memory).", {"innodb_max_dirty_pages_pct", OPT_INNODB_MAX_DIRTY_PAGES_PCT, "Percentage of dirty pages allowed in bufferpool.", (gptr*) &srv_max_buf_pool_modified_pct, (gptr*) &srv_max_buf_pool_modified_pct, 0, GET_ULONG, REQUIRED_ARG, 90, 0, 100, 0, 0, 0}, + {"innodb_max_purge_lag", OPT_INNODB_MAX_PURGE_LAG, + "", + (gptr*) &srv_max_purge_lag, + (gptr*) &srv_max_purge_lag, 0, GET_LONG, REQUIRED_ARG, 0, 0, ~0L, + 0, 1L, 0}, {"innodb_status_file", OPT_INNODB_STATUS_FILE, "Enable SHOW INNODB STATUS output in the innodb_status.<pid> file", (gptr*) &innobase_create_status_file, (gptr*) &innobase_create_status_file, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"innodb_table_locks", OPT_INNODB_TABLE_LOCKS, + "If Innodb should enforce LOCK TABLE", + (gptr*) &global_system_variables.innodb_table_locks, + (gptr*) &global_system_variables.innodb_table_locks, + 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, #endif /* End HAVE_INNOBASE_DB */ {"isam", OPT_ISAM, "Enable ISAM (if this version of MySQL supports it). \ Disable with --skip-isam.", @@ -4764,6 +4827,11 @@ log and this option does nothing anymore.", (gptr*) &innobase_additional_mem_pool_size, (gptr*) &innobase_additional_mem_pool_size, 0, GET_LONG, REQUIRED_ARG, 1*1024*1024L, 512*1024L, ~0L, 0, 1024, 0}, + {"innodb_autoextend_increment", OPT_INNODB_AUTOEXTEND_INCREMENT, + "Data file autoextend increment in megabytes", + (gptr*) &srv_auto_extend_increment, + (gptr*) &srv_auto_extend_increment, + 0, GET_LONG, REQUIRED_ARG, 8L, 1L, ~0L, 0, 1L, 0}, {"innodb_buffer_pool_awe_mem_mb", OPT_INNODB_BUFFER_POOL_AWE_MEM_MB, "If Windows AWE is used, the size of InnoDB buffer pool allocated from the AWE memory.", (gptr*) &innobase_buffer_pool_awe_mem_mb, (gptr*) &innobase_buffer_pool_awe_mem_mb, 0, @@ -4846,7 +4914,7 @@ log and this option does nothing anymore.", (gptr*) &dflt_key_cache_var.param_buff_size, (gptr*) 0, 0, (GET_ULL | GET_ASK_ADDR), - REQUIRED_ARG, KEY_CACHE_SIZE, MALLOC_OVERHEAD, (long) ~0, MALLOC_OVERHEAD, + REQUIRED_ARG, KEY_CACHE_SIZE, MALLOC_OVERHEAD, UINT_MAX32, MALLOC_OVERHEAD, IO_SIZE, 0}, {"key_cache_age_threshold", OPT_KEY_CACHE_AGE_THRESHOLD, "This characterizes the number of hits a hot block has to be untouched until it is considered aged enough to be downgraded to a warm block. This specifies the percentage ratio of that number of hits to the total number of blocks in key cache", diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 78d254fccb1..0a728440e4d 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -193,9 +193,7 @@ my_bool net_realloc(NET *net, ulong length) { net->error= 1; net->report_error= 1; -#ifdef MYSQL_SERVER net->last_errno= ER_OUT_OF_RESOURCES; -#endif DBUG_RETURN(1); } net->buff=net->write_pos=buff; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 683bf1bdc13..286d0d01842 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -714,6 +714,7 @@ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr, bool no_alloc, MEM_ROOT *parent_alloc) :dont_free(0),error(0),free_file(0),cur_range(NULL),range(0) { + sorted= 0; index= key_nr; head= table; key_part_info= head->key_info[index].key_part; @@ -1725,9 +1726,10 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, } if (tree) - {/* - It is possible to use a range-based quick select (but it might be slower - than 'all' table scan). + { + /* + It is possible to use a range-based quick select (but it might be + slower than 'all' table scan). */ if (tree->merges.is_empty()) { @@ -4839,7 +4841,7 @@ void SEL_ARG::test_use_count(SEL_ARG *root) uint e_count=0; if (this == root && use_count != 1) { - sql_print_error("Note: Use_count: Wrong count %lu for root",use_count); + sql_print_information("Use_count: Wrong count %lu for root",use_count); return; } if (this->type != SEL_ARG::KEY_RANGE) @@ -4852,7 +4854,7 @@ void SEL_ARG::test_use_count(SEL_ARG *root) ulong count=count_key_part_usage(root,pos->next_key_part); if (count > pos->next_key_part->use_count) { - sql_print_error("Note: Use_count: Wrong count for key at 0x%lx, %lu should be %lu", + sql_print_information("Use_count: Wrong count for key at 0x%lx, %lu should be %lu", pos,pos->next_key_part->use_count,count); return; } @@ -4860,7 +4862,7 @@ void SEL_ARG::test_use_count(SEL_ARG *root) } } if (e_count != elements) - sql_print_error("Warning: Wrong use count: %u (should be %u) for tree at 0x%lx", + sql_print_warning("Wrong use count: %u (should be %u) for tree at 0x%lx", e_count, elements, (gptr) this); } @@ -5233,8 +5235,6 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree, QUICK_RANGE_SELECT *quick; DBUG_ENTER("get_quick_select"); - - if (param->table->key_info[param->real_keynr[idx]].flags & HA_SPATIAL) quick=new QUICK_RANGE_SELECT_GEOM(param->thd, param->table, param->real_keynr[idx], @@ -5243,7 +5243,7 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree, else quick=new QUICK_RANGE_SELECT(param->thd, param->table, param->real_keynr[idx], - test(parent_alloc), parent_alloc); + test(parent_alloc)); if (quick) { @@ -5370,7 +5370,6 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key, if (insert_dynamic(&quick->ranges, (gptr)&range)) return 1; - end: if (key_tree->right != &null_element) return get_quick_keys(param,quick,key,key_tree->right, @@ -5455,14 +5454,18 @@ bool QUICK_ROR_UNION_SELECT::check_if_keys_used(List<Item> *fields) return 0; } + /**************************************************************************** Create a QUICK RANGE based on a key + This allocates things in a new memory root, as this may be called many times + during a query. ****************************************************************************/ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, TABLE_REF *ref) { - QUICK_RANGE_SELECT *quick=new QUICK_RANGE_SELECT(thd, table, ref->key, 1); + MEM_ROOT *old_root= my_pthread_getspecific_ptr(MEM_ROOT*, THR_MALLOC); + QUICK_RANGE_SELECT *quick= new QUICK_RANGE_SELECT(thd, table, ref->key, 0); KEY *key_info = &table->key_info[ref->key]; KEY_PART *key_part; QUICK_RANGE *range; @@ -5473,17 +5476,12 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, if (quick->init()) { delete quick; - return 0; - } - - if (cp_buffer_from_ref(ref)) - { - if (thd->is_fatal_error) - goto err; // out of memory + goto err; } - if (!(range= new QUICK_RANGE())) - goto err; // out of memory + if (cp_buffer_from_ref(ref) && thd->is_fatal_error || + !(range= new QUICK_RANGE())) + goto err; // out of memory range->min_key=range->max_key=(char*) ref->key_buff; range->min_length=range->max_length=ref->key_length; @@ -5526,9 +5524,12 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, goto err; } +ok: + my_pthread_setspecific_ptr(THR_MALLOC, old_root); return quick; err: + my_pthread_setspecific_ptr(THR_MALLOC, old_root); delete quick; return 0; } @@ -6481,8 +6482,8 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts, - GA = <G_1, ..., G_k> - from the GROUP BY clause (if any) = SA - if Q is a DISTINCT query (based on the equivalence of DISTINCT and GROUP queries. - - NGA = QA - (GA union C) = {NG_1, ..., NG_m} - the ones not in GROUP BY - and not referenced by MIN/MAX functions. + - NGA = QA - (GA union C) = {NG_1, ..., NG_m} - the ones not in + GROUP BY and not referenced by MIN/MAX functions. with the following properties specified below. SA1. There is at most one attribute in SA referenced by any number of @@ -6860,9 +6861,9 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) if (!index_info) /* No usable index found. */ DBUG_RETURN(NULL); - - /* Check (SA3,WA1) for the where clause. */ - if (!check_group_min_max_predicates(join->conds, min_max_arg_item, + /* Check (SA3) for the where clause. */ + if (join->conds && min_max_arg_item && + !check_group_min_max_predicates(join->conds, min_max_arg_item, (index_info->flags & HA_SPATIAL) ? Field::itMBR : Field::itRAW)) DBUG_RETURN(NULL); @@ -6920,8 +6921,7 @@ check_group_min_max_predicates(COND *cond, Item_field *min_max_arg_item, Field::imagetype image_type) { DBUG_ENTER("check_group_min_max_predicates"); - if (!cond) /* If no WHERE clause, then all is OK. */ - DBUG_RETURN(TRUE); + DBUG_ASSERT(cond && min_max_arg_item); Item::Type cond_type= cond->type(); if (cond_type == Item::COND_ITEM) /* 'AND' or 'OR' */ @@ -6938,6 +6938,19 @@ check_group_min_max_predicates(COND *cond, Item_field *min_max_arg_item, DBUG_RETURN(TRUE); } + /* + TODO: + This is a very crude fix to handle sub-selects in the WHERE clause + (Item_subselect objects). With the test below we rule out from the + optimization all queries with subselects in the WHERE clause. What has to + be done, is that here we should analyze whether the subselect references + the MIN/MAX argument field, and disallow the optimization only if this is + so. + */ + if (cond_type == Item::SUBSELECT_ITEM) + DBUG_RETURN(FALSE); + + /* We presume that at this point there are no other Items than functions. */ DBUG_ASSERT(cond_type == Item::FUNC_ITEM); /* Test if cond references only group-by or non-group fields. */ @@ -6951,11 +6964,11 @@ check_group_min_max_predicates(COND *cond, Item_field *min_max_arg_item, DBUG_PRINT("info", ("cur_arg: %s", cur_arg->full_name())); if (cur_arg->type() == Item::FIELD_ITEM) { - if (min_max_arg_item && /* pred references min_max_arg_item. */ - min_max_arg_item->eq(cur_arg, 1)) - {/* - Check if pred is a range condition that compares the MIN/MAX argument - with a constant. + if (min_max_arg_item->eq(cur_arg, 1)) + { + /* + If pred references the MIN/MAX argument, check whether pred is a range + condition that compares the MIN/MAX argument with a constant. */ Item_func::Functype pred_type= pred->functype(); if (pred_type != Item_func::EQUAL_FUNC && @@ -7194,16 +7207,16 @@ SEL_ARG * get_index_range_tree(uint index, SEL_TREE* range_tree, PARAM *param, group_key_parts [in] Number of index key parts in the group prefix range_tree [in] Tree of ranges for all indexes index_tree [in] The range tree for the current index - quick_prefix_records [in] Number of records retrieved by the internally used - quick range select if any + quick_prefix_records [in] Number of records retrieved by the internally + used quick range select if any have_min [in] True if there is a MIN function have_max [in] True if there is a MAX function read_cost [out] The cost to retrieve rows via this quick select records [out] The number of rows retrieved DESCRIPTION - This method computes the access cost of a TRP_GROUP_MIN_MAX instance and the - number of rows returned. It updates this->read_cost and this->records. + This method computes the access cost of a TRP_GROUP_MIN_MAX instance and + the number of rows returned. It updates this->read_cost and this->records. NOTES The cost computation distinguishes several cases: @@ -7260,8 +7273,8 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts, double quick_prefix_selectivity; double io_cost; double cpu_cost= 0; /* TODO: CPU cost of index_read calls? */ - DBUG_ENTER("TRP_GROUP_MIN_MAX::cost"); + table_records= table->file->records; keys_per_block= (table->file->block_size / 2 / (index_info->key_length + table->file->ref_length) @@ -7349,7 +7362,6 @@ TRP_GROUP_MIN_MAX::make_quick(PARAM *param, bool retrieve_full_rows, MEM_ROOT *parent_alloc) { QUICK_GROUP_MIN_MAX_SELECT *quick; - DBUG_ENTER("TRP_GROUP_MIN_MAX::make_quick"); quick= new QUICK_GROUP_MIN_MAX_SELECT(param->table, @@ -7357,7 +7369,8 @@ TRP_GROUP_MIN_MAX::make_quick(PARAM *param, bool retrieve_full_rows, have_min, have_max, min_max_arg_part, group_prefix_len, used_key_parts, index_info, index, read_cost, records, - key_infix_len, key_infix, parent_alloc); + key_infix_len, key_infix, + parent_alloc); if (!quick) DBUG_RETURN(NULL); @@ -7440,17 +7453,20 @@ TRP_GROUP_MIN_MAX::make_quick(PARAM *param, bool retrieve_full_rows, None */ -QUICK_GROUP_MIN_MAX_SELECT::QUICK_GROUP_MIN_MAX_SELECT( - TABLE *table, JOIN *join_arg, bool have_min_arg, bool have_max_arg, - KEY_PART_INFO *min_max_arg_part_arg, uint group_prefix_len_arg, - uint used_key_parts_arg, KEY *index_info_arg, uint use_index, - double read_cost_arg, ha_rows records_arg, uint key_infix_len_arg, - byte *key_infix_arg, MEM_ROOT *parent_alloc) -: join(join_arg), index_info(index_info_arg), - group_prefix_len(group_prefix_len_arg), have_min(have_min_arg), - have_max(have_max_arg), seen_first_key(FALSE), - min_max_arg_part(min_max_arg_part_arg), key_infix(key_infix_arg), - key_infix_len(key_infix_len_arg) +QUICK_GROUP_MIN_MAX_SELECT:: +QUICK_GROUP_MIN_MAX_SELECT(TABLE *table, JOIN *join_arg, bool have_min_arg, + bool have_max_arg, + KEY_PART_INFO *min_max_arg_part_arg, + uint group_prefix_len_arg, + uint used_key_parts_arg, KEY *index_info_arg, + uint use_index, double read_cost_arg, + ha_rows records_arg, uint key_infix_len_arg, + byte *key_infix_arg, MEM_ROOT *parent_alloc) + :join(join_arg), index_info(index_info_arg), + group_prefix_len(group_prefix_len_arg), have_min(have_min_arg), + have_max(have_max_arg), seen_first_key(FALSE), + min_max_arg_part(min_max_arg_part_arg), key_infix(key_infix_arg), + key_infix_len(key_infix_len_arg) { head= table; file= head->file; @@ -7463,6 +7479,12 @@ QUICK_GROUP_MIN_MAX_SELECT::QUICK_GROUP_MIN_MAX_SELECT( real_prefix_len= group_prefix_len + key_infix_len; group_prefix= NULL; min_max_arg_len= min_max_arg_part ? min_max_arg_part->store_length : 0; + + /* + We can't have parent_alloc set as the init function can't handle this case + yet. + */ + DBUG_ASSERT(!parent_alloc); if (!parent_alloc) { init_sql_alloc(&alloc, join->thd->variables.range_alloc_block_size, 0); @@ -8571,7 +8593,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::dbug_dump(int indent, bool verbose) } -#endif +#endif /* NOT_USED */ /***************************************************************************** ** Instantiate templates diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 6b966c28b1a..30033bc39eb 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -186,16 +186,14 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) if (!ref.key_length) error= table->file->index_first(table->record[0]); else - { error= table->file->index_read(table->record[0],key_buff, ref.key_length, range_fl & NEAR_MIN ? HA_READ_AFTER_KEY : HA_READ_KEY_OR_NEXT); - if (!error && reckey_in_range(0, &ref, item_field->field, - conds, range_fl, prefix_len)) - error= HA_ERR_KEY_NOT_FOUND; - } + if (!error && reckey_in_range(0, &ref, item_field->field, + conds, range_fl, prefix_len)) + error= HA_ERR_KEY_NOT_FOUND; if (table->key_read) { table->key_read= 0; @@ -260,16 +258,14 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) if (!ref.key_length) error= table->file->index_last(table->record[0]); else - { error= table->file->index_read(table->record[0], key_buff, ref.key_length, range_fl & NEAR_MAX ? HA_READ_BEFORE_KEY : HA_READ_PREFIX_LAST_OR_PREV); - if (!error && reckey_in_range(1, &ref, item_field->field, - conds, range_fl, prefix_len)) - error= HA_ERR_KEY_NOT_FOUND; - } + if (!error && reckey_in_range(1, &ref, item_field->field, + conds, range_fl, prefix_len)) + error= HA_ERR_KEY_NOT_FOUND; if (table->key_read) { table->key_read=0; diff --git a/sql/password.c b/sql/password.c index 115c1ef83ef..e2f7e14d39f 100644 --- a/sql/password.c +++ b/sql/password.c @@ -35,7 +35,7 @@ update user set password=PASSWORD("hello") where user="test" This saves a hashed number as a string in the password field. - The new autentication is performed in following manner: + The new authentication is performed in following manner: SERVER: public_seed=create_random_string() send(public_seed) diff --git a/sql/protocol.cc b/sql/protocol.cc index 187989158df..051eaaf7bda 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -225,6 +225,10 @@ net_printf(THD *thd, uint errcode, ...) format,args); va_end(args); + /* Replication slave relies on net->last_* to see if there was error */ + net->last_errno= errcode; + strmake(net->last_error, text_pos, sizeof(net->last_error)-1); + #ifndef EMBEDDED_LIBRARY if (net->vio == 0) { @@ -503,7 +507,7 @@ void Protocol::init(THD *thd_arg) */ #ifndef EMBEDDED_LIBRARY -bool Protocol::send_fields(List<Item> *list, int flags) +bool Protocol::send_fields(List<Item> *list, uint flags) { List_iterator_fast<Item> it(*list); Item *item; @@ -894,10 +898,9 @@ bool Protocol_simple::store_date(TIME *tm) field_types[field_pos] == MYSQL_TYPE_DATE); field_pos++; #endif - char buff[40]; - String tmp((char*) buff,sizeof(buff),&my_charset_bin); - make_date((DATE_TIME_FORMAT *) 0, tm, &tmp); - return net_store_data((char*) tmp.ptr(), tmp.length()); + char buff[MAX_DATE_STRING_REP_LENGTH]; + int length= my_date_to_str(tm, buff); + return net_store_data(buff, (uint) length); } @@ -1108,6 +1111,13 @@ bool Protocol_prep::store_time(TIME *tm) field_pos++; pos= buff+1; pos[0]= tm->neg ? 1 : 0; + if (tm->hour >= 24) + { + /* Fix if we come from Item::send */ + uint days= tm->hour/24; + tm->hour-= days*24; + tm->day+= days; + } int4store(pos+1, tm->day); pos[5]= (uchar) tm->hour; pos[6]= (uchar) tm->minute; diff --git a/sql/protocol.h b/sql/protocol.h index 1a5896a3ae5..7d390242417 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -52,7 +52,7 @@ public: void init(THD* thd_arg); enum { SEND_NUM_ROWS= 1, SEND_DEFAULTS= 2, SEND_EOF= 4 }; - virtual bool send_fields(List<Item> *list, int flags); + virtual bool send_fields(List<Item> *list, uint flags); bool send_records_num(List<Item> *list, ulonglong records); bool store(I_List<i_string> *str_list); @@ -166,7 +166,7 @@ public: prev_record= &data; return Protocol_simple::prepare_for_send(item_list); } - bool send_fields(List<Item> *list, int flags); + bool send_fields(List<Item> *list, uint flags); bool write(); uint get_field_count() { return field_count; } }; diff --git a/sql/protocol_cursor.cc b/sql/protocol_cursor.cc index 8904aba7b88..104457b3bcc 100644 --- a/sql/protocol_cursor.cc +++ b/sql/protocol_cursor.cc @@ -26,22 +26,21 @@ #include "mysql_priv.h" #include <mysql.h> -bool Protocol_cursor::send_fields(List<Item> *list, int flags) +bool Protocol_cursor::send_fields(List<Item> *list, uint flags) { List_iterator_fast<Item> it(*list); Item *item; MYSQL_FIELD *client_field; - - DBUG_ENTER("send_fields"); + DBUG_ENTER("Protocol_cursor::send_fields"); + if (prepare_for_send(list)) - return FALSE; + return FALSE; fields= (MYSQL_FIELD *)alloc_root(alloc, sizeof(MYSQL_FIELD) * field_count); if (!fields) goto err; - client_field= fields; - while ((item= it++)) + for (client_field= fields; (item= it++) ; client_field++) { Send_field server_field; item->make_field(&server_field); @@ -67,7 +66,7 @@ bool Protocol_cursor::send_fields(List<Item> *list, int flags) if (INTERNAL_NUM_FIELD(client_field)) client_field->flags|= NUM_FLAG; - if (flags & (int) Protocol::SEND_DEFAULTS) + if (flags & (uint) Protocol::SEND_DEFAULTS) { char buff[80]; String tmp(buff, sizeof(buff), default_charset_info), *res; @@ -80,16 +79,18 @@ bool Protocol_cursor::send_fields(List<Item> *list, int flags) else client_field->def=0; client_field->max_length= 0; - ++client_field; } DBUG_RETURN(FALSE); - err: + +err: send_error(thd, ER_OUT_OF_RESOURCES); /* purecov: inspected */ DBUG_RETURN(TRUE); /* purecov: inspected */ } + /* Get the length of next field. Change parameter to point at fieldstart */ + bool Protocol_cursor::write() { byte *cp= (byte *)packet->ptr(); @@ -121,7 +122,7 @@ bool Protocol_cursor::write() { if ((long)len > (end_pos - cp)) { -// TODO error signal send_error(thd, CR_MALFORMED_PACKET); + // TODO error signal send_error(thd, CR_MALFORMED_PACKET); return TRUE; } *data_tmp= to; @@ -141,6 +142,6 @@ bool Protocol_cursor::write() row_count++; return FALSE; err: -// TODO error signal send_error(thd, ER_OUT_OF_RESOURCES); + // TODO error signal send_error(thd, ER_OUT_OF_RESOURCES); return TRUE; } diff --git a/sql/records.cc b/sql/records.cc index 8dd4f548093..5963c36afd9 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -156,17 +156,26 @@ void end_read_record(READ_RECORD *info) static int rr_quick(READ_RECORD *info) { - int tmp=info->select->quick->get_next(); - if (tmp) + int tmp; + while ((tmp= info->select->quick->get_next())) { - if (tmp == HA_ERR_END_OF_FILE) - tmp= -1; - else + if (info->thd->killed) { - if (info->print_error) - info->file->print_error(tmp,MYF(0)); - if (tmp < 0) // Fix negative BDB errno - tmp=1; + my_error(ER_SERVER_SHUTDOWN, MYF(0)); + return 1; + } + 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; + } + break; } } return tmp; @@ -330,9 +339,10 @@ static int init_rr_cache(READ_RECORD *info) rec_cache_size=info->cache_records*info->reclength; info->rec_cache_size=info->cache_records*info->ref_length; + // We have to allocate one more byte to use uint3korr (see comments for it) if (info->cache_records <= 2 || !(info->cache=(byte*) my_malloc_lock(rec_cache_size+info->cache_records* - info->struct_length, + info->struct_length+1, MYF(0)))) DBUG_RETURN(1); #ifdef HAVE_purify diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 10ff5fa3596..dc23b014e31 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -14,8 +14,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -// Sasha Pachev <sasha@mysql.com> is currently in charge of this file - #include "mysql_priv.h" #ifdef HAVE_REPLICATION @@ -37,7 +35,7 @@ HASH slave_list; const char *rpl_role_type[] = {"MASTER","SLAVE",NullS}; TYPELIB rpl_role_typelib = {array_elements(rpl_role_type)-1,"", - rpl_role_type}; + rpl_role_type, NULL}; const char* rpl_status_type[]= { @@ -45,7 +43,7 @@ const char* rpl_status_type[]= "RECOVERY_CAPTAIN","NULL",NullS }; TYPELIB rpl_status_typelib= {array_elements(rpl_status_type)-1,"", - rpl_status_type}; + rpl_status_type, NULL}; static Slave_log_event* find_slave_event(IO_CACHE* log, diff --git a/sql/repl_failsafe.h b/sql/repl_failsafe.h index a9c504330ab..ad0219bb735 100644 --- a/sql/repl_failsafe.h +++ b/sql/repl_failsafe.h @@ -1,6 +1,20 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB & Sasha + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifdef HAVE_REPLICATION -#ifndef REPL_FAILSAFE_H -#define REPL_FAILSAFE_H #include "mysql.h" #include "my_sys.h" @@ -35,5 +49,4 @@ void end_slave_list(); int register_slave(THD* thd, uchar* packet, uint packet_length); void unregister_slave(THD* thd, bool only_mine, bool need_mutex); -#endif #endif /* HAVE_REPLICATION */ diff --git a/sql/set_var.cc b/sql/set_var.cc index a43a2c13aca..8ffed33d442 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -73,13 +73,14 @@ static HASH system_variable_hash; const char *bool_type_names[]= { "OFF", "ON", NullS }; TYPELIB bool_typelib= { - array_elements(bool_type_names)-1, "", bool_type_names + array_elements(bool_type_names)-1, "", bool_type_names, NULL }; const char *delay_key_write_type_names[]= { "OFF", "ON", "ALL", NullS }; TYPELIB delay_key_write_typelib= { - array_elements(delay_key_write_type_names)-1, "", delay_key_write_type_names + array_elements(delay_key_write_type_names)-1, "", + delay_key_write_type_names, NULL }; static int sys_check_charset(THD *thd, set_var *var); @@ -366,6 +367,12 @@ sys_var_thd_ulong sys_net_wait_timeout("wait_timeout", #ifdef HAVE_INNOBASE_DB sys_var_long_ptr sys_innodb_max_dirty_pages_pct("innodb_max_dirty_pages_pct", &srv_max_buf_pool_modified_pct); +sys_var_long_ptr sys_innodb_max_purge_lag("innodb_max_purge_lag", + &srv_max_purge_lag); +sys_var_thd_bool sys_innodb_table_locks("innodb_table_locks", + &SV::innodb_table_locks); +sys_var_long_ptr sys_innodb_autoextend_increment("innodb_autoextend_increment", + &srv_auto_extend_increment); #endif /* Time/date/datetime formats */ @@ -620,6 +627,10 @@ sys_var *sys_variables[]= &sys_os, #ifdef HAVE_INNOBASE_DB &sys_innodb_max_dirty_pages_pct, + &sys_innodb_max_purge_lag, + &sys_innodb_table_locks, + &sys_innodb_max_purge_lag, + &sys_innodb_autoextend_increment, #endif &sys_unique_checks, &sys_updatable_views_with_limit, @@ -696,6 +707,7 @@ struct show_var_st init_vars[]= { {"init_slave", (char*) &sys_init_slave, SHOW_SYS}, #ifdef HAVE_INNOBASE_DB {"innodb_additional_mem_pool_size", (char*) &innobase_additional_mem_pool_size, SHOW_LONG }, + {sys_innodb_autoextend_increment.name, (char*) &sys_innodb_autoextend_increment, SHOW_SYS}, {"innodb_buffer_pool_awe_mem_mb", (char*) &innobase_buffer_pool_awe_mem_mb, SHOW_LONG }, {"innodb_buffer_pool_size", (char*) &innobase_buffer_pool_size, SHOW_LONG }, {"innodb_data_file_path", (char*) &innobase_data_file_path, SHOW_CHAR_PTR}, @@ -715,6 +727,9 @@ struct show_var_st init_vars[]= { {"innodb_log_files_in_group", (char*) &innobase_log_files_in_group, SHOW_LONG}, {"innodb_log_group_home_dir", (char*) &innobase_log_group_home_dir, SHOW_CHAR_PTR}, {sys_innodb_max_dirty_pages_pct.name, (char*) &sys_innodb_max_dirty_pages_pct, SHOW_SYS}, + {sys_innodb_max_purge_lag.name, (char*) &sys_innodb_max_purge_lag, SHOW_SYS}, + {sys_innodb_table_locks.name, (char*) &sys_innodb_table_locks, SHOW_SYS}, + {sys_innodb_max_purge_lag.name, (char*) &sys_innodb_max_purge_lag, SHOW_SYS}, {"innodb_mirrored_log_groups", (char*) &innobase_mirrored_log_groups, SHOW_LONG}, {"innodb_open_files", (char*) &innobase_open_files, SHOW_LONG }, {"innodb_thread_concurrency", (char*) &innobase_thread_concurrency, SHOW_LONG }, @@ -1475,7 +1490,9 @@ bool sys_var::check_set(THD *thd, set_var *var, TYPELIB *enum_names) goto err; var->save_result.ulong_value= ((ulong) find_set(enum_names, res->c_ptr(), - res->length(), &error, &error_len, + res->length(), + NULL, + &error, &error_len, ¬_used)); if (error_len) { @@ -2999,7 +3016,8 @@ void sys_var_thd_table_type::warn_deprecated(THD *thd) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DEPRECATED_SYNTAX, - ER(ER_WARN_DEPRECATED_SYNTAX), "table_type", "storage_engine"); + ER(ER_WARN_DEPRECATED_SYNTAX), "table_type", + "storage_engine"); } void sys_var_thd_table_type::set_default(THD *thd, enum_var_type type) @@ -3077,7 +3095,7 @@ ulong fix_sql_mode(ulong sql_mode) sql_mode|= (MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES | MODE_IGNORE_SPACE | MODE_NO_KEY_OPTIONS | MODE_NO_TABLE_OPTIONS | - MODE_NO_FIELD_OPTIONS); + MODE_NO_FIELD_OPTIONS | MODE_NO_AUTO_CREATE_USER); if (sql_mode & MODE_MSSQL) sql_mode|= (MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES | MODE_IGNORE_SPACE | @@ -3097,7 +3115,7 @@ ulong fix_sql_mode(ulong sql_mode) sql_mode|= (MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES | MODE_IGNORE_SPACE | MODE_NO_KEY_OPTIONS | MODE_NO_TABLE_OPTIONS | - MODE_NO_FIELD_OPTIONS); + MODE_NO_FIELD_OPTIONS | MODE_NO_AUTO_CREATE_USER); if (sql_mode & MODE_MYSQL40) sql_mode|= MODE_NO_FIELD_OPTIONS; if (sql_mode & MODE_MYSQL323) @@ -3105,7 +3123,7 @@ ulong fix_sql_mode(ulong sql_mode) if (sql_mode & MODE_TRADITIONAL) sql_mode|= (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES | MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | - MODE_ERROR_FOR_DIVISION_BY_ZERO); + MODE_ERROR_FOR_DIVISION_BY_ZERO | MODE_NO_AUTO_CREATE_USER); return sql_mode; } diff --git a/sql/share/charsets/Index.xml b/sql/share/charsets/Index.xml index 5e75cde5f2a..9595d4a7ddb 100644 --- a/sql/share/charsets/Index.xml +++ b/sql/share/charsets/Index.xml @@ -2,6 +2,24 @@ <charsets max-id="94"> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <description> This file lists all of the available character sets. To make maintaining easier please: diff --git a/sql/share/charsets/armscii8.xml b/sql/share/charsets/armscii8.xml index 8ca5675012b..d0ab428345f 100644 --- a/sql/share/charsets/armscii8.xml +++ b/sql/share/charsets/armscii8.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="armscii8"> <ctype> diff --git a/sql/share/charsets/ascii.xml b/sql/share/charsets/ascii.xml index 1007f078f5a..3813bd42601 100644 --- a/sql/share/charsets/ascii.xml +++ b/sql/share/charsets/ascii.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="ascii"> <ctype> diff --git a/sql/share/charsets/cp1250.xml b/sql/share/charsets/cp1250.xml index 5ac63b836b0..8e7102b6737 100644 --- a/sql/share/charsets/cp1250.xml +++ b/sql/share/charsets/cp1250.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="cp1250"> <ctype> diff --git a/sql/share/charsets/cp1251.xml b/sql/share/charsets/cp1251.xml index 94774cca0f1..7f94788c0d0 100644 --- a/sql/share/charsets/cp1251.xml +++ b/sql/share/charsets/cp1251.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="cp1251"> <ctype> diff --git a/sql/share/charsets/cp1256.xml b/sql/share/charsets/cp1256.xml index 818a5583368..69eb6a68238 100644 --- a/sql/share/charsets/cp1256.xml +++ b/sql/share/charsets/cp1256.xml @@ -4,6 +4,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="cp1256"> <ctype> diff --git a/sql/share/charsets/cp1257.xml b/sql/share/charsets/cp1257.xml index bd4489a5a79..93a1bd47a77 100644 --- a/sql/share/charsets/cp1257.xml +++ b/sql/share/charsets/cp1257.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="cp1257"> <ctype> diff --git a/sql/share/charsets/cp850.xml b/sql/share/charsets/cp850.xml index d750bac37e2..79497aa17f1 100644 --- a/sql/share/charsets/cp850.xml +++ b/sql/share/charsets/cp850.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="cp850"> <ctype> diff --git a/sql/share/charsets/cp852.xml b/sql/share/charsets/cp852.xml index 958587d0399..73a81e54b02 100644 --- a/sql/share/charsets/cp852.xml +++ b/sql/share/charsets/cp852.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="cp852"> <ctype> diff --git a/sql/share/charsets/cp866.xml b/sql/share/charsets/cp866.xml index f698e735b58..1a72b396c7c 100644 --- a/sql/share/charsets/cp866.xml +++ b/sql/share/charsets/cp866.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="cp866"> <!-- cp866_DOSCyrillicRussian --> diff --git a/sql/share/charsets/dec8.xml b/sql/share/charsets/dec8.xml index f1bcfd05b30..2cb28cb0f4f 100644 --- a/sql/share/charsets/dec8.xml +++ b/sql/share/charsets/dec8.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="dec8"> <ctype> diff --git a/sql/share/charsets/geostd8.xml b/sql/share/charsets/geostd8.xml index caf01af58d0..c09aa078fb7 100644 --- a/sql/share/charsets/geostd8.xml +++ b/sql/share/charsets/geostd8.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="geostd8"> <ctype> diff --git a/sql/share/charsets/greek.xml b/sql/share/charsets/greek.xml index defeaf049c8..1cfe6b49610 100644 --- a/sql/share/charsets/greek.xml +++ b/sql/share/charsets/greek.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="greek"> <!-- It's ISO Greek rahter than WIN Greek because --> diff --git a/sql/share/charsets/hebrew.xml b/sql/share/charsets/hebrew.xml index ec3fa242ced..5bcf222a728 100644 --- a/sql/share/charsets/hebrew.xml +++ b/sql/share/charsets/hebrew.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="hebrew"> <ctype> diff --git a/sql/share/charsets/hp8.xml b/sql/share/charsets/hp8.xml index 0e16551b1b6..35224f8c544 100644 --- a/sql/share/charsets/hp8.xml +++ b/sql/share/charsets/hp8.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="hp8"> <ctype> diff --git a/sql/share/charsets/keybcs2.xml b/sql/share/charsets/keybcs2.xml index 6180dd8550d..6332891ef23 100644 --- a/sql/share/charsets/keybcs2.xml +++ b/sql/share/charsets/keybcs2.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="keybcs2"> <ctype> diff --git a/sql/share/charsets/koi8r.xml b/sql/share/charsets/koi8r.xml index b8bfc8ecccc..033597e9bfc 100644 --- a/sql/share/charsets/koi8r.xml +++ b/sql/share/charsets/koi8r.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="koi8r"> <ctype> diff --git a/sql/share/charsets/koi8u.xml b/sql/share/charsets/koi8u.xml index 0c05f2f1195..4f5fa35af3d 100644 --- a/sql/share/charsets/koi8u.xml +++ b/sql/share/charsets/koi8u.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="koi8u"> <ctype> diff --git a/sql/share/charsets/latin1.xml b/sql/share/charsets/latin1.xml index 178fd07e7f6..0b6f54dc800 100644 --- a/sql/share/charsets/latin1.xml +++ b/sql/share/charsets/latin1.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="latin1"> <ctype> diff --git a/sql/share/charsets/latin2.xml b/sql/share/charsets/latin2.xml index 4de138d3ea0..702f052604b 100644 --- a/sql/share/charsets/latin2.xml +++ b/sql/share/charsets/latin2.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="latin2"> <ctype> diff --git a/sql/share/charsets/latin5.xml b/sql/share/charsets/latin5.xml index 963691ad110..67e5873c503 100644 --- a/sql/share/charsets/latin5.xml +++ b/sql/share/charsets/latin5.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="latin5"> <ctype> diff --git a/sql/share/charsets/latin7.xml b/sql/share/charsets/latin7.xml index 1c25f5894b6..dd87a1a2d89 100644 --- a/sql/share/charsets/latin7.xml +++ b/sql/share/charsets/latin7.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="latin7"> <ctype> diff --git a/sql/share/charsets/macce.xml b/sql/share/charsets/macce.xml index 7e955157bb7..61f6d79b34f 100644 --- a/sql/share/charsets/macce.xml +++ b/sql/share/charsets/macce.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="macce"> <ctype> diff --git a/sql/share/charsets/macroman.xml b/sql/share/charsets/macroman.xml index ffedaa88274..36c8e8cf13a 100644 --- a/sql/share/charsets/macroman.xml +++ b/sql/share/charsets/macroman.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="macroman"> <ctype> diff --git a/sql/share/charsets/swe7.xml b/sql/share/charsets/swe7.xml index 5bcf9a6501b..2b8ff4edcce 100644 --- a/sql/share/charsets/swe7.xml +++ b/sql/share/charsets/swe7.xml @@ -2,6 +2,24 @@ <charsets> +<copyright> + Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +</copyright> + <charset name="swe7"> <ctype> diff --git a/sql/slave.cc b/sql/slave.cc index 2c6fb06b7cb..88dffbd8411 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1465,6 +1465,7 @@ be equal for replication to work"; Used by fetch_master_table (used by LOAD TABLE tblname FROM MASTER and LOAD DATA FROM MASTER). Drops the table (if 'overwrite' is true) and recreates it from the dump. Honours replication inclusion/exclusion rules. + db must be non-zero (guarded by assertion). RETURN VALUES 0 success @@ -1475,8 +1476,8 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, const char* table_name, bool overwrite) { ulong packet_len; - char *query; - char* save_db; + char *query, *save_db; + uint32 save_db_length; Vio* save_vio; HA_CHECK_OPT check_opt; TABLE_LIST tables; @@ -1532,9 +1533,13 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, thd->proc_info = "Creating table from master dump"; // save old db in case we are creating in a different database save_db = thd->db; + save_db_length= thd->db_length; thd->db = (char*)db; + DBUG_ASSERT(thd->db); + thd->db_length= strlen(thd->db); mysql_parse(thd, thd->query, packet_len); // run create table thd->db = save_db; // leave things the way the were before + thd->db_length= save_db_length; thd->options = save_options; if (thd->query_error) @@ -3468,7 +3473,7 @@ err: IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff)); VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query = thd->db = 0; // extra safety - thd->query_length = 0; + thd->query_length= thd->db_length= 0; VOID(pthread_mutex_unlock(&LOCK_thread_count)); if (mysql) { @@ -3636,7 +3641,7 @@ Slave SQL thread aborted. Can't execute init_slave query"); while (!sql_slave_killed(thd,rli)) { - thd->proc_info = "Reading event from the relay log"; + thd->proc_info = "Reading event from the relay log"; DBUG_ASSERT(rli->sql_thd == thd); THD_CHECK_SENTRY(thd); if (exec_relay_log_event(thd,rli)) @@ -3646,16 +3651,15 @@ Slave SQL thread aborted. Can't execute init_slave query"); sql_print_error("\ Error running query, slave SQL thread aborted. Fix the problem, and restart \ the slave SQL thread with \"SLAVE START\". We stopped at log \ -'%s' position %s", - RPL_LOG_NAME, llstr(rli->group_master_log_pos, llbuff)); +'%s' position %s", RPL_LOG_NAME, llstr(rli->group_master_log_pos, llbuff)); goto err; } } /* Thread stopped. Print the current replication position to the log */ - sql_print_information("Slave SQL thread exiting, replication stopped in log \ - '%s' at position %s", - RPL_LOG_NAME, llstr(rli->group_master_log_pos,llbuff)); + sql_print_information("Slave SQL thread exiting, replication stopped in log " + "'%s' at position %s", + RPL_LOG_NAME, llstr(rli->group_master_log_pos,llbuff)); err: VOID(pthread_mutex_lock(&LOCK_thread_count)); @@ -3665,7 +3669,7 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ variables is supposed to set them to 0 before terminating)). */ thd->query= thd->db= thd->catalog= 0; - thd->query_length = 0; + thd->query_length= thd->db_length= 0; VOID(pthread_mutex_unlock(&LOCK_thread_count)); thd->proc_info = "Waiting for slave mutex on exit"; pthread_mutex_lock(&rli->run_lock); diff --git a/sql/slave.h b/sql/slave.h index 46fe58e1976..2a9b96b75a1 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -1,3 +1,19 @@ +/* Copyright (C) 2000-2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifdef HAVE_REPLICATION #ifndef SLAVE_H diff --git a/sql/sp.cc b/sql/sp.cc index e444a412760..a1e52ee1ca7 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -293,7 +293,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) */ List<Item> vals= thd->lex->value_list; - mysql_init_query(thd, (uchar*)defstr.c_ptr(), defstr.length(), TRUE); + lex_start(thd, (uchar*)defstr.c_ptr(), defstr.length()); thd->lex->value_list= vals; } @@ -455,7 +455,7 @@ db_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) if (ret == SP_OK) { store_record(table,record[1]); - table->timestamp_on_update_now = 0; // Don't update create time now. + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time(); if (chistics->suid != SP_IS_DEFAULT_SUID) table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]-> @@ -1055,6 +1055,10 @@ create_string(THD *thd, String *buf, case SP_MODIFIES_SQL_DATA: buf->append(" MODIFIES SQL DATA\n"); break; + case SP_DEFAULT_ACCESS: + case SP_CONTAINS_SQL: + /* Do nothing */ + break; } if (chistics->detistic) buf->append(" DETERMINISTIC\n", 18); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 82f8e88d889..8de1b6e906a 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -765,7 +765,7 @@ sp_head::reset_lex(THD *thd) thd->lex= sublex= new st_lex; /* Reset most stuff. The length arguments doesn't matter here. */ - mysql_init_query(thd,oldlex->buf, oldlex->end_of_query - oldlex->ptr, TRUE); + lex_start(thd, oldlex->buf, (ulong) (oldlex->end_of_query - oldlex->ptr)); /* We must reset ptr and end_of_query again */ sublex->ptr= oldlex->ptr; diff --git a/sql/spatial.h b/sql/spatial.h index 45db1ca8d00..b96434831a1 100644 --- a/sql/spatial.h +++ b/sql/spatial.h @@ -97,12 +97,14 @@ struct MBR int equals(const MBR *mbr) { + /* The following should be safe, even if we compare doubles */ return ((mbr->xmin == xmin) && (mbr->ymin == ymin) && (mbr->xmax == xmax) && (mbr->ymax == ymax)); } int disjoint(const MBR *mbr) { + /* The following should be safe, even if we compare doubles */ return ((mbr->xmin > xmax) || (mbr->ymin > ymax) || (mbr->xmax < xmin) || (mbr->ymax < ymin)); } @@ -114,6 +116,7 @@ struct MBR int touches(const MBR *mbr) { + /* The following should be safe, even if we compare doubles */ return ((((mbr->xmin == xmax) || (mbr->xmax == xmin)) && ((mbr->ymin >= ymin) && (mbr->ymin <= ymax) || (mbr->ymax >= ymin) && (mbr->ymax <= ymax))) || @@ -124,18 +127,21 @@ struct MBR int within(const MBR *mbr) { + /* The following should be safe, even if we compare doubles */ return ((mbr->xmin <= xmin) && (mbr->ymin <= ymin) && (mbr->xmax >= xmax) && (mbr->ymax >= ymax)); } int contains(const MBR *mbr) { + /* The following should be safe, even if we compare doubles */ return ((mbr->xmin >= xmin) && (mbr->ymin >= ymin) && (mbr->xmax <= xmax) && (mbr->ymax <= ymax)); } bool inner_point(double x, double y) const { + /* The following should be safe, even if we compare doubles */ return (xmin<x) && (xmax>x) && (ymin<y) && (ymax>x); } @@ -164,6 +170,9 @@ public: return buffer; } + static void operator delete(void *ptr, void *buffer) + {} + enum wkbType { wkb_point= 1, diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index df615383ec3..bed3130462d 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -203,7 +203,7 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) host.sort= get_sort(2,host.host.hostname,host.db); if (check_no_resolve && hostname_requires_resolving(host.host.hostname)) { - sql_print_error("Warning: 'host' entry '%s|%s' " + sql_print_warning("'host' entry '%s|%s' " "ignored in --skip-name-resolve mode.", host.host.hostname, host.db, host.host.hostname); continue; @@ -271,8 +271,8 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) user.user= get_field(&mem, table->field[1]); if (check_no_resolve && hostname_requires_resolving(user.host.hostname)) { - sql_print_error("Warning: 'user' entry '%s@%s' " - "ignored in --skip-name-resolve mode.", + sql_print_warning("'user' entry '%s@%s' " + "ignored in --skip-name-resolve mode.", user.user, user.host.hostname, user.host.hostname); continue; } @@ -284,16 +284,16 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) { switch (password_len) { case 45: /* 4.1: to be removed */ - sql_print_error("Found 4.1 style password for user '%s@%s'. " - "Ignoring user. " - "You should change password for this user.", - user.user ? user.user : "", - user.host.hostname ? user.host.hostname : ""); + sql_print_warning("Found 4.1 style password for user '%s@%s'. " + "Ignoring user. " + "You should change password for this user.", + user.user ? user.user : "", + user.host.hostname ? user.host.hostname : ""); break; default: - sql_print_error("Found invalid password for user: '%s@%s'; " - "Ignoring user", user.user ? user.user : "", - user.host.hostname ? user.host.hostname : ""); + sql_print_warning("Found invalid password for user: '%s@%s'; " + "Ignoring user", user.user ? user.user : "", + user.host.hostname ? user.host.hostname : ""); break; } } @@ -375,15 +375,15 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) db.db=get_field(&mem, table->field[1]); if (!db.db) { - sql_print_error("Found an entry in the 'db' table with empty database name; Skipped"); + sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped"); continue; } db.user=get_field(&mem, table->field[2]); if (check_no_resolve && hostname_requires_resolving(db.host.hostname)) { - sql_print_error("Warning: 'db' entry '%s %s@%s' " - "ignored in --skip-name-resolve mode.", - db.db, db.user, db.host.hostname, db.host.hostname); + sql_print_warning("'db' entry '%s %s@%s' " + "ignored in --skip-name-resolve mode.", + db.db, db.user, db.host.hostname, db.host.hostname); continue; } db.access=get_access(table,3); @@ -740,9 +740,9 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, else { if (global_system_variables.log_warnings) - sql_print_error("X509 ciphers mismatch: should be '%s' but is '%s'", - acl_user->ssl_cipher, - SSL_get_cipher(ssl)); + sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'", + acl_user->ssl_cipher, + SSL_get_cipher(ssl)); break; } } @@ -764,8 +764,8 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, if (strcmp(acl_user->x509_issuer, ptr)) { if (global_system_variables.log_warnings) - sql_print_error("X509 issuer mismatch: should be '%s' " - "but is '%s'", acl_user->x509_issuer, ptr); + sql_print_information("X509 issuer mismatch: should be '%s' " + "but is '%s'", acl_user->x509_issuer, ptr); free(ptr); break; } @@ -782,7 +782,7 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, if (strcmp(acl_user->x509_subject,ptr)) { if (global_system_variables.log_warnings) - sql_print_error("X509 subject mismatch: '%s' vs '%s'", + sql_print_information("X509 subject mismatch: '%s' vs '%s'", acl_user->x509_subject, ptr); } else @@ -1550,13 +1550,28 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, (byte*) table->field[0]->ptr,0, HA_READ_KEY_EXACT)) { - if (!create_user) + /* what == 'N' means revoke */ + if (what == 'N') { - if (what == 'N') - my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str); - else - my_error(ER_NO_PERMISSION_TO_CREATE_USER, MYF(0), - thd->user, thd->host_or_ip); + my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str); + goto end; + } + /* + There are four options which affect the process of creation of + a new user(mysqld option --safe-create-user, 'insert' privilege + on 'mysql.user' table, using 'GRANT' with 'IDENTIFIED BY' and + SQL_MODE flag NO_AUTO_CREATE_USER). Below is the simplified rule + how it should work. + if (safe-user-create && ! INSERT_priv) => reject + else if (identified_by) => create + else if (no_auto_create_user) => reject + else create + */ + else if (((thd->variables.sql_mode & MODE_NO_AUTO_CREATE_USER) && + !password_len) || !create_user) + { + my_error(ER_NO_PERMISSION_TO_CREATE_USER, MYF(0), + thd->user, thd->host_or_ip); goto end; } old_row_exists = 0; @@ -1570,6 +1585,17 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, } else { + /* + Check that the user isn't trying to change a password for another + user if he doesn't have UPDATE privilege to the MySQL database + */ + DBUG_ASSERT(combo.host.str); + if (thd->user && combo.password.str && + (strcmp(thd->user,combo.user.str) || + my_strcasecmp(&my_charset_latin1, + combo.host.str, thd->host_or_ip)) && + check_access(thd, UPDATE_ACL, "mysql",0,1,0)) + goto end; old_row_exists = 1; store_record(table,record[1]); // Save copy for update if (combo.password.str) // If password given @@ -2493,7 +2519,8 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, if (replace_table_table(thd, grant_table, tables[1].table, *Str, db_name, real_name, rights, column_priv, revoke_grant)) - { // Crashend table ?? + { + /* Should only happen if table is crashed */ result= -1; /* purecov: deadcode */ } else if (tables[2].table) @@ -2702,10 +2729,10 @@ my_bool grant_init(THD *org_thd) { if (hostname_requires_resolving(mem_check->host)) { - sql_print_error("Warning: 'tables_priv' entry '%s %s@%s' " - "ignored in --skip-name-resolve mode.", - mem_check->tname, mem_check->user, - mem_check->host, mem_check->host); + sql_print_warning("'tables_priv' entry '%s %s@%s' " + "ignored in --skip-name-resolve mode.", + mem_check->tname, mem_check->user, + mem_check->host, mem_check->host); continue; } } @@ -3710,7 +3737,7 @@ int mysql_drop_user(THD *thd, List <LEX_USER> &list) int mysql_revoke_all(THD *thd, List <LEX_USER> &list) { - uint counter; + uint counter, revoked; int result; ACL_DB *acl_db; TABLE_LIST tables[4]; @@ -3743,73 +3770,96 @@ int mysql_revoke_all(THD *thd, List <LEX_USER> &list) } /* Remove db access privileges */ - for (counter= 0 ; counter < acl_dbs.elements ; counter++) + /* + Because acl_dbs and column_priv_hash shrink and may re-order + as privileges are removed, removal occurs in a repeated loop + until no more privileges are revoked. + */ + do { - const char *user,*host; - - acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*); - if (!(user=acl_db->user)) - user= ""; - if (!(host=acl_db->host.hostname)) - host= ""; - - if (!strcmp(lex_user->user.str,user) && - !my_strcasecmp(system_charset_info, lex_user->host.str, host)) + for (counter= 0, revoked= 0 ; counter < acl_dbs.elements ; ) { - if (replace_db_table(tables[1].table, acl_db->db, *lex_user, ~0, 1)) - result= -1; + const char *user,*host; + + acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*); + if (!(user=acl_db->user)) + user= ""; + if (!(host=acl_db->host.hostname)) + host= ""; + + if (!strcmp(lex_user->user.str,user) && + !my_strcasecmp(system_charset_info, lex_user->host.str, host)) + { + if (!replace_db_table(tables[1].table, acl_db->db, *lex_user, ~0, 1)) + { + /* + Don't increment counter as replace_db_table deleted the + current element in acl_dbs. + */ + revoked= 1; + continue; + } + result= -1; // Something went wrong + } + counter++; } - } + } while (revoked); /* Remove column access */ - for (counter= 0 ; counter < column_priv_hash.records ; counter++) + do { - const char *user,*host; - GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash, - counter); - if (!(user=grant_table->user)) - user= ""; - if (!(host=grant_table->host)) - host= ""; - - if (!strcmp(lex_user->user.str,user) && - !my_strcasecmp(system_charset_info, lex_user->host.str, host)) + for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; ) { - if (replace_table_table(thd,grant_table,tables[2].table,*lex_user, - grant_table->db, - grant_table->tname, - ~0, 0, 1)) + const char *user,*host; + GRANT_TABLE *grant_table= (GRANT_TABLE*)hash_element(&column_priv_hash, + counter); + if (!(user=grant_table->user)) + user= ""; + if (!(host=grant_table->host)) + host= ""; + + if (!strcmp(lex_user->user.str,user) && + !my_strcasecmp(system_charset_info, lex_user->host.str, host)) { - result= -1; - continue; - } - if (grant_table->cols) - { - List<LEX_COLUMN> columns; - if (replace_column_table(grant_table,tables[3].table, *lex_user, - columns, - grant_table->db, - grant_table->tname, - ~0, 1)) + if (replace_table_table(thd,grant_table,tables[2].table,*lex_user, + grant_table->db, + grant_table->tname, + ~0, 0, 1)) + { + result= -1; + } + else + { + if (!grant_table->cols) + { + revoked= 1; + continue; + } + List<LEX_COLUMN> columns; + if (!replace_column_table(grant_table,tables[3].table, *lex_user, + columns, + grant_table->db, + grant_table->tname, + ~0, 1)) + { + revoked= 1; + continue; + } result= -1; + } } + counter++; } - } + } while (revoked); } - + VOID(pthread_mutex_unlock(&acl_cache->lock)); rw_unlock(&LOCK_grant); close_thread_tables(thd); - - /* XXX this should not be necessary. The error message is already printed - by replace_xxx_table. my_error() should be use above instead of - sql_print_error(), and print ER_NONEXISTING_GRANT - as other grant - commands do */ - /* when this code is deleted, the error slot (error 1268) can be reused, - as this error code was not present in any MySQL release */ + if (result) my_error(ER_REVOKE_GRANTS, MYF(0)); - + DBUG_RETURN(result); } diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index 3f75dadb6f0..1e0aebbc1ec 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -799,6 +799,13 @@ void field_real::get_opt_type(String *answer, if (min_arg >= 0) answer->append(" UNSIGNED"); } + else if (item->decimals == NOT_FIXED_DEC) + { + if (min_arg >= -FLT_MAX && max_arg <= FLT_MAX) + answer->append("FLOAT", 5); + else + answer->append("DOUBLE", 6); + } else { if (min_arg >= -FLT_MAX && max_arg <= FLT_MAX) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 4f273fbd0c4..b3be1fc7338 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -308,7 +308,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, thd->proc_info="Flushing tables"; close_old_data_files(thd,thd->open_tables,1,1); - mysql_ha_close_list(thd, tables); + mysql_ha_flush(thd, tables, MYSQL_HA_REOPEN_ON_USAGE | MYSQL_HA_FLUSH_ALL); bool found=1; /* Wait until all threads has closed all the tables we had locked */ DBUG_PRINT("info", @@ -371,7 +371,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) { - bool found_old_table=0; + bool found_old_table; DBUG_ENTER("close_thread_tables"); if (thd->derived_tables && !skip_derived) @@ -406,6 +406,7 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) DBUG_PRINT("info", ("thd->open_tables: %p", thd->open_tables)); + found_old_table= 0; while (thd->open_tables) found_old_table|=close_thread_table(thd, &thd->open_tables); thd->some_tables_deleted=0; @@ -876,8 +877,9 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, { for (table= thd->temporary_tables; table ; table=table->next) { - if (table->key_length == key_length + 8 && - !memcmp(table->table_cache_key, key, key_length + 8)) + if (table->key_length == key_length + TMP_TABLE_KEY_EXTRA && + !memcmp(table->table_cache_key, key, + key_length + TMP_TABLE_KEY_EXTRA)) { if (table->query_id == thd->query_id) { @@ -888,7 +890,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table->query_id= thd->query_id; table->clear_query_id= 1; thd->tmp_table_used= 1; - goto reset; + DBUG_PRINT("info",("Using temporary table")); + goto reset; } } } @@ -903,6 +906,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table->query_id != thd->query_id) { table->query_id=thd->query_id; + DBUG_PRINT("info",("Using locked table")); goto reset; } } @@ -954,7 +958,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } /* close handler tables which are marked for flush */ - mysql_ha_close_list(thd, (TABLE_LIST*) NULL, /*flushed*/ 1); + if (thd->handler_tables) + mysql_ha_flush(thd, (TABLE_LIST*) NULL, MYSQL_HA_REOPEN_ON_USAGE); for (table=(TABLE*) hash_search(&open_cache,(byte*) key,key_length) ; table && table->in_use ; @@ -1047,6 +1052,31 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, for (uint i=0 ; i < table->fields ; i++) table->field[i]->table_name=table->table_name; } +#if MYSQL_VERSION_ID < 40100 + /* + If per-connection "new" variable (represented by variables.new_mode) + is set then we should pretend that the length of TIMESTAMP field is 19. + The cheapest (from perfomance viewpoint) way to achieve that is to set + field_length of all Field_timestamp objects in a table after opening + it (to 19 if new_mode is true or to original field length otherwise). + We save value of new_mode variable in TABLE::timestamp_mode to + not perform this setup if new_mode value is the same between sequential + table opens. + */ + my_bool new_mode= thd->variables.new_mode; + if (table->timestamp_mode != new_mode) + { + for (uint i=0 ; i < table->fields ; i++) + { + Field *field= table->field[i]; + + if (field->type() == FIELD_TYPE_TIMESTAMP) + field->field_length= new_mode ? 19 : + ((Field_timestamp *)(field))->orig_field_length; + } + table->timestamp_mode= new_mode; + } +#endif /* These variables are also set in reopen_table() */ table->tablenr=thd->current_tablenr++; table->used_fields=0; @@ -1056,7 +1086,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table->keys_in_use_for_query= table->keys_in_use; table->used_keys= table->keys_for_keyread; if (table->timestamp_field) - table->timestamp_field->set_timestamp_offsets(); + table->timestamp_field_type= table->timestamp_field->get_auto_set_type(); table_list->updatable= 1; // It is not derived table nor non-updatable VIEW DBUG_ASSERT(table->key_read == 0); DBUG_RETURN(table); @@ -1339,7 +1369,7 @@ bool wait_for_tables(THD *thd) { thd->some_tables_deleted=0; close_old_data_files(thd,thd->open_tables,0,dropping_tables != 0); - mysql_ha_close_list(thd, (TABLE_LIST*) NULL, /*flushed*/ 1); + mysql_ha_flush(thd, (TABLE_LIST*) NULL, MYSQL_HA_REOPEN_ON_USAGE); if (!table_is_used(thd->open_tables,1)) break; (void) pthread_cond_wait(&COND_refresh,&LOCK_open); @@ -1469,7 +1499,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, */ if (discover_retry_count++ != 0) goto err; - if (create_table_from_handler(db, name, true) != 0) + if (ha_create_table_from_engine(thd, db, name, TRUE) != 0) goto err; thd->clear_error(); // Clear error message @@ -1510,7 +1540,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, /* Give right error message */ thd->clear_error(); my_error(ER_NOT_KEYFILE, MYF(0), name, my_errno); - sql_print_error("Error: Couldn't repair table: %s.%s",db,name); + sql_print_error("Couldn't repair table: %s.%s",db,name); if (entry->file) closefrm(entry); error=1; @@ -1557,7 +1587,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, DBA on top of warning the client (which will automatically be done because of MYF(MY_WME) in my_malloc() above). */ - sql_print_error("Error: when opening HEAP table, could not allocate \ + sql_print_error("When opening HEAP table, could not allocate \ memory to write 'DELETE FROM `%s`.`%s`' to the binary log",db,name); delete entry->triggers; if (entry->file) @@ -1827,13 +1857,22 @@ int open_and_lock_tables(THD *thd, TABLE_LIST *tables) { DBUG_ENTER("open_and_lock_tables"); uint counter; - if (open_tables(thd, tables, &counter) || lock_tables(thd, tables, counter) - || mysql_handle_derived(thd->lex)) + if (open_tables(thd, tables, &counter) || + lock_tables(thd, tables, counter) || + mysql_handle_derived(thd->lex)) DBUG_RETURN(thd->net.report_error ? -1 : 1); /* purecov: inspected */ - /* - Let us propagate pointers to open tables from global table list - to table lists in particular selects if needed. - */ + relink_tables_for_derived(thd); + DBUG_RETURN(0); +} + + +/* + Let us propagate pointers to open tables from global table list + to table lists in particular selects if needed. +*/ + +void relink_tables_for_derived(THD *thd) +{ if (thd->lex->all_selects_list->next_select_in_list() || thd->lex->time_zone_tables_used) { @@ -1850,7 +1889,6 @@ int open_and_lock_tables(THD *thd, TABLE_LIST *tables) } } } - DBUG_RETURN(0); } @@ -1890,7 +1928,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count) if (!table->placeholder()) *(ptr++)= table->table; } - if (!(thd->lock=mysql_lock_tables(thd,start,count))) + if (!(thd->lock=mysql_lock_tables(thd,start, (uint) (ptr - start)))) return -1; /* purecov: inspected */ } else @@ -1983,8 +2021,8 @@ bool rm_temporary_table(enum db_type base, char *path) if (file && file->delete_table(path)) { error=1; - sql_print_error("Warning: Could not remove tmp table: '%s', error: %d", - path, my_errno); + sql_print_warning("Could not remove tmp table: '%s', error: %d", + path, my_errno); } delete file; DBUG_RETURN(error); @@ -2058,11 +2096,14 @@ find_field_in_table(THD *thd, TABLE_LIST *table_list, *ref= trans[i]; else { - *ref= new Item_ref(trans + i, ref, table_list->view_name.str, - item_name); - /* as far as Item_ref have defined refernce it do not need tables */ - if (*ref) + Item_ref *item_ref= new Item_ref(trans + i, table_list->view_name.str, + item_name); + /* as far as Item_ref have defined reference it do not need tables */ + if (item_ref) + { + thd->change_item_tree(ref, item_ref); (*ref)->fix_fields(thd, 0, ref); + } } return (Field*) view_ref_found; } @@ -2367,14 +2408,17 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, return not_found_item, report other errors, return 0 IGNORE_ERRORS Do not report errors, return 0 if error - + unaliased Set to true if item is field which was found + by original field name and not by its alias + in item list. Set to false otherwise. + RETURN VALUES 0 Item is not found or item is not unique, error message is reported not_found_item Function was called with report_error == REPORT_EXCEPT_NOT_FOUND and item was not found. No error message was reported - found field + found field */ // Special Item pointer for find_item_in_list returning @@ -2383,7 +2427,7 @@ const Item **not_found_item= (const Item**) 0x1; Item ** find_item_in_list(Item *find, List<Item> &items, uint *counter, - find_item_error_report_type report_error) + find_item_error_report_type report_error, bool *unaliased) { List_iterator<Item> li(items); Item **found=0, **found_unaliased= 0, *item; @@ -2392,6 +2436,9 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, const char *table_name=0; bool found_unaliased_non_uniq= 0; uint unaliased_counter; + + *unaliased= FALSE; + if (find->type() == Item::FIELD_ITEM || find->type() == Item::REF_ITEM) { field_name= ((Item_ident*) find)->field_name; @@ -2419,32 +2466,42 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, /* If table name is specified we should find field 'field_name' in table 'table_name'. According to SQL-standard we should ignore - aliases in this case. Note that we should prefer fields from the - select list over other fields from the tables participating in - this select in case of ambiguity. + aliases in this case. + + Since we should NOT prefer fields from the select list over + other fields from the tables participating in this select in + case of ambiguity we have to do extra check outside this function. We use strcmp for table names and database names as these may be - case sensitive. - In cases where they are not case sensitive, they are always in lower - case. + case sensitive. In cases where they are not case sensitive, they + are always in lower case. + + item_field->field_name and item_field->table_name can be 0x0 if + item is not fix_field()'ed yet. */ - if (!my_strcasecmp(system_charset_info, item_field->field_name, + if (item_field->field_name && item_field->table_name && + !my_strcasecmp(system_charset_info, item_field->field_name, field_name) && !strcmp(item_field->table_name, table_name) && (!db_name || (item_field->db_name && !strcmp(item_field->db_name, db_name)))) { - if (found) + if (found_unaliased) { - if ((*found)->eq(item, 0)) - continue; // Same field twice + if ((*found_unaliased)->eq(item, 0)) + continue; + /* + Two matching fields in select list. + We already can bail out because we are searching through + unaliased names only and will have duplicate error anyway. + */ if (report_error != IGNORE_ERRORS) my_printf_error(ER_NON_UNIQ_ERROR, ER(ER_NON_UNIQ_ERROR), MYF(0), find->full_name(), current_thd->where); return (Item**) 0; } - found= li.ref(); - *counter= i; + found_unaliased= li.ref(); + unaliased_counter= i; if (db_name) break; // Perfect match } @@ -2516,6 +2573,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, { found= found_unaliased; *counter= unaliased_counter; + *unaliased= TRUE; } } if (found) @@ -2540,8 +2598,11 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, uint wild_num) { Item *item; + DBUG_ENTER("setup_wild"); + if (!wild_num) - return 0; + DBUG_RETURN(0); + Item_arena *arena= thd->current_arena, backup; /* @@ -2556,7 +2617,8 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, List_iterator<Item> it(fields); while (wild_num && (item= it++)) { - if (item->type() == Item::FIELD_ITEM && ((Item_field*) item)->field_name && + if (item->type() == Item::FIELD_ITEM && + ((Item_field*) item)->field_name && ((Item_field*) item)->field_name[0] == '*' && !((Item_field*) item)->field) { @@ -2579,7 +2641,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, { if (arena) thd->restore_backup_item_arena(arena, &backup); - return (-1); + DBUG_RETURN(-1); } if (sum_func_list) { @@ -2602,7 +2664,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, thd->restore_backup_item_arena(arena, &backup); } - return 0; + DBUG_RETURN(0); } /**************************************************************************** @@ -2635,7 +2697,7 @@ int setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, *(ref++)= item; if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM && sum_func_list) - item->split_sum_func(ref_pointer_array, *sum_func_list); + item->split_sum_func(thd, ref_pointer_array, *sum_func_list); thd->used_tables|=item->used_tables(); } DBUG_RETURN(test(thd->net.report_error)); @@ -2647,23 +2709,21 @@ int setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, SYNOPSIS setup_tables() - thd - thread handler - tables - tables list - conds - condition of current SELECT (can be changed by VIEW) + thd Thread handler + tables Table list + conds Condition of current SELECT (can be changed by VIEW) - RETURN - 0 ok; In this case *map will includes the choosed index - 1 error - - NOTE - Remap table numbers if INSERT ... SELECT - Check also that the 'used keys' and 'ignored keys' exists and set up the - table structure accordingly + NOTE + Remap table numbers if INSERT ... SELECT + Check also that the 'used keys' and 'ignored keys' exists and set up the + table structure accordingly - This has to be called for all tables that are used by items, as otherwise - table->map is not set and all Item_field will be regarded as const items. + This has to be called for all tables that are used by items, as otherwise + table->map is not set and all Item_field will be regarded as const items. - if tables do not contain VIEWs it is OK to pass 0 as conds + RETURN + 0 ok; In this case *map will includes the choosed index + 1 error */ bool setup_tables(THD *thd, TABLE_LIST *tables, Item **conds) @@ -2767,7 +2827,7 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, RETURN 0 ok 'it' is updated to point at last inserted - 1 error. The error message is sent to client + 1 error. Error message is generated but not sent to client */ bool @@ -2943,9 +3003,8 @@ insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name, during cleunup() this item will be put in list to replace expression from VIEW */ - item->changed_during_fix_field= it->ref(); + thd->nocheck_register_item_tree_change(it->ref(), item, &thd->mem_root); } - } /* All fields are used */ table->used_fields=table->fields; @@ -2960,7 +3019,6 @@ insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name, my_error(ER_BAD_TABLE_ERROR, MYF(0), table_name); err: - send_error(thd); DBUG_RETURN(1); } @@ -2973,8 +3031,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) { table_map not_null_tables= 0; SELECT_LEX *select_lex= thd->lex->current_select; - Item_arena *arena= thd->current_arena; - Item_arena backup; + Item_arena *arena= thd->current_arena, backup; bool save_wrapper= thd->lex->current_select->no_wrap_view_item; DBUG_ENTER("setup_conds"); @@ -3090,7 +3147,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) { if (t2_field != view_ref_found) { - if (!(item_t2= new Item_field(t2_field))) + if (!(item_t2= new Item_field(thd, t2_field))) goto err; /* Mark field used for table cache */ t2_field->query_id= thd->query_id; @@ -3143,6 +3200,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) } } } + else if (arena) + thd->restore_backup_item_arena(arena, &backup); } embedding= embedded->embedding; } @@ -3301,8 +3360,15 @@ void flush_tables() /* -** Mark all entries with the table as deleted to force an reopen of the table -** Returns true if the table is in use by another thread + Mark all entries with the table as deleted to force an reopen of the table + + The table will be closed (not stored in cache) by the current thread when + close_thread_tables() is called. + + RETURN + 0 This thread now have exclusive access to this table and no other thread + can access the table until close_thread_tables() is called. + 1 Table is in use by another thread */ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 35bf6f8701f..39ee8f61f1a 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -363,7 +363,7 @@ TODO list: const char *query_cache_type_names[]= { "OFF", "ON", "DEMAND",NullS }; TYPELIB query_cache_type_typelib= { - array_elements(query_cache_type_names)-1,"", query_cache_type_names + array_elements(query_cache_type_names)-1,"", query_cache_type_names, NULL }; /***************************************************************************** @@ -788,6 +788,9 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) thd->variables.collation_connection->number; flags.limit= thd->variables.select_limit; flags.time_zone= thd->variables.time_zone; + flags.sql_mode= thd->variables.sql_mode; + flags.max_sort_length= thd->variables.max_sort_length; + flags.group_concat_max_len= thd->variables.group_concat_max_len; STRUCT_LOCK(&structure_guard_mutex); if (query_cache_size == 0) @@ -980,8 +983,11 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) flags.collation_connection_num= thd->variables.collation_connection->number; flags.limit= thd->variables.select_limit; flags.time_zone= thd->variables.time_zone; + flags.sql_mode= thd->variables.sql_mode; + flags.max_sort_length= thd->variables.max_sort_length; + flags.group_concat_max_len= thd->variables.group_concat_max_len; memcpy((void *)(sql + (tot_length - QUERY_CACHE_FLAGS_SIZE)), - &flags, QUERY_CACHE_FLAGS_SIZE); + &flags, QUERY_CACHE_FLAGS_SIZE); query_block = (Query_cache_block *) hash_search(&queries, (byte*) sql, tot_length); /* Quick abort on unlocked data */ @@ -1025,9 +1031,38 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) for (; block_table != block_table_end; block_table++) { TABLE_LIST table_list; - bzero((char*) &table_list,sizeof(table_list)); + TABLE *tmptable; Query_cache_table *table = block_table->parent; + + /* + Check that we have not temporary tables with same names of tables + of this query. If we have such tables, we will not send data from + query cache, because temporary tables hide real tables by which + query in query cache was made. + */ + for (tmptable= thd->temporary_tables; tmptable ; tmptable= tmptable->next) + { + if (tmptable->key_length - TMP_TABLE_KEY_EXTRA == table->key_length() && + !memcmp(tmptable->table_cache_key, table->data(), + table->key_length())) + { + DBUG_PRINT("qcache", + ("Temporary table detected: '%s.%s'", + table_list.db, table_list.alias)); + STRUCT_UNLOCK(&structure_guard_mutex); + /* + We should not store result of this query because it contain + temporary tables => assign following variable to make check + faster. + */ + thd->lex->safe_to_cache_query=0; + BLOCK_UNLOCK_RD(query_block); + DBUG_RETURN(-1); + } + } + + bzero((char*) &table_list,sizeof(table_list)); table_list.db = table->db(); table_list.alias= table_list.real_name= table->table(); #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -1533,11 +1568,11 @@ ulong Query_cache::init_cache() query_cache_table_get_key, 0, 0)); #else /* - On windows, OS/2, MacOS X with HFS+ or any other case insensitive - file system if lower_case_table_names!=0 we have same situation as - in previous case, but if lower_case_table_names==0 then we should - not distinguish cases (to be compatible in behavior with underlaying - file system) and so should use case insensitive collation for + On windows, OS/2, MacOS X with HFS+ or any other case insensitive + file system if lower_case_table_names!=0 we have same situation as + in previous case, but if lower_case_table_names==0 then we should + not distinguish cases (to be compatible in behavior with underlying + file system) and so should use case insensitive collation for comparison. */ VOID(hash_init(&tables, @@ -1946,7 +1981,6 @@ inline ulong Query_cache::get_min_append_result_data_size() /* Allocate one or more blocks to hold data */ - my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block, ulong data_len, Query_cache_block *query_block, @@ -1954,55 +1988,55 @@ my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block, { ulong all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) + ALIGN_SIZE(sizeof(Query_cache_result))); - ulong len= data_len + all_headers_len; - ulong align_len= ALIGN_SIZE(len); - DBUG_ENTER("Query_cache::allocate_data_chain"); - DBUG_PRINT("qcache", ("data_len %lu, all_headers_len %lu", - data_len, all_headers_len)); - ulong min_size = (first_block_arg ? get_min_first_result_data_size(): get_min_append_result_data_size()); - *result_block = allocate_block(max(min_size, align_len), - min_result_data_size == 0, - all_headers_len + min_result_data_size, - 1); - my_bool success = (*result_block != 0); - if (success) + Query_cache_block *prev_block= NULL; + Query_cache_block *new_block; + DBUG_ENTER("Query_cache::allocate_data_chain"); + DBUG_PRINT("qcache", ("data_len %lu, all_headers_len %lu", + data_len, all_headers_len)); + + do { - Query_cache_block *new_block= *result_block; + ulong len= data_len + all_headers_len; + ulong align_len= ALIGN_SIZE(len); + + if (!(new_block= allocate_block(max(min_size, align_len), + min_result_data_size == 0, + all_headers_len + min_result_data_size, + 1))) + { + DBUG_PRINT("warning", ("Can't allocate block for results")); + DBUG_RETURN(FALSE); + } + new_block->n_tables = 0; - new_block->used = 0; + new_block->used = min(len, new_block->length); new_block->type = Query_cache_block::RES_INCOMPLETE; new_block->next = new_block->prev = new_block; Query_cache_result *header = new_block->result(); header->parent(query_block); - if (new_block->length < len) - { - /* - We got less memory then we need (no big memory blocks) => - Continue to allocated more blocks until we got everything we need. - */ - Query_cache_block *next_block; - if ((success = allocate_data_chain(&next_block, - len - new_block->length, - query_block, first_block_arg))) - double_linked_list_join(new_block, next_block); - } - if (success) - { - new_block->used = min(len, new_block->length); - - DBUG_PRINT("qcache", ("Block len %lu used %lu", + DBUG_PRINT("qcache", ("Block len %lu used %lu", new_block->length, new_block->used)); - } + + if (prev_block) + double_linked_list_join(prev_block, new_block); else - DBUG_PRINT("warning", ("Can't allocate block for continue")); - } - else - DBUG_PRINT("warning", ("Can't allocate block for results")); - DBUG_RETURN(success); + *result_block= new_block; + if (new_block->length >= len) + break; + + /* + We got less memory then we need (no big memory blocks) => + Continue to allocated more blocks until we got everything we need. + */ + data_len= len - new_block->length; + prev_block= new_block; + } while(1); + + DBUG_RETURN(TRUE); } /***************************************************************************** diff --git a/sql/sql_cache.h b/sql/sql_cache.h index b54bc4f7b7b..93d89aeae4f 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -53,10 +53,6 @@ of list of free blocks */ #define QUERY_CACHE_MEM_BIN_TRY 5 -/* query flags masks */ -#define QUERY_CACHE_CLIENT_LONG_FLAG_MASK 0x80 -#define QUERY_CACHE_CHARSET_CONVERT_MASK 0x7F - /* packing parameters */ #define QUERY_CACHE_PACK_ITERATION 2 #define QUERY_CACHE_PACK_LIMIT (512*1024L) @@ -148,14 +144,14 @@ struct Query_cache_query struct Query_cache_table { char *tbl; - uint key_len; + uint32 key_len; uint8 table_type; inline char *db() { return (char *) data(); } inline char *table() { return tbl; } inline void table(char *table) { tbl= table; } - inline uint key_length() { return key_len; } - inline void key_length(uint len) { key_len= len; } + inline uint32 key_length() { return key_len; } + inline void key_length(uint32 len) { key_len= len; } inline uint8 type() { return table_type; } inline void type(uint8 t) { table_type= t; } inline gptr data() diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 392534492df..f5283020b30 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -165,6 +165,9 @@ THD::THD() in_lock_tables(0), bootstrap(0), spcont(NULL) { current_arena= this; +#ifndef DBUG_OFF + backup_arena= 0; +#endif host= user= priv_user= db= ip= 0; catalog= (char*)"std"; // the only catalog we have for now host_or_ip= "connecting host"; @@ -176,6 +179,7 @@ THD::THD() query_error= tmp_table_used= 0; next_insert_id=last_insert_id=0; open_tables= temporary_tables= handler_tables= derived_tables= 0; + hash_clear(&handler_tables_hash); tmp_table=0; lock=locked_tables=0; used_tables=0; @@ -225,8 +229,7 @@ THD::THD() init(); /* Initialize sub structures */ - clear_alloc_root(&transaction.mem_root); - init_alloc_root(&warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE); + init_sql_alloc(&warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE); user_connect=(USER_CONN *)0; hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0, (hash_get_key) get_var_key, @@ -265,6 +268,7 @@ THD::THD() transaction.trans_log.end_of_file= max_binlog_cache_size; } #endif + init_sql_alloc(&transaction.mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0); { ulong tmp=sql_rnd_with_mutex(); randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::query_id); @@ -310,12 +314,13 @@ void THD::init(void) void THD::init_for_queries() { - init_sql_alloc(&mem_root, - variables.query_alloc_block_size, - variables.query_prealloc_size); - init_sql_alloc(&transaction.mem_root, - variables.trans_alloc_block_size, - variables.trans_prealloc_size); + ha_enable_transaction(this,TRUE); + + reset_root_defaults(&mem_root, variables.query_alloc_block_size, + variables.query_prealloc_size); + reset_root_defaults(&transaction.mem_root, + variables.trans_alloc_block_size, + variables.trans_prealloc_size); } @@ -335,6 +340,7 @@ void THD::change_user(void) cleanup(); cleanup_done= 0; init(); + stmt_map.reset(); hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0, (hash_get_key) get_var_key, (hash_free_key) free_user_var, 0); @@ -354,11 +360,9 @@ void THD::cleanup(void) lock=locked_tables; locked_tables=0; close_thread_tables(this); } - if (handler_tables) - { - open_tables=handler_tables; handler_tables=0; - close_thread_tables(this); - } + mysql_ha_flush(this, (TABLE_LIST*) 0, + MYSQL_HA_CLOSE_FINAL | MYSQL_HA_FLUSH_ALL); + hash_free(&handler_tables_hash); close_temporary_tables(this); my_free((char*) variables.time_format, MYF(MY_ALLOW_ZERO_PTR)); my_free((char*) variables.date_format, MYF(MY_ALLOW_ZERO_PTR)); @@ -709,27 +713,29 @@ CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, long key_length) return new_table; } + int THD::send_explain_fields(select_result *result) { List<Item> field_list; Item *item; + CHARSET_INFO *cs= system_charset_info; field_list.push_back(new Item_return_int("id",3, MYSQL_TYPE_LONGLONG)); - field_list.push_back(new Item_empty_string("select_type",19)); - field_list.push_back(new Item_empty_string("table",NAME_LEN)); - field_list.push_back(new Item_empty_string("type",10)); + field_list.push_back(new Item_empty_string("select_type", 19, cs)); + field_list.push_back(new Item_empty_string("table", NAME_LEN, cs)); + field_list.push_back(new Item_empty_string("type", 10, cs)); field_list.push_back(item=new Item_empty_string("possible_keys", - NAME_LEN*MAX_KEY)); + NAME_LEN*MAX_KEY, cs)); item->maybe_null=1; - field_list.push_back(item=new Item_empty_string("key",NAME_LEN)); + field_list.push_back(item=new Item_empty_string("key", NAME_LEN, cs)); item->maybe_null=1; field_list.push_back(item=new Item_empty_string("key_len", NAME_LEN*MAX_KEY)); item->maybe_null=1; field_list.push_back(item=new Item_empty_string("ref", - NAME_LEN*MAX_REF_PARTS)); + NAME_LEN*MAX_REF_PARTS, cs)); item->maybe_null=1; - field_list.push_back(new Item_return_int("rows",10, MYSQL_TYPE_LONGLONG)); - field_list.push_back(new Item_empty_string("Extra",255)); + field_list.push_back(new Item_return_int("rows", 10, MYSQL_TYPE_LONGLONG)); + field_list.push_back(new Item_empty_string("Extra", 255, cs)); return (result->send_fields(field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)); } @@ -751,6 +757,57 @@ void THD::close_active_vio() #endif +struct Item_change_record: public ilink +{ + Item **place; + Item *old_value; + /* Placement new was hidden by `new' in ilink (TODO: check): */ + static void *operator new(size_t size, void *mem) { return mem; } +}; + + +/* + Register an item tree tree transformation, performed by the query + optimizer. We need a pointer to runtime_memroot because it may be != + thd->mem_root (due to possible set_n_backup_item_arena called for thd). +*/ + +void THD::nocheck_register_item_tree_change(Item **place, Item *old_value, + MEM_ROOT *runtime_memroot) +{ + Item_change_record *change; + /* + Now we use one node per change, which adds some memory overhead, + but still is rather fast as we use alloc_root for allocations. + A list of item tree changes of an average query should be short. + */ + void *change_mem= alloc_root(runtime_memroot, sizeof(*change)); + if (change_mem == 0) + { + fatal_error(); + return; + } + change= new (change_mem) Item_change_record; + change->place= place; + change->old_value= old_value; + change_list.append(change); +} + + +void THD::rollback_item_tree_changes() +{ + I_List_iterator<Item_change_record> it(change_list); + Item_change_record *change; + DBUG_ENTER("rollback_item_tree_changes"); + + while ((change= it++)) + *change->place= change->old_value; + /* We can forget about changes memory: it's allocated in runtime memroot */ + change_list.empty(); + DBUG_VOID_RETURN; +} + + /***************************************************************************** ** Functions to provide a interface to select results *****************************************************************************/ @@ -847,7 +904,8 @@ bool select_send::send_eof() /* Unlock tables before sending packet to gain some speed */ if (thd->lock) { - mysql_unlock_tables(thd, thd->lock); thd->lock=0; + mysql_unlock_tables(thd, thd->lock); + thd->lock=0; } if (!thd->net.report_error) { @@ -948,9 +1006,14 @@ static File create_file(THD *thd, char *path, sql_exchange *exchange, option|= MY_REPLACE_DIR; // Force use of db directory #endif - strxnmov(path, FN_REFLEN, mysql_real_data_home, thd->db ? thd->db : "", - NullS); - (void) fn_format(path, exchange->file_name, path, "", option); + if (!dirname_length(exchange->file_name)) + { + strxnmov(path, FN_REFLEN, mysql_real_data_home, thd->db ? thd->db : "", NullS); + (void) fn_format(path, exchange->file_name, path, "", option); + } + else + (void) fn_format(path, exchange->file_name, mysql_real_data_home, "", option); + if (!access(path, F_OK)) { my_error(ER_FILE_EXISTS_ERROR, MYF(0), exchange->file_name); @@ -1388,6 +1451,17 @@ void select_dumpvar::cleanup() } +/* + Create arena for already constructed THD. + + SYNOPSYS + Item_arena() + thd - thread for which arena is created + + DESCRIPTION + Create arena for already existing THD using its variables as parameters + for memory root initialization. +*/ Item_arena::Item_arena(THD* thd) :free_list(0), state(INITIALIZED) @@ -1398,24 +1472,31 @@ Item_arena::Item_arena(THD* thd) } -/* This constructor is called when Item_arena is a subobject of THD */ +/* + Create arena and optionally initialize memory root. -Item_arena::Item_arena() - :free_list(0), - state(CONVENTIONAL_EXECUTION) -{ - clear_alloc_root(&mem_root); -} + SYNOPSYS + Item_arena() + init_mem_root - whenever we need to initialize memory root + DESCRIPTION + Create arena and optionally initialize memory root with minimal + possible parameters. + NOTE + We use this constructor when arena is part of THD, but reinitialize + its memory root in THD::init_for_queries() before execution of real + statements. +*/ Item_arena::Item_arena(bool init_mem_root) :free_list(0), - state(INITIALIZED) + state(CONVENTIONAL_EXECUTION) { if (init_mem_root) - clear_alloc_root(&mem_root); + init_sql_alloc(&mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0); } + Item_arena::Type Item_arena::type() const { DBUG_ASSERT(0); /* Should never be called */ @@ -1423,10 +1504,6 @@ Item_arena::Type Item_arena::type() const } -Item_arena::~Item_arena() -{} - - /* Statement functions */ @@ -1451,7 +1528,7 @@ Statement::Statement(THD *thd) */ Statement::Statement() - :Item_arena(), + :Item_arena((bool)TRUE), id(0), set_query_id(1), allow_sum_func(0), /* initialized later */ @@ -1496,7 +1573,7 @@ void Statement::restore_backup_statement(Statement *stmt, Statement *backup) } -void Statement::end_statement() +void THD::end_statement() { /* Cleanup SQL processing state to resuse this statement in next query. */ lex_end(lex); @@ -1514,18 +1591,35 @@ void Statement::end_statement() void Item_arena::set_n_backup_item_arena(Item_arena *set, Item_arena *backup) { DBUG_ENTER("Item_arena::set_n_backup_item_arena"); + DBUG_ASSERT(backup_arena == 0); backup->set_item_arena(this); set_item_arena(set); +#ifndef DBUG_OFF + backup_arena= 1; +#endif DBUG_VOID_RETURN; } void Item_arena::restore_backup_item_arena(Item_arena *set, Item_arena *backup) { + DBUG_ENTER("Item_arena::restore_backup_item_arena"); set->set_item_arena(this); set_item_arena(backup); - // reset backup mem_root to avoid its freeing +#ifndef DBUG_OFF + backup_arena= 0; +#endif +#ifdef NOT_NEEDED_NOW + /* + Reset backup mem_root to avoid its freeing. + Since Item_arena's mem_root is freed only when it is part of Statement + we need this only if we use some Statement's arena as backup storage. + But we do this only with THD::stmt_backup and this Statement is specially + handled in this respect. So this code is not really needed now. + */ clear_alloc_root(&backup->mem_root); +#endif + DBUG_VOID_RETURN; } void Item_arena::set_item_arena(Item_arena *set) @@ -1573,7 +1667,7 @@ Statement_map::Statement_map() : START_STMT_HASH_SIZE = 16, START_NAME_HASH_SIZE = 16 }; - hash_init(&st_hash, default_charset_info, START_STMT_HASH_SIZE, 0, 0, + hash_init(&st_hash, &my_charset_bin, START_STMT_HASH_SIZE, 0, 0, get_statement_id_as_hash_key, delete_statement_as_hash_key, MYF(0)); hash_init(&names_hash, system_charset_info, START_NAME_HASH_SIZE, 0, 0, diff --git a/sql/sql_class.h b/sql/sql_class.h index 32f156fbee8..fcaebe64b43 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -427,6 +427,9 @@ struct system_variables my_bool low_priority_updates; my_bool new_mode; my_bool query_cache_wlock_invalidate; +#ifdef HAVE_INNOBASE_DB + my_bool innodb_table_locks; +#endif /* HAVE_INNOBASE_DB */ my_bool old_passwords; /* Only charset part of these variables is sensible */ @@ -513,6 +516,9 @@ public: */ Item *free_list; MEM_ROOT mem_root; +#ifndef DBUG_OFF + bool backup_arena; +#endif enum enum_state { INITIALIZED= 0, PREPARED= 1, EXECUTED= 3, CONVENTIONAL_EXECUTION= 2, @@ -527,14 +533,28 @@ public: STATEMENT, PREPARED_STATEMENT, STORED_PROCEDURE }; + /* + This constructor is used only when Item_arena is created as + backup storage for another instance of Item_arena. + */ + Item_arena() {}; + /* + Create arena for already constructed THD using its variables as + parameters for memory root initialization. + */ Item_arena(THD *thd); - Item_arena(); + /* + Create arena and optionally init memory root with minimal values. + Particularly used if Item_arena is part of Statement. + */ Item_arena(bool init_mem_root); virtual Type type() const; - virtual ~Item_arena(); + virtual ~Item_arena() {}; inline bool is_stmt_prepare() const { return (int)state < (int)PREPARED; } inline bool is_first_stmt_execute() const { return state == PREPARED; } + inline bool is_stmt_execute() const + { return state == PREPARED || state == EXECUTED; } inline bool is_conventional() const { return state == CONVENTIONAL_EXECUTION; } inline gptr alloc(unsigned int size) { return alloc_root(&mem_root,size); } @@ -620,6 +640,23 @@ public: Points to the query associated with this statement. It's const, but we need to declare it char * because all table handlers are written in C and need to point to it. + + Note that (A) if we set query = NULL, we must at the same time set + query_length = 0, and protect the whole operation with the + LOCK_thread_count mutex. And (B) we are ONLY allowed to set query to a + non-NULL value if its previous value is NULL. We do not need to protect + operation (B) with any mutex. To avoid crashes in races, if we do not + know that thd->query cannot change at the moment, one should print + thd->query like this: + (1) reserve the LOCK_thread_count mutex; + (2) check if thd->query is NULL; + (3) if not NULL, then print at most thd->query_length characters from + it. We will see the query_length field as either 0, or the right value + for it. + Assuming that the write and read of an n-bit memory field in an n-bit + computer is atomic, we can avoid races in the above way. + This printing is needed at least in SHOW PROCESSLIST and SHOW INNODB + STATUS. */ char *query; uint32 query_length; // current query length @@ -642,12 +679,6 @@ public: void restore_backup_statement(Statement *stmt, Statement *backup); /* return class type */ virtual Type type() const; - - /* - Cleanup statement parse state (parse tree, lex) after execution of - a non-prepared SQL statement. - */ - void end_statement(); }; @@ -657,7 +688,7 @@ public: assignment in Statement::Statement) Non-empty statement names are unique too: attempt to insert a new statement with duplicate name causes older statement to be deleted - + Statements are auto-deleted when they are removed from the map and when the map is deleted. */ @@ -666,7 +697,7 @@ class Statement_map { public: Statement_map(); - + int insert(Statement *statement); Statement *find_by_name(LEX_STRING *name) @@ -699,11 +730,18 @@ public: } hash_delete(&st_hash, (byte *) statement); } + /* Erase all statements (calls Statement destructor) */ + void reset() + { + hash_reset(&names_hash); + hash_reset(&st_hash); + last_found_statement= 0; + } ~Statement_map() { - hash_free(&st_hash); hash_free(&names_hash); + hash_free(&st_hash); } private: HASH st_hash; @@ -713,6 +751,17 @@ private: /* + A registry for item tree transformations performed during + query optimization. We register only those changes which require + a rollback to re-execute a prepared statement or stored procedure + yet another time. +*/ + +struct Item_change_record; +typedef I_List<Item_change_record> Item_change_list; + + +/* For each client connection we create a separate thread with THD serving as a thread/connection descriptor */ @@ -744,24 +793,6 @@ public: struct system_variables variables; // Changeable local variables struct system_status_var status_var; // Per thread statistic vars pthread_mutex_t LOCK_delete; // Locked before thd is deleted - /* - Note that (A) if we set query = NULL, we must at the same time set - query_length = 0, and protect the whole operation with the - LOCK_thread_count mutex. And (B) we are ONLY allowed to set query to a - non-NULL value if its previous value is NULL. We do not need to protect - operation (B) with any mutex. To avoid crashes in races, if we do not - know that thd->query cannot change at the moment, one should print - thd->query like this: - (1) reserve the LOCK_thread_count mutex; - (2) check if thd->query is NULL; - (3) if not NULL, then print at most thd->query_length characters from - it. We will see the query_length field as either 0, or the right value - for it. - Assuming that the write and read of an n-bit memory field in an n-bit - computer is atomic, we can avoid races in the above way. - This printing is needed at least in SHOW PROCESSLIST and SHOW INNODB - STATUS. - */ /* all prepared statements and cursors of this connection */ Statement_map stmt_map; /* @@ -831,6 +862,7 @@ public: */ MYSQL_LOCK *lock; /* Current locks */ MYSQL_LOCK *locked_tables; /* Tables locked with LOCK */ + HASH handler_tables_hash; /* One thread can hold up to one named user-level lock. This variable points to a lock object if the lock is present. See item_func.cc and @@ -864,10 +896,10 @@ public: THD_TRANS all; // Trans since BEGIN WORK THD_TRANS stmt; // Trans for current statement uint bdb_lock_count; - uint ndb_lock_count; #ifdef HAVE_NDBCLUSTER_DB - void* ndb; + void* thd_ndb; #endif + bool on; /* Tables changed in transaction (that must be invalidated in query cache). List contain only transactional tables, that not invalidated in query @@ -889,6 +921,14 @@ public: Vio* active_vio; #endif /* + This is to track items changed during execution of a prepared + statement/stored procedure. It's created by + register_item_tree_change() in memory root of THD, and freed in + rollback_item_tree_changes(). For conventional execution it's always 0. + */ + Item_change_list change_list; + + /* Current prepared Item_arena if there one, or 0 */ Item_arena *current_arena; @@ -1126,6 +1166,23 @@ public: } inline CHARSET_INFO *charset() { return variables.character_set_client; } void update_charset(); + + void change_item_tree(Item **place, Item *new_value) + { + /* TODO: check for OOM condition here */ + if (!current_arena->is_conventional()) + nocheck_register_item_tree_change(place, *place, &mem_root); + *place= new_value; + } + void nocheck_register_item_tree_change(Item **place, Item *old_value, + MEM_ROOT *runtime_memroot); + void rollback_item_tree_changes(); + + /* + Cleanup statement parse state (parse tree, lex) and execution + state after execution of a non-prepared SQL statement. + */ + void end_statement(); inline int killed_errno() const { return killed != KILL_BAD_DATA ? killed : 0; @@ -1150,27 +1207,6 @@ public: #define SYSTEM_THREAD_SLAVE_SQL 4 /* - Disables binary logging for one thread, and resets it back to what it was - before being disabled. - Some functions (like the internal mysql_create_table() when it's called by - mysql_alter_table()) must NOT write to the binlog (binlogging is done at the - at a later stage of the command already, and must be, for locking reasons); - so we internally disable it temporarily by creating the Disable_binlog - object and reset the state by destroying the object (don't forget that! or - write code so that the object gets automatically destroyed when leaving a - block, see example in sql_table.cc). -*/ -class Disable_binlog { -private: - THD *thd; - ulong save_options; -public: - Disable_binlog(THD *thd_arg); - ~Disable_binlog(); -}; - - -/* Used to hold information about file and file structure in exchainge via non-DB file (...INTO OUTFILE..., ...LOAD DATA...) XXX: We never call destructor for objects of this class. @@ -1209,11 +1245,19 @@ public: unit= u; return 0; } + /* + Because of peculiarities of prepared statements protocol + we need to know number of columns in the result set (if + there is a result set) apart from sending columns metadata. + */ + virtual uint field_count(List<Item> &fields) const + { return fields.elements; } virtual bool send_fields(List<Item> &list, uint flags)=0; virtual bool send_data(List<Item> &items)=0; virtual bool initialize_tables (JOIN *join=0) { return 0; } virtual void send_error(uint errcode,const char *err); virtual bool send_eof()=0; + virtual bool simple_select() { return 0; } virtual void abort() {} /* Cleanup instance of this class for next execution of a prepared @@ -1223,16 +1267,31 @@ public: }; +/* + Base class for select_result descendands which intercept and + transform result set rows. As the rows are not sent to the client, + sending of result set metadata should be suppressed as well. +*/ + +class select_result_interceptor: public select_result +{ +public: + uint field_count(List<Item> &fields) const { return 0; } + bool send_fields(List<Item> &fields, uint flag) { return FALSE; } +}; + + class select_send :public select_result { public: select_send() {} bool send_fields(List<Item> &list, uint flags); bool send_data(List<Item> &items); bool send_eof(); + bool simple_select() { return 1; } }; -class select_to_file :public select_result { +class select_to_file :public select_result_interceptor { protected: sql_exchange *exchange; File file; @@ -1244,7 +1303,6 @@ public: select_to_file(sql_exchange *ex) :exchange(ex), file(-1),row_count(0L) { path[0]=0; } ~select_to_file(); - bool send_fields(List<Item> &list, uint flags) { return 0; } void send_error(uint errcode,const char *err); bool send_eof(); void cleanup(); @@ -1271,7 +1329,7 @@ public: }; -class select_insert :public select_result { +class select_insert :public select_result_interceptor { public: TABLE_LIST *table_list; TABLE *table; @@ -1285,7 +1343,6 @@ class select_insert :public select_result { bool ignore_check_option_errors); ~select_insert(); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); - bool send_fields(List<Item> &list, uint flags) { return 0; } bool send_data(List<Item> &items); void send_error(uint errcode,const char *err); bool send_eof(); @@ -1345,10 +1402,12 @@ public: uint group_parts,group_length,group_null_parts; uint quick_group; bool using_indirect_summary_function; + /* If >0 convert all blob fields to varchar(convert_blob_length) */ + uint convert_blob_length; TMP_TABLE_PARAM() :copy_funcs_it(copy_funcs), copy_field(0), group_parts(0), - group_length(0), group_null_parts(0) + group_length(0), group_null_parts(0), convert_blob_length(0) {} ~TMP_TABLE_PARAM() { @@ -1365,7 +1424,7 @@ public: } }; -class select_union :public select_result { +class select_union :public select_result_interceptor { public: TABLE *table; COPY_INFO info; @@ -1374,7 +1433,6 @@ class select_union :public select_result { select_union(TABLE *table_par); ~select_union(); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); - bool send_fields(List<Item> &list, uint flags) { return 0; } bool send_data(List<Item> &items); bool send_eof(); bool flush(); @@ -1382,17 +1440,14 @@ class select_union :public select_result { }; /* Base subselect interface class */ -class select_subselect :public select_result +class select_subselect :public select_result_interceptor { protected: Item_subselect *item; public: select_subselect(Item_subselect *item); - bool send_fields(List<Item> &list, uint flags) { return 0; } bool send_data(List<Item> &items)=0; bool send_eof() { return 0; }; - - friend class Ttem_subselect; }; /* Single value subselect interface class */ @@ -1544,7 +1599,7 @@ public: }; -class multi_delete :public select_result +class multi_delete :public select_result_interceptor { TABLE_LIST *delete_tables, *table_being_deleted; Unique **tempfiles; @@ -1557,7 +1612,6 @@ public: multi_delete(THD *thd, TABLE_LIST *dt, uint num_of_tables); ~multi_delete(); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); - bool send_fields(List<Item> &list, uint flags) { return 0; } bool send_data(List<Item> &items); bool initialize_tables (JOIN *join); void send_error(uint errcode,const char *err); @@ -1566,7 +1620,7 @@ public: }; -class multi_update :public select_result +class multi_update :public select_result_interceptor { TABLE_LIST *all_tables, *update_tables, *table_being_updated; THD *thd; @@ -1585,7 +1639,6 @@ public: List<Item> *values, enum_duplicates handle_duplicates); ~multi_update(); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); - bool send_fields(List<Item> &list, uint flags) { return 0; } bool send_data(List<Item> &items); bool initialize_tables (JOIN *join); void send_error(uint errcode,const char *err); @@ -1605,7 +1658,7 @@ public: ~my_var() {} }; -class select_dumpvar :public select_result { +class select_dumpvar :public select_result_interceptor { ha_rows row_count; public: List<my_var> var_list; @@ -1614,7 +1667,6 @@ public: select_dumpvar(void) { var_list.empty(); local_vars.empty(); vars.empty(); row_count=0;} ~select_dumpvar() {} int prepare(List<Item> &list, SELECT_LEX_UNIT *u); - bool send_fields(List<Item> &list, uint flags) { return 0; } bool send_data(List<Item> &items); bool send_eof(); void cleanup(); diff --git a/sql/sql_db.cc b/sql/sql_db.cc index f41e03b0602..982ad4a34a9 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -29,12 +29,12 @@ const char *del_exts[]= {".frm", ".BAK", ".TMD",".opt", NullS}; static TYPELIB deletable_extentions= -{array_elements(del_exts)-1,"del_exts", del_exts}; +{array_elements(del_exts)-1,"del_exts", del_exts, NULL}; const char *known_exts[]= {".ISM",".ISD",".ISM",".MRG",".MYI",".MYD",".db", ".ibd", NullS}; static TYPELIB known_extentions= -{array_elements(known_exts)-1,"known_exts", known_exts}; +{array_elements(known_exts)-1,"known_exts", known_exts, NULL}; static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, const char *path, @@ -966,6 +966,11 @@ err: communication packet (in case of 'connect' or 'COM_INIT_DB') we have to do end space removal in this function. + NOTES + Do as little as possible in this function, as it is not called for the + replication slave SQL thread (for that thread, setting of thd->db is done + in ::exec_event() methods of log_event.cc). + RETURN VALUES 0 ok 1 error diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index f9dba49d2e3..37121e64ecc 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -429,6 +429,8 @@ multi_delete::initialize_tables(JOIN *join) walk= walk->next_local; /* Don't use KEYREAD optimization on this table */ tbl->no_keyread=1; + /* Don't use record cache */ + tbl->no_cache= 1; tbl->used_keys.clear_all(); if (tbl->file->has_transactions()) log_delayed= transactional_tables= 1; @@ -744,8 +746,11 @@ int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) { /* Probably InnoDB table */ table_list->lock_type= TL_WRITE; - DBUG_RETURN(mysql_delete(thd, table_list, (COND*) 0, (SQL_LIST*) 0, - HA_POS_ERROR, 0)); + ha_enable_transaction(thd, FALSE); + error= mysql_delete(thd, table_list, (COND*) 0, (SQL_LIST*) 0, + HA_POS_ERROR, 0); + ha_enable_transaction(thd, TRUE); + DBUG_RETURN(error); } if (lock_and_wait_for_table_name(thd, table_list)) DBUG_RETURN(-1); diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 5c827741e6d..0923cf811f5 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -222,7 +222,10 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, } } else + { free_tmp_table(thd, table); + unit->cleanup(); + } exit: delete derived_result; diff --git a/sql/sql_error.cc b/sql/sql_error.cc index c0f76ab0388..c09bfe0aea8 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -103,12 +103,11 @@ void mysql_reset_errors(THD *thd) MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, uint code, const char *msg) { - MYSQL_ERROR *err= NULL; + MYSQL_ERROR *err= 0; DBUG_ENTER("push_warning"); if (thd->query_id != thd->warn_id) mysql_reset_errors(thd); - if (thd->spcont && thd->spcont->find_handler(code, ((int) level >= diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 38bc4756f81..edb895fd24a 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -1,5 +1,4 @@ -/* Copyright (C) 2000-2003 MySQL AB - +/* Copyright (C) 2000-2004 MySQL AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -17,9 +16,6 @@ /* HANDLER ... commands - direct access to ISAM */ -#include "mysql_priv.h" -#include "sql_select.h" - /* TODO: HANDLER blabla OPEN [ AS foobar ] [ (column-list) ] @@ -37,195 +33,390 @@ all the sql_alloc'ed memory. It's harder to work around... */ +/* + There are two containers holding information about open handler tables. + The first is 'thd->handler_tables'. It is a linked list of TABLE objects. + It is used like 'thd->open_tables' in the table cache. The trick is to + exchange these two lists during open and lock of tables. Thus the normal + table cache code can be used. + The second container is a HASH. It holds objects of the type TABLE_LIST. + Despite its name, no lists of tables but only single structs are hashed + (the 'next' pointer is always NULL). The reason for theis second container + is, that we want handler tables to survive FLUSH TABLE commands. A table + affected by FLUSH TABLE must be closed so that other threads are not + blocked by handler tables still in use. Since we use the normal table cache + functions with 'thd->handler_tables', the closed tables are removed from + this list. Hence we need the original open information for the handler + table in the case that it is used again. This information is handed over + to mysql_ha_open() as a TABLE_LIST. So we store this information in the + second container, where it is not affected by FLUSH TABLE. The second + container is implemented as a hash for performance reasons. Consequently, + we use it not only for re-opening a handler table, but also for the + HANDLER ... READ commands. For this purpose, we store a pointer to the + TABLE structure (in the first container) in the TBALE_LIST object in the + second container. When the table is flushed, the pointer is cleared. +*/ + +#include "mysql_priv.h" +#include "sql_select.h" +#include <assert.h> + +#define HANDLER_TABLES_HASH_SIZE 120 + +static enum enum_ha_read_modes rkey_to_rnext[]= +{ RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV }; + #define HANDLER_TABLES_HACK(thd) { \ TABLE *tmp=thd->open_tables; \ thd->open_tables=thd->handler_tables; \ thd->handler_tables=tmp; } -static TABLE **find_table_ptr_by_name(THD *thd,const char *db, - const char *table_name, - bool is_alias, bool dont_lock, - bool *was_flushed); +static int mysql_ha_flush_table(THD *thd, TABLE **table_ptr, uint mode_flags); + + +/* + Get hash key and hash key length. + + SYNOPSIS + mysql_ha_hash_get_key() + tables Pointer to the hash object. + key_len_p (out) Pointer to the result for key length. + first Unused. + + DESCRIPTION + The hash object is an TABLE_LIST struct. + The hash key is the alias name. + The hash key length is the alias name length plus one for the + terminateing NUL character. + + RETURN + Pointer to the TABLE_LIST struct. +*/ -int mysql_ha_open(THD *thd, TABLE_LIST *tables) +static char *mysql_ha_hash_get_key(TABLE_LIST *tables, uint *key_len_p, + my_bool first __attribute__((unused))) { - HANDLER_TABLES_HACK(thd); - uint counter; + *key_len_p= strlen(tables->alias) + 1 ; /* include '\0' in comparisons */ + return tables->alias; +} - /* for now HANDLER can be used only for real TABLES */ - tables->required_type= FRMTYPE_TABLE; - int err=open_tables(thd, tables, &counter); - HANDLER_TABLES_HACK(thd); - if (err) - return -1; +/* + Free an hash object. - // there can be only one table in *tables - if (!(tables->table->file->table_flags() & HA_CAN_SQL_HANDLER)) - { - my_printf_error(ER_ILLEGAL_HA,ER(ER_ILLEGAL_HA),MYF(0), tables->alias); - mysql_ha_close(thd, tables,1); - return -1; - } + SYNOPSIS + mysql_ha_hash_free() + tables Pointer to the hash object. - send_ok(thd); - return 0; + DESCRIPTION + The hash object is an TABLE_LIST struct. + + RETURN + Nothing +*/ + +static void mysql_ha_hash_free(TABLE_LIST *tables) +{ + my_free((char*) tables, MYF(0)); } /* - Close a HANDLER table. + Open a HANDLER table. SYNOPSIS - mysql_ha_close() + mysql_ha_open() thd Thread identifier. - tables A list of tables with the first entry to close. - dont_send_ok Suppresses the commands' ok message and - error message and error return. - dont_lock Suppresses the normal locking of LOCK_open. + tables A list of tables with the first entry to open. + reopen Re-open a previously opened handler table. DESCRIPTION Though this function takes a list of tables, only the first list entry - will be closed. Broadcasts a COND_refresh condition. - If mysql_ha_close() is not called from the parser, 'dont_send_ok' - must be set. - If the caller did already lock LOCK_open, it must set 'dont_lock'. - - IMPLEMENTATION - find_table_ptr_by_name() closes the table, if a FLUSH TABLE is outstanding. - It returns a NULL pointer in this case, but flags the situation in - 'was_flushed'. In that case the normal ER_UNKNOWN_TABLE error messages - is suppressed. + will be opened. + 'reopen' is set when a handler table is to be re-opened. In this case, + 'tables' is the pointer to the hashed TABLE_LIST object which has been + saved on the original open. + 'reopen' is also used to suppress the sending of an 'ok' message or + error messages. RETURN - 0 ok - -1 error + 0 ok + != 0 error */ -int mysql_ha_close(THD *thd, TABLE_LIST *tables, - bool dont_send_ok, bool dont_lock, bool no_alias) +int mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) { - TABLE **table_ptr; - bool was_flushed; - - table_ptr= find_table_ptr_by_name(thd, tables->db, tables->alias, - !no_alias, dont_lock, &was_flushed); - if (*table_ptr) + TABLE_LIST *hash_tables; + char *db, *name, *alias; + uint dblen, namelen, aliaslen, counter; + int error; + DBUG_ENTER("mysql_ha_open"); + DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d", + tables->db, tables->real_name, tables->alias, + (int) reopen)); + + if (! hash_inited(&thd->handler_tables_hash)) { - (*table_ptr)->file->ha_index_or_rnd_end(); - if (!dont_lock) - VOID(pthread_mutex_lock(&LOCK_open)); - if (close_thread_table(thd, table_ptr)) + /* + HASH entries are of type TABLE_LIST. + */ + if (hash_init(&thd->handler_tables_hash, &my_charset_latin1, + HANDLER_TABLES_HASH_SIZE, 0, 0, + (hash_get_key) mysql_ha_hash_get_key, + (hash_free_key) mysql_ha_hash_free, 0)) + goto err; + } + else if (! reopen) /* Otherwise we have 'tables' already. */ + { + if (hash_search(&thd->handler_tables_hash, (byte*) tables->alias, + strlen(tables->alias) + 1)) { - /* Tell threads waiting for refresh that something has happened */ - VOID(pthread_cond_broadcast(&COND_refresh)); + DBUG_PRINT("info",("duplicate '%s'", tables->alias)); + if (! reopen) + my_printf_error(ER_NONUNIQ_TABLE, ER(ER_NONUNIQ_TABLE), + MYF(0), tables->alias); + goto err; } - if (!dont_lock) - VOID(pthread_mutex_unlock(&LOCK_open)); } - else if (!was_flushed && !dont_send_ok) + + /* + open_tables() will set 'tables->table' if successful. + It must be NULL for a real open when calling open_tables(). + */ + DBUG_ASSERT(! tables->table); + HANDLER_TABLES_HACK(thd); + + /* for now HANDLER can be used only for real TABLES */ + tables->required_type= FRMTYPE_TABLE; + error= open_tables(thd, tables, &counter); + + HANDLER_TABLES_HACK(thd); + if (error) + goto err; + + /* There can be only one table in '*tables'. */ + if (! (tables->table->file->table_flags() & HA_CAN_SQL_HANDLER)) + { + if (! reopen) + my_printf_error(ER_ILLEGAL_HA,ER(ER_ILLEGAL_HA),MYF(0), tables->alias); + mysql_ha_close(thd, tables); + goto err; + } + + if (! reopen) { - my_printf_error(ER_UNKNOWN_TABLE, ER(ER_UNKNOWN_TABLE), MYF(0), - tables->alias, "HANDLER"); - return -1; + /* copy the TABLE_LIST struct */ + dblen= strlen(tables->db) + 1; + namelen= strlen(tables->real_name) + 1; + aliaslen= strlen(tables->alias) + 1; + if (!(my_multi_malloc(MYF(MY_WME), + &hash_tables, sizeof(*hash_tables), + &db, dblen, + &name, namelen, + &alias, aliaslen, + NullS))) + goto err; + /* structure copy */ + *hash_tables= *tables; + hash_tables->db= db; + hash_tables->real_name= name; + hash_tables->alias= alias; + memcpy(hash_tables->db, tables->db, dblen); + memcpy(hash_tables->real_name, tables->real_name, namelen); + memcpy(hash_tables->alias, tables->alias, aliaslen); + + /* add to hash */ + if (my_hash_insert(&thd->handler_tables_hash, (byte*) hash_tables)) + { + mysql_ha_close(thd, tables); + goto err; + } } - if (!dont_send_ok) + + if (! reopen) send_ok(thd); - return 0; + DBUG_PRINT("exit",("OK")); + DBUG_RETURN(0); + +err: + DBUG_PRINT("exit",("ERROR")); + DBUG_RETURN(-1); } /* - Close a list of HANDLER tables. + Close a HANDLER table. SYNOPSIS - mysql_ha_close_list() + mysql_ha_close() thd Thread identifier. - tables The list of tables to close. If NULL, - close all HANDLER tables. - flushed Close only tables which are marked flushed. - Used only if tables is NULL. + tables A list of tables with the first entry to close. DESCRIPTION - The list of HANDLER tables may be NULL, in which case all HANDLER - tables are closed. Broadcasts a COND_refresh condition, for - every table closed. If 'tables' is NULL and 'flushed' is set, - all HANDLER tables marked for flush are closed. - The caller must lock LOCK_open. - - IMPLEMENTATION - find_table_ptr_by_name() closes the table, if it is marked for flush. - It returns a NULL pointer in this case, but flags the situation in - 'was_flushed'. In that case the normal ER_UNKNOWN_TABLE error messages - is suppressed. + Though this function takes a list of tables, only the first list entry + will be closed. Broadcasts a COND_refresh condition. RETURN - 0 ok + 0 ok + != 0 error */ -int mysql_ha_close_list(THD *thd, TABLE_LIST *tables, bool flushed) +int mysql_ha_close(THD *thd, TABLE_LIST *tables) { - TABLE_LIST *tl_item; + TABLE_LIST *hash_tables; TABLE **table_ptr; + DBUG_ENTER("mysql_ha_close"); + DBUG_PRINT("enter",("'%s'.'%s' as '%s'", + tables->db, tables->real_name, tables->alias)); - if (tables) + if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash, + (byte*) tables->alias, + strlen(tables->alias) + 1))) { - for (tl_item= tables ; tl_item; tl_item= tl_item->next_local) + /* + Though we could take the table pointer from hash_tables->table, + we must follow the thd->handler_tables chain anyway, as we need the + address of the 'next' pointer referencing this table + for close_thread_table(). + */ + for (table_ptr= &(thd->handler_tables); + *table_ptr && (*table_ptr != hash_tables->table); + table_ptr= &(*table_ptr)->next) + ; + + if (*table_ptr) { - mysql_ha_close(thd, tl_item, /*dont_send_ok*/ 1, - /*dont_lock*/ 1, /*no_alias*/ 1); + (*table_ptr)->file->ha_index_or_rnd_end(); + VOID(pthread_mutex_lock(&LOCK_open)); + if (close_thread_table(thd, table_ptr)) + { + /* Tell threads waiting for refresh that something has happened */ + VOID(pthread_cond_broadcast(&COND_refresh)); + } + VOID(pthread_mutex_unlock(&LOCK_open)); } + hash_delete(&thd->handler_tables_hash, (byte*) hash_tables); } else { - table_ptr= &(thd->handler_tables); - while (*table_ptr) - { - if (! flushed || ((*table_ptr)->version != refresh_version)) - { - (*table_ptr)->file->ha_index_or_rnd_end(); - if (close_thread_table(thd, table_ptr)) - { - /* Tell threads waiting for refresh that something has happened */ - VOID(pthread_cond_broadcast(&COND_refresh)); - } - continue; - } - table_ptr= &((*table_ptr)->next); - } + my_printf_error(ER_UNKNOWN_TABLE, ER(ER_UNKNOWN_TABLE), MYF(0), + tables->alias, "HANDLER"); + DBUG_PRINT("exit",("ERROR")); + DBUG_RETURN(-1); } - return 0; + + send_ok(thd); + DBUG_PRINT("exit", ("OK")); + DBUG_RETURN(0); } -static enum enum_ha_read_modes rkey_to_rnext[]= -{ RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV }; +/* + Read from a HANDLER table. + + SYNOPSIS + mysql_ha_read() + thd Thread identifier. + tables A list of tables with the first entry to read. + mode + keyname + key_expr + ha_rkey_mode + cond + select_limit + offset_limit + RETURN + 0 ok + != 0 error +*/ + int mysql_ha_read(THD *thd, TABLE_LIST *tables, enum enum_ha_read_modes mode, char *keyname, List<Item> *key_expr, enum ha_rkey_function ha_rkey_mode, Item *cond, ha_rows select_limit,ha_rows offset_limit) { - int err, keyno=-1; - bool was_flushed; - TABLE *table= *find_table_ptr_by_name(thd, tables->db, tables->alias, - /*is_alias*/ 1, /*dont_lock*/ 0, - &was_flushed); + TABLE_LIST *hash_tables; + TABLE *table; + MYSQL_LOCK *lock; + List<Item> list; + Protocol *protocol= thd->protocol; + char buff[MAX_FIELD_WIDTH]; + String buffer(buff, sizeof(buff), system_charset_info); + int error, keyno= -1; + uint num_rows; + byte *key; + uint key_len; + DBUG_ENTER("mysql_ha_read"); + DBUG_PRINT("enter",("'%s'.'%s' as '%s'", + tables->db, tables->real_name, tables->alias)); + + LINT_INIT(key); + LINT_INIT(key_len); + + list.push_front(new Item_field(NULL,NULL,"*")); + List_iterator<Item> it(list); + it++; + + if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash, + (byte*) tables->alias, + strlen(tables->alias) + 1))) + { + table= hash_tables->table; + DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' tab %p", + hash_tables->db, hash_tables->real_name, + hash_tables->alias, table)); + if (!table) + { + /* + The handler table has been closed. Re-open it. + */ + if (mysql_ha_open(thd, hash_tables, 1)) + { + DBUG_PRINT("exit",("reopen failed")); + goto err0; + } + + table= hash_tables->table; + DBUG_PRINT("info",("re-opened '%s'.'%s' as '%s' tab %p", + hash_tables->db, hash_tables->real_name, + hash_tables->alias, table)); + } + +#if MYSQL_VERSION_ID < 40100 + if (*tables->db && strcmp(table->table_cache_key, tables->db)) + { + DBUG_PRINT("info",("wrong db")); + table= NULL; + } +#endif + } + else + table= NULL; + if (!table) { - my_printf_error(ER_UNKNOWN_TABLE,ER(ER_UNKNOWN_TABLE),MYF(0), - tables->alias,"HANDLER"); - return -1; +#if MYSQL_VERSION_ID < 40100 + char buff[MAX_DBKEY_LENGTH]; + if (*tables->db) + strxnmov(buff, sizeof(buff), tables->db, ".", tables->real_name, NullS); + else + strncpy(buff, tables->alias, sizeof(buff)); + my_printf_error(ER_UNKNOWN_TABLE, ER(ER_UNKNOWN_TABLE), MYF(0), + buff, "HANDLER"); +#else + my_printf_error(ER_UNKNOWN_TABLE, ER(ER_UNKNOWN_TABLE), MYF(0), + tables->alias, "HANDLER"); +#endif + goto err0; } tables->table=table; if (cond && (cond->fix_fields(thd, tables, &cond) || cond->check_cols(1))) - return -1; - - /* InnoDB needs to know that this table handle is used in the HANDLER */ + goto err0; - table->file->init_table_handle_for_HANDLER(); + table->file->init_table_handle_for_HANDLER(); // Only InnoDB requires it if (keyname) { @@ -233,34 +424,22 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, { my_printf_error(ER_KEY_DOES_NOT_EXITS,ER(ER_KEY_DOES_NOT_EXITS),MYF(0), keyname,tables->alias); - return -1; + goto err0; } table->file->ha_index_or_rnd_end(); table->file->ha_index_init(keyno); } - List<Item> list; - list.push_front(new Item_field(NULL,NULL,"*")); - List_iterator<Item> it(list); - Protocol *protocol= thd->protocol; - char buff[MAX_FIELD_WIDTH]; - String buffer(buff, sizeof(buff), system_charset_info); - uint num_rows; - byte *key; - uint key_len; - LINT_INIT(key); - LINT_INIT(key_len); - - it++; // Skip first NULL field - - insert_fields(thd, tables, tables->db, tables->alias, &it, 0, 0); + if (insert_fields(thd, tables, tables->db, tables->alias, &it, 0, 0)) + goto err0; select_limit+=offset_limit; protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); HANDLER_TABLES_HACK(thd); - MYSQL_LOCK *lock=mysql_lock_tables(thd,&tables->table,1); + lock= mysql_lock_tables(thd, &tables->table, 1); HANDLER_TABLES_HACK(thd); + if (!lock) goto err0; // mysql_lock_tables() printed error message already @@ -277,33 +456,33 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, switch (mode) { case RFIRST: if (keyname) - err=table->file->index_first(table->record[0]); + error= table->file->index_first(table->record[0]); else { table->file->ha_index_or_rnd_end(); - if (!(err=table->file->ha_rnd_init(1))) - err=table->file->rnd_next(table->record[0]); + if (!(error= table->file->ha_rnd_init(1))) + error= table->file->rnd_next(table->record[0]); } mode=RNEXT; break; case RLAST: DBUG_ASSERT(keyname != 0); - err=table->file->index_last(table->record[0]); + error= table->file->index_last(table->record[0]); mode=RPREV; break; case RNEXT: - err=keyname ? - table->file->index_next(table->record[0]) : - table->file->rnd_next(table->record[0]); - break; + error= (keyname ? + table->file->index_next(table->record[0]) : + table->file->rnd_next(table->record[0])); + break; case RPREV: DBUG_ASSERT(keyname != 0); - err=table->file->index_prev(table->record[0]); + error= table->file->index_prev(table->record[0]); break; case RNEXT_SAME: /* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...) */ DBUG_ASSERT(keyname != 0); - err= table->file->index_next_same(table->record[0], key, key_len); + error= table->file->index_next_same(table->record[0], key, key_len); break; case RKEY: { @@ -338,7 +517,7 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, goto err; } key_copy(key, table->record[0], table->key_info + keyno, key_len); - err=table->file->index_read(table->record[0], + error= table->file->index_read(table->record[0], key,key_len,ha_rkey_mode); mode=rkey_to_rnext[(int)ha_rkey_mode]; break; @@ -348,15 +527,15 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, goto err; } - if (err == HA_ERR_RECORD_DELETED) - continue; - if (err) + if (error) { - if (err != HA_ERR_KEY_NOT_FOUND && err != HA_ERR_END_OF_FILE) + if (error == HA_ERR_RECORD_DELETED) + continue; + if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) { sql_print_error("mysql_ha_read: Got error %d when reading table '%s'", - err, tables->real_name); - table->file->print_error(err,MYF(0)); + error, tables->real_name); + table->file->print_error(error,MYF(0)); goto err; } goto ok; @@ -384,85 +563,152 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, ok: mysql_unlock_tables(thd,lock); send_eof(thd); - return 0; + DBUG_PRINT("exit",("OK")); + DBUG_RETURN(0); + err: mysql_unlock_tables(thd,lock); err0: - return -1; + DBUG_PRINT("exit",("ERROR")); + DBUG_RETURN(-1); } /* - Find a HANDLER table by name. + Flush (close) a list of HANDLER tables. SYNOPSIS - find_table_ptr_by_name() + mysql_ha_flush() thd Thread identifier. - db Database (schema) name. - table_name Table name ;-). - is_alias Table name may be an alias name. - dont_lock Suppresses the normal locking of LOCK_open. + tables The list of tables to close. If NULL, + close all HANDLER tables [marked as flushed]. + mode_flags MYSQL_HA_CLOSE_FINAL finally close the table. + MYSQL_HA_REOPEN_ON_USAGE mark for reopen. + MYSQL_HA_FLUSH_ALL flush all tables, not only + those marked for flush. DESCRIPTION - Find the table 'db'.'table_name' in the list of HANDLER tables of the - thread 'thd'. If the table has been marked by FLUSH TABLE(S), close it, - flag this situation in '*was_flushed' and broadcast a COND_refresh - condition. - An empty database (schema) name matches all database (schema) names. - If the caller did already lock LOCK_open, it must set 'dont_lock'. - - IMPLEMENTATION - Just in case that the table is twice in 'thd->handler_tables' (!?!), - the loop does not break when the table was flushed. If another table - by that name was found and not flushed, '*was_flushed' is cleared again, - since a pointer to an open HANDLER table is returned. + The list of HANDLER tables may be NULL, in which case all HANDLER + tables are closed (if MYSQL_HA_FLUSH_ALL) is set. + If 'tables' is NULL and MYSQL_HA_FLUSH_ALL is not set, + all HANDLER tables marked for flush are closed. + Broadcasts a COND_refresh condition, for every table closed. + The caller must lock LOCK_open. + + NOTE + Since mysql_ha_flush() is called when the base table has to be closed, + we compare real table names, not aliases. Hence, database names matter. RETURN - *was_flushed Table has been closed due to FLUSH TABLE. - NULL A HANDLER Table by that name does not exist (any more). - != NULL Pointer to the TABLE structure. + 0 ok */ -static TABLE **find_table_ptr_by_name(THD *thd, const char *db, - const char *table_name, - bool is_alias, bool dont_lock, - bool *was_flushed) +int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags) { - int dblen; - TABLE **table_ptr; - - DBUG_ASSERT(db); - dblen= strlen(db); - table_ptr= &(thd->handler_tables); - *was_flushed= FALSE; + TABLE_LIST *tmp_tables; + TABLE **table_ptr; + DBUG_ENTER("mysql_ha_flush"); + DBUG_PRINT("enter", ("tables: %p mode_flags: 0x%02x", tables, mode_flags)); - for (TABLE *table= *table_ptr; table ; table= *table_ptr) + if (tables) { - if ((db == any_db || !memcmp(table->table_cache_key, db, dblen)) && - !my_strcasecmp(system_charset_info, - (is_alias ? table->table_name : table->real_name), - table_name)) + /* Close all tables in the list. */ + for (tmp_tables= tables ; tmp_tables; tmp_tables= tmp_tables->next_local) { - if (table->version != refresh_version) + DBUG_PRINT("info-in-tables-list",("'%s'.'%s' as '%s'", + tmp_tables->db, tmp_tables->real_name, + tmp_tables->alias)); + /* Close all currently open handler tables with the same base table. */ + table_ptr= &(thd->handler_tables); + while (*table_ptr) { - if (!dont_lock) - VOID(pthread_mutex_lock(&LOCK_open)); - - table->file->ha_index_or_rnd_end(); - if (close_thread_table(thd, table_ptr)) + if ((! *tmp_tables->db || + ! my_strcasecmp(&my_charset_latin1, (*table_ptr)->table_cache_key, + tmp_tables->db)) && + ! my_strcasecmp(&my_charset_latin1, (*table_ptr)->real_name, + tmp_tables->real_name)) { - /* Tell threads waiting for refresh that something has happened */ - VOID(pthread_cond_broadcast(&COND_refresh)); + DBUG_PRINT("info",("*table_ptr '%s'.'%s' as '%s'", + (*table_ptr)->table_cache_key, + (*table_ptr)->real_name, + (*table_ptr)->table_name)); + mysql_ha_flush_table(thd, table_ptr, mode_flags); + continue; } - if (!dont_lock) - VOID(pthread_mutex_unlock(&LOCK_open)); - *was_flushed= TRUE; + table_ptr= &(*table_ptr)->next; + } + /* end of handler_tables list */ + } + /* end of flush tables list */ + } + else + { + /* Close all currently open tables [which are marked for flush]. */ + table_ptr= &(thd->handler_tables); + while (*table_ptr) + { + if ((mode_flags & MYSQL_HA_FLUSH_ALL) || + ((*table_ptr)->version != refresh_version)) + { + mysql_ha_flush_table(thd, table_ptr, mode_flags); continue; } - *was_flushed= FALSE; - break; + table_ptr= &(*table_ptr)->next; + } + } + + DBUG_RETURN(0); +} + +/* + Flush (close) a table. + + SYNOPSIS + mysql_ha_flush_table() + thd Thread identifier. + table The table to close. + mode_flags MYSQL_HA_CLOSE_FINAL finally close the table. + MYSQL_HA_REOPEN_ON_USAGE mark for reopen. + + DESCRIPTION + Broadcasts a COND_refresh condition, for every table closed. + The caller must lock LOCK_open. + + RETURN + 0 ok +*/ + +static int mysql_ha_flush_table(THD *thd, TABLE **table_ptr, uint mode_flags) +{ + TABLE_LIST *hash_tables; + TABLE *table= *table_ptr; + DBUG_ENTER("mysql_ha_flush_table"); + DBUG_PRINT("enter",("'%s'.'%s' as '%s' flags: 0x%02x", + table->table_cache_key, table->real_name, + table->table_name, mode_flags)); + + if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash, + (byte*) (*table_ptr)->table_name, + strlen((*table_ptr)->table_name) + 1))) + { + if (! (mode_flags & MYSQL_HA_REOPEN_ON_USAGE)) + { + /* This is a final close. Remove from hash. */ + hash_delete(&thd->handler_tables_hash, (byte*) hash_tables); + } + else + { + /* Mark table as closed, ready for re-open. */ + hash_tables->table= NULL; } - table_ptr= &(table->next); + } + + (*table_ptr)->file->ha_index_or_rnd_end(); + if (close_thread_table(thd, table_ptr)) + { + /* Tell threads waiting for refresh that something has happened */ + VOID(pthread_cond_broadcast(&COND_refresh)); } - return table_ptr; + + DBUG_RETURN(0); } diff --git a/sql/sql_help.cc b/sql/sql_help.cc index cba74c93a6a..b349a09e49e 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -747,7 +747,7 @@ int mysqld_help(THD *thd, const char *mask) select,&subcategories_list); delete select; String *cat= categories_list.head(); - if (send_header_2(protocol, true) || + if (send_header_2(protocol, TRUE) || send_variant_2_list(mem_root,protocol,&topics_list, "N",cat) || send_variant_2_list(mem_root,protocol,&subcategories_list,"Y",cat)) goto end; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index d9002c2da29..63e11822f6e 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -44,8 +44,8 @@ static void unlink_blobs(register TABLE *table); /* Check if insert fields are correct. - Sets table->timestamp_default_now/on_update_now to 0 o leaves it to point - to timestamp field, depending on if timestamp should be updated or not. + Sets table->timestamp_field_type to TIMESTAMP_NO_AUTO_SET or leaves it + as is, depending on if timestamp should be updated or not. */ static int @@ -74,7 +74,7 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields, return -1; } #endif - table->timestamp_default_now= table->timestamp_on_update_now= 0; + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; } else { // Part field list @@ -105,7 +105,7 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields, } if (table->timestamp_field && // Don't set timestamp if used table->timestamp_field->query_id == thd->query_id) - table->timestamp_default_now= table->timestamp_on_update_now= 0; + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; } // For the values we need select_priv #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -767,7 +767,8 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) */ if (last_uniq_key(table,key_nr) && !table->file->referenced_by_foreign_key() && - table->timestamp_default_now == table->timestamp_on_update_now) + (table->timestamp_field_type == TIMESTAMP_NO_AUTO_SET || + table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_BOTH)) { if ((error=table->file->update_row(table->record[1], table->record[0]))) @@ -845,8 +846,7 @@ public: time_t start_time; bool query_start_used,last_insert_id_used,insert_id_used, log_query; ulonglong last_insert_id; - ulong timestamp_default_now; - ulong timestamp_on_update_now; + timestamp_auto_set_type timestamp_field_type; uint query_length; delayed_row(enum_duplicates dup_arg, bool log_query_arg) @@ -1140,7 +1140,7 @@ TABLE *delayed_insert::get_local_table(THD* client_thd) copy->timestamp_field= (Field_timestamp*) copy->field[table->timestamp_field_offset]; copy->timestamp_field->unireg_check= table->timestamp_field->unireg_check; - copy->timestamp_field->set_timestamp_offsets(); + copy->timestamp_field_type= copy->timestamp_field->get_auto_set_type(); } /* _rowid is not used with delayed insert */ @@ -1195,8 +1195,7 @@ static int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic, row->last_insert_id_used= thd->last_insert_id_used; row->insert_id_used= thd->insert_id_used; row->last_insert_id= thd->last_insert_id; - row->timestamp_default_now= table->timestamp_default_now; - row->timestamp_on_update_now= table->timestamp_on_update_now; + row->timestamp_field_type= table->timestamp_field_type; di->rows.push_back(row); di->stacked_inserts++; @@ -1537,8 +1536,7 @@ bool delayed_insert::handle_inserts(void) thd.last_insert_id=row->last_insert_id; thd.last_insert_id_used=row->last_insert_id_used; thd.insert_id_used=row->insert_id_used; - table->timestamp_default_now= row->timestamp_default_now; - table->timestamp_on_update_now= row->timestamp_on_update_now; + table->timestamp_field_type= row->timestamp_field_type; info.handle_duplicates= row->dup; if (info.handle_duplicates == DUP_IGNORE || @@ -1892,7 +1890,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) field=table->field+table->fields - values.elements; /* Don't set timestamp if used */ - table->timestamp_default_now= table->timestamp_on_update_now= 0; + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; table->next_number_field=table->found_next_number_field; @@ -1949,7 +1947,13 @@ bool select_create::send_eof() We should be able to just keep the table in the table cache. */ if (!table->tmp_table) + { + ulong version= table->version; hash_delete(&open_cache,(byte*) table); + /* Tell threads waiting for refresh that something has happened */ + if (version != refresh_version) + VOID(pthread_cond_broadcast(&COND_refresh)); + } lock=0; table=0; VOID(pthread_mutex_unlock(&LOCK_open)); @@ -1971,9 +1975,13 @@ void select_create::abort() enum db_type table_type=table->db_type; if (!table->tmp_table) { + ulong version= table->version; hash_delete(&open_cache,(byte*) table); if (!create_info->table_existed) quick_rm_table(table_type, create_table->db, create_table->real_name); + /* Tell threads waiting for refresh that something has happened */ + if (version != refresh_version) + VOID(pthread_cond_broadcast(&COND_refresh)); } else if (!create_info->table_existed) close_temporary_table(thd, create_table->db, create_table->real_name); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 73f99d0dd68..d978cc14f64 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -124,7 +124,38 @@ void lex_free(void) void lex_start(THD *thd, uchar *buf,uint length) { LEX *lex= thd->lex; - lex->thd= thd; + lex->unit.init_query(); + lex->unit.init_select(); + lex->thd= lex->unit.thd= thd; + lex->select_lex.init_query(); + lex->value_list.empty(); + lex->param_list.empty(); + lex->view_list.empty(); + lex->unit.next= lex->unit.master= + lex->unit.link_next= lex->unit.return_to= 0; + lex->unit.prev= lex->unit.link_prev= 0; + lex->unit.slave= lex->unit.global_parameters= lex->current_select= + lex->all_selects_list= &lex->select_lex; + lex->select_lex.master= &lex->unit; + lex->select_lex.prev= &lex->unit.slave; + lex->select_lex.link_next= lex->select_lex.slave= lex->select_lex.next= 0; + lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list); + lex->select_lex.options= 0; + lex->select_lex.init_order(); + lex->select_lex.group_list.empty(); + lex->describe= 0; + lex->derived_tables= FALSE; + lex->view_prepare_mode= FALSE; + lex->lock_option= TL_READ; + lex->found_colon= 0; + lex->safe_to_cache_query= 1; + lex->time_zone_tables_used= 0; + lex->proc_table= lex->query_tables= 0; + lex->query_tables_last= &lex->query_tables; + lex->variables_used= 0; + lex->select_lex.parent_lex= lex; + lex->empty_field_list_on_rset= 0; + lex->select_lex.select_number= 1; lex->next_state=MY_LEX_START; lex->buf= lex->ptr= buf; lex->end_of_query=buf+length; @@ -533,6 +564,7 @@ int yylex(void *arg, void *yythd) /* Fall through */ case MY_LEX_IDENT_OR_BIN: // TODO: Add binary string handling case MY_LEX_IDENT: + uchar *start; #if defined(USE_MB) && defined(USE_MB_IDENT) if (use_mb(cs)) { @@ -569,11 +601,16 @@ int yylex(void *arg, void *yythd) result_state= result_state & 0x80 ? IDENT_QUOTED : IDENT; } length= (uint) (lex->ptr - lex->tok_start)-1; + start= lex->ptr; if (lex->ignore_space) { - for (; state_map[c] == MY_LEX_SKIP ; c= yyGet()); + /* + If we find a space then this can't be an identifier. We notice this + below by checking start != lex->ptr. + */ + for (; state_map[c] == MY_LEX_SKIP ; c= yyGet()); } - if (c == '.' && ident_map[yyPeek()]) + if (start == lex->ptr && c == '.' && ident_map[yyPeek()]) lex->next_state=MY_LEX_IDENT_SEP; else { // '(' must follow directly if function @@ -1413,6 +1450,7 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) 1 - found 0 - OK (table did not found) */ + bool st_select_lex_unit::check_updateable(char *db, char *table) { for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) @@ -1423,8 +1461,8 @@ bool st_select_lex_unit::check_updateable(char *db, char *table) /* - Find db.table which will be updated in this select and - underlayed ones (except derived tables) + Find db.table which will be updated in this select and + underlying ones (except derived tables) SYNOPSIS st_select_lex::check_updateable() @@ -1435,11 +1473,30 @@ bool st_select_lex_unit::check_updateable(char *db, char *table) 1 - found 0 - OK (table did not found) */ + bool st_select_lex::check_updateable(char *db, char *table) { if (find_table_in_local_list(get_table_list(), db, table)) return 1; + return check_updateable_in_subqueries(db, table); +} + +/* + Find db.table which will be updated in underlying subqueries + + SYNOPSIS + st_select_lex::check_updateable_in_subqueries() + db - data base name + table - real table name + + RETURN + 1 - found + 0 - OK (table did not found) +*/ + +bool st_select_lex::check_updateable_in_subqueries(char *db, char *table) +{ for (SELECT_LEX_UNIT *un= first_inner_unit(); un; un= un->next_unit()) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index af5b0896fa5..2bdc35f35cb 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -283,6 +283,7 @@ public: static void *operator new(size_t size, MEM_ROOT *mem_root) { return (void*) alloc_root(mem_root, (uint) size); } static void operator delete(void *ptr,size_t size) {} + static void operator delete(void *ptr,size_t size, MEM_ROOT *mem_root) {} st_select_lex_node(): linkage(UNSPECIFIED_TYPE) {} virtual ~st_select_lex_node() {} inline st_select_lex_node* get_master() { return master; } @@ -333,6 +334,7 @@ class THD; class select_result; class JOIN; class select_union; +class Procedure; class st_select_lex_unit: public st_select_lex_node { protected: TABLE_LIST result_table_list; @@ -379,6 +381,7 @@ public: st_select_lex *union_distinct; /* pointer to the last UNION DISTINCT */ bool describe; /* union exec() called for EXPLAIN */ + Procedure *last_procedure; /* Pointer to procedure, if such exists */ void init_query(); st_select_lex_unit* master_unit(); @@ -413,7 +416,7 @@ public: int change_result(select_subselect *result, select_subselect *old_result); void set_limit(st_select_lex *values, st_select_lex *sl); - friend void mysql_init_query(THD *thd, uchar *buf, uint length, bool lexonly); + friend void lex_start(THD *thd, uchar *buf, uint length); friend int subselect_union_engine::exec(); }; typedef class st_select_lex_unit SELECT_LEX_UNIT; @@ -569,7 +572,7 @@ public: bool test_limit(); - friend void mysql_init_query(THD *thd, uchar *buf, uint length, bool lexonly); + friend void lex_start(THD *thd, uchar *buf, uint length); st_select_lex() {} void make_empty_select() { @@ -578,6 +581,7 @@ public: } bool setup_ref_array(THD *thd, uint order_group_num); bool check_updateable(char *db, char *table); + bool check_updateable_in_subqueries(char *db, char *table); void print(THD *thd, String *str); static void print_order(String *str, ORDER *order); void print_limit(THD *thd, String *str); @@ -593,6 +597,9 @@ typedef class st_select_lex SELECT_LEX; #define ALTER_RENAME 32 #define ALTER_ORDER 64 #define ALTER_OPTIONS 128 +#define ALTER_CHANGE_COLUMN_DEFAULT 256 +#define ALTER_KEYS_ONOFF 512 +#define ALTER_CONVERT 1024 typedef struct st_alter_info { @@ -601,7 +608,6 @@ typedef struct st_alter_info uint flags; enum enum_enable_or_disable keys_onoff; enum tablespace_op_type tablespace_op; - bool is_simple; st_alter_info(){clear();} void clear(){keys_onoff= LEAVE_AS_IS;tablespace_op= NO_TABLESPACE_OP;} @@ -687,7 +693,7 @@ typedef struct st_lex USER_RESOURCES mqh; ulong thread_id,type; enum_sql_command sql_command; - thr_lock_type lock_option; + thr_lock_type lock_option, multi_lock_option; enum SSL_type ssl_type; /* defined in violite.h */ enum my_lex_states next_state; enum enum_duplicates duplicates; diff --git a/sql/sql_list.h b/sql/sql_list.h index 40530314893..d8d3719d39d 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -41,6 +41,8 @@ public: static void *operator new(size_t size, MEM_ROOT *mem_root) { return (void*) alloc_root(mem_root, (uint) size); } static void operator delete(void *ptr, size_t size) { TRASH(ptr, size); } + static void operator delete(void *ptr, size_t size, MEM_ROOT *mem_root) + { TRASH(ptr, size); } static void operator delete[](void *ptr, size_t size) { TRASH(ptr, size); } #ifdef HAVE_purify bool dummy; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 95025a8fd77..1735da6b717 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -197,17 +197,16 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, #ifdef DONT_ALLOW_FULL_LOAD_DATA_PATHS ex->file_name+=dirname_length(ex->file_name); #endif - if (!dirname_length(ex->file_name) && - strlen(ex->file_name)+strlen(mysql_real_data_home)+strlen(tdb)+3 < - FN_REFLEN) + if (!dirname_length(ex->file_name)) { - (void) sprintf(name,"%s%s/%s",mysql_real_data_home,tdb,ex->file_name); - unpack_filename(name,name); /* Convert to system format */ + strxnmov(name, FN_REFLEN, mysql_real_data_home, tdb, NullS); + (void) fn_format(name, ex->file_name, name, "", + MY_RELATIVE_PATH | MY_UNPACK_FILENAME); } else { - my_load_path(name, ex->file_name, mysql_real_data_home); - unpack_filename(name, name); + (void) fn_format(name, ex->file_name, mysql_real_data_home, "", + MY_RELATIVE_PATH | MY_UNPACK_FILENAME); #if !defined(__WIN__) && !defined(OS2) && ! defined(__NETWARE__) MY_STAT stat_info; if (!my_stat(name,&stat_info,MYF(MY_WME))) @@ -283,12 +282,13 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if (!(error=test(read_info.error))) { if (use_timestamp) - table->timestamp_default_now= table->timestamp_on_update_now= 0; + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; table->next_number_field=table->found_next_number_field; if (handle_duplicates == DUP_IGNORE || handle_duplicates == DUP_REPLACE) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); + ha_enable_transaction(thd, FALSE); table->file->start_bulk_insert((ha_rows) 0); table->copy_blobs=1; @@ -307,6 +307,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ignore_check_option_errors); if (table->file->end_bulk_insert()) error=1; /* purecov: inspected */ + ha_enable_transaction(thd, TRUE); table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); table->next_number_field=0; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 079d8994549..60f69b578e9 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -76,7 +76,7 @@ const char *any_db="*any*"; // Special symbol for check_access const char *command_name[]={ "Sleep", "Quit", "Init DB", "Query", "Field List", "Create DB", "Drop DB", "Refresh", "Shutdown", "Statistics", "Processlist", - "Connect","Kill","Debug","Ping","Time","Delayed_insert","Change user", + "Connect","Kill","Debug","Ping","Time","Delayed insert","Change user", "Binlog Dump","Table Dump", "Connect Out", "Register Slave", "Prepare", "Prepare Execute", "Long Data", "Close stmt", "Reset stmt", "Set option", "Fetch", @@ -516,12 +516,17 @@ void free_max_user_conn(void) /* Mark all commands that somehow changes a table This is used to check number of updates / hour + + sql_command is actually set to SQLCOM_END sometimes + so we need the +1 to include it in the array. */ -char uc_update_queries[SQLCOM_END]; +char uc_update_queries[SQLCOM_END+1]; void init_update_queries(void) { + bzero((gptr) &uc_update_queries, sizeof(uc_update_queries)); + uc_update_queries[SQLCOM_CREATE_TABLE]=1; uc_update_queries[SQLCOM_CREATE_INDEX]=1; uc_update_queries[SQLCOM_ALTER_TABLE]=1; @@ -548,6 +553,7 @@ void init_update_queries(void) bool is_update_query(enum enum_sql_command command) { + DBUG_ASSERT(command >= 0 && command <= SQLCOM_END); return uc_update_queries[command]; } @@ -912,7 +918,7 @@ static int check_connection(THD *thd) x_free(thd->user); if (!(thd->user= my_strdup(user, MYF(0)))) return (ER_OUT_OF_RESOURCES); - return check_user(thd, COM_CONNECT, passwd, passwd_len, db, true); + return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE); } @@ -1019,6 +1025,10 @@ pthread_handler_decl(handle_one_connection,arg) net->compress=1; // Use compression thd->version= refresh_version; + thd->proc_info= 0; + thd->set_time(); + thd->init_for_queries(); + if (sys_init_connect.value_length && !(thd->master_access & SUPER_ACL)) { execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect); @@ -1039,12 +1049,12 @@ pthread_handler_decl(handle_one_connection,arg) if (net->error && net->vio != 0 && net->report_error) { if (!thd->killed && thd->variables.log_warnings > 1) - sql_print_error(ER(ER_NEW_ABORTING_CONNECTION), - thd->thread_id,(thd->db ? thd->db : "unconnected"), - thd->user ? thd->user : "unauthenticated", - thd->host_or_ip, - (net->last_errno ? ER(net->last_errno) : - ER(ER_UNKNOWN_ERROR))); + sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), + thd->thread_id,(thd->db ? thd->db : "unconnected"), + thd->user ? thd->user : "unauthenticated", + thd->host_or_ip, + (net->last_errno ? ER(net->last_errno) : + ER(ER_UNKNOWN_ERROR))); send_error(thd,net->last_errno,NullS); statistic_increment(aborted_threads,&LOCK_status); } @@ -1599,6 +1609,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_CREATE_DB: // QQ: To be removed { char *db=thd->strdup(packet), *alias; + HA_CREATE_INFO create_info; statistic_increment(thd->status_var.com_stat[SQLCOM_CREATE_DB], &LOCK_status); @@ -1611,7 +1622,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (check_access(thd,CREATE_ACL,db,0,1,0)) break; mysql_log.write(thd,command,packet); - mysql_create_db(thd,(lower_case_table_names == 2 ? alias : db),0,0); + bzero(&create_info, sizeof(create_info)); + if (mysql_create_db(thd, (lower_case_table_names == 2 ? alias : db), + &create_info, 0) < 0) + send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0); break; } case COM_DROP_DB: // QQ: To be removed @@ -1633,7 +1647,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } mysql_log.write(thd,command,db); - mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : db), 0, 0); + if (mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : db), + 0, 0) < 0) + send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0); break; } #ifndef EMBEDDED_LIBRARY @@ -1904,12 +1920,12 @@ mysql_execute_command(THD *thd) { int res= 0; LEX *lex= thd->lex; + /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ + SELECT_LEX *select_lex= &lex->select_lex; /* first table of first SELECT_LEX */ - TABLE_LIST *first_table= (TABLE_LIST*) lex->select_lex.table_list.first; + TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first; /* list of all tables in query */ TABLE_LIST *all_tables; - /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ - SELECT_LEX *select_lex= &lex->select_lex; /* most outer SELECT_LEX_UNIT of query */ SELECT_LEX_UNIT *unit= &lex->unit; DBUG_ENTER("mysql_execute_command"); @@ -2084,6 +2100,7 @@ mysql_execute_command(THD *thd) CHARSET_INFO *to_cs= thd->variables.collation_connection; bool need_conversion; user_var_entry *entry; + String *pstr= &str; uint32 unused; /* Convert @var contents to string in connection character set. Although @@ -2096,29 +2113,45 @@ mysql_execute_command(THD *thd) lex->prepared_stmt_code.length)) && entry->value) { - String *pstr; my_bool is_var_null; pstr= 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) - send_error(thd, ER_OUT_OF_RESOURCES); - DBUG_ASSERT(pstr == &str); + { + res= -1; + break; // EOM (error should be reported by allocator) + } } else + { + /* + variable absent or equal to NULL, so we need to set variable to + something reasonable to get readable error message during parsing + */ str.set("NULL", 4, &my_charset_latin1); + } + need_conversion= - String::needs_conversion(str.length(), str.charset(), to_cs, &unused); + String::needs_conversion(pstr->length(), pstr->charset(), + to_cs, &unused); - query_len= need_conversion? (str.length() * to_cs->mbmaxlen) : - str.length(); + query_len= need_conversion? (pstr->length() * to_cs->mbmaxlen) : + pstr->length(); if (!(query_str= alloc_root(&thd->mem_root, query_len+1))) - send_error(thd, ER_OUT_OF_RESOURCES); + { + res= -1; + break; // EOM (error should be reported by allocator) + } if (need_conversion) - query_len= copy_and_convert(query_str, query_len, to_cs, str.ptr(), - str.length(), str.charset()); + query_len= copy_and_convert(query_str, query_len, to_cs, pstr->ptr(), + pstr->length(), pstr->charset()); else - memcpy(query_str, str.ptr(), str.length()); + memcpy(query_str, pstr->ptr(), pstr->length()); query_str[query_len]= 0; } else @@ -2414,9 +2447,6 @@ mysql_execute_command(THD *thd) { select_result *result; - if (select_tables && - check_table_access(thd, SELECT_ACL, select_tables, 0)) - goto create_error; // Error message is given select_lex->options|= SELECT_NO_UNLOCK; unit->set_limit(select_lex, select_lex); @@ -2552,7 +2582,7 @@ unsent_create_error: if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN)) { net_printf(thd, ER_WRONG_TABLE_NAME, lex->name); - res=0; + res= 1; break; } if (!select_lex->db) @@ -2775,12 +2805,12 @@ unsent_create_error: case SQLCOM_INSERT: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - my_bool update= (lex->value_list.elements ? UPDATE_ACL : 0); - if ((res= insert_precheck(thd, all_tables, update))) + if ((res= insert_precheck(thd, all_tables))) break; res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values, select_lex->item_list, lex->value_list, - (update ? DUP_UPDATE : lex->duplicates)); + (lex->value_list.elements ? + DUP_UPDATE : lex->duplicates)); if (thd->net.report_error) res= -1; if (first_table->view && !first_table->contain_auto_increment) @@ -2895,10 +2925,8 @@ unsent_create_error: } thd->proc_info="init"; - if ((res= open_and_lock_tables(thd, all_tables))) - break; - - if ((res= mysql_multi_delete_prepare(thd))) + if ((res= open_and_lock_tables(thd, all_tables)) || + (res= mysql_multi_delete_prepare(thd))) break; if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables, @@ -3377,29 +3405,6 @@ purposes internal to the MySQL server", MYF(0)); first_table ? 0 : 1, 0)) goto error; - /* - Check that the user isn't trying to change a password for another - user if he doesn't have UPDATE privilege to the MySQL database - */ - - if (thd->user) // If not replication - { - LEX_USER *user; - List_iterator <LEX_USER> user_list(lex->users_list); - while ((user=user_list++)) - { - if (user->password.str && - (strcmp(thd->user,user->user.str) || - user->host.str && - my_strcasecmp(&my_charset_latin1, - user->host.str, thd->host_or_ip))) - { - if (check_access(thd, UPDATE_ACL, "mysql",0,1,0)) - goto error; - break; // We are allowed to do changes - } - } - } if (specialflag & SPECIAL_NO_RESOLVE) { LEX_USER *user; @@ -4134,6 +4139,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, thd->priv_user, db, test(want_access & GRANT_ACL)); else db_access=thd->db_access; + DBUG_PRINT("info",("db_access: %lu", db_access)); /* Remove SHOW attribute and access rights we already have */ want_access &= ~(thd->master_access | EXTRA_ACL); DBUG_PRINT("info",("db_access: %lu want_access: %lu", @@ -4206,7 +4212,10 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, TABLE_LIST *org_tables=tables; for (; tables; tables= tables->next_global) { - if (tables->derived || (tables->table && (int)tables->table->tmp_table)) + if (tables->derived || + (tables->table && (int)tables->table->tmp_table) || + my_tz_check_n_skip_implicit_tables(&tables, + thd->lex->time_zone_tables_used)) continue; if ((thd->master_access & want_access) == (want_access & ~EXTRA_ACL) && thd->db) @@ -4411,58 +4420,44 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) ****************************************************************************/ void -mysql_init_query(THD *thd, uchar *buf, uint length, bool lexonly) +mysql_init_query(THD *thd, uchar *buf, uint length) { DBUG_ENTER("mysql_init_query"); - LEX *lex= thd->lex; - lex->unit.init_query(); - lex->unit.init_select(); - lex->unit.thd= thd; - lex->select_lex.init_query(); - lex->value_list.empty(); - lex->param_list.empty(); - lex->view_list.empty(); - lex->unit.next= lex->unit.master= - lex->unit.link_next= lex->unit.return_to=0; - lex->unit.prev= lex->unit.link_prev= 0; - lex->unit.slave= lex->unit.global_parameters= lex->current_select= - lex->all_selects_list= &lex->select_lex; - lex->select_lex.master= &lex->unit; - lex->select_lex.prev= &lex->unit.slave; - lex->select_lex.link_next= lex->select_lex.slave= lex->select_lex.next= 0; - lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list); - lex->select_lex.options=0; - lex->select_lex.init_order(); - lex->select_lex.group_list.empty(); - lex->describe= 0; - lex->derived_tables= 0; - lex->view_prepare_mode= FALSE; - lex->lock_option= TL_READ; - lex->found_colon= 0; - lex->safe_to_cache_query= 1; - lex->time_zone_tables_used= 0; - lex->proc_table= lex->query_tables= 0; - lex->query_tables_last= &lex->query_tables; - lex->variables_used= 0; - lex->select_lex.parent_lex= lex; - lex->empty_field_list_on_rset= 0; lex_start(thd, buf, length); - if (! lexonly) - { - thd->select_number= lex->select_lex.select_number= 1; - thd->free_list= 0; - thd->total_warn_count=0; // Warnings for this query - thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0; - thd->sent_row_count= thd->examined_row_count= 0; - thd->is_fatal_error= thd->rand_used= thd->time_zone_used= 0; - thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | - SERVER_QUERY_NO_INDEX_USED | - SERVER_QUERY_NO_GOOD_INDEX_USED); - thd->tmp_table_used= 0; - if (opt_bin_log) - reset_dynamic(&thd->user_var_events); - thd->clear_error(); - } + mysql_reset_thd_for_next_command(thd); + DBUG_VOID_RETURN; +} + + +/* + Reset THD part responsible for command processing state. + + DESCRIPTION + This needs to be called before execution of every statement + (prepared or conventional). + + TODO + Make it a method of THD and align its name with the rest of + reset/end/start/init methods. + Call it after we use THD for queries, not before. +*/ + +void mysql_reset_thd_for_next_command(THD *thd) +{ + DBUG_ENTER("mysql_reset_thd_for_next_command"); + thd->free_list= 0; + thd->select_number= 1; + thd->total_warn_count=0; // Warnings for this query + thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0; + thd->sent_row_count= thd->examined_row_count= 0; + thd->is_fatal_error= thd->rand_used= thd->time_zone_used= 0; + thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | + SERVER_QUERY_NO_INDEX_USED | + SERVER_QUERY_NO_GOOD_INDEX_USED); + thd->tmp_table_used= 0; + if (opt_bin_log) + reset_dynamic(&thd->user_var_events); + thd->clear_error(); DBUG_VOID_RETURN; } @@ -4647,6 +4642,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length) thd->proc_info="freeing items"; thd->end_statement(); thd->cleanup_after_query(); + DBUG_ASSERT(thd->change_list.is_empty()); } DBUG_VOID_RETURN; } @@ -4679,6 +4675,32 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) #endif + +/* + Calculate interval lengths. + Strip trailing spaces from all strings. + After this function call: + - ENUM uses max_length + - SET uses tot_length. +*/ +void calculate_interval_lengths(THD *thd, TYPELIB *interval, + uint *max_length, uint *tot_length) +{ + const char **pos; + uint *len; + CHARSET_INFO *cs= thd->variables.character_set_client; + *max_length= *tot_length= 0; + for (pos= interval->type_names, len= interval->type_lengths; + *pos ; pos++, len++) + { + *len= (uint) strip_sp((char*) *pos); + uint length= cs->cset->numchars(cs, *pos, *pos + *len); + *tot_length+= length; + set_if_bigger(*max_length, length); + } +} + + /***************************************************************************** ** Store field definition for create ** Return 0 if ok @@ -4737,7 +4759,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, } else if (default_value->type() == Item::NULL_ITEM) { - default_value=0; + default_value= 0; if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) == NOT_NULL_FLAG) { @@ -4808,23 +4830,23 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, switch (type) { case FIELD_TYPE_TINY: - if (!length) new_field->length=3+sign_len; + if (!length) new_field->length=MAX_TINYINT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case FIELD_TYPE_SHORT: - if (!length) new_field->length=5+sign_len; + if (!length) new_field->length=MAX_SMALLINT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case FIELD_TYPE_INT24: - if (!length) new_field->length=8+sign_len; + if (!length) new_field->length=MAX_MEDIUMINT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case FIELD_TYPE_LONG: - if (!length) new_field->length=10+sign_len; + if (!length) new_field->length=MAX_INT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case FIELD_TYPE_LONGLONG: - if (!length) new_field->length=20; + if (!length) new_field->length=MAX_BIGINT_WIDTH; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case FIELD_TYPE_NULL: @@ -4939,7 +4961,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, new_field->length=((new_field->length+1)/2)*2; /* purecov: inspected */ new_field->length= min(new_field->length,14); /* purecov: inspected */ } - new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG | NOT_NULL_FLAG; + new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG; if (default_value) { /* Grammar allows only NOW() value for ON UPDATE clause */ @@ -4960,13 +4982,24 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, } else { - /* - We are setting TIMESTAMP_OLD_FIELD here only temporary, we will - replace this value by TIMESTAMP_DNUN_FIELD or NONE later when - information about all TIMESTAMP fields in table will be available. + /* + If we have default TIMESTAMP NOT NULL column without explicit DEFAULT + or ON UPDATE values then for the sake of compatiblity we should treat + this column as having DEFAULT NOW() ON UPDATE NOW() (when we don't + have another TIMESTAMP column with auto-set option before this one) + or DEFAULT 0 (in other cases). + So here we are setting TIMESTAMP_OLD_FIELD only temporary, and will + replace this value by TIMESTAMP_DNUN_FIELD or NONE later when + information about all TIMESTAMP fields in table will be availiable. + + If we have TIMESTAMP NULL column without explicit DEFAULT value + we treat it as having DEFAULT NULL attribute. */ - new_field->unireg_check= on_update_value?Field::TIMESTAMP_UN_FIELD: - Field::TIMESTAMP_OLD_FIELD; + new_field->unireg_check= (on_update_value ? + Field::TIMESTAMP_UN_FIELD : + (new_field->flags & NOT_NULL_FLAG ? + Field::TIMESTAMP_OLD_FIELD: + Field::NONE)); } break; case FIELD_TYPE_DATE: // Old date type @@ -4993,15 +5026,10 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, if (new_field->pack_length > 4) new_field->pack_length=8; new_field->interval=interval; - new_field->length=0; - for (const char **pos=interval->type_names; *pos ; pos++) - { - uint length= (uint) strip_sp((char*) *pos)+1; - CHARSET_INFO *cs= thd->variables.character_set_client; - length= cs->cset->numchars(cs, *pos, *pos+length); - new_field->length+= length; - } - new_field->length--; + uint dummy_max_length; + calculate_interval_lengths(thd, interval, + &dummy_max_length, &new_field->length); + new_field->length+= (interval->count - 1); set_if_smaller(new_field->length,MAX_FIELD_WIDTH-1); if (default_value) { @@ -5012,8 +5040,9 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, thd->cuted_fields=0; String str,*res; res=default_value->val_str(&str); - (void) find_set(interval, res->ptr(), res->length(), ¬_used, - ¬_used2, ¬_used3); + (void) find_set(interval, res->ptr(), res->length(), + &my_charset_bin, + ¬_used, ¬_used2, ¬_used3); if (thd->cuted_fields) { net_printf(thd,ER_INVALID_DEFAULT,field_name); @@ -5026,14 +5055,10 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, { new_field->interval=interval; new_field->pack_length=interval->count < 256 ? 1 : 2; // Should be safe - new_field->length=(uint) strip_sp((char*) interval->type_names[0]); - for (const char **pos=interval->type_names+1; *pos ; pos++) - { - uint length=(uint) strip_sp((char*) *pos); - CHARSET_INFO *cs= thd->variables.character_set_client; - length= cs->cset->numchars(cs, *pos, *pos+length); - set_if_bigger(new_field->length,length); - } + + uint dummy_tot_length; + calculate_interval_lengths(thd, interval, + &new_field->length, &dummy_tot_length); set_if_smaller(new_field->length,MAX_FIELD_WIDTH-1); if (default_value) { @@ -5225,6 +5250,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->db= empty_c_string; ptr->db_length= 0; } + if (thd->current_arena->is_stmt_prepare()) + ptr->db= thd->strdup(ptr->db); ptr->alias= alias_str; if (lower_case_table_names && table->table.length) @@ -5601,7 +5628,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, acl_reload(thd); grant_reload(thd); if (mqh_used) - reset_mqh(thd,(LEX_USER *) NULL,true); + reset_mqh(thd,(LEX_USER *) NULL,TRUE); } #endif if (options & REFRESH_LOG) @@ -5925,7 +5952,6 @@ int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys) List<create_field> fields; ALTER_INFO alter_info; alter_info.flags= ALTER_ADD_INDEX; - alter_info.is_simple= 0; HA_CREATE_INFO create_info; DBUG_ENTER("mysql_create_index"); bzero((char*) &create_info,sizeof(create_info)); @@ -5949,7 +5975,6 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info) create_info.default_table_charset= thd->variables.collation_database; alter_info->clear(); alter_info->flags= ALTER_DROP_INDEX; - alter_info->is_simple= 0; DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name, &create_info, table_list, fields, keys, 0, (ORDER*)0, @@ -5990,12 +6015,15 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables) */ for (table= tables; table; table= table->next_local) { - if ((check_access(thd, UPDATE_ACL, table->db, - &table->grant.privilege, 0, 1) || - grant_option && check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) && - (check_access(thd, SELECT_ACL, table->db, - &table->grant.privilege, 0, 0) || - grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))) + if (table->derived) + table->grant.privilege= SELECT_ACL; + else if ((check_access(thd, UPDATE_ACL, table->db, + &table->grant.privilege, 0, 1) || + grant_option && + check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) && + (check_access(thd, SELECT_ACL, table->db, + &table->grant.privilege, 0, 0) || + grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))) DBUG_RETURN(1); table->table_in_first_from_clause= 1; @@ -6005,9 +6033,10 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables) */ if (&lex->select_lex != lex->all_selects_list) { + DBUG_PRINT("info",("Checking sub query list")); for (table= tables; table; table= table->next_global) { - if (!table->table_in_first_from_clause) + if (!table->table_in_first_from_clause && table->derived) { if (check_access(thd, SELECT_ACL, table->db, &table->grant.privilege, 0, 0) || @@ -6094,7 +6123,7 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) INSERT ... SELECT query pre-check SYNOPSIS - multi_delete_precheck() + insert_delete_precheck() thd Thread handler tables Global table list @@ -6183,13 +6212,14 @@ int delete_precheck(THD *thd, TABLE_LIST *tables) -1 error (message is not sent to user) */ -int insert_precheck(THD *thd, TABLE_LIST *tables, bool update) +int insert_precheck(THD *thd, TABLE_LIST *tables) { LEX *lex= thd->lex; DBUG_ENTER("insert_precheck"); - ulong privilege= (lex->duplicates == DUP_REPLACE ? - INSERT_ACL | DELETE_ACL : INSERT_ACL | update); + ulong privilege= (INSERT_ACL | + (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) | + (lex->value_list.elements ? UPDATE_ACL : 0)); if (check_one_table_access(thd, privilege, tables)) DBUG_RETURN(1); @@ -6215,26 +6245,60 @@ int insert_precheck(THD *thd, TABLE_LIST *tables, bool update) RETURN VALUE 0 OK 1 Error (message is sent to user) - -1 Error (message is not sent to user) */ int create_table_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *create_table) { LEX *lex= thd->lex; + SELECT_LEX *select_lex= &lex->select_lex; + ulong want_priv; + int error= 1; // Error message is given DBUG_ENTER("create_table_precheck"); - ulong want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ? - CREATE_TMP_ACL : CREATE_ACL); + + want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ? + CREATE_TMP_ACL : CREATE_ACL); lex->create_info.alias= create_table->alias; if (check_access(thd, want_priv, create_table->db, &create_table->grant.privilege, 0, 0) || check_merge_table_access(thd, create_table->db, (TABLE_LIST *) lex->create_info.merge_list.first)) - DBUG_RETURN(1); - DBUG_RETURN((grant_option && want_priv != CREATE_TMP_ACL && - check_grant(thd, want_priv, create_table, 0, UINT_MAX, 0)) ? - 1 : 0); + goto err; + if (grant_option && want_priv != CREATE_TMP_ACL && + check_grant(thd, want_priv, create_table, 0, UINT_MAX, 0)) + goto err; + + if (select_lex->item_list.elements) + { + /* Check permissions for used tables in CREATE TABLE ... SELECT */ + + /* + Only do the check for PS, becasue we on execute we have to check that + against the opened tables to ensure we don't use a table that is part + of the view (which can only be done after the table has been opened). + */ + if (thd->current_arena->is_stmt_prepare()) + { + /* + For temporary tables we don't have to check if the created table exists + */ + if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) && + find_table_in_global_list(tables, create_table->db, + create_table->real_name)) + { + net_printf(thd,ER_UPDATE_TABLE_USED, create_table->real_name); + + goto err; + } + } + if (tables && check_table_access(thd, SELECT_ACL, tables,0)) + goto err; + } + error= 0; + +err: + DBUG_RETURN(error); } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 982e00391ef..bb2362dd562 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -156,6 +156,8 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns) { NET *net= &stmt->thd->net; char buff[9]; + DBUG_ENTER("send_prep_stmt"); + buff[0]= 0; /* OK packet indicator */ int4store(buff+1, stmt->id); int2store(buff+5, columns); @@ -164,13 +166,12 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns) Send types and names of placeholders to the client XXX: fix this nasty upcast from List<Item_param> to List<Item> */ - return my_net_write(net, buff, sizeof(buff)) || - (stmt->param_count && - stmt->thd->protocol_simple.send_fields((List<Item> *) - &stmt->lex->param_list, - Protocol::SEND_EOF)) || - net_flush(net); - return 0; + DBUG_RETURN(my_net_write(net, buff, sizeof(buff)) || + (stmt->param_count && + stmt->thd->protocol_simple.send_fields((List<Item> *) + &stmt->lex->param_list, + Protocol::SEND_EOF)) || + net_flush(net)); } #else static bool send_prep_stmt(Prepared_statement *stmt, @@ -899,10 +900,9 @@ static int mysql_test_insert(Prepared_statement *stmt, List_iterator_fast<List_item> its(values_list); List_item *values; int res; - my_bool update= (lex->value_list.elements ? UPDATE_ACL : 0); DBUG_ENTER("mysql_test_insert"); - if ((res= insert_precheck(thd, table_list, update))) + if ((res= insert_precheck(thd, table_list))) DBUG_RETURN(res); /* @@ -1071,6 +1071,12 @@ static int mysql_test_select(Prepared_statement *stmt, DBUG_RETURN(1); #endif + if (!lex->result && !(lex->result= new (&stmt->mem_root) select_send)) + { + send_error(thd); + goto err; + } + if ((result= open_and_lock_tables(thd, tables))) { result= 1; // Error sent @@ -1096,9 +1102,20 @@ static int mysql_test_select(Prepared_statement *stmt, } else { - if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) || - thd->protocol_simple.send_fields(&lex->select_lex.item_list, - Protocol::SEND_EOF) + /* Make copy of item list, as change_columns may change it */ + List<Item> fields(lex->select_lex.item_list); + + /* Change columns if a procedure like analyse() */ + if (unit->last_procedure && + unit->last_procedure->change_columns(fields)) + goto err_prep; + + /* + We can use lex->result as it should've been + prepared in unit->prepare call above. + */ + if (send_prep_stmt(stmt, lex->result->field_count(fields)) || + lex->result->send_fields(fields, Protocol::SEND_EOF) #ifndef EMBEDDED_LIBRARY || net_flush(&thd->net) #endif @@ -1398,8 +1415,7 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol) res= mysql_test_insert(stmt, tables, lex->field_list, lex->many_values, select_lex->item_list, lex->value_list, - (lex->value_list.elements ? - DUP_UPDATE : lex->duplicates)); + lex->duplicates); break; case SQLCOM_UPDATE: @@ -1491,8 +1507,16 @@ error: static bool init_param_array(Prepared_statement *stmt) { LEX *lex= stmt->lex; + THD *thd= stmt->thd; if ((stmt->param_count= lex->param_list.elements)) { + if (stmt->param_count > (uint) UINT_MAX16) + { + /* Error code to be defined in 5.0 */ + send_error(thd, ER_UNKNOWN_ERROR, + "Prepared statement contains too many placeholders."); + return 1; + } Item_param **to; List_iterator<Item_param> param_iterator(lex->param_list); /* Use thd->mem_root as it points at statement mem_root */ @@ -1501,7 +1525,7 @@ static bool init_param_array(Prepared_statement *stmt) sizeof(Item_param*) * stmt->param_count); if (!stmt->param_array) { - send_error(stmt->thd, ER_OUT_OF_RESOURCES); + send_error(thd, ER_OUT_OF_RESOURCES); return 1; } for (to= stmt->param_array; @@ -1596,11 +1620,13 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, thd->current_arena= stmt; mysql_init_query(thd, (uchar *) thd->query, thd->query_length); + /* Reset warnings from previous command */ + mysql_reset_errors(thd); lex= thd->lex; lex->safe_to_cache_query= 0; error= yyparse((void *)thd) || thd->is_fatal_error || - init_param_array(stmt); + thd->net.report_error || init_param_array(stmt); /* While doing context analysis of the query (in send_prepare_results) we allocate a lot of additional memory: for open tables, JOINs, derived @@ -1628,6 +1654,7 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, thd->restore_backup_statement(stmt, &thd->stmt_backup); cleanup_items(stmt->free_list); close_thread_tables(thd); + thd->rollback_item_tree_changes(); thd->cleanup_after_query(); thd->current_arena= thd; @@ -1636,7 +1663,9 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, /* Statement map deletes statement on erase */ thd->stmt_map.erase(stmt); stmt= NULL; - /* error is sent inside yyparse/send_prepare_results */ + if (thd->net.report_error) + send_error(thd); + /* otherwise the error is sent inside yyparse/send_prepare_results */ } else { @@ -1653,11 +1682,13 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, DBUG_RETURN(!stmt); } + /* Reinit statement before execution */ void reset_stmt_for_execute(THD *thd, LEX *lex) { SELECT_LEX *sl= lex->all_selects_list; + DBUG_ENTER("reset_stmt_for_execute"); if (lex->empty_field_list_on_rset) { @@ -1720,6 +1751,8 @@ void reset_stmt_for_execute(THD *thd, LEX *lex) lex->current_select= &lex->select_lex; if (lex->result) lex->result->cleanup(); + + DBUG_VOID_RETURN; } @@ -1781,10 +1814,13 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) DBUG_VOID_RETURN; } + DBUG_ASSERT(thd->free_list == NULL); + mysql_reset_thd_for_next_command(thd); if (flags & (ulong) CURSOR_TYPE_READ_ONLY) { - if (stmt->lex->result) + if (!stmt->lex->result || !stmt->lex->result->simple_select()) { + DBUG_PRINT("info",("Cursor asked for not SELECT stmt")); /* If lex->result is set in the parser, this is not a SELECT statement: we can't open a cursor for it. @@ -1793,6 +1829,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) } else { + DBUG_PRINT("info",("Using READ_ONLY cursor")); if (!stmt->cursor && !(stmt->cursor= new (&stmt->mem_root) Cursor())) { @@ -1821,7 +1858,6 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) if (stmt->param_count && stmt->set_params_data(stmt, &expanded_query)) goto set_params_data_err; #endif - DBUG_ASSERT(thd->free_list == NULL); thd->stmt_backup.set_statement(thd); thd->set_statement(stmt); thd->current_arena= stmt; @@ -1855,6 +1891,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) cleanup_items(stmt->free_list); reset_stmt_params(stmt); close_thread_tables(thd); /* to close derived tables */ + thd->rollback_item_tree_changes(); thd->cleanup_after_query(); } @@ -1903,6 +1940,8 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name) DBUG_VOID_RETURN; } + /* Must go before setting variables, as it clears thd->user_var_events */ + mysql_reset_thd_for_next_command(thd); thd->set_n_backup_statement(stmt, &thd->stmt_backup); thd->set_statement(stmt); if (stmt->set_params_from_vars(stmt, @@ -1949,8 +1988,7 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt, transformations of the query tree (i.e. negations elimination). This should be done permanently on the parse tree of this statement. */ - if (stmt->state == Item_arena::PREPARED) - thd->current_arena= stmt; + thd->current_arena= stmt; if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),QUERY_PRIOR); @@ -1959,7 +1997,9 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt, my_pthread_setprio(pthread_self(), WAIT_PRIOR); thd->lex->unit.cleanup(); + thd->current_arena= thd; cleanup_items(stmt->free_list); + thd->rollback_item_tree_changes(); reset_stmt_params(stmt); close_thread_tables(thd); // to close derived tables thd->set_statement(&thd->stmt_backup); @@ -2062,6 +2102,7 @@ void mysql_stmt_reset(THD *thd, char *packet) */ reset_stmt_params(stmt); + mysql_reset_thd_for_next_command(thd); send_ok(thd); DBUG_VOID_RETURN; diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 9e38a65d412..10f0c23f54d 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -14,8 +14,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -// Sasha Pachev <sasha@mysql.com> is currently in charge of this file - #include "mysql_priv.h" #ifdef HAVE_REPLICATION @@ -311,7 +309,7 @@ int purge_master_logs(THD* thd, const char* to_log) char search_file_name[FN_REFLEN]; if (!mysql_bin_log.is_open()) { - send_ok(current_thd); + send_ok(thd); return 0; } @@ -324,8 +322,13 @@ int purge_master_logs(THD* thd, const char* to_log) int purge_master_logs_before_date(THD* thd, time_t purge_time) { - int res = mysql_bin_log.purge_logs_before_date(purge_time); - return purge_error_message(thd ,res); + if (!mysql_bin_log.is_open()) + { + send_ok(thd); + return 0; + } + return purge_error_message(thd, + mysql_bin_log.purge_logs_before_date(purge_time)); } int test_for_non_eof_log_read_errors(int error, const char **errmsg) diff --git a/sql/sql_repl.h b/sql/sql_repl.h index c39ef90114d..66fdfb4c022 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -1,3 +1,19 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB & Sasha + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifdef HAVE_REPLICATION #include "slave.h" diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 9ba191a3f3a..2e1429feb3e 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -91,7 +91,7 @@ static int return_zero_rows(JOIN *join, select_result *res,TABLE_LIST *tables, uint select_options, const char *info, Item *having, Procedure *proc, SELECT_LEX_UNIT *unit); -static COND *build_equal_items(COND *cond, +static COND *build_equal_items(THD *thd, COND *cond, COND_EQUAL *inherited, List<TABLE_LIST> *join_list, COND_EQUAL **cond_equal_ref); @@ -101,6 +101,7 @@ static COND* substitute_for_best_equal_field(COND *cond, static COND *simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top); 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, @@ -228,16 +229,11 @@ int handle_select(THD *thd, LEX *lex, select_result *result) select_lex->options | thd->options, result, unit, select_lex); } - - /* Don't set res if it's -1 as we may want this later */ DBUG_PRINT("info",("res: %d report_error: %d", res, thd->net.report_error)); - if (thd->net.report_error) - res= 1; - if (unlikely(res)) + if (thd->net.report_error || res < 0) { - if (res > 0) - result->send_error(0, NullS); + result->send_error(0, NullS); result->abort(); res= 1; // Error sent to client } @@ -333,7 +329,7 @@ JOIN::prepare(Item ***rref_pointer_array, if (having_fix_rc || thd->net.report_error) DBUG_RETURN(-1); /* purecov: inspected */ if (having->with_sum_func) - having->split_sum_func(ref_pointer_array, all_fields); + having->split_sum_func(thd, ref_pointer_array, all_fields); } if (!thd->lex->view_prepare_mode) @@ -558,17 +554,7 @@ JOIN::optimize() thd->restore_backup_item_arena(arena, &backup); } - /* - Build all multiple equality predicates and eliminate equality - predicates that can be inferred from these multiple equalities. - For each reference of a field included into a multiple equality - that occurs in a function set a pointer to the multiple equality - predicate. Substitute a constant instead of this field if the - multiple equality contains a constant. - */ - conds= build_equal_items(conds, NULL, join_list, &cond_equal); - - conds= optimize_cond(this, conds,&cond_value); + conds= optimize_cond(this, conds, join_list, &cond_value); if (thd->net.report_error) { error= 1; @@ -686,6 +672,7 @@ JOIN::optimize() { conds= substitute_for_best_equal_field(conds, cond_equal, map2table); conds->update_used_tables(); + DBUG_EXECUTE("where", print_where(conds, "after substitute_best_equal");); } /* Permorm the the optimization on fields evaluation mentioned above @@ -1725,11 +1712,10 @@ int Cursor::open(JOIN *join_arg) { join= join_arg; - THD *thd= join->thd; - /* First non-constant table */ JOIN_TAB *join_tab= join->join_tab + join->const_tables; + DBUG_ENTER("Cursor::open"); /* Send fields description to the client; server_status is sent @@ -1751,7 +1737,9 @@ Cursor::open(JOIN *join_arg) join->fetch_limit= join->unit->offset_limit_cnt; /* Disable JOIN CACHE as it is not working with cursors yet */ - for (JOIN_TAB *tab= join_tab; tab != join->join_tab + join->tables - 1; ++tab) + for (JOIN_TAB *tab= join_tab; + tab != join->join_tab + join->tables - 1; + tab++) { if (tab->next_select == sub_select_cache) tab->next_select= sub_select; @@ -1765,7 +1753,7 @@ Cursor::open(JOIN *join_arg) */ DBUG_ASSERT(join_tab->table->null_row == 0); - return join_tab->read_first_record(join_tab); + DBUG_RETURN(join_tab->read_first_record(join_tab)); } @@ -6030,7 +6018,7 @@ template class List_iterator<Item_func_match>; find_item_equal() cond_equal multiple equalities to search in field field to look for - inherited_fl :out set up to TRUE iff multiple equality is found + inherited_fl :out set up to TRUE if multiple equality is found on upper levels (not on current level of cond_equal) DESCRIPTION @@ -6448,12 +6436,14 @@ static COND *build_equal_items_for_cond(COND *cond, return cond; } + /* Build multiple equalities for a condition and all on expressions that inherit these multiple equalities SYNOPSIS build_equal_items() + thd Thread handler cond condition to build the multiple equalities for inherited path to all inherited multiple equality items join_list list of join tables to which the condition refers to @@ -6505,7 +6495,7 @@ static COND *build_equal_items_for_cond(COND *cond, pointer to the transformed condition containing multiple equalities */ -static COND *build_equal_items(COND *cond, +static COND *build_equal_items(THD *thd, COND *cond, COND_EQUAL *inherited, List<TABLE_LIST> *join_list, COND_EQUAL **cond_equal_ref) @@ -6542,12 +6532,13 @@ static COND *build_equal_items(COND *cond, { if (table->on_expr) { + Item *expr; List<TABLE_LIST> *join_list= table->nested_join ? &table->nested_join->join_list : NULL; - table->on_expr= build_equal_items(table->on_expr, - inherited, - join_list, - &table->cond_equal); + expr= build_equal_items(thd, table->on_expr, inherited, join_list, + &table->cond_equal); + if (expr != table->on_expr) + thd->change_item_tree(&table->on_expr, expr); } } } @@ -6555,6 +6546,7 @@ static COND *build_equal_items(COND *cond, return cond; } + /* Compare field items by table order in the execution plan @@ -6799,14 +6791,16 @@ static COND* substitute_for_best_equal_field(COND *cond, return cond; } + /* change field = field to field = const for each found field = const in the and_level */ static void -change_cond_ref_to_const(I_List<COND_CMP> *save_list,Item *and_father, - Item *cond, Item *field, Item *value) +change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list, + Item *and_father, Item *cond, + Item *field, Item *value) { if (cond->type() == Item::COND_ITEM) { @@ -6815,7 +6809,7 @@ change_cond_ref_to_const(I_List<COND_CMP> *save_list,Item *and_father, List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); Item *item; while ((item=li++)) - change_cond_ref_to_const(save_list,and_level ? cond : item, item, + change_cond_ref_to_const(thd, save_list,and_level ? cond : item, item, field, value); return; } @@ -6823,8 +6817,9 @@ change_cond_ref_to_const(I_List<COND_CMP> *save_list,Item *and_father, return; // Not a boolean function Item_bool_func2 *func= (Item_bool_func2*) cond; - Item *left_item= func->arguments()[0]; - Item *right_item= func->arguments()[1]; + Item **args= func->arguments(); + Item *left_item= args[0]; + Item *right_item= args[1]; Item_func::Functype functype= func->functype(); if (right_item->eq(field,0) && left_item != value && @@ -6835,7 +6830,7 @@ change_cond_ref_to_const(I_List<COND_CMP> *save_list,Item *and_father, Item *tmp=value->new_item(); if (tmp) { - func->arguments()[1] = tmp; + thd->change_item_tree(args + 1, tmp); func->update_used_tables(); if ((functype == Item_func::EQ_FUNC || functype == Item_func::EQUAL_FUNC) && and_father != cond && !left_item->const_item()) @@ -6856,13 +6851,14 @@ change_cond_ref_to_const(I_List<COND_CMP> *save_list,Item *and_father, Item *tmp=value->new_item(); if (tmp) { - func->arguments()[0] = value = tmp; + thd->change_item_tree(args, tmp); + value= tmp; func->update_used_tables(); if ((functype == Item_func::EQ_FUNC || functype == Item_func::EQUAL_FUNC) && and_father != cond && !right_item->const_item()) { - func->arguments()[0] = func->arguments()[1]; // For easy check - func->arguments()[1] = value; + args[0]= args[1]; // For easy check + thd->change_item_tree(args + 1, value); cond->marker=1; COND_CMP *tmp2; if ((tmp2=new COND_CMP(and_father,func))) @@ -6908,8 +6904,8 @@ static Item *remove_additional_cond(Item* conds) } static void -propagate_cond_constants(I_List<COND_CMP> *save_list,COND *and_father, - COND *cond) +propagate_cond_constants(THD *thd, I_List<COND_CMP> *save_list, + COND *and_father, COND *cond) { if (cond->type() == Item::COND_ITEM) { @@ -6920,18 +6916,19 @@ propagate_cond_constants(I_List<COND_CMP> *save_list,COND *and_father, I_List<COND_CMP> save; while ((item=li++)) { - propagate_cond_constants(&save,and_level ? cond : item, item); + propagate_cond_constants(thd, &save,and_level ? cond : item, item); } if (and_level) { // Handle other found items I_List_iterator<COND_CMP> cond_itr(save); COND_CMP *cond_cmp; while ((cond_cmp=cond_itr++)) - if (!cond_cmp->cmp_func->arguments()[0]->const_item()) - change_cond_ref_to_const(&save,cond_cmp->and_level, - cond_cmp->and_level, - cond_cmp->cmp_func->arguments()[0], - cond_cmp->cmp_func->arguments()[1]); + { + Item **args= cond_cmp->cmp_func->arguments(); + if (!args[0]->const_item()) + change_cond_ref_to_const(thd, &save,cond_cmp->and_level, + cond_cmp->and_level, args[0], args[1]); + } } } else if (and_father != cond && !cond->marker) // In a AND group @@ -6941,29 +6938,25 @@ propagate_cond_constants(I_List<COND_CMP> *save_list,COND *and_father, ((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC)) { Item_func_eq *func=(Item_func_eq*) cond; - bool left_const= func->arguments()[0]->const_item(); - bool right_const=func->arguments()[1]->const_item(); + Item **args= func->arguments(); + bool left_const= args[0]->const_item(); + bool right_const= args[1]->const_item(); if (!(left_const && right_const) && - (func->arguments()[0]->result_type() == - (func->arguments()[1]->result_type()))) + args[0]->result_type() == args[1]->result_type()) { if (right_const) { - func->arguments()[1]=resolve_const_item(func->arguments()[1], - func->arguments()[0]); + resolve_const_item(thd, &args[1], args[0]); func->update_used_tables(); - change_cond_ref_to_const(save_list,and_father,and_father, - func->arguments()[0], - func->arguments()[1]); + change_cond_ref_to_const(thd, save_list, and_father, and_father, + args[0], args[1]); } else if (left_const) { - func->arguments()[0]=resolve_const_item(func->arguments()[0], - func->arguments()[1]); + resolve_const_item(thd, &args[0], args[1]); func->update_used_tables(); - change_cond_ref_to_const(save_list,and_father,and_father, - func->arguments()[1], - func->arguments()[0]); + change_cond_ref_to_const(thd, save_list, and_father, and_father, + args[1], args[0]); } } } @@ -7091,6 +7084,7 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top) */ if (table->on_expr) { + Item *expr; /* If an on expression E is attached to the table, check all null rejected predicates in this expression. @@ -7099,8 +7093,9 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top) the outer join is converted to an inner join and the corresponding on expression is added to E. */ - table->on_expr= simplify_joins(join, &nested_join->join_list, - table->on_expr, FALSE); + expr= simplify_joins(join, &nested_join->join_list, + table->on_expr, FALSE); + table->on_expr= expr; } nested_join->used_tables= (table_map) 0; nested_join->not_null_tables=(table_map) 0; @@ -7209,8 +7204,10 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top) DBUG_RETURN(conds); } + static COND * -optimize_cond(JOIN *join, COND *conds, Item::cond_result *cond_value) +optimize_cond(JOIN *join, COND *conds, List<TABLE_LIST> *join_list, + Item::cond_result *cond_value) { THD *thd= join->thd; SELECT_LEX *select= thd->lex->current_select; @@ -7223,9 +7220,21 @@ optimize_cond(JOIN *join, COND *conds, Item::cond_result *cond_value) } else { + /* + Build all multiple equality predicates and eliminate equality + predicates that can be inferred from these multiple equalities. + For each reference of a field included into a multiple equality + that occurs in a function set a pointer to the multiple equality + predicate. Substitute a constant instead of this field if the + multiple equality contains a constant. + */ DBUG_EXECUTE("where", print_where(conds, "original");); + conds= build_equal_items(join->thd, conds, NULL, join_list, + &join->cond_equal); + DBUG_EXECUTE("where",print_where(conds,"after equal_items");); + /* change field = field to field = const for each found field = const */ - propagate_cond_constants((I_List<COND_CMP> *) 0,conds,conds); + propagate_cond_constants(thd, (I_List<COND_CMP> *) 0, conds, conds); /* Remove all instances of item == item Remove all and-levels where CONST item != CONST item @@ -7461,21 +7470,28 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item) the record in the original table. If modify_item is 0 then fill_record() will update the temporary table + convert_blob_length If >0 create a varstring(convert_blob_length) field + instead of blob. RETURN 0 on error new_created field */ -static Field* create_tmp_field_from_field(THD *thd, - Field* org_field, - Item *item, - TABLE *table, - bool modify_item) + +static Field* create_tmp_field_from_field(THD *thd, Field* org_field, + Item *item, TABLE *table, + bool modify_item, + uint convert_blob_length) { Field *new_field; - // The following should always be true - if ((new_field= org_field->new_field(&thd->mem_root,table))) + if (convert_blob_length && org_field->flags & BLOB_FLAG) + new_field= new Field_varstring(convert_blob_length, org_field->maybe_null(), + org_field->field_name, table, + org_field->charset()); + else + new_field= org_field->new_field(&thd->mem_root, table); + if (new_field) { if (modify_item) ((Item_field *)item)->result_field= new_field; @@ -7506,16 +7522,16 @@ static Field* create_tmp_field_from_field(THD *thd, the record in the original table. If modify_item is 0 then fill_record() will update the temporary table + convert_blob_length If >0 create a varstring(convert_blob_length) field + instead of blob. RETURN 0 on error new_created field */ -static Field* create_tmp_field_from_item(THD *thd, - Item *item, - TABLE *table, - Item ***copy_func, - bool modify_item) +static Field* create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, + Item ***copy_func, bool modify_item, + uint convert_blob_length) { bool maybe_null=item->maybe_null; Field *new_field; @@ -7532,13 +7548,18 @@ static Field* create_tmp_field_from_item(THD *thd, break; case STRING_RESULT: if (item->max_length > 255) - new_field= new Field_blob(item->max_length, maybe_null, - item->name, table, - item->collation.collation); + { + if (convert_blob_length) + new_field= new Field_varstring(convert_blob_length, maybe_null, + item->name, table, + item->collation.collation); + else + new_field= new Field_blob(item->max_length, maybe_null, item->name, + table, item->collation.collation); + } else - new_field= new Field_string(item->max_length, maybe_null, - item->name, table, - item->collation.collation); + new_field= new Field_string(item->max_length, maybe_null, item->name, + table, item->collation.collation); break; case ROW_RESULT: default: @@ -7575,6 +7596,8 @@ static Field* create_tmp_field_from_item(THD *thd, the record in the original table. If modify_item is 0 then fill_record() will update the temporary table + convert_blob_length If >0 create a varstring(convert_blob_length) field + instead of blob. RETURN 0 on error @@ -7582,8 +7605,8 @@ static Field* create_tmp_field_from_item(THD *thd, */ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, - Item ***copy_func, Field **from_field, - bool group, bool modify_item) + Item ***copy_func, Field **from_field, + bool group, bool modify_item, uint convert_blob_length) { switch (type) { case Item::SUM_FUNC_ITEM: @@ -7618,8 +7641,15 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, item->name,table,item->unsigned_flag); case STRING_RESULT: if (item_sum->max_length > 255) - return new Field_blob(item_sum->max_length,maybe_null, - item->name,table,item->collation.collation); + { + if (convert_blob_length) + return new Field_varstring(convert_blob_length, maybe_null, + item->name, table, + item->collation.collation); + else + return new Field_blob(item_sum->max_length, maybe_null, item->name, + table, item->collation.collation); + } return new Field_string(item_sum->max_length,maybe_null, item->name,table,item->collation.collation); case ROW_RESULT: @@ -7636,8 +7666,8 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, case Item::DEFAULT_VALUE_ITEM: { Item_field *field= (Item_field*) item; - return create_tmp_field_from_field(thd, (*from_field= field->field), - item, table, modify_item); + return create_tmp_field_from_field(thd, (*from_field= field->field), item, + table, modify_item, convert_blob_length); } case Item::FUNC_ITEM: case Item::COND_ITEM: @@ -7652,14 +7682,16 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, case Item::REF_ITEM: case Item::NULL_ITEM: case Item::VARBIN_ITEM: - return create_tmp_field_from_item(thd, item, table, - copy_func, modify_item); + return create_tmp_field_from_item(thd, item, table, copy_func, modify_item, + convert_blob_length); case Item::TYPE_HOLDER: { Field *example= ((Item_type_holder *)item)->example(); if (example) - return create_tmp_field_from_field(thd, example, item, table, 0); - return create_tmp_field_from_item(thd, item, table, copy_func, 0); + return create_tmp_field_from_field(thd, example, item, table, 0, + convert_blob_length); + return create_tmp_field_from_item(thd, item, table, copy_func, 0, + convert_blob_length); } default: // Dosen't have to be stored return 0; @@ -7818,12 +7850,14 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, ((Item_sum*) item)->result_field=0; for (i=0 ; i < ((Item_sum*) item)->arg_count ; i++) { - Item *arg= ((Item_sum*) item)->args[i]; + Item **argp= ((Item_sum*) item)->args + i; + Item *arg= *argp; if (!arg->const_item()) { Field *new_field= - create_tmp_field(thd, table,arg,arg->type(),©_func, - tmp_from_field, group != 0,not_all_columns); + create_tmp_field(thd, table, arg, arg->type(), ©_func, + tmp_from_field, group != 0,not_all_columns, + param->convert_blob_length); if (!new_field) goto err; // Should be OOM tmp_from_field++; @@ -7834,7 +7868,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, *blob_field++= new_field; blob_count++; } - ((Item_sum*) item)->args[i]= new Item_field(new_field); + thd->change_item_tree(argp, new Item_field(new_field)); if (!(new_field->flags & NOT_NULL_FLAG)) { null_count++; @@ -7842,7 +7876,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, new_field->maybe_null() is still false, it will be changed below. But we have to setup Item_field correctly */ - ((Item_sum*) item)->args[i]->maybe_null=1; + (*argp)->maybe_null=1; } } } @@ -7859,9 +7893,10 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, We here distinguish between UNION and multi-table-updates by the fact that in the later case group is set to the row pointer. */ - Field *new_field=create_tmp_field(thd, table, item,type, ©_func, - tmp_from_field, group != 0, - not_all_columns || group !=0); + Field *new_field= create_tmp_field(thd, table, item, type, ©_func, + tmp_from_field, group != 0, + not_all_columns || group !=0, + param->convert_blob_length); if (!new_field) { if (thd->is_fatal_error) @@ -8569,7 +8604,7 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) DBUG_PRINT("error",("Error: do_select() failed")); } #endif - DBUG_RETURN(error || join->thd->net.report_error); + DBUG_RETURN(join->thd->net.report_error ? -1 : error); } @@ -9050,6 +9085,7 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos) { table->key_read=1; table->file->extra(HA_EXTRA_KEYREAD); + tab->index= tab->ref.key; } if ((error=join_read_const(tab))) { @@ -10037,10 +10073,23 @@ part_of_refkey(TABLE *table,Field *field) /***************************************************************************** Test if one can use the key to resolve ORDER BY - Returns: 1 if key is ok. - 0 if key can't be used - -1 if reverse key can be used - used_key_parts is set to key parts used if length != 0 + + SYNOPSIS + test_if_order_by_key() + order Sort order + table Table to sort + idx Index to check + used_key_parts Return value for used key parts. + + + NOTES + used_key_parts is set to correct key parts used if return value != 0 + (On other cases, used_key_part may be changed) + + RETURN + 1 key is ok. + 0 Key can't be used + -1 Reverse key can be used *****************************************************************************/ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx, @@ -10069,13 +10118,17 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx, DBUG_RETURN(0); /* set flag to 1 if we can use read-next on key, else to -1 */ - flag= ((order->asc == !(key_part->key_part_flag & HA_REVERSE_SORT)) ? 1 : -1); + flag= ((order->asc == !(key_part->key_part_flag & HA_REVERSE_SORT)) ? + 1 : -1); if (reverse && flag != reverse) DBUG_RETURN(0); reverse=flag; // Remember if reverse key_part++; } *used_key_parts= (uint) (key_part - table->key_info[idx].key_part); + if (reverse == -1 && !(table->file->index_flags(idx, *used_key_parts-1, 1) & + HA_READ_PREV)) + reverse= 0; // Index can't be used DBUG_RETURN(reverse); } @@ -10288,14 +10341,11 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, if (!select->quick->reverse_sorted()) { int quick_type= select->quick->get_type(); - /* here used_key_parts >0 */ - if (!(table->file->index_flags(ref_key,used_key_parts-1, 1) - & HA_READ_PREV) || - quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE || + if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE || quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT || quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION || quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX) - DBUG_RETURN(0); // Use filesort + DBUG_RETURN(0); // Use filesort /* ORDER BY range_key DESC */ QUICK_SELECT_DESC *tmp=new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick), @@ -10317,9 +10367,6 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, Use a traversal function that starts by reading the last row with key part (A) and then traverse the index backwards. */ - if (!(table->file->index_flags(ref_key,used_key_parts-1, 1) - & HA_READ_PREV)) - DBUG_RETURN(0); // Use filesort tab->read_first_record= join_read_last_key; tab->read_record.read_record= join_read_prev_same; /* fall through */ @@ -10365,7 +10412,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, if (keys.is_set(nr)) { int flag; - if ((flag=test_if_order_by_key(order, table, nr, ¬_used))) + if ((flag= test_if_order_by_key(order, table, nr, ¬_used))) { if (!no_changes) { @@ -11125,15 +11172,14 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,ORDER *order, List<Item> &fields, List<Item> &all_fields) { - Item *itemptr=*order->item; - if (itemptr->type() == Item::INT_ITEM) + Item *it= *order->item; + if (it->type() == Item::INT_ITEM) { /* Order by position */ - uint count= (uint) itemptr->val_int(); + uint count= (uint) it->val_int(); if (!count || count > fields.elements) { my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR), - MYF(0),itemptr->full_name(), - thd->where); + MYF(0), it->full_name(), thd->where); return 1; } order->item= ref_pointer_array + count - 1; @@ -11143,20 +11189,28 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, return 0; } uint counter; - Item **item= find_item_in_list(itemptr, fields, &counter, - REPORT_EXCEPT_NOT_FOUND); + bool unaliased; + Item **item= find_item_in_list(it, fields, &counter, + REPORT_EXCEPT_NOT_FOUND, &unaliased); if (!item) return 1; if (item != (Item **)not_found_item) { + /* + If we have found field not by its alias in select list but by its + original field name, we should additionaly check if we have conflict + for this name (in case if we would perform lookup in all tables). + */ + if (unaliased && !it->fixed && it->fix_fields(thd, tables, order->item)) + return 1; + order->item= ref_pointer_array + counter; order->in_field_list=1; return 0; } order->in_field_list=0; - Item *it= *order->item; /* We check it->fixed because Item_func_group_concat can put arguments for which fix_fields already was called. @@ -11285,10 +11339,11 @@ setup_new_fields(THD *thd,TABLE_LIST *tables,List<Item> &fields, thd->set_query_id=1; // Not really needed, but... uint counter; + bool not_used; for (; new_field ; new_field= new_field->next) { if ((item= find_item_in_list(*new_field->item, fields, &counter, - IGNORE_ERRORS))) + IGNORE_ERRORS, ¬_used))) new_field->item=item; /* Change to shared Item */ else { @@ -11646,6 +11701,8 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, } } else if ((pos->type() == Item::FUNC_ITEM || + pos->type() == Item::SUBSELECT_ITEM || + pos->type() == Item::CACHE_ITEM || pos->type() == Item::COND_ITEM) && !pos->with_sum_func) { // Save for send fields diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 968058f0cfc..28e9baa1c36 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB +/* Copyright (C) 2000-2004 MySQL AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -34,7 +34,7 @@ static const char *grant_names[]={ #ifndef NO_EMBEDDED_ACCESS_CHECKS static TYPELIB grant_types = { sizeof(grant_names)/sizeof(char **), "grant_types", - grant_names}; + grant_names, NULL}; #endif static int @@ -407,6 +407,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, if (wild && !wild[0]) wild=0; + bzero((char*) &table_list,sizeof(table_list)); if (!(dirp = my_dir(path,MYF(MY_WME | (dir ? MY_WANT_STAT : 0))))) @@ -419,27 +420,23 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, { /* Return databases */ #ifdef USE_SYMDIR char *ext; + char buff[FN_REFLEN]; if (my_use_symdir && !strcmp(ext=fn_ext(file->name), ".sym")) { /* Only show the sym file if it points to a directory */ - char buff[FN_REFLEN], *end; - MY_STAT status; + char *end; *ext=0; /* Remove extension */ unpack_dirname(buff, file->name); end= strend(buff); if (end != buff && end[-1] == FN_LIBCHAR) end[-1]= 0; // Remove end FN_LIBCHAR - if (!my_stat(buff, &status, MYF(0)) || - !MY_S_ISDIR(status.st_mode)) - continue; - } - else + if (!my_stat(buff, file->mystat, MYF(0))) + continue; + } #endif - { if (file->name[0] == '.' || !MY_S_ISDIR(file->mystat->st_mode) || (wild && wild_compare(file->name,wild,0))) continue; - } } else { @@ -478,6 +475,9 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, } DBUG_PRINT("info",("found: %d files", files->elements)); my_dirend(dirp); + + VOID(ha_find_files(thd,db,path,wild,dir,files)); + DBUG_RETURN(0); } @@ -761,10 +761,11 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild, protocol->store(field->has_charset() ? field->charset()->name : "NULL", system_charset_info); /* - Although TIMESTAMP fields can't contain NULL as its value they + Even if TIMESTAMP field can't contain NULL as its value it will accept NULL if you will try to insert such value and will - convert it to current TIMESTAMP. So YES here means that NULL - is allowed for assignment but can't be returned. + convert NULL value to current TIMESTAMP. So YES here means + that NULL is allowed for assignment (but may be won't be + returned). */ pos=(byte*) ((flags & NOT_NULL_FLAG) && field->type() != FIELD_TYPE_TIMESTAMP ? @@ -1240,51 +1241,33 @@ static const char *require_quotes(const char *name, uint name_length) } -static void append_quoted_simple_identifier(String *packet, char quote_char, - const char *name, uint length) -{ - packet->append("e_char, 1, system_charset_info); - packet->append(name, length, system_charset_info); - packet->append("e_char, 1, system_charset_info); -} - - void append_identifier(THD *thd, String *packet, const char *name, uint length) { const char *name_end; char quote_char; + int q= get_quote_char_for_identifier(thd, name, length); - if (thd->variables.sql_mode & MODE_ANSI_QUOTES) - quote_char= '\"'; - else - quote_char= '`'; - - if (is_keyword(name,length)) + if (q == EOF) { - append_quoted_simple_identifier(packet, quote_char, name, length); - return; - } - - if (!require_quotes(name, length)) - { - if (!(thd->options & OPTION_QUOTE_SHOW_CREATE)) - packet->append(name, length, system_charset_info); - else - append_quoted_simple_identifier(packet, quote_char, name, length); + packet->append(name, length, system_charset_info); return; } - /* The identifier must be quoted as it includes a quote character */ + /* + The identifier must be quoted as it includes a quote character or + it's a keyword + */ packet->reserve(length*2 + 2); + quote_char= (char) q; packet->append("e_char, 1, system_charset_info); for (name_end= name+length ; name < name_end ; name+= length) { - char chr= *name; + uchar chr= (uchar) *name; length= my_mbcharlen(system_charset_info, chr); - if (length == 1 && chr == quote_char) + if (length == 1 && chr == (uchar) quote_char) packet->append("e_char, 1, system_charset_info); packet->append(name, length, packet->charset()); } @@ -1292,15 +1275,46 @@ append_identifier(THD *thd, String *packet, const char *name, uint length) } +/* + Get the quote character for displaying an identifier. + + SYNOPSIS + get_quote_char_for_identifier() + thd Thread handler + name name to quote + length length of name + + IMPLEMENTATION + If name is a keyword or includes a special character, then force + quoting. + Otherwise identifier is quoted only if the option OPTION_QUOTE_SHOW_CREATE + is set. + + RETURN + EOF No quote character is needed + # Quote character +*/ + +int get_quote_char_for_identifier(THD *thd, const char *name, uint length) +{ + if (!is_keyword(name,length) && + !require_quotes(name, length) && + !(thd->options & OPTION_QUOTE_SHOW_CREATE)) + return EOF; + if (thd->variables.sql_mode & MODE_ANSI_QUOTES) + return '"'; + return '`'; +} + + /* Append directory name (if exists) to CREATE INFO */ static void append_directory(THD *thd, String *packet, const char *dir_type, const char *filename) { - uint length; if (filename && !(thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)) { - length= dirname_length(filename); + uint length= dirname_length(filename); packet->append(' '); packet->append(dir_type); packet->append(" DIRECTORY='", 12); @@ -1362,6 +1376,8 @@ store_create_info(THD *thd, TABLE *table, String *packet) // check for surprises from the previous call to Field::sql_type() if (type.ptr() != tmp) type.set(tmp, sizeof(tmp), system_charset_info); + else + type.set_charset(system_charset_info); field->sql_type(type); packet->append(type.ptr(), type.length(), system_charset_info); @@ -1386,7 +1402,14 @@ store_create_info(THD *thd, TABLE *table, String *packet) if (flags & NOT_NULL_FLAG) packet->append(" NOT NULL", 9); - + else if (field->type() == FIELD_TYPE_TIMESTAMP) + { + /* + TIMESTAMP field require explicit NULL flag, because unlike + all other fields they are treated as NOT NULL by default. + */ + packet->append(" NULL", 5); + } /* Again we are using CURRENT_TIMESTAMP instead of NOW because it is diff --git a/sql/sql_string.cc b/sql/sql_string.cc index ba9431f27c4..7d0dd3ca566 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -331,19 +331,26 @@ bool String::set_or_copy_aligned(const char *str,uint32 arg_length, /* Copy with charset convertion */ bool String::copy(const char *str, uint32 arg_length, - CHARSET_INFO *from_cs, CHARSET_INFO *to_cs) + CHARSET_INFO *from_cs, CHARSET_INFO *to_cs, uint *errors) { uint32 offset; if (!needs_conversion(arg_length, from_cs, to_cs, &offset)) + { + if (errors) + *errors= 0; return copy(str, arg_length, to_cs); + } if ((from_cs == &my_charset_bin) && offset) + { + if (errors) + *errors= 0; return copy_aligned(str, arg_length, offset, to_cs); - + } uint32 new_length= to_cs->mbmaxlen*arg_length; if (alloc(new_length)) return TRUE; str_length=copy_and_convert((char*) Ptr, new_length, to_cs, - str, arg_length, from_cs); + str, arg_length, from_cs, errors); str_charset=to_cs; return FALSE; } @@ -537,7 +544,8 @@ uint32 String::numchars() int String::charpos(int i,uint32 offset) { - if (i<0) return i; + if (i <= 0) + return i; return str_charset->cset->charpos(str_charset,Ptr+offset,Ptr+str_length,i); } @@ -781,7 +789,8 @@ String *copy_if_not_alloced(String *to,String *from,uint32 from_length) uint32 copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs, - const char *from, uint32 from_length, CHARSET_INFO *from_cs) + const char *from, uint32 from_length, CHARSET_INFO *from_cs, + uint *errors) { int cnvres; my_wc_t wc; @@ -792,6 +801,7 @@ copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs, const uchar *) = from_cs->cset->mb_wc; int (*wc_mb)(struct charset_info_st *, my_wc_t, uchar *s, uchar *e)= to_cs->cset->wc_mb; + uint error_count= 0; while (1) { @@ -800,6 +810,7 @@ copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs, from+= cnvres; else if (cnvres == MY_CS_ILSEQ) { + error_count++; from++; wc= '?'; } @@ -811,12 +822,15 @@ outp: to+= cnvres; else if (cnvres == MY_CS_ILUNI && wc != '?') { + error_count++; wc= '?'; goto outp; } else break; } + if (errors) + *errors= error_count; return (uint32) (to - to_start); } diff --git a/sql/sql_string.h b/sql/sql_string.h index ad32305e9b4..d6807fa83ad 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -29,7 +29,7 @@ int sortcmp(const String *a,const String *b, CHARSET_INFO *cs); String *copy_if_not_alloced(String *a,String *b,uint32 arg_length); uint32 copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs, const char *from, uint32 from_length, - CHARSET_INFO *from_cs); + CHARSET_INFO *from_cs, uint *errors= 0); class String { @@ -71,7 +71,9 @@ public: } static void *operator new(size_t size, MEM_ROOT *mem_root) { return (void*) alloc_root(mem_root, (uint) size); } - static void operator delete(void *ptr_arg,size_t size) /*lint -e715 */ + static void operator delete(void *ptr_arg,size_t size) + {} + static void operator delete(void *ptr_arg,size_t size, MEM_ROOT *mem_root) {} ~String() { free(); } @@ -197,7 +199,7 @@ public: CHARSET_INFO *cs); bool set_or_copy_aligned(const char *s, uint32 arg_length, CHARSET_INFO *cs); bool copy(const char*s,uint32 arg_length, CHARSET_INFO *csfrom, - CHARSET_INFO *csto); + CHARSET_INFO *csto, uint *errors= 0); bool append(const String &s); bool append(const char *s); bool append(const char *s,uint32 arg_length); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 399fed00040..26c483a61a8 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -29,7 +29,13 @@ #include <io.h> #endif -const char *primary_key_name= "PRIMARY"; +#define tmp_disable_binlog(A) \ + ulong save_options= (A)->options; \ + (A)->options&= ~OPTION_BIN_LOG; + +#define reenable_binlog(A) (A)->options= save_options; + +const char *primary_key_name="PRIMARY"; static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end); static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end); @@ -193,7 +199,7 @@ 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; - mysql_ha_close(thd, table, /*dont_send_ok*/ 1, /*dont_lock*/ 1); + mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL); if (!close_temporary_table(thd, db, table->real_name)) { tmp_table_deleted=1; @@ -218,8 +224,9 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, strxmov(path, mysql_data_home, "/", db, "/", alias, reg_ext, NullS); (void) unpack_filename(path,path); } - if (drop_temporary || access(path,F_OK) || - (!drop_view && mysql_frm_type(path) != FRMTYPE_TABLE)) + if (drop_temporary || + (access(path,F_OK) && ha_create_table_from_engine(thd,db,alias,TRUE)) || + (!drop_view && mysql_frm_type(path) != FRMTYPE_TABLE)) { if (if_exists) push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, @@ -362,19 +369,19 @@ static int sort_keys(KEY *a, KEY *b) */ void check_duplicates_in_interval(const char *set_or_name, - const char *name, TYPELIB *typelib) + const char *name, TYPELIB *typelib, + CHARSET_INFO *cs) { - unsigned int old_count= typelib->count; - const char **old_type_names= typelib->type_names; - - old_count= typelib->count; - old_type_names= typelib->type_names; + TYPELIB tmp= *typelib; const char **cur_value= typelib->type_names; - for ( ; typelib->count > 1; cur_value++) + unsigned int *cur_length= typelib->type_lengths; + + for ( ; tmp.count > 1; cur_value++, cur_length++) { - typelib->type_names++; - typelib->count--; - if (find_type((char*)*cur_value,typelib,1)) + tmp.type_names++; + tmp.type_lengths++; + tmp.count--; + if (find_type2(&tmp, (const char*)*cur_value, *cur_length, cs)) { push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_NOTE, ER_DUPLICATED_VALUE_IN_TYPE, @@ -382,8 +389,6 @@ void check_duplicates_in_interval(const char *set_or_name, name,*cur_value,set_or_name); } } - typelib->count= old_count; - typelib->type_names= old_type_names; } /* @@ -563,7 +568,8 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, sql_field->pack_flag|=FIELDFLAG_BINARY; sql_field->unireg_check=Field::INTERVAL_FIELD; check_duplicates_in_interval("ENUM",sql_field->field_name, - sql_field->interval); + sql_field->interval, + sql_field->charset); break; case FIELD_TYPE_SET: sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) | @@ -572,7 +578,8 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, sql_field->pack_flag|=FIELDFLAG_BINARY; sql_field->unireg_check=Field::BIT_FIELD; check_duplicates_in_interval("SET",sql_field->field_name, - sql_field->interval); + sql_field->interval, + sql_field->charset); break; case FIELD_TYPE_DATE: // Rest of string types case FIELD_TYPE_NEWDATE: @@ -1093,7 +1100,7 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, (From ALTER TABLE) DESCRIPTION - If one creates a temporary table, this is automaticly opened + If one creates a temporary table, this is automatically opened no_log is needed for the case of CREATE ... SELECT, as the logging will be done later in sql_insert.cc @@ -1238,8 +1245,8 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, { bool create_if_not_exists = create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS; - if (!create_table_from_handler(db, table_name, - create_if_not_exists)) + if (!ha_create_table_from_engine(thd, db, table_name, + create_if_not_exists)) { DBUG_PRINT("info", ("Table already existed in handler")); @@ -1344,10 +1351,9 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, MYSQL_LOCK **lock) { TABLE tmp_table; // Used during 'create_field()' - TABLE *table; + TABLE *table= 0; tmp_table.table_name=0; uint select_field_count= items->elements; - Disable_binlog disable_binlog(thd); DBUG_ENTER("create_table_from_items"); /* Add selected items to field list */ @@ -1368,7 +1374,7 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, field=item->tmp_table_field(&tmp_table); else field=create_tmp_field(thd, &tmp_table, item, item->type(), - (Item ***) 0, &tmp_field,0,0); + (Item ***) 0, &tmp_field,0,0,0); if (!field || !(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ? ((Item_field *)item)->field : @@ -1376,25 +1382,34 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, DBUG_RETURN(0); extra_fields->push_back(cr_field); } - /* create and lock table */ - /* QQ: This should be done atomic ! */ - if (mysql_create_table(thd, create_table->db, create_table->real_name, - create_info, *extra_fields, *keys, 0, - select_field_count)) - DBUG_RETURN(0); /* + create and lock table + + We don't log the statement, it will be logged later. + If this is a HEAP table, the automatic DELETE FROM which is written to the binlog when a HEAP table is opened for the first time since startup, must not be written: 1) it would be wrong (imagine we're in CREATE SELECT: we don't want to delete from it) 2) it would be written before the CREATE - TABLE, which is a wrong order. So we keep binary logging disabled. + TABLE, which is a wrong order. So we keep binary logging disabled when we + open_table(). + TODO: create and open should be done atomic ! */ - if (!(table= open_table(thd, create_table, &thd->mem_root, (bool*) 0))) { - quick_rm_table(create_info->db_type, create_table->db, - table_case_name(create_info, create_table->real_name)); - DBUG_RETURN(0); + tmp_disable_binlog(thd); + if (!mysql_create_table(thd, create_table->db, create_table->real_name, + create_info, *extra_fields, *keys, 0, + select_field_count)) + { + if (!(table= open_table(thd, create_table, &thd->mem_root, (bool*) 0))) + quick_rm_table(create_info->db_type, create_table->db, + table_case_name(create_info, create_table->real_name)); + } + reenable_binlog(thd); + if (!table) // open failed + DBUG_RETURN(0); } + table->reginfo.lock_type=TL_WRITE; if (!((*lock)= mysql_lock_tables(thd, &table,1))) { @@ -1424,11 +1439,12 @@ mysql_rename_table(enum db_type base, { char from[FN_REFLEN], to[FN_REFLEN]; char tmp_from[NAME_LEN+1], tmp_to[NAME_LEN+1]; - handler *file=get_new_handler((TABLE*) 0, base); + handler *file=(base == DB_TYPE_UNKNOWN ? 0 : get_new_handler((TABLE*) 0, base)); int error=0; DBUG_ENTER("mysql_rename_table"); - if (lower_case_table_names == 2 && !(file->table_flags() & HA_FILE_BASED)) + if (lower_case_table_names == 2 && file && + !(file->table_flags() & HA_FILE_BASED)) { /* Table handler expects to get all file names as lower case */ strmov(tmp_from, old_name); @@ -1446,13 +1462,15 @@ mysql_rename_table(enum db_type base, fn_format(from,from,"","",4); fn_format(to,to, "","",4); - if (!(error=file->rename_table((const char*) from,(const char *) to))) + if (!file || + !(error=file->rename_table((const char*) from,(const char *) to))) { if (rename_file_ext(from,to,reg_ext)) { error=my_errno; /* Restore old file name */ - file->rename_table((const char*) to,(const char *) from); + if (file) + file->rename_table((const char*) to,(const char *) from); } } delete file; @@ -1751,7 +1769,7 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(-1); - mysql_ha_close(thd, tables, /*dont_send_ok*/ 1, /*dont_lock*/ 1); + mysql_ha_flush(thd, tables, MYSQL_HA_CLOSE_FINAL); for (table= tables; table; table= table->next_local) { char table_name[NAME_LEN*2+2]; @@ -2255,31 +2273,32 @@ int mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt) &handler::check)); } + /* table_list should contain just one table */ -int mysql_discard_or_import_tablespace(THD *thd, - TABLE_LIST *table_list, - enum tablespace_op_type tablespace_op) +static int +mysql_discard_or_import_tablespace(THD *thd, + TABLE_LIST *table_list, + enum tablespace_op_type tablespace_op) { TABLE *table; my_bool discard; int error; DBUG_ENTER("mysql_discard_or_import_tablespace"); - /* Note that DISCARD/IMPORT TABLESPACE always is the only operation in an - ALTER TABLE */ + /* + Note that DISCARD/IMPORT TABLESPACE always is the only operation in an + ALTER TABLE + */ thd->proc_info="discard_or_import_tablespace"; - if (tablespace_op == DISCARD_TABLESPACE) - discard = TRUE; - else - discard = FALSE; - - thd->tablespace_op=TRUE; /* we set this flag so that ha_innobase::open - and ::external_lock() do not complain when we - lock the table */ - mysql_ha_close(thd, table_list, /*dont_send_ok*/ 1, /*dont_lock*/ 1); + discard= test(tablespace_op == DISCARD_TABLESPACE); + /* + We set this flag so that ha_innobase::open and ::external_lock() do + not complain when we lock the table + */ + thd->tablespace_op= TRUE; if (!(table=open_ltable(thd,table_list,TL_WRITE))) { thd->tablespace_op=FALSE; @@ -2293,8 +2312,10 @@ int mysql_discard_or_import_tablespace(THD *thd, if (error) goto err; - /* The 0 in the call below means 'not in a transaction', which means - immediate invalidation; that is probably what we wish here */ + /* + The 0 in the call below means 'not in a transaction', which means + immediate invalidation; that is probably what we wish here + */ query_cache_invalidate3(thd, table_list, 0); /* The ALTER TABLE is always in its own transaction */ @@ -2536,7 +2557,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, enum enum_duplicates handle_duplicates, ALTER_INFO *alter_info, bool do_send_ok) { - TABLE *table,*new_table; + TABLE *table,*new_table=0; int error; char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN]; char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias; @@ -2545,6 +2566,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, ulonglong next_insert_id; uint db_create_options, used_fields; enum db_type old_db_type,new_db_type; + bool need_copy_table; DBUG_ENTER("mysql_alter_table"); thd->proc_info="init"; @@ -2556,8 +2578,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, new_db= db; used_fields=create_info->used_fields; - mysql_ha_close(thd, table_list, /*dont_send_ok*/ 1, /*dont_lock*/ 1); - + mysql_ha_flush(thd, table_list, MYSQL_HA_CLOSE_FINAL); /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */ if (alter_info->tablespace_op != NO_TABLESPACE_OP) DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list, @@ -2600,7 +2621,9 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, } else { - if (!access(fn_format(new_name_buff,new_name_buff,new_db,reg_ext,0), + char dir_buff[FN_REFLEN]; + strxnmov(dir_buff, FN_REFLEN, mysql_real_data_home, new_db, NullS); + if (!access(fn_format(new_name_buff,new_name_buff,dir_buff,reg_ext,0), F_OK)) { /* Table will be closed in do_command() */ @@ -2633,7 +2656,8 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, create_info->row_type=table->row_type; thd->proc_info="setup"; - if (alter_info->is_simple && !table->tmp_table) + if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) && + !table->tmp_table) // no need to touch frm { error=0; if (new_name != table_name || new_db != db) @@ -2661,23 +2685,24 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, { switch (alter_info->keys_onoff) { case LEAVE_AS_IS: - break; + break; case ENABLE: - VOID(pthread_mutex_lock(&LOCK_open)); - wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); - VOID(pthread_mutex_unlock(&LOCK_open)); - error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); - /* COND_refresh will be signaled in close_thread_tables() */ - break; + VOID(pthread_mutex_lock(&LOCK_open)); + wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); + VOID(pthread_mutex_unlock(&LOCK_open)); + error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); + /* COND_refresh will be signaled in close_thread_tables() */ + break; case DISABLE: - VOID(pthread_mutex_lock(&LOCK_open)); - wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); - VOID(pthread_mutex_unlock(&LOCK_open)); - error=table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); - /* COND_refresh will be signaled in close_thread_tables() */ - break; + VOID(pthread_mutex_lock(&LOCK_open)); + wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN); + VOID(pthread_mutex_unlock(&LOCK_open)); + error=table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); + /* COND_refresh will be signaled in close_thread_tables() */ + break; } } + if (error == HA_ERR_WRONG_COMMAND) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, @@ -2708,7 +2733,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, /* Full alter table */ - /* let new create options override the old ones */ + /* Let new create options override the old ones */ if (!(used_fields & HA_CREATE_USED_MIN_ROWS)) create_info->min_rows=table->min_rows; if (!(used_fields & HA_CREATE_USED_MAX_ROWS)) @@ -2969,6 +2994,16 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, create_info->options|=HA_LEX_CREATE_TMP_TABLE; /* + better have a negative test here, instead of positive, like + alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|... + so that ALTER TABLE won't break when somebody will add new flag + */ + need_copy_table=(alter_info->flags & ~(ALTER_CHANGE_COLUMN_DEFAULT|ALTER_OPTIONS) || + create_info->used_fields & ~(HA_CREATE_USED_COMMENT|HA_CREATE_USED_PASSWORD) || + table->tmp_table); + create_info->frm_only= !need_copy_table; + + /* Handling of symlinked tables: If no rename: Create new data file and index file on the same disk as the @@ -3014,53 +3049,55 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, } else create_info->data_file_name=create_info->index_file_name=0; + + /* We don't log the statement, it will be logged later. */ { - /* We don't log the statement, it will be logged later */ - Disable_binlog disable_binlog(thd); - if ((error=mysql_create_table(thd, new_db, tmp_name, - create_info, - create_list,key_list,1,0))) + tmp_disable_binlog(thd); + error= mysql_create_table(thd, new_db, tmp_name, + create_info,create_list,key_list,1,0); + reenable_binlog(thd); + if (error) DBUG_RETURN(error); } - if (table->tmp_table) + if (need_copy_table) { - TABLE_LIST tbl; - bzero((void*) &tbl, sizeof(tbl)); - tbl.db= new_db; - tbl.real_name= tbl.alias= tmp_name; - new_table= open_table(thd, &tbl, &thd->mem_root, 0); - } - else - { - char path[FN_REFLEN]; - my_snprintf(path, sizeof(path), "%s/%s/%s", mysql_data_home, - new_db, tmp_name); - fn_format(path,path,"","",4); - new_table=open_temporary_table(thd, path, new_db, tmp_name,0); - } - if (!new_table) - { - VOID(quick_rm_table(new_db_type,new_db,tmp_name)); - goto err; + if (table->tmp_table) + { + TABLE_LIST tbl; + bzero((void*) &tbl, sizeof(tbl)); + tbl.db= new_db; + tbl.real_name= tbl.alias= tmp_name; + new_table= open_table(thd, &tbl, &thd->mem_root, 0); + } + else + { + char path[FN_REFLEN]; + my_snprintf(path, sizeof(path), "%s/%s/%s", mysql_data_home, + new_db, tmp_name); + fn_format(path,path,"","",4); + new_table=open_temporary_table(thd, path, new_db, tmp_name,0); + } + if (!new_table) + { + VOID(quick_rm_table(new_db_type,new_db,tmp_name)); + goto err; + } } - - /* - We don't want update TIMESTAMP fields during ALTER TABLE - and copy_data_between_tables uses only write_row() for new_table so - don't need to set up timestamp_on_update_now member. - */ - new_table->timestamp_default_now= 0; - new_table->next_number_field=new_table->found_next_number_field; + /* We don't want update TIMESTAMP fields during ALTER TABLE. */ thd->count_cuted_fields= CHECK_FIELD_WARN; // calc cuted fields thd->cuted_fields=0L; thd->proc_info="copy to tmp table"; - next_insert_id=thd->next_insert_id; // Remember for loggin + next_insert_id=thd->next_insert_id; // Remember for logging copied=deleted=0; - if (!new_table->is_view) + if (new_table && !new_table->is_view) + { + new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; + new_table->next_number_field=new_table->found_next_number_field; error=copy_data_between_tables(table,new_table,create_list, handle_duplicates, order_num, order, &copied, &deleted); + } thd->last_insert_id=next_insert_id; // Needed for correct log thd->count_cuted_fields= CHECK_FIELD_IGNORE; @@ -3099,8 +3136,11 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, goto end_temporary; } - intern_close_table(new_table); /* close temporary table */ - my_free((gptr) new_table,MYF(0)); + if (new_table) + { + intern_close_table(new_table); /* close temporary table */ + my_free((gptr) new_table,MYF(0)); + } VOID(pthread_mutex_lock(&LOCK_open)); if (error) { @@ -3112,7 +3152,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, /* Data is copied. Now we rename the old table to a temp name, rename the new one to the old name, remove all entries from the old table - from the cash, free all locks, close the old table and remove it. + from the cache, free all locks, close the old table and remove it. */ thd->proc_info="rename result table"; @@ -3151,6 +3191,8 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, error=0; + if (!need_copy_table) + new_db_type=old_db_type=DB_TYPE_UNKNOWN; // this type cannot happen in regular ALTER if (mysql_rename_table(old_db_type,db,table_name,db,old_name)) { error=1; @@ -3246,8 +3288,8 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, my_free((char*) table, MYF(0)); } else - sql_print_error("Warning: Could not open BDB table %s.%s after rename\n", - new_db,table_name); + sql_print_warning("Could not open BDB table %s.%s after rename\n", + new_db,table_name); (void) berkeley_flush_logs(); } #endif @@ -3288,15 +3330,19 @@ copy_data_between_tables(TABLE *from,TABLE *to, List<Item> all_fields; ha_rows examined_rows; bool auto_increment_field_copied= 0; + ulong save_sql_mode; DBUG_ENTER("copy_data_between_tables"); if (!(copy= new Copy_field[to->fields])) DBUG_RETURN(-1); /* purecov: inspected */ - to->file->external_lock(thd,F_WRLCK); + if (to->file->external_lock(thd, F_WRLCK)) + DBUG_RETURN(-1); from->file->info(HA_STATUS_VARIABLE); to->file->start_bulk_insert(from->file->records); + save_sql_mode= thd->variables.sql_mode; + List_iterator<create_field> it(create); create_field *def; copy_end=copy; @@ -3306,7 +3352,17 @@ copy_data_between_tables(TABLE *from,TABLE *to, if (def->field) { if (*ptr == to->next_number_field) + { auto_increment_field_copied= TRUE; + /* + If we are going to copy contents of one auto_increment column to + another auto_increment column it is sensible to preserve zeroes. + This condition also covers case when we are don't actually alter + auto_increment column. + */ + if (def->field == from->found_next_number_field) + thd->variables.sql_mode|= MODE_NO_AUTO_VALUE_ON_ZERO; + } (copy_end++)->set(*ptr,def->field,0); } @@ -3339,7 +3395,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, Turn off recovery logging since rollback of an alter table is to delete the new table so there is no need to log the changes to it. */ - error= ha_recovery_logging(thd,FALSE); + error= ha_enable_transaction(thd,FALSE); if (error) { error= 1; @@ -3402,7 +3458,8 @@ copy_data_between_tables(TABLE *from,TABLE *to, } to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); - ha_recovery_logging(thd,TRUE); + ha_enable_transaction(thd,TRUE); + /* Ensure that the new table is saved properly to disk so that we can do a rename @@ -3411,12 +3468,14 @@ copy_data_between_tables(TABLE *from,TABLE *to, error=1; if (ha_commit(thd)) error=1; - if (to->file->external_lock(thd,F_UNLCK)) - error=1; + err: + thd->variables.sql_mode= save_sql_mode; free_io_cache(from); *copied= found_count; *deleted=delete_count; + if (to->file->external_lock(thd,F_UNLCK)) + error=1; DBUG_RETURN(error > 0 ? -1 : 0); } @@ -3443,7 +3502,6 @@ int mysql_recreate_table(THD *thd, TABLE_LIST *table_list, lex->key_list.empty(); lex->col_list.empty(); lex->alter_info.reset(); - lex->alter_info.is_simple= 0; // Force full recreate bzero((char*) &create_info,sizeof(create_info)); create_info.db_type=DB_TYPE_DEFAULT; create_info.row_type=ROW_TYPE_DEFAULT; diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index add4078ce8e..17f3bc48678 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -16,9 +16,9 @@ static const char * const triggers_file_ext= ".TRG"; */ static File_option triggers_file_parameters[]= { - {{(char*)"triggers", 8}, offsetof(Table_triggers_list, definitions_list), + {{(char*)"triggers", 8}, offsetof(class Table_triggers_list, definitions_list), FILE_OPTIONS_STRLIST}, - {{NULL, 0}, 0, FILE_OPTIONS_STRING} + {{0, 0}, 0, FILE_OPTIONS_STRING} }; @@ -373,8 +373,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, while ((trg_create_str= it++)) { - mysql_init_query(thd, (uchar*)trg_create_str->str, - trg_create_str->length, true); + lex_start(thd, (uchar*)trg_create_str->str, trg_create_str->length); lex.trg_table= table; if (yyparse((void *)thd) || thd->is_fatal_error) { diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 561f79f9de1..0bb8ac8a28b 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -184,8 +184,7 @@ void udf_init() if (!(dl = dlopen(tmp->dl, RTLD_NOW))) { /* Print warning to log */ - sql_print_error(ER(ER_CANT_OPEN_LIBRARY), - tmp->dl,errno,dlerror()); + sql_print_error(ER(ER_CANT_OPEN_LIBRARY), tmp->dl,errno,dlerror()); /* Keep the udf in the hash so that we can remove it later */ continue; } diff --git a/sql/sql_union.cc b/sql/sql_union.cc index cf4203bf5b8..46f11683e4e 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -153,6 +153,7 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, SELECT_LEX *lex_select_save= thd_arg->lex->current_select; SELECT_LEX *sl, *first_select; select_result *tmp_result; + bool is_union; DBUG_ENTER("st_select_lex_unit::prepare"); describe= test(additional_options & SELECT_DESCRIBE); @@ -189,10 +190,11 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, thd_arg->lex->current_select= sl= first_select= first_select_in_union(); found_rows_for_union= first_select->options & OPTION_FOUND_ROWS; + is_union= test(first_select->next_select()); /* Global option */ - if (first_select->next_select()) + if (is_union) { if (!(tmp_result= union_result= new select_union(0))) goto err; @@ -201,14 +203,11 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, tmp_result= sel_result; } else - { tmp_result= sel_result; - // single select should be processed like select in p[arantses - first_select->braces= 1; - } for (;sl; sl= sl->next_select()) { + bool can_skip_order_by; sl->options|= SELECT_NO_UNLOCK; JOIN *join= new JOIN(thd_arg, sl->item_list, sl->options | thd_arg->options | additional_options, @@ -220,18 +219,26 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, set_limit(sl, sl); if (sl->braces) sl->options&= ~OPTION_FOUND_ROWS; + + can_skip_order_by= is_union && + (!sl->braces || select_limit_cnt == HA_POS_ERROR); res= join->prepare(&sl->ref_pointer_array, (TABLE_LIST*) sl->table_list.first, sl->with_wild, sl->where, - ((sl->braces) ? sl->order_list.elements : 0) + - sl->group_list.elements, - (sl->braces) ? - (ORDER *)sl->order_list.first : (ORDER *) 0, + (can_skip_order_by ? 0 : sl->order_list.elements) + + sl->group_list.elements, + can_skip_order_by ? + (ORDER*) 0 : (ORDER *)sl->order_list.first, (ORDER*) sl->group_list.first, sl->having, - (ORDER*) NULL, + (is_union ? (ORDER*) 0 : + (ORDER*) thd_arg->lex->proc_list.first), sl, this); + /* There are no * in the statement anymore (for PS) */ + sl->with_wild= 0; + last_procedure= join->procedure; + if (res || thd_arg->is_fatal_error) goto err; if (sl == first_select) @@ -267,9 +274,26 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, } } - // it is not single select - if (first_select->next_select()) + if (is_union) { + /* + Check that it was possible to aggregate + all collations together for UNION. + */ + List_iterator_fast<Item> tp(types); + Item_arena *arena= thd->current_arena; + Item *type; + + while ((type= tp++)) + { + if (type->result_type() == STRING_RESULT && + type->collation.derivation == DERIVATION_NONE) + { + my_error(ER_CANT_AGGREGATE_NCOLLATIONS, MYF(0), "UNION"); + goto err; + } + } + union_result->tmp_table_param.field_count= types.elements; if (!(table= create_tmp_table(thd_arg, &union_result->tmp_table_param, types, @@ -291,7 +315,6 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, if (!item_list.elements) { Field **field; - Item_arena *arena= thd->current_arena; Item_arena backup; if (arena->is_conventional()) arena= 0; @@ -334,14 +357,27 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, 0, 0, fake_select_lex->order_list.elements, (ORDER*) fake_select_lex->order_list.first, - (ORDER*) NULL, NULL, (ORDER*) NULL, + (ORDER*) NULL, NULL, + (ORDER*) NULL, fake_select_lex, this); fake_select_lex->table_list.empty(); } } + else if (!arena->is_conventional()) + { + /* + We're in execution of a prepared statement or stored procedure: + reset field items to point at fields from the created temporary table. + */ + List_iterator_fast<Item> it(item_list); + for (Field **field= table->field; *field; field++) + { + Item_field *item_field= (Item_field*) it++; + DBUG_ASSERT(item_field); + item_field->reset_field(*field); + } + } } - else - first_select->braces= 0; // remove our changes thd_arg->lex->current_select= lex_select_save; @@ -358,6 +394,7 @@ int st_select_lex_unit::exec() SELECT_LEX *lex_select_save= thd->lex->current_select; SELECT_LEX *select_cursor=first_select_in_union(); ulonglong add_rows=0; + ha_rows examined_rows= 0; DBUG_ENTER("st_select_lex_unit::exec"); if (executed && !uncacheable && !describe) @@ -438,6 +475,7 @@ int st_select_lex_unit::exec() offset_limit_cnt= sl->offset_limit; if (!res && union_result->flush()) { + examined_rows+= thd->examined_row_count; thd->lex->current_select= lex_select_save; DBUG_RETURN(1); } @@ -516,7 +554,10 @@ int st_select_lex_unit::exec() fake_select_lex->table_list.empty(); if (!res) + { thd->limit_found_rows = (ulonglong)table->file->records + add_rows; + thd->examined_row_count+= examined_rows; + } /* Mark for slow query log if any of the union parts didn't use indexes efficiently diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 00e70ccb484..5e91e730079 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -80,8 +80,7 @@ static bool check_fields(THD *thd, List<Item> &items) we make temporary copy of Item_field, to avoid influence of changing result_field on Item_ref which refer on this field */ - it.replace(new Item_field(thd, field)); - field->register_item_tree_changing(it.ref()); + thd->change_item_tree(it.ref(), new Item_field(thd, field)); } return FALSE; } @@ -171,7 +170,7 @@ int mysql_update(THD *thd, { // Don't set timestamp column if this is modified if (table->timestamp_field->query_id == thd->query_id) - table->timestamp_on_update_now= 0; + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; else table->timestamp_field->query_id=timestamp_query_id; } @@ -392,6 +391,7 @@ int mysql_update(THD *thd, else if (handle_duplicates != DUP_IGNORE || error != HA_ERR_FOUND_DUPP_KEY) { + thd->fatal_error(); // Force error message table->file->print_error(error,MYF(0)); error= 1; break; @@ -544,9 +544,22 @@ int mysql_prepare_update(THD *thd, TABLE_LIST *table_list, ***************************************************************************/ /* - Setup multi-update handling and call SELECT to do the join + Get table map for list of Item_field */ +static table_map get_table_map(List<Item> *items) +{ + List_iterator_fast<Item> item_it(*items); + Item_field *item; + table_map map= 0; + + while ((item= (Item_field *) item_it++)) + map|= item->used_tables(); + DBUG_PRINT("info",("table_map: 0x%08x", map)); + return map; +} + + /* make update specific preparation and checks after opening tables @@ -636,7 +649,7 @@ int mysql_multi_update_prepare(THD *thd) // Only set timestamp column if this is not modified if (table->timestamp_field && table->timestamp_field->query_id == thd->query_id) - table->timestamp_on_update_now= 0; + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; if (!tl->updatable || check_key_in_view(thd, tl)) readonly_tables|= table->map; @@ -658,6 +671,10 @@ int mysql_multi_update_prepare(THD *thd) } +/* + Setup multi-update handling and call SELECT to do the join +*/ + int mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields, @@ -671,6 +688,8 @@ int mysql_multi_update(THD *thd, multi_update *result; DBUG_ENTER("mysql_multi_update"); + /* QQ: This should be fixed soon to get lower granularity locks */ + select_lex->set_lock_for_tables(thd->lex->multi_lock_option); if ((res= open_and_lock_tables(thd, table_list))) DBUG_RETURN(res); @@ -719,7 +738,7 @@ int multi_update::prepare(List<Item> ¬_used_values, { TABLE_LIST *table_ref; SQL_LIST update; - table_map tables_to_update= 0; + table_map tables_to_update; Item_field *item; List_iterator_fast<Item> field_it(*fields); List_iterator_fast<Item> value_it(*values); @@ -730,8 +749,7 @@ int multi_update::prepare(List<Item> ¬_used_values, thd->cuted_fields=0L; thd->proc_info="updating main table"; - while ((item= (Item_field *) field_it++)) - tables_to_update|= item->used_tables(); + tables_to_update= get_table_map(fields); if (!tables_to_update) { @@ -794,7 +812,6 @@ int multi_update::prepare(List<Item> ¬_used_values, /* Split fields into fields_for_table[] and values_by_table[] */ - field_it.rewind(); while ((item= (Item_field *) field_it++)) { Item *value= value_it++; @@ -1049,11 +1066,16 @@ bool multi_update::send_data(List<Item> ¬_used_values) if ((error=table->file->update_row(table->record[1], table->record[0]))) { - table->file->print_error(error,MYF(0)); updated--; - DBUG_RETURN(1); + if (handle_duplicates != DUP_IGNORE || + error != HA_ERR_FOUND_DUPP_KEY) + { + thd->fatal_error(); // Force error message + table->file->print_error(error,MYF(0)); + DBUG_RETURN(1); + } } - if (!table->file->has_transactions()) + else if (!table->file->has_transactions()) thd->no_trans_update= 1; } } @@ -1119,7 +1141,6 @@ int multi_update::do_updates(bool from_send_error) TABLE *table, *tmp_table; DBUG_ENTER("do_updates"); - do_update= 0; // Don't retry this function if (!found) DBUG_RETURN(0); @@ -1207,7 +1228,10 @@ int multi_update::do_updates(bool from_send_error) err: if (!from_send_error) + { + thd->fatal_error(); table->file->print_error(local_error,MYF(0)); + } (void) table->file->ha_rnd_end(); (void) tmp_table->file->ha_rnd_end(); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 4cdbfe9728b..f8994148ffc 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -28,7 +28,8 @@ const char *updatable_views_with_limit_names[]= { "NO", "YES", NullS }; TYPELIB updatable_views_with_limit_typelib= { array_elements(updatable_views_with_limit_names)-1, "", - updatable_views_with_limit_names + updatable_views_with_limit_names, + 0 }; @@ -347,7 +348,7 @@ static File_option view_parameters[]= FILE_OPTIONS_ULONGLONG}, {{(char*) "algorithm", 9}, offsetof(TABLE_LIST, algorithm), FILE_OPTIONS_ULONGLONG}, - {{"with_check_option", 17}, offsetof(TABLE_LIST, with_check), + {{(char*) "with_check_option", 17}, offsetof(TABLE_LIST, with_check), FILE_OPTIONS_ULONGLONG}, {{(char*) "revision", 8}, offsetof(TABLE_LIST, revision), FILE_OPTIONS_REV}, @@ -357,7 +358,7 @@ static File_option view_parameters[]= FILE_OPTIONS_ULONGLONG}, {{(char*) "source", 6}, offsetof(TABLE_LIST, source), FILE_OPTIONS_ESTRING}, - {{NULL, 0}, 0, + {{NullS, 0}, 0, FILE_OPTIONS_STRING} }; @@ -586,7 +587,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) now Lex placed in statement memory */ table->view= lex= thd->lex= (LEX*) new(&thd->mem_root) st_lex_local; - mysql_init_query(thd, (uchar*)table->query.str, table->query.length, TRUE); + lex_start(thd, (uchar*)table->query.str, table->query.length); lex->select_lex.select_number= ++thd->select_number; old_lex->derived_tables|= DERIVED_VIEW; { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index d792d144545..c658d2ae016 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -425,6 +425,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token RETURN_SYM %token RETURNS_SYM %token UDF_SONAME_SYM +%token UDF_RETURNS_SYM %token FUNCTION_SYM %token UNCOMMITTED_SYM %token UNDEFINED_SYM @@ -792,7 +793,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic END_OF_INPUT -%type <NONE> call sp_proc_stmts sp_proc_stmt +%type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt %type <num> sp_decl_idents sp_opt_inout sp_handler_type sp_hcond_list %type <spcondtype> sp_cond sp_hcond %type <spblock> sp_decls sp_decl @@ -917,14 +918,14 @@ prepare_src: THD *thd=YYTHD; LEX *lex= thd->lex; lex->prepared_stmt_code= $1; - lex->prepared_stmt_code_is_varref= false; + lex->prepared_stmt_code_is_varref= FALSE; } | '@' ident_or_text { THD *thd=YYTHD; LEX *lex= thd->lex; lex->prepared_stmt_code= $2; - lex->prepared_stmt_code_is_varref= true; + lex->prepared_stmt_code_is_varref= TRUE; }; execute: @@ -1507,7 +1508,11 @@ sp_opt_inout: sp_proc_stmts: /* Empty */ {} | sp_proc_stmts { Lex->query_tables= 0; } sp_proc_stmt ';' + ; +sp_proc_stmts1: + sp_proc_stmt ';' {} + | sp_proc_stmts1 { Lex->query_tables= 0; } sp_proc_stmt ';' ; sp_decls: @@ -2070,16 +2075,16 @@ sp_proc_stmt: i= new sp_instr_copen(sp->instructions(), lex->spcont, offset); sp->add_instr(i); } - | FETCH_SYM ident INTO + | FETCH_SYM sp_opt_fetch_noise ident INTO { LEX *lex= Lex; sp_head *sp= lex->sphead; uint offset; sp_instr_cfetch *i; - if (! lex->spcont->find_cursor(&$2, &offset)) + if (! lex->spcont->find_cursor(&$3, &offset)) { - net_printf(YYTHD, ER_SP_CURSOR_MISMATCH, $2.str); + net_printf(YYTHD, ER_SP_CURSOR_MISMATCH, $3.str); YYABORT; } i= new sp_instr_cfetch(sp->instructions(), lex->spcont, offset); @@ -2104,6 +2109,12 @@ sp_proc_stmt: } ; +sp_opt_fetch_noise: + /* Empty */ + | NEXT_SYM FROM + | FROM + ; + sp_fetch_list: ident { @@ -2164,7 +2175,7 @@ sp_if: sp->push_backpatch(i, ctx->push_label((char *)"", 0)); sp->add_instr(i); } - sp_proc_stmts + sp_proc_stmts1 { sp_head *sp= Lex->sphead; sp_pcontext *ctx= Lex->spcont; @@ -2186,7 +2197,7 @@ sp_if: sp_elseifs: /* Empty */ | ELSEIF_SYM sp_if - | ELSE sp_proc_stmts + | ELSE sp_proc_stmts1 ; sp_case: @@ -2218,7 +2229,7 @@ sp_case: lex->query_tables= 0; sp->add_instr(i); } - sp_proc_stmts + sp_proc_stmts1 { sp_head *sp= Lex->sphead; sp_pcontext *ctx= Lex->spcont; @@ -2247,7 +2258,7 @@ sp_whens: sp->add_instr(i); } - | ELSE sp_proc_stmts {} + | ELSE sp_proc_stmts1 {} | WHEN_SYM sp_case {} ; @@ -2325,7 +2336,7 @@ sp_unlabeled_control: lex->spcont= ctx->pop_context(); } | LOOP_SYM - sp_proc_stmts END LOOP_SYM + sp_proc_stmts1 END LOOP_SYM { LEX *lex= Lex; uint ip= lex->sphead->instructions(); @@ -2348,7 +2359,7 @@ sp_unlabeled_control: lex->query_tables= 0; sp->add_instr(i); } - sp_proc_stmts END WHILE_SYM + sp_proc_stmts1 END WHILE_SYM { LEX *lex= Lex; uint ip= lex->sphead->instructions(); @@ -2357,7 +2368,7 @@ sp_unlabeled_control: lex->sphead->add_instr(i); } - | REPEAT_SYM sp_proc_stmts UNTIL_SYM expr END REPEAT_SYM + | REPEAT_SYM sp_proc_stmts1 UNTIL_SYM expr END REPEAT_SYM { LEX *lex= Lex; uint ip= lex->sphead->instructions(); @@ -2388,20 +2399,20 @@ trg_event: ; create2: - '(' create2a {} - | opt_create_table_options create3 {} - | LIKE table_ident - { - LEX *lex=Lex; - if (!(lex->name= (char *)$2)) + '(' create2a {} + | opt_create_table_options create3 {} + | LIKE table_ident + { + LEX *lex=Lex; + if (!(lex->name= (char *)$2)) YYABORT; - } - | '(' LIKE table_ident ')' - { - LEX *lex=Lex; - if (!(lex->name= (char *)$3)) + } + | '(' LIKE table_ident ')' + { + LEX *lex=Lex; + if (!(lex->name= (char *)$3)) YYABORT; - } + } ; create2a: @@ -2493,19 +2504,19 @@ create_table_options: | create_table_option ',' create_table_options; create_table_option: - ENGINE_SYM opt_equal storage_engines { Lex->create_info.db_type= $3; } - | TYPE_SYM opt_equal storage_engines { Lex->create_info.db_type= $3; WARN_DEPRECATED("TYPE=storage_engine","ENGINE=storage_engine"); } + ENGINE_SYM opt_equal storage_engines { Lex->create_info.db_type= $3; Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; } + | TYPE_SYM opt_equal storage_engines { Lex->create_info.db_type= $3; WARN_DEPRECATED("TYPE=storage_engine","ENGINE=storage_engine"); Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; } | MAX_ROWS opt_equal ulonglong_num { Lex->create_info.max_rows= $3; Lex->create_info.used_fields|= HA_CREATE_USED_MAX_ROWS;} | MIN_ROWS opt_equal ulonglong_num { Lex->create_info.min_rows= $3; Lex->create_info.used_fields|= HA_CREATE_USED_MIN_ROWS;} | AVG_ROW_LENGTH opt_equal ULONG_NUM { Lex->create_info.avg_row_length=$3; Lex->create_info.used_fields|= HA_CREATE_USED_AVG_ROW_LENGTH;} - | PASSWORD opt_equal TEXT_STRING_sys { Lex->create_info.password=$3.str; } - | COMMENT_SYM opt_equal TEXT_STRING_sys { Lex->create_info.comment=$3.str; } + | PASSWORD opt_equal TEXT_STRING_sys { Lex->create_info.password=$3.str; Lex->create_info.used_fields|= HA_CREATE_USED_PASSWORD; } + | COMMENT_SYM opt_equal TEXT_STRING_sys { Lex->create_info.comment=$3.str; Lex->create_info.used_fields|= HA_CREATE_USED_COMMENT; } | AUTO_INC opt_equal ulonglong_num { Lex->create_info.auto_increment_value=$3; Lex->create_info.used_fields|= HA_CREATE_USED_AUTO;} | PACK_KEYS_SYM opt_equal ULONG_NUM { Lex->create_info.table_options|= $3 ? HA_OPTION_PACK_KEYS : HA_OPTION_NO_PACK_KEYS; Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS;} | PACK_KEYS_SYM opt_equal DEFAULT { Lex->create_info.table_options&= ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS); Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS;} - | CHECKSUM_SYM opt_equal ULONG_NUM { Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_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; } - | ROW_FORMAT_SYM opt_equal row_types { Lex->create_info.row_type= $3; } + | 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;} @@ -2527,9 +2538,9 @@ create_table_option: | default_charset | default_collation | INSERT_METHOD opt_equal merge_insert_types { Lex->create_info.merge_insert_method= $3; Lex->create_info.used_fields|= HA_CREATE_USED_INSERT_METHOD;} - | DATA_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys - { Lex->create_info.data_file_name= $4.str; } - | INDEX_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys { Lex->create_info.index_file_name= $4.str; }; + | DATA_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys { Lex->create_info.data_file_name= $4.str; Lex->create_info.used_fields|= HA_CREATE_USED_DATADIR; } + | INDEX_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys { Lex->create_info.index_file_name= $4.str; Lex->create_info.used_fields|= HA_CREATE_USED_INDEXDIR; } + ; default_charset: opt_default charset opt_equal charset_name_or_default @@ -2739,10 +2750,21 @@ type: if (YYTHD->variables.sql_mode & MODE_MAXDB) $$=FIELD_TYPE_DATETIME; else + { + /* + Unlike other types TIMESTAMP fields are NOT NULL by default. + */ + Lex->type|= NOT_NULL_FLAG; $$=FIELD_TYPE_TIMESTAMP; + } } - | TIMESTAMP '(' NUM ')' { Lex->length=$3.str; - $$=FIELD_TYPE_TIMESTAMP; } + | TIMESTAMP '(' NUM ')' + { + LEX *lex= Lex; + lex->length= $3.str; + lex->type|= NOT_NULL_FLAG; + $$= FIELD_TYPE_TIMESTAMP; + } | DATETIME { $$=FIELD_TYPE_DATETIME; } | TINYBLOB { Lex->charset=&my_charset_bin; $$=FIELD_TYPE_TINY_BLOB; } @@ -3130,7 +3152,17 @@ key_list: key_part: ident { $$=new key_part_spec($1.str); } - | ident '(' NUM ')' { $$=new key_part_spec($1.str,(uint) atoi($3.str)); }; + | ident '(' NUM ')' + { + int key_part_len= atoi($3.str); + if (!key_part_len) + { + my_printf_error(ER_UNKNOWN_ERROR, + "Key part '%s' length cannot be 0", + MYF(0), $1.str); + } + $$=new key_part_spec($1.str,(uint) key_part_len); + }; opt_ident: /* empty */ { $$=(char*) 0; } /* Defaultlength */ @@ -3167,8 +3199,7 @@ alter: lex->create_info.db_type= DB_TYPE_DEFAULT; lex->create_info.default_table_charset= NULL; lex->create_info.row_type= ROW_TYPE_NOT_USED; - lex->alter_info.reset(); - lex->alter_info.is_simple= 1; + lex->alter_info.reset(); lex->alter_info.flags= 0; } alter_list @@ -3233,27 +3264,27 @@ alter_list: | alter_list ',' alter_list_item; add_column: - ADD opt_column + ADD opt_column { LEX *lex=Lex; - lex->change=0; - lex->alter_info.flags|= ALTER_ADD_COLUMN; + lex->change=0; + lex->alter_info.flags|= ALTER_ADD_COLUMN; }; alter_list_item: - add_column column_def opt_place { Lex->alter_info.is_simple= 0; } - | ADD key_def - { - LEX *lex=Lex; - lex->alter_info.is_simple= 0; - lex->alter_info.flags|= ALTER_ADD_INDEX; + add_column column_def opt_place { } + | ADD key_def + { + Lex->alter_info.flags|= ALTER_ADD_INDEX; } - | add_column '(' field_list ')' { Lex->alter_info.is_simple= 0; } + | add_column '(' field_list ')' + { + Lex->alter_info.flags|= ALTER_ADD_COLUMN | ALTER_ADD_INDEX; + } | CHANGE opt_column field_ident { LEX *lex=Lex; - lex->change= $3.str; - lex->alter_info.is_simple= 0; + lex->change= $3.str; lex->alter_info.flags|= ALTER_CHANGE_COLUMN; } field_spec opt_place @@ -3264,7 +3295,6 @@ alter_list_item: lex->default_value= lex->on_update_value= 0; lex->comment=0; lex->charset= NULL; - lex->alter_info.is_simple= 0; lex->alter_info.flags|= ALTER_CHANGE_COLUMN; } type opt_attribute @@ -3284,17 +3314,18 @@ alter_list_item: { LEX *lex=Lex; lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::COLUMN, - $3.str)); - lex->alter_info.is_simple= 0; + $3.str)); lex->alter_info.flags|= ALTER_DROP_COLUMN; } - | DROP FOREIGN KEY_SYM opt_ident { Lex->alter_info.is_simple= 0; } + | DROP FOREIGN KEY_SYM opt_ident + { + Lex->alter_info.flags|= ALTER_DROP_INDEX; + } | DROP PRIMARY_SYM KEY_SYM { LEX *lex=Lex; lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::KEY, primary_key_name)); - lex->alter_info.is_simple= 0; lex->alter_info.flags|= ALTER_DROP_INDEX; } | DROP key_or_index field_ident @@ -3302,25 +3333,32 @@ alter_list_item: LEX *lex=Lex; lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::KEY, $3.str)); - lex->alter_info.is_simple= 0; lex->alter_info.flags|= ALTER_DROP_INDEX; } - | DISABLE_SYM KEYS { Lex->alter_info.keys_onoff= DISABLE; } - | ENABLE_SYM KEYS { Lex->alter_info.keys_onoff= ENABLE; } + | DISABLE_SYM KEYS + { + LEX *lex=Lex; + lex->alter_info.keys_onoff= DISABLE; + lex->alter_info.flags|= ALTER_KEYS_ONOFF; + } + | ENABLE_SYM KEYS + { + LEX *lex=Lex; + lex->alter_info.keys_onoff= ENABLE; + lex->alter_info.flags|= ALTER_KEYS_ONOFF; + } | ALTER opt_column field_ident SET DEFAULT signed_literal { LEX *lex=Lex; lex->alter_info.alter_list.push_back(new Alter_column($3.str,$6)); - lex->alter_info.is_simple= 0; - lex->alter_info.flags|= ALTER_CHANGE_COLUMN; + lex->alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT; } | ALTER opt_column field_ident DROP DEFAULT { LEX *lex=Lex; lex->alter_info.alter_list.push_back(new Alter_column($3.str, (Item*) 0)); - lex->alter_info.is_simple= 0; - lex->alter_info.flags|= ALTER_CHANGE_COLUMN; + lex->alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT; } | RENAME opt_to table_ident { @@ -3350,22 +3388,20 @@ alter_list_item: YYABORT; } LEX *lex= Lex; - lex->create_info.table_charset= + lex->create_info.table_charset= lex->create_info.default_table_charset= $5; lex->create_info.used_fields|= (HA_CREATE_USED_CHARSET | HA_CREATE_USED_DEFAULT_CHARSET); - lex->alter_info.is_simple= 0; + lex->alter_info.flags|= ALTER_CONVERT; } - | create_table_options_space_separated + | create_table_options_space_separated { LEX *lex=Lex; - lex->alter_info.is_simple= 0; lex->alter_info.flags|= ALTER_OPTIONS; } - | order_clause + | order_clause { LEX *lex=Lex; - lex->alter_info.is_simple= 0; lex->alter_info.flags|= ALTER_ORDER; }; @@ -3399,7 +3435,7 @@ opt_to: */ slave: - START_SYM SLAVE slave_thread_opts + START_SYM SLAVE slave_thread_opts { LEX *lex=Lex; lex->sql_command = SQLCOM_SLAVE_START; @@ -4242,7 +4278,7 @@ simple_expr: | CONCAT '(' expr_list ')' { $$= new Item_func_concat(* $3); } | CONCAT_WS '(' expr ',' expr_list ')' - { $$= new Item_func_concat_ws($3, *$5); } + { $5->push_front($3); $$= new Item_func_concat_ws(*$5); } | CONTAINS_SYM '(' expr ',' expr ')' { $$= create_func_contains($3, $5); } | CONVERT_TZ_SYM '(' expr ',' expr ',' expr ')' @@ -5395,15 +5431,11 @@ select_var_ident: into: INTO OUTFILE TEXT_STRING_sys { - LEX *lex=Lex; - if (!lex->describe) - { - lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - if (!(lex->exchange= new sql_exchange($3.str,0))) - YYABORT; - if (!(lex->result= new select_export(lex->exchange))) - YYABORT; - } + LEX *lex= Lex; + lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + if (!(lex->exchange= new sql_exchange($3.str, 0)) || + !(lex->result= new select_export(lex->exchange))) + YYABORT; } opt_field_term opt_line_term | INTO DUMPFILE TEXT_STRING_sys @@ -5469,7 +5501,7 @@ drop: lex->drop_if_exists=$3; lex->name=$4.str; } - | DROP FUNCTION_SYM if_exists sp_name opt_restrict + | DROP FUNCTION_SYM if_exists sp_name { LEX *lex=Lex; if (lex->sphead) @@ -5481,7 +5513,7 @@ drop: lex->drop_if_exists= $3; lex->spname= $4; } - | DROP PROCEDURE if_exists sp_name opt_restrict + | DROP PROCEDURE if_exists sp_name { LEX *lex=Lex; if (lex->sphead) @@ -5702,10 +5734,13 @@ expr_or_default: opt_insert_update: /* empty */ | ON DUPLICATE_SYM - { /* for simplisity, let's forget about - INSERT ... SELECT ... UPDATE - for a moment */ - if (Lex->sql_command != SQLCOM_INSERT) + { + LEX *lex= Lex; + /* + For simplicity, let's forget about INSERT ... SELECT ... UPDATE + for a moment. + */ + if (lex->sql_command != SQLCOM_INSERT) { yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; @@ -5725,12 +5760,14 @@ update: lex->lock_option= TL_UNLOCK; /* Will be set later */ } opt_low_priority opt_ignore join_table_list - SET update_list where_clause opt_order_clause delete_limit_clause + SET update_list { LEX *lex= Lex; - Select->set_lock_for_tables($3); if (lex->select_lex.table_list.elements > 1) + { lex->sql_command= SQLCOM_UPDATE_MULTI; + lex->multi_lock_option= $3; + } else if (lex->select_lex.get_table_list()->derived) { /* it is single table update and it is update of derived table */ @@ -5738,7 +5775,10 @@ update: lex->select_lex.get_table_list()->alias, "UPDATE"); YYABORT; } + else + Select->set_lock_for_tables($3); } + where_clause opt_order_clause delete_limit_clause {} ; update_list: @@ -6319,15 +6359,28 @@ field_term_list: | field_term; field_term: - TERMINATED BY text_string { Lex->exchange->field_term= $3;} + TERMINATED BY text_string + { + DBUG_ASSERT(Lex->exchange); + Lex->exchange->field_term= $3; + } | OPTIONALLY ENCLOSED BY text_string { - LEX *lex=Lex; - lex->exchange->enclosed= $4; - lex->exchange->opt_enclosed=1; + LEX *lex= Lex; + DBUG_ASSERT(lex->exchange); + lex->exchange->enclosed= $4; + lex->exchange->opt_enclosed= 1; } - | ENCLOSED BY text_string { Lex->exchange->enclosed= $3;} - | ESCAPED BY text_string { Lex->exchange->escaped= $3;}; + | ENCLOSED BY text_string + { + DBUG_ASSERT(Lex->exchange); + Lex->exchange->enclosed= $3; + } + | ESCAPED BY text_string + { + DBUG_ASSERT(Lex->exchange); + Lex->exchange->escaped= $3; + }; opt_line_term: /* empty */ @@ -6338,13 +6391,24 @@ line_term_list: | line_term; line_term: - TERMINATED BY text_string { Lex->exchange->line_term= $3;} - | STARTING BY text_string { Lex->exchange->line_start= $3;}; + TERMINATED BY text_string + { + DBUG_ASSERT(Lex->exchange); + Lex->exchange->line_term= $3; + } + | STARTING BY text_string + { + DBUG_ASSERT(Lex->exchange); + Lex->exchange->line_start= $3; + }; opt_ignore_lines: /* empty */ - | IGNORE_SYM NUM LINES - { Lex->exchange->skip_lines=atol($2.str); }; + | IGNORE_SYM NUM LINES + { + DBUG_ASSERT(Lex->exchange); + Lex->exchange->skip_lines= atol($2.str); + }; /* Common definitions */ @@ -6505,7 +6569,7 @@ simple_ident: $$= (sel->parsing_place != IN_HAVING || sel->get_in_sum_expr() > 0) ? (Item*) new Item_field(NullS,NullS,$1.str) : - (Item*) new Item_ref(0,0, NullS,NullS,$1.str); + (Item*) new Item_ref(NullS,NullS,$1.str); } } | simple_ident_q { $$= $1; } @@ -6518,7 +6582,7 @@ simple_ident_nospvar: $$= (sel->parsing_place != IN_HAVING || sel->get_in_sum_expr() > 0) ? (Item*) new Item_field(NullS,NullS,$1.str) : - (Item*) new Item_ref(0,0,NullS,NullS,$1.str); + (Item*) new Item_ref(NullS,NullS,$1.str); } | simple_ident_q { $$= $1; } ; @@ -6588,7 +6652,7 @@ simple_ident_q: $$= (sel->parsing_place != IN_HAVING || sel->get_in_sum_expr() > 0) ? (Item*) new Item_field(NullS,$1.str,$3.str) : - (Item*) new Item_ref(0,0,NullS,$1.str,$3.str); + (Item*) new Item_ref(NullS,$1.str,$3.str); } } | '.' ident '.' ident @@ -6605,7 +6669,7 @@ simple_ident_q: $$= (sel->parsing_place != IN_HAVING || sel->get_in_sum_expr() > 0) ? (Item*) new Item_field(NullS,$2.str,$4.str) : - (Item*) new Item_ref(0,0,NullS,$2.str,$4.str); + (Item*) new Item_ref(NullS, $2.str, $4.str); } | ident '.' ident '.' ident { @@ -6623,8 +6687,8 @@ simple_ident_q: (Item*) new Item_field((YYTHD->client_capabilities & CLIENT_NO_SCHEMA ? NullS : $1.str), $3.str, $5.str) : - (Item*) new Item_ref(0,0,(YYTHD->client_capabilities & - CLIENT_NO_SCHEMA ? NullS : $1.str), + (Item*) new Item_ref((YYTHD->client_capabilities & + CLIENT_NO_SCHEMA ? NullS : $1.str), $3.str, $5.str); }; @@ -6809,6 +6873,7 @@ keyword: | EXPANSION_SYM {} | EXTENDED_SYM {} | FAST_SYM {} + | FOUND_SYM {} | DISABLE_SYM {} | ENABLE_SYM {} | FULL {} @@ -6958,6 +7023,7 @@ keyword: | TIME_SYM {} | TYPE_SYM {} | TYPES_SYM {} + | UDF_RETURNS_SYM {} | FUNCTION_SYM {} | UNCOMMITTED_SYM {} | UNDEFINED_SYM {} diff --git a/sql/stacktrace.c b/sql/stacktrace.c index d0478052fb1..322d647e741 100644 --- a/sql/stacktrace.c +++ b/sql/stacktrace.c @@ -197,7 +197,7 @@ terribly wrong...\n"); fprintf(stderr, "Stack trace seems successful - bottom reached\n"); end: - fprintf(stderr, "Please read http://www.mysql.com/doc/en/Using_stack_trace.html and follow instructions on how to resolve the stack trace. Resolved\n\ + fprintf(stderr, "Please read http://dev.mysql.com/doc/mysql/en/Using_stack_trace.html and follow instructions on how to resolve the stack trace. Resolved\n\ stack trace is much more helpful in diagnosing the problem, so please do \n\ resolve it\n"); } diff --git a/sql/strfunc.cc b/sql/strfunc.cc index d00e57df5a1..b5255e9be06 100644 --- a/sql/strfunc.cc +++ b/sql/strfunc.cc @@ -39,16 +39,13 @@ static const char field_separator=','; -ulonglong find_set(TYPELIB *lib, const char *str, uint length, char **err_pos, - uint *err_len, bool *set_warning) +ulonglong find_set(TYPELIB *lib, const char *str, uint length, CHARSET_INFO *cs, + char **err_pos, uint *err_len, bool *set_warning) { - const char *end= str + length; - *err_pos= 0; // No error yet - while (end > str && my_isspace(system_charset_info, end[-1])) - end--; - - *err_len= 0; + CHARSET_INFO *strip= cs ? cs : &my_charset_latin1; + const char *end= str + strip->cset->lengthsp(strip, str, length); ulonglong found= 0; + *err_pos= 0; // No error yet if (str != end) { const char *start= str; @@ -59,7 +56,8 @@ ulonglong find_set(TYPELIB *lib, const char *str, uint length, char **err_pos, for (; pos != end && *pos != field_separator; pos++) ; var_len= (uint) (pos - start); - uint find= find_type(lib, start, var_len, 0); + uint find= cs ? find_type2(lib, start, var_len, cs) : + find_type(lib, start, var_len, (bool) 0); if (!find) { *err_pos= (char*) start; @@ -117,6 +115,47 @@ uint find_type(TYPELIB *lib, const char *find, uint length, bool part_match) /* + Find a string in a list of strings according to collation + + SYNOPSIS + find_type2() + lib TYPELIB (struct of pointer to values + count) + x String to find + length String length + cs Character set + collation to use for comparison + + NOTES + + RETURN + 0 No matching value + >0 Offset+1 in typelib for matched string +*/ + +uint find_type2(TYPELIB *typelib, const char *x, uint length, CHARSET_INFO *cs) +{ + int find,pos,findpos; + const char *j; + DBUG_ENTER("find_type2"); + DBUG_PRINT("enter",("x: '%s' lib: 0x%lx",x,typelib)); + + if (!typelib->count) + { + DBUG_PRINT("exit",("no count")); + DBUG_RETURN(0); + } + LINT_INIT(findpos); + for (find=0, pos=0 ; (j=typelib->type_names[pos]) ; pos++) + { + if (!my_strnncoll(cs, (const uchar*) x, length, + (const uchar*) j, typelib->type_lengths[pos])) + DBUG_RETURN(pos+1); + } + DBUG_PRINT("exit",("Couldn't find type")); + DBUG_RETURN(0); +} /* find_type */ + + +/* Check if the first word in a string is one of the ones in TYPELIB SYNOPSIS diff --git a/sql/table.cc b/sql/table.cc index e372de57177..0116cf180c1 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -379,6 +379,24 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, fix_type_pointers(&int_array,&outparam->fieldnames,1,&names); fix_type_pointers(&int_array,outparam->intervals,interval_count, &names); + + { + /* Set ENUM and SET lengths */ + TYPELIB *interval; + for (interval= outparam->intervals; + interval < outparam->intervals + interval_count; + interval++) + { + uint count= (uint) (interval->count + 1) * sizeof(uint); + if (!(interval->type_lengths= (uint *) alloc_root(&outparam->mem_root, + count))) + goto err_not_open; + for (count= 0; count < interval->count; count++) + interval->type_lengths[count]= strlen(interval->type_names[count]); + interval->type_lengths[count]= 0; + } + } + if (keynames) fix_type_pointers(&int_array,&outparam->keynames,1,&keynames); VOID(my_close(file,MYF(MY_WME))); @@ -731,6 +749,14 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, outparam->crashed=((err == HA_ERR_CRASHED_ON_USAGE) && outparam->file->auto_repair() && !(ha_open_flags & HA_OPEN_FOR_REPAIR)); + + if (err==HA_ERR_NO_SUCH_TABLE) + { + /* The table did not exists in storage engine, use same error message + as if the .frm file didn't exist */ + error= 1; + my_errno= ENOENT; + } goto err_not_open; /* purecov: inspected */ } } @@ -1030,14 +1056,19 @@ TYPELIB *typelib(List<String> &strings) return 0; result->count=strings.elements; result->name=""; - if (!(result->type_names=(const char **) sql_alloc(sizeof(char *)* - (result->count+1)))) + uint nbytes= (sizeof(char*) + sizeof(uint)) * (result->count + 1); + if (!(result->type_names= (const char**) sql_alloc(nbytes))) return 0; + result->type_lengths= (uint*) (result->type_names + result->count + 1); List_iterator<String> it(strings); String *tmp; for (uint i=0; (tmp=it++) ; i++) - result->type_names[i]=tmp->ptr(); - result->type_names[result->count]=0; // End marker + { + result->type_names[i]= tmp->ptr(); + result->type_lengths[i]= tmp->length(); + } + result->type_names[result->count]= 0; // End marker + result->type_lengths[result->count]= 0; return result; } diff --git a/sql/table.h b/sql/table.h index 3e2a61d5a21..d5bbd0ac2e2 100644 --- a/sql/table.h +++ b/sql/table.h @@ -70,6 +70,16 @@ typedef struct st_filesort_info } FILESORT_INFO; +/* + Values in this enum are used to indicate during which operations value + of TIMESTAMP field should be set to current timestamp. +*/ +enum timestamp_auto_set_type +{ + TIMESTAMP_NO_AUTO_SET= 0, TIMESTAMP_AUTO_SET_ON_INSERT= 1, + TIMESTAMP_AUTO_SET_ON_UPDATE= 2, TIMESTAMP_AUTO_SET_ON_BOTH= 3 +}; + /* Table cache entry struct */ class Field_timestamp; @@ -113,16 +123,19 @@ struct st_table { uint status; /* Used by postfix.. */ uint system; /* Set if system record */ - /* - These two members hold offset in record + 1 for TIMESTAMP field - with NOW() as default value or/and with ON UPDATE NOW() option. - If 0 then such field is absent in this table or auto-set for default - or/and on update should be temporally disabled for some reason. - These values is setup to offset value for each statement in open_table() - and turned off in statement processing code (see mysql_update as example). + /* + If this table has TIMESTAMP field with auto-set property (pointed by + timestamp_field member) then this variable indicates during which + operations (insert only/on update/in both cases) we should set this + field to current timestamp. If there are no such field in this table + or we should not automatically set its value during execution of current + statement then the variable contains TIMESTAMP_NO_AUTO_SET (i.e. 0). + + Value of this variable is set for each statement in open_table() and + if needed cleared later in statement processing code (see mysql_update() + as example). */ - ulong timestamp_default_now; - ulong timestamp_on_update_now; + timestamp_auto_set_type timestamp_field_type; /* Index of auto-updated TIMESTAMP field in field array */ uint timestamp_field_offset; @@ -151,6 +164,14 @@ struct st_table { *found_next_number_field, /* Set on open */ *rowid_field; Field_timestamp *timestamp_field; +#if MYSQL_VERSION_ID < 40100 + /* + Indicates whenever we have to set field_length members of all TIMESTAMP + fields to 19 (to honour 'new_mode' variable) or to original + field_length values. + */ + my_bool timestamp_mode; +#endif my_string comment; /* Comment about table */ CHARSET_INFO *table_charset; /* Default charset of string fields */ REGINFO reginfo; /* field connections */ @@ -159,6 +180,7 @@ struct st_table { /* Table's triggers, 0 if there are no of them */ Table_triggers_list *triggers; + /* A pair "database_name\0table_name\0", widely used as simply a db name */ char *table_cache_key; char *table_name,*real_name,*path; uint key_length; /* Length of key */ diff --git a/sql/time.cc b/sql/time.cc index f47d7652864..562f9956ccc 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -746,13 +746,7 @@ const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format, void make_time(const DATE_TIME_FORMAT *format __attribute__((unused)), const TIME *l_time, String *str) { - long length= my_sprintf((char*) str->ptr(), - ((char*) str->ptr(), - "%s%02d:%02d:%02d", - (l_time->neg ? "-" : ""), - l_time->hour, - l_time->minute, - l_time->second)); + uint length= (uint) my_time_to_str(l_time, (char*) str->ptr()); str->length(length); str->set_charset(&my_charset_bin); } @@ -761,12 +755,7 @@ void make_time(const DATE_TIME_FORMAT *format __attribute__((unused)), void make_date(const DATE_TIME_FORMAT *format __attribute__((unused)), const TIME *l_time, String *str) { - long length= my_sprintf((char*) str->ptr(), - ((char*) str->ptr(), - "%04d-%02d-%02d", - l_time->year, - l_time->month, - l_time->day)); + uint length= (uint) my_date_to_str(l_time, (char*) str->ptr()); str->length(length); str->set_charset(&my_charset_bin); } @@ -775,15 +764,7 @@ void make_date(const DATE_TIME_FORMAT *format __attribute__((unused)), void make_datetime(const DATE_TIME_FORMAT *format __attribute__((unused)), const TIME *l_time, String *str) { - long length= my_sprintf((char*) str->ptr(), - ((char*) str->ptr(), - "%04d-%02d-%02d %02d:%02d:%02d", - l_time->year, - l_time->month, - l_time->day, - l_time->hour, - l_time->minute, - l_time->second)); + uint length= (uint) my_datetime_to_str(l_time, (char*) str->ptr()); str->length(length); str->set_charset(&my_charset_bin); } @@ -899,38 +880,4 @@ ulonglong TIME_to_ulonglong(const TIME *time) return 0; } - -/* - Convert struct DATE/TIME/DATETIME value to string using built-in - MySQL time conversion formats. - - SYNOPSIS - TIME_to_string() - - NOTE - The string must have at least MAX_DATE_REP_LENGTH bytes reserved. -*/ - -void TIME_to_string(const TIME *time, String *str) -{ - switch (time->time_type) { - case MYSQL_TIMESTAMP_DATETIME: - make_datetime((DATE_TIME_FORMAT*) 0, time, str); - break; - case MYSQL_TIMESTAMP_DATE: - make_date((DATE_TIME_FORMAT*) 0, time, str); - break; - case MYSQL_TIMESTAMP_TIME: - make_time((DATE_TIME_FORMAT*) 0, time, str); - break; - case MYSQL_TIMESTAMP_NONE: - case MYSQL_TIMESTAMP_ERROR: - str->length(0); - str->set_charset(&my_charset_bin); - break; - default: - DBUG_ASSERT(0); - } -} - #endif diff --git a/sql/tztime.cc b/sql/tztime.cc index 9fd0694ade4..50c496577e0 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -1456,6 +1456,10 @@ tz_init_table_list(TABLE_LIST *tz_tabs, TABLE_LIST ***global_next_ptr) link this list to the end of global table list (it will read and update accordingly variable pointed by global_next_ptr for this). + NOTE + my_tz_check_n_skip_implicit_tables() function depends on fact that + elements of list created are allocated as TABLE_LIST[4] array. + RETURN VALUES Returns pointer to first TABLE_LIST object, (could be 0 if time zone tables don't exist) and &fake_time_zone_tables_list in case of error. @@ -1662,8 +1666,8 @@ end_with_setting_default_tz: /* If we have default time zone try to load it */ if (default_tzname) { - String tzname(default_tzname, &my_charset_latin1); - if (!(global_system_variables.time_zone= my_tz_find(&tzname, tables))) + String tmp_tzname(default_tzname, &my_charset_latin1); + if (!(global_system_variables.time_zone= my_tz_find(&tmp_tzname, tables))) { sql_print_error("Fatal error: Illegal or unknown default time zone '%s'", default_tzname); diff --git a/sql/tztime.h b/sql/tztime.h index 1325e921c67..6d2388bb160 100644 --- a/sql/tztime.h +++ b/sql/tztime.h @@ -64,6 +64,35 @@ extern Time_zone * my_tz_find(const String *name, TABLE_LIST *tz_tables); extern my_bool my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap); extern void my_tz_free(); + +/* + Check if we have pointer to the beggining of list of implictly used + time zone tables and fast-forward to its end. + + SYNOPSIS + my_tz_check_n_skip_implicit_tables() + table - (in/out) pointer to element of table list to check + tz_tables - list of implicitly used time zone tables received + from my_tz_get_table_list() function. + + NOTE + This function relies on my_tz_get_table_list() implementation. + + RETURN VALUE + TRUE - if table points to the beggining of tz_tables list + FALSE - otherwise. +*/ +inline bool my_tz_check_n_skip_implicit_tables(TABLE_LIST **table, + TABLE_LIST *tz_tables) +{ + if (*table == tz_tables) + { + (*table)+= 3; + return TRUE; + } + return FALSE; +} + /* Maximum length of time zone name that we support (Time zone name is char(64) in db) diff --git a/sql/unireg.cc b/sql/unireg.cc index 2a6abdb1cfd..decac2a0549 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -199,7 +199,7 @@ err3: keys number of keys to create key_info Keys to create db_file Handler to use. May be zero, in which case we use - create_info->db_type + create_info->db_type RETURN 0 ok 1 error @@ -213,11 +213,11 @@ int rea_create_table(THD *thd, my_string file_name, DBUG_ENTER("rea_create_table"); if (mysql_create_frm(thd, file_name, create_info, - create_fields, keys, key_info, NULL)) + create_fields, keys, key_info, NULL)) DBUG_RETURN(1); - if (ha_create_table(file_name,create_info,0)) + if (!create_info->frm_only && ha_create_table(file_name,create_info,0)) { - my_delete(file_name,MYF(0)); + my_delete(file_name,MYF(0)); DBUG_RETURN(1); } DBUG_RETURN(0); |