summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <petr@mysql.com>2004-11-04 02:57:31 +0300
committerunknown <petr@mysql.com>2004-11-04 02:57:31 +0300
commit14437e9c8f82a77b4f3f04e5b24b3c58db8e3a2b (patch)
treebae058b5ba3fbcdf419cf63b3c15c4aed5e43d4a /sql
parentbb63229331d461baedecd0ef439914ff9458e441 (diff)
parente981c836104675533a2eb6810f7c1606377b0491 (diff)
downloadmariadb-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')
-rw-r--r--sql/Makefile.am2
-rw-r--r--sql/discover.cc43
-rw-r--r--sql/examples/ha_archive.cc584
-rw-r--r--sql/examples/ha_archive.h15
-rw-r--r--sql/examples/ha_example.cc4
-rw-r--r--sql/examples/ha_tina.cc8
-rw-r--r--sql/examples/ha_tina.h6
-rw-r--r--sql/field.cc265
-rw-r--r--sql/field.h32
-rw-r--r--sql/field_conv.cc28
-rw-r--r--sql/filesort.cc85
-rw-r--r--sql/ha_berkeley.cc16
-rw-r--r--sql/ha_berkeley.h2
-rw-r--r--sql/ha_heap.cc19
-rw-r--r--sql/ha_innodb.cc462
-rw-r--r--sql/ha_innodb.h12
-rw-r--r--sql/ha_isam.cc8
-rw-r--r--sql/ha_isammrg.cc4
-rw-r--r--sql/ha_myisam.cc50
-rw-r--r--sql/ha_myisammrg.cc8
-rw-r--r--sql/ha_ndbcluster.cc1759
-rw-r--r--sql/ha_ndbcluster.h58
-rw-r--r--sql/handler.cc194
-rw-r--r--sql/handler.h59
-rw-r--r--sql/item.cc354
-rw-r--r--sql/item.h71
-rw-r--r--sql/item_cmpfunc.cc91
-rw-r--r--sql/item_cmpfunc.h17
-rw-r--r--sql/item_func.cc77
-rw-r--r--sql/item_func.h12
-rw-r--r--sql/item_geofunc.cc33
-rw-r--r--sql/item_geofunc.h62
-rw-r--r--sql/item_row.cc8
-rw-r--r--sql/item_row.h2
-rw-r--r--sql/item_strfunc.cc137
-rw-r--r--sql/item_strfunc.h22
-rw-r--r--sql/item_subselect.cc139
-rw-r--r--sql/item_subselect.h4
-rw-r--r--sql/item_sum.cc123
-rw-r--r--sql/item_sum.h11
-rw-r--r--sql/item_timefunc.cc35
-rw-r--r--sql/item_timefunc.h5
-rw-r--r--sql/lock.cc10
-rw-r--r--sql/log.cc29
-rw-r--r--sql/log_event.cc24
-rw-r--r--sql/log_event.h2
-rw-r--r--sql/mysql_priv.h44
-rw-r--r--sql/mysqld.cc162
-rw-r--r--sql/net_serv.cc2
-rw-r--r--sql/opt_range.cc124
-rw-r--r--sql/opt_sum.cc16
-rw-r--r--sql/password.c2
-rw-r--r--sql/protocol.cc20
-rw-r--r--sql/protocol.h4
-rw-r--r--sql/protocol_cursor.cc23
-rw-r--r--sql/records.cc30
-rw-r--r--sql/repl_failsafe.cc6
-rw-r--r--sql/repl_failsafe.h19
-rw-r--r--sql/set_var.cc32
-rw-r--r--sql/share/charsets/Index.xml18
-rw-r--r--sql/share/charsets/armscii8.xml18
-rw-r--r--sql/share/charsets/ascii.xml18
-rw-r--r--sql/share/charsets/cp1250.xml18
-rw-r--r--sql/share/charsets/cp1251.xml18
-rw-r--r--sql/share/charsets/cp1256.xml18
-rw-r--r--sql/share/charsets/cp1257.xml18
-rw-r--r--sql/share/charsets/cp850.xml18
-rw-r--r--sql/share/charsets/cp852.xml18
-rw-r--r--sql/share/charsets/cp866.xml18
-rw-r--r--sql/share/charsets/dec8.xml18
-rw-r--r--sql/share/charsets/geostd8.xml18
-rw-r--r--sql/share/charsets/greek.xml18
-rw-r--r--sql/share/charsets/hebrew.xml18
-rw-r--r--sql/share/charsets/hp8.xml18
-rw-r--r--sql/share/charsets/keybcs2.xml18
-rw-r--r--sql/share/charsets/koi8r.xml18
-rw-r--r--sql/share/charsets/koi8u.xml18
-rw-r--r--sql/share/charsets/latin1.xml18
-rw-r--r--sql/share/charsets/latin2.xml18
-rw-r--r--sql/share/charsets/latin5.xml18
-rw-r--r--sql/share/charsets/latin7.xml18
-rw-r--r--sql/share/charsets/macce.xml18
-rw-r--r--sql/share/charsets/macroman.xml18
-rw-r--r--sql/share/charsets/swe7.xml18
-rw-r--r--sql/slave.cc24
-rw-r--r--sql/slave.h16
-rw-r--r--sql/sp.cc8
-rw-r--r--sql/sp_head.cc2
-rw-r--r--sql/spatial.h9
-rw-r--r--sql/sql_acl.cc216
-rw-r--r--sql/sql_analyse.cc7
-rw-r--r--sql/sql_base.cc202
-rw-r--r--sql/sql_cache.cc126
-rw-r--r--sql/sql_cache.h10
-rw-r--r--sql/sql_class.cc178
-rw-r--r--sql/sql_class.h190
-rw-r--r--sql/sql_db.cc9
-rw-r--r--sql/sql_delete.cc9
-rw-r--r--sql/sql_derived.cc3
-rw-r--r--sql/sql_error.cc3
-rw-r--r--sql/sql_handler.cc664
-rw-r--r--sql/sql_help.cc2
-rw-r--r--sql/sql_insert.cc34
-rw-r--r--sql/sql_lex.cc67
-rw-r--r--sql/sql_lex.h14
-rw-r--r--sql/sql_list.h2
-rw-r--r--sql/sql_load.cc17
-rw-r--r--sql/sql_parse.cc386
-rw-r--r--sql/sql_prepare.cc83
-rw-r--r--sql/sql_repl.cc13
-rw-r--r--sql/sql_repl.h16
-rw-r--r--sql/sql_select.cc307
-rw-r--r--sql/sql_show.cc113
-rw-r--r--sql/sql_string.cc24
-rw-r--r--sql/sql_string.h8
-rw-r--r--sql/sql_table.cc298
-rw-r--r--sql/sql_trigger.cc7
-rw-r--r--sql/sql_udf.cc3
-rw-r--r--sql/sql_union.cc73
-rw-r--r--sql/sql_update.cc50
-rw-r--r--sql/sql_view.cc9
-rw-r--r--sql/sql_yacc.yy276
-rw-r--r--sql/stacktrace.c2
-rw-r--r--sql/strfunc.cc57
-rw-r--r--sql/table.cc39
-rw-r--r--sql/table.h40
-rw-r--r--sql/time.cc59
-rw-r--r--sql/tztime.cc8
-rw-r--r--sql/tztime.h29
-rw-r--r--sql/unireg.cc8
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, &not_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, &not_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,
&not_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, &not_used, &not_used2,
- &got_warning);
+ ulonglong tmp= find_set(typelib, from, length, field_charset,
+ &not_used, &not_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,
+ &not_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), &not_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,
+ &not_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, &not_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(&ltime, 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(&ltime);
value= TIME_to_ulonglong_time(&ltime);
- make_time((DATE_TIME_FORMAT *) 0, &ltime, &tmp);
- max_length= buff_length= tmp.length();
+ buff_length= (uint) my_time_to_str(&ltime, 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(&ltime);
value= (longlong) TIME_to_ulonglong_datetime(&ltime);
- make_datetime((DATE_TIME_FORMAT *) 0, &ltime, &tmp);
- max_length= buff_length= tmp.length();
+ buff_length= (uint) my_datetime_to_str(&ltime, 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,
&not_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(), &not_used,
- &not_used2, &not_used3);
+ (void) find_set(interval, res->ptr(), res->length(),
+ &my_charset_bin,
+ &not_used, &not_used2, &not_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(),&copy_func,
- tmp_from_field, group != 0,not_all_columns);
+ create_tmp_field(thd, table, arg, arg->type(), &copy_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, &copy_func,
- tmp_from_field, group != 0,
- not_all_columns || group !=0);
+ Field *new_field= create_tmp_field(thd, table, item, type, &copy_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, &not_used)))
+ if ((flag= test_if_order_by_key(order, table, nr, &not_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, &not_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(&quote_char, 1, system_charset_info);
- packet->append(name, length, system_charset_info);
- packet->append(&quote_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(&quote_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(&quote_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> &not_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> &not_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> &not_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> &not_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);