summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/mysql.cc79
-rw-r--r--mysql-test/r/innodb.result9
-rw-r--r--mysql-test/r/insert.result6
-rw-r--r--mysql-test/r/lock.result11
-rw-r--r--mysql-test/r/mysql.result15
-rw-r--r--mysql-test/t/innodb.test12
-rw-r--r--mysql-test/t/insert.test12
-rw-r--r--mysql-test/t/lock.test18
-rw-r--r--mysql-test/t/mysql.test6
-rw-r--r--mysys/my_lib.c97
-rw-r--r--sql/item_timefunc.cc35
-rw-r--r--sql/item_timefunc.h1
-rw-r--r--sql/lock.cc314
-rw-r--r--sql/sql_insert.cc7
-rw-r--r--sql/table.h9
15 files changed, 430 insertions, 201 deletions
diff --git a/client/mysql.cc b/client/mysql.cc
index 2001056dea1..cd4cbf49918 100644
--- a/client/mysql.cc
+++ b/client/mysql.cc
@@ -185,7 +185,7 @@ void tee_fprintf(FILE *file, const char *fmt, ...);
void tee_fputs(const char *s, FILE *file);
void tee_puts(const char *s, FILE *file);
void tee_putc(int c, FILE *file);
-static void tee_print_sized_data(const char *data, unsigned int length, unsigned int width);
+static void tee_print_sized_data(const char *, unsigned int, unsigned int, bool);
/* The names of functions that actually do the manipulation. */
static int get_options(int argc,char **argv);
static int com_quit(String *str,char*),
@@ -2311,35 +2311,52 @@ print_table_data(MYSQL_RES *result)
while ((cur= mysql_fetch_row(result)))
{
ulong *lengths= mysql_fetch_lengths(result);
- (void) tee_fputs("|", PAGER);
+ (void) tee_fputs("| ", PAGER);
mysql_field_seek(result, 0);
for (uint off= 0; off < mysql_num_fields(result); off++)
{
- const char *str= cur[off] ? cur[off] : "NULL";
- uint currlength;
- uint maxlength;
- uint numcells;
-
- field= mysql_fetch_field(result);
- maxlength= field->max_length;
- currlength= (uint) lengths[off];
- numcells= charset_info->cset->numcells(charset_info,
- str, str + currlength);
- if (maxlength > MAX_COLUMN_LENGTH)
+ const char *buffer;
+ uint data_length;
+ uint field_max_length;
+ bool right_justified;
+ uint visible_length;
+ uint extra_padding;
+
+ if (lengths[off] == 0)
+ {
+ buffer= "NULL";
+ data_length= 4;
+ }
+ else
{
- tee_print_sized_data(str, currlength, maxlength);
- tee_fputs(" |", PAGER);
+ buffer= cur[off];
+ data_length= (uint) lengths[off];
}
+
+ field= mysql_fetch_field(result);
+ field_max_length= field->max_length;
+
+ /*
+ How many text cells on the screen will this string span? If it contains
+ multibyte characters, then the number of characters we occupy on screen
+ will be fewer than the number of bytes we occupy in memory.
+
+ We need to find how much screen real-estate we will occupy to know how
+ many extra padding-characters we should send with the printing function.
+ */
+ visible_length= charset_info->cset->numcells(charset_info, buffer, buffer + data_length);
+ extra_padding= data_length - visible_length;
+
+ if (field_max_length > MAX_COLUMN_LENGTH)
+ tee_print_sized_data(buffer, data_length, MAX_COLUMN_LENGTH+extra_padding, FALSE);
else
{
- if (num_flag[off] != 0)
- tee_fprintf(PAGER, " %-*s|", maxlength + currlength - numcells, str);
+ if (num_flag[off] != 0) /* if it is numeric, we right-justify it */
+ tee_print_sized_data(buffer, data_length, field_max_length+extra_padding, TRUE);
else
- {
- tee_print_sized_data(str, currlength, maxlength);
- tee_fputs(" |", PAGER);
- }
+ tee_print_sized_data(buffer, data_length, field_max_length+extra_padding, FALSE);
}
+ tee_fputs(" | ", PAGER);
}
(void) tee_fputs("\n", PAGER);
}
@@ -2349,10 +2366,9 @@ print_table_data(MYSQL_RES *result)
static void
-tee_print_sized_data(const char *data, unsigned int length, unsigned int width)
+tee_print_sized_data(const char *data, unsigned int data_length, unsigned int total_bytes_to_send, bool right_justified)
{
/*
- It is not a number, so print each character justified to the left.
For '\0's print ASCII spaces instead, as '\0' is eaten by (at
least my) console driver, and that messes up the pretty table
grid. (The \0 is also the reason we can't use fprintf() .)
@@ -2360,9 +2376,14 @@ tee_print_sized_data(const char *data, unsigned int length, unsigned int width)
unsigned int i;
const char *p;
- tee_putc(' ', PAGER);
+ total_bytes_to_send -= 1;
+ /* Off by one, perhaps mistakenly accounting for a terminating NUL. */
+
+ if (right_justified)
+ for (i= 0; i < (total_bytes_to_send - data_length); i++)
+ tee_putc((int)' ', PAGER);
- for (i= 0, p= data; i < length; i+= 1, p+= 1)
+ for (i= 0, p= data; i < data_length; i+= 1, p+= 1)
{
if (*p == '\0')
tee_putc((int)' ', PAGER);
@@ -2370,9 +2391,9 @@ tee_print_sized_data(const char *data, unsigned int length, unsigned int width)
tee_putc((int)*p, PAGER);
}
- i+= 1;
- for ( ; i < width; i+= 1)
- tee_putc((int)' ', PAGER);
+ if (! right_justified)
+ for (i= 0; i < (total_bytes_to_send - data_length); i++)
+ tee_putc((int)' ', PAGER);
}
@@ -3361,7 +3382,7 @@ put_info(const char *str,INFO_TYPE info_type, uint error, const char *sqlstate)
if (info_type == INFO_ERROR)
{
if (!opt_nobeep)
- putchar('\007'); /* This should make a bell */
+ putchar('\a'); /* This should make a bell */
vidattr(A_STANDOUT);
if (error)
{
diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result
index c5658d22eb6..9a190557211 100644
--- a/mysql-test/r/innodb.result
+++ b/mysql-test/r/innodb.result
@@ -3232,3 +3232,12 @@ drop trigger t2t;
drop trigger t3t;
drop trigger t4t;
drop table t1, t2, t3, t4, t5;
+create table t1(a date) engine=innodb;
+create table t2(a date, key(a)) engine=innodb;
+insert into t1 values('2005-10-01');
+insert into t2 values('2005-10-01');
+select * from t1, t2
+where t2.a between t1.a - interval 2 day and t1.a + interval 2 day;
+a a
+2005-10-01 2005-10-01
+drop table t1, t2;
diff --git a/mysql-test/r/insert.result b/mysql-test/r/insert.result
index f788576f824..00a987c9254 100644
--- a/mysql-test/r/insert.result
+++ b/mysql-test/r/insert.result
@@ -299,3 +299,9 @@ select count(*) from t2;
count(*)
25500
drop table t1,t2,t3;
+create table t1 (n int);
+create view v1 as select * from t1;
+insert delayed into v1 values (1);
+ERROR HY000: 'test.v1' is not BASE TABLE
+drop table t1;
+drop view v1;
diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result
index a1efb1a8e69..079b0253ff6 100644
--- a/mysql-test/r/lock.result
+++ b/mysql-test/r/lock.result
@@ -47,6 +47,17 @@ unlock tables;
lock tables t1 write, t1 as t1_alias read;
insert into t1 select index1,nr from t1 as t1_alias;
drop table t1,t2;
+create table t1 (c1 int);
+create table t2 (c1 int);
+create table t3 (c1 int);
+lock tables t1 write, t2 write, t3 write;
+drop table t2, t3, t1;
+create table t1 (c1 int);
+create table t2 (c1 int);
+create table t3 (c1 int);
+lock tables t1 write, t2 write, t3 write, t1 as t4 read;
+alter table t2 add column c2 int;
+drop table t1, t2, t3;
create table t1 ( a int(11) not null auto_increment, primary key(a));
create table t2 ( a int(11) not null auto_increment, primary key(a));
lock tables t1 write, t2 read;
diff --git a/mysql-test/r/mysql.result b/mysql-test/r/mysql.result
index 57067bea36b..ae50c714bba 100644
--- a/mysql-test/r/mysql.result
+++ b/mysql-test/r/mysql.result
@@ -72,7 +72,16 @@ c_cp932
+----------------------+------------+--------+
| concat('>',col1,'<') | col2 | col3 |
+----------------------+------------+--------+
-| >a < | b | 123421 |
-| >a < | 0123456789 | 4 |
-| >abcd< | | 4 |
+| >a < | b | 123421 |
+| >a < | 0123456789 | 4 |
+| >abcd< | NULL | 4 |
+----------------------+------------+--------+
++------+------+---------------------------+
+| i | j | k |
++------+------+---------------------------+
+| 1 | NULL | NULL |
+| NULL | NULL | <-----------------------> |
+| NULL | NULL | <----- |
+| NULL | NULL | Τη γλώσσα |
+| NULL | NULL | ᛖᚴ ᚷᛖᛏ |
++------+------+---------------------------+
diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test
index 832c4635815..b0835cd8419 100644
--- a/mysql-test/t/innodb.test
+++ b/mysql-test/t/innodb.test
@@ -2127,3 +2127,15 @@ drop table t1, t2, t3, t4, t5;
connection default;
disconnect a;
disconnect b;
+
+#
+# Bug #14360: problem with intervals
+#
+
+create table t1(a date) engine=innodb;
+create table t2(a date, key(a)) engine=innodb;
+insert into t1 values('2005-10-01');
+insert into t2 values('2005-10-01');
+select * from t1, t2
+ where t2.a between t1.a - interval 2 day and t1.a + interval 2 day;
+drop table t1, t2;
diff --git a/mysql-test/t/insert.test b/mysql-test/t/insert.test
index ddde6cfa5d3..0c64dd80bec 100644
--- a/mysql-test/t/insert.test
+++ b/mysql-test/t/insert.test
@@ -175,3 +175,15 @@ select count(*) from t2;
insert into t2 select t1.* from t1, t2 t, t3 where t1.id1 = t.id2 and t.id2 = t3.id3;
select count(*) from t2;
drop table t1,t2,t3;
+
+#
+# Test for INSERT DELAYED INTO a <view>
+# BUG#13683: INSERT DELAYED into a view creates an infinite loop
+#
+
+create table t1 (n int);
+create view v1 as select * from t1;
+--error 1347
+insert delayed into v1 values (1);
+drop table t1;
+drop view v1;
diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test
index 4815c1a30b7..8300219b3d4 100644
--- a/mysql-test/t/lock.test
+++ b/mysql-test/t/lock.test
@@ -61,6 +61,24 @@ insert into t1 select index1,nr from t1 as t1_alias;
drop table t1,t2;
#
+# BUG#5390 - problems with merge tables
+# Supplement test for the after-fix optimization
+# Check that a dropped table is correctly removed from a lock.
+create table t1 (c1 int);
+create table t2 (c1 int);
+create table t3 (c1 int);
+lock tables t1 write, t2 write, t3 write;
+# This removes one table after the other from the lock.
+drop table t2, t3, t1;
+#
+# Check that a lock merge works.
+create table t1 (c1 int);
+create table t2 (c1 int);
+create table t3 (c1 int);
+lock tables t1 write, t2 write, t3 write, t1 as t4 read;
+alter table t2 add column c2 int;
+drop table t1, t2, t3;
+
# Bug7241 - Invalid response when DELETE .. USING and LOCK TABLES used.
#
create table t1 ( a int(11) not null auto_increment, primary key(a));
diff --git a/mysql-test/t/mysql.test b/mysql-test/t/mysql.test
index dbf65845e6a..95cba2743da 100644
--- a/mysql-test/t/mysql.test
+++ b/mysql-test/t/mysql.test
@@ -61,3 +61,9 @@ drop table t1;
# Bug#16859 -- NULLs in columns must not truncate data as if a C-language "string".
#
--exec $MYSQL -t test -e "create table t1 (col1 binary(4), col2 varchar(10), col3 int); insert into t1 values ('a', 'b', 123421),('a ', '0123456789', 4), ('abcd', '', 4); select concat('>',col1,'<'), col2, col3 from t1; drop table t1;" 2>&1
+
+#
+# Bug#18265 -- mysql client: No longer right-justifies numeric columns
+#
+--exec $MYSQL -t --default-character-set utf8 test -e "create table t1 (i int, j int, k char(25) charset utf8); insert into t1 (i) values (1); insert into t1 (k) values ('<----------------------->'); insert into t1 (k) values ('<-----'); insert into t1 (k) values ('Τη γλώσσα'); insert into t1 (k) values ('ᛖᚴ ᚷᛖᛏ'); select * from t1; DROP TABLE t1;"
+
diff --git a/mysys/my_lib.c b/mysys/my_lib.c
index 522fa56cbf1..03f2d91916d 100644
--- a/mysys/my_lib.c
+++ b/mysys/my_lib.c
@@ -384,11 +384,10 @@ MY_DIR *my_dir(const char *path, myf MyFlags)
DBUG_PRINT("my",("path: '%s' stat: %d MyFlags: %d",path,MyFlags));
/* Put LIB-CHAR as last path-character if not there */
-
tmp_file=tmp_path;
if (!*path)
*tmp_file++ ='.'; /* From current dir */
- tmp_file= strmov(tmp_file,path);
+ tmp_file= strnmov(tmp_file, path, FN_REFLEN-5);
if (tmp_file[-1] == FN_DEVCHAR)
*tmp_file++= '.'; /* From current dev-dir */
if (tmp_file[-1] != FN_LIBCHAR)
@@ -424,7 +423,7 @@ MY_DIR *my_dir(const char *path, myf MyFlags)
if ((handle=_findfirst(tmp_path,&find)) == -1L)
#endif
{
- DBUG_PRINT("info", ("find_first returned error"));
+ DBUG_PRINT("info", ("findfirst returned error, errno: %d", errno));
if (errno != EINVAL)
goto error;
/*
@@ -433,72 +432,76 @@ MY_DIR *my_dir(const char *path, myf MyFlags)
continue and return zero files in dir
*/
}
-
- do
+ else
{
+
+ do
+ {
#ifdef __BORLANDC__
- attrib= find.ff_attrib;
+ attrib= find.ff_attrib;
#else
- attrib= find.attrib;
- /*
- Do not show hidden and system files which Windows sometimes create.
- Note. Because Borland's findfirst() is called with the third
- argument = 0 hidden/system files are excluded from the search.
- */
- if (attrib & (_A_HIDDEN | _A_SYSTEM))
- continue;
-#endif
+ attrib= find.attrib;
+ /*
+ Do not show hidden and system files which Windows sometimes create.
+ Note. Because Borland's findfirst() is called with the third
+ argument = 0 hidden/system files are excluded from the search.
+ */
+ if (attrib & (_A_HIDDEN | _A_SYSTEM))
+ continue;
+#endif
#ifdef __BORLANDC__
- if (!(finfo.name= strdup_root(names_storage, find.ff_name)))
- goto error;
+ if (!(finfo.name= strdup_root(names_storage, find.ff_name)))
+ goto error;
#else
- if (!(finfo.name= strdup_root(names_storage, find.name)))
- goto error;
-#endif
- if (MyFlags & MY_WANT_STAT)
- {
- if (!(finfo.mystat= (MY_STAT*)alloc_root(names_storage,
- sizeof(MY_STAT))))
+ if (!(finfo.name= strdup_root(names_storage, find.name)))
goto error;
-
- bzero(finfo.mystat, sizeof(MY_STAT));
+#endif
+ if (MyFlags & MY_WANT_STAT)
+ {
+ if (!(finfo.mystat= (MY_STAT*)alloc_root(names_storage,
+ sizeof(MY_STAT))))
+ goto error;
+
+ bzero(finfo.mystat, sizeof(MY_STAT));
#ifdef __BORLANDC__
- finfo.mystat->st_size=find.ff_fsize;
+ finfo.mystat->st_size=find.ff_fsize;
#else
- finfo.mystat->st_size=find.size;
+ finfo.mystat->st_size=find.size;
#endif
- mode=MY_S_IREAD;
- if (!(attrib & _A_RDONLY))
- mode|=MY_S_IWRITE;
- if (attrib & _A_SUBDIR)
- mode|=MY_S_IFDIR;
- finfo.mystat->st_mode=mode;
+ mode= MY_S_IREAD;
+ if (!(attrib & _A_RDONLY))
+ mode|= MY_S_IWRITE;
+ if (attrib & _A_SUBDIR)
+ mode|= MY_S_IFDIR;
+ finfo.mystat->st_mode= mode;
#ifdef __BORLANDC__
- finfo.mystat->st_mtime=((uint32) find.ff_ftime);
+ finfo.mystat->st_mtime= ((uint32) find.ff_ftime);
#else
- finfo.mystat->st_mtime=((uint32) find.time_write);
+ finfo.mystat->st_mtime= ((uint32) find.time_write);
#endif
- }
- else
- finfo.mystat= NULL;
+ }
+ else
+ finfo.mystat= NULL;
- if (push_dynamic(dir_entries_storage, (gptr)&finfo))
- goto error;
-
+ if (push_dynamic(dir_entries_storage, (gptr)&finfo))
+ goto error;
+ }
#ifdef __BORLANDC__
- } while (findnext(&find) == 0);
+ while (findnext(&find) == 0);
#else
- } while (_findnext(handle,&find) == 0);
-
- _findclose(handle);
+ while (_findnext(handle,&find) == 0);
+
+ _findclose(handle);
#endif
+ }
result->dir_entry= (FILEINFO *)dir_entries_storage->buffer;
result->number_off_files= dir_entries_storage->elements;
-
+
if (!(MyFlags & MY_DONT_SORT))
qsort((void *) result->dir_entry, result->number_off_files,
sizeof(FILEINFO), (qsort_cmp) comp_names);
+ DBUG_PRINT(exit, ("found %d files", result->number_off_files));
DBUG_RETURN(result);
error:
my_errno=errno;
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index e139eba385e..ee74d30ce53 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -2125,6 +2125,41 @@ longlong Item_date_add_interval::val_int()
((date*100L + ltime.hour)*100L+ ltime.minute)*100L + ltime.second;
}
+
+
+bool Item_date_add_interval::eq(const Item *item, bool binary_cmp) const
+{
+ INTERVAL interval, other_interval;
+ String val= value; // Because of const
+
+ if (this == item)
+ return TRUE;
+
+ if ((item->type() != FUNC_ITEM) ||
+ (arg_count != ((Item_func*) item)->arg_count) ||
+ (func_name() != ((Item_func*) item)->func_name()))
+ return FALSE;
+
+ Item_date_add_interval *other= (Item_date_add_interval*) item;
+
+ if ((int_type != other->int_type) ||
+ (!args[0]->eq(other->args[0], binary_cmp)) ||
+ (get_interval_value(args[1], int_type, &val, &interval)))
+ return FALSE;
+
+ val= other->value;
+
+ if ((get_interval_value(other->args[1], other->int_type, &val,
+ &other_interval)) ||
+ ((date_sub_interval ^ interval.neg) ^
+ (other->date_sub_interval ^ other_interval.neg)))
+ return FALSE;
+
+ // Assume comparing same types here due to earlier check
+ return memcmp(&interval, &other_interval, sizeof(INTERVAL)) == 0;
+}
+
+
static const char *interval_names[]=
{
"year", "quarter", "month", "day", "hour",
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index 71f595184ec..761faf85e63 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -656,6 +656,7 @@ public:
double val_real() { DBUG_ASSERT(fixed == 1); return (double) val_int(); }
longlong val_int();
bool get_date(TIME *res, uint fuzzy_date);
+ bool eq(const Item *item, bool binary_cmp) const;
void print(String *str);
};
diff --git a/sql/lock.cc b/sql/lock.cc
index d0bfcfd7272..a584b063a0b 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -68,20 +68,20 @@ TODO:
#include "mysql_priv.h"
#include <hash.h>
-#include "ha_myisammrg.h"
-#ifndef MASTER
-#include "../srclib/myisammrg/myrg_def.h"
-#else
-#include "../myisammrg/myrg_def.h"
-#endif
+#include <assert.h>
+
+extern HASH open_cache;
+
+/* flags for get_lock_data */
+#define GET_LOCK_UNLOCK 1
+#define GET_LOCK_STORE_LOCKS 2
static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table,uint count,
- bool unlock, TABLE **write_locked);
+ uint flags, TABLE **write_locked);
static int lock_external(THD *thd, TABLE **table,uint count);
static int unlock_external(THD *thd, TABLE **table,uint count);
static void print_lock_error(int error, const char *);
-
/*
Lock tables.
@@ -122,7 +122,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
for (;;)
{
- if (!(sql_lock = get_lock_data(thd,tables,count, 0,&write_lock_used)))
+ if (! (sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS,
+ &write_lock_used)))
break;
if (global_read_lock && write_lock_used &&
@@ -154,7 +155,12 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
}
thd->proc_info="Table lock";
thd->locked=1;
- rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks,
+ /* Copy the lock data array. thr_multi_lock() reorders its contens. */
+ memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
+ sql_lock->lock_count * sizeof(*sql_lock->locks));
+ /* Lock on the copied half of the lock data array. */
+ rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks +
+ sql_lock->lock_count,
sql_lock->lock_count,
thd->lock_id)];
if (rc > 1) /* a timeout or a deadlock */
@@ -267,7 +273,8 @@ void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count)
{
MYSQL_LOCK *sql_lock;
TABLE *write_lock_used;
- if ((sql_lock = get_lock_data(thd, table, count, 1, &write_lock_used)))
+ if ((sql_lock= get_lock_data(thd, table, count, GET_LOCK_UNLOCK,
+ &write_lock_used)))
mysql_unlock_tables(thd, sql_lock);
}
@@ -304,6 +311,7 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
TABLE **table=sql_lock->table;
for (i=found=0 ; i < sql_lock->table_count ; i++)
{
+ DBUG_ASSERT(sql_lock->table[i]->lock_position == i);
if ((uint) sql_lock->table[i]->reginfo.lock_type >= TL_WRITE_ALLOW_READ)
{
swap_variables(TABLE *, *table, sql_lock->table[i]);
@@ -317,6 +325,17 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
VOID(unlock_external(thd,table,i-found));
sql_lock->table_count=found;
}
+ /* Fix the lock positions in TABLE */
+ table= sql_lock->table;
+ found= 0;
+ for (i= 0; i < sql_lock->table_count; i++)
+ {
+ TABLE *tbl= *table;
+ tbl->lock_position= table - sql_lock->table;
+ tbl->lock_data_start= found;
+ found+= tbl->lock_count;
+ table++;
+ }
DBUG_VOID_RETURN;
}
@@ -332,20 +351,51 @@ void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table)
{
if (locked->table[i] == table)
{
- locked->table_count--;
+ uint j, removed_locks, old_tables;
+ TABLE *tbl;
+ uint lock_data_end;
+
+ DBUG_ASSERT(table->lock_position == i);
+
+ /* Decrement table_count in advance, making below expressions easier */
+ old_tables= --locked->table_count;
+
+ /* The table has 'removed_locks' lock data elements in locked->locks */
+ removed_locks= table->lock_count;
+
+ /* Move down all table pointers above 'i'. */
bmove((char*) (locked->table+i),
(char*) (locked->table+i+1),
- (locked->table_count-i)* sizeof(TABLE*));
+ (old_tables - i) * sizeof(TABLE*));
+
+ lock_data_end= table->lock_data_start + table->lock_count;
+ /* Move down all lock data pointers above 'table->lock_data_end-1' */
+ bmove((char*) (locked->locks + table->lock_data_start),
+ (char*) (locked->locks + lock_data_end),
+ (locked->lock_count - lock_data_end) *
+ sizeof(THR_LOCK_DATA*));
+
+ /*
+ Fix moved table elements.
+ lock_position is the index in the 'locked->table' array,
+ it must be fixed by one.
+ table->lock_data_start is pointer to the lock data for this table
+ in the 'locked->locks' array, they must be fixed by 'removed_locks',
+ the lock data count of the removed table.
+ */
+ for (j= i ; j < old_tables; j++)
+ {
+ tbl= locked->table[j];
+ tbl->lock_position--;
+ DBUG_ASSERT(tbl->lock_position == j);
+ tbl->lock_data_start-= removed_locks;
+ }
+
+ /* Finally adjust lock_count. */
+ locked->lock_count-= removed_locks;
break;
}
}
- THR_LOCK_DATA **prev=locked->locks;
- for (i=0 ; i < locked->lock_count ; i++)
- {
- if (locked->locks[i]->type != TL_UNLOCK)
- *prev++ = locked->locks[i];
- }
- locked->lock_count=(uint) (prev - locked->locks);
}
}
@@ -355,7 +405,8 @@ void mysql_lock_abort(THD *thd, TABLE *table)
{
MYSQL_LOCK *locked;
TABLE *write_lock_used;
- if ((locked = get_lock_data(thd,&table,1,1,&write_lock_used)))
+ if ((locked= get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK,
+ &write_lock_used)))
{
for (uint i=0; i < locked->lock_count; i++)
thr_abort_locks(locked->locks[i]->lock);
@@ -384,7 +435,8 @@ bool mysql_lock_abort_for_thread(THD *thd, TABLE *table)
bool result= FALSE;
DBUG_ENTER("mysql_lock_abort_for_thread");
- if ((locked = get_lock_data(thd,&table,1,1,&write_lock_used)))
+ if ((locked= get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK,
+ &write_lock_used)))
{
for (uint i=0; i < locked->lock_count; i++)
{
@@ -401,7 +453,9 @@ bool mysql_lock_abort_for_thread(THD *thd, TABLE *table)
MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
{
MYSQL_LOCK *sql_lock;
+ TABLE **table, **end_table;
DBUG_ENTER("mysql_lock_merge");
+
if (!(sql_lock= (MYSQL_LOCK*)
my_malloc(sizeof(*sql_lock)+
sizeof(THR_LOCK_DATA*)*(a->lock_count+b->lock_count)+
@@ -417,6 +471,21 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
memcpy(sql_lock->table,a->table,a->table_count*sizeof(*a->table));
memcpy(sql_lock->table+a->table_count,b->table,
b->table_count*sizeof(*b->table));
+
+ /*
+ Now adjust lock_position and lock_data_start for all objects that was
+ moved in 'b' (as there is now all objects in 'a' before these).
+ */
+ for (table= sql_lock->table + a->table_count,
+ end_table= table + b->table_count;
+ table < end_table;
+ table++)
+ {
+ (*table)->lock_position+= a->table_count;
+ (*table)->lock_data_start+= a->lock_count;
+ }
+
+ /* Delete old, not needed locks */
my_free((gptr) a,MYF(0));
my_free((gptr) b,MYF(0));
DBUG_RETURN(sql_lock);
@@ -435,112 +504,96 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
NOTE
This is mainly meant for MERGE tables in INSERT ... SELECT
situations. The 'real', underlying tables can be found only after
- the table is opened. The easier way is to check this after the
- tables are locked.
+ the MERGE tables are opened. This function assumes that the tables are
+ already locked.
+
+ Temporary tables are ignored here like they are ignored in
+ get_lock_data(). If we allow two opens on temporary tables later,
+ both functions should be checked.
RETURN
- 1 A table from 'tables' matches a lock on 'table'.
- 0 No duplicate lock is present.
- -1 Error.
+ NULL No duplicate lock found.
+ ! NULL First table from 'haystack' that matches a lock on 'needle'.
*/
TABLE_LIST *mysql_lock_have_duplicate(THD *thd, TABLE_LIST *needle,
TABLE_LIST *haystack)
{
- uint count;
- uint dup_pos;
- TABLE *write_lock_used; /* dummy */
- TABLE **tables1;
- TABLE **tables2;
- TABLE **table_ptr;
- TABLE_LIST *tlist_ptr;
- MYSQL_LOCK *sql_lock1;
- MYSQL_LOCK *sql_lock2;
- THR_LOCK_DATA **lock_data1;
- THR_LOCK_DATA **end_data1;
+ MYSQL_LOCK *mylock;
+ TABLE **lock_tables;
+ TABLE *table;
+ TABLE *table2;
+ THR_LOCK_DATA **lock_locks;
+ THR_LOCK_DATA **table_lock_data;
+ THR_LOCK_DATA **end_data;
THR_LOCK_DATA **lock_data2;
THR_LOCK_DATA **end_data2;
- THR_LOCK *lock1;
DBUG_ENTER("mysql_lock_have_duplicate");
- /* Table may not be defined for derived or view tables. */
- if (! needle->table)
- DBUG_RETURN(NULL);
-
- /* Get lock(s) for needle. */
- tables1= &needle->table;
- if (! (sql_lock1= get_lock_data(thd, tables1, 1, 1, &write_lock_used)))
- goto err0;
-
- /* Count real tables in list. */
- count=0;
- for (tlist_ptr = haystack; tlist_ptr; tlist_ptr= tlist_ptr->next_global)
- if (! tlist_ptr->placeholder() && ! tlist_ptr->schema_table)
- count++;
- /* Allocate a table array. */
- if (! (tables2= (TABLE**) sql_alloc(sizeof(TABLE*) * count)))
- goto err1;
- table_ptr= tables2;
- /* Assign table pointers. */
- for (tlist_ptr = haystack; tlist_ptr; tlist_ptr= tlist_ptr->next_global)
- if (! tlist_ptr->placeholder() && ! tlist_ptr->schema_table)
- *(table_ptr++)= tlist_ptr->table;
- /* Get lock(s) for haystack. */
- if (! (sql_lock2= get_lock_data(thd, tables2, count, 1, &write_lock_used)))
- goto err1;
-
- /* Initialize duplicate position to an impossible value. */
- dup_pos= UINT_MAX;
/*
- Find a duplicate lock.
- In case of merge tables, sql_lock1 can have more than 1 lock.
+ Table may not be defined for derived or view tables.
+ Table may not be part of a lock for delayed operations.
*/
- for (lock_data1= sql_lock1->locks,
- end_data1= lock_data1 + sql_lock1->lock_count;
- lock_data1 < end_data1;
- lock_data1++)
+ if (! (table= needle->table) || ! table->lock_count)
+ goto end;
+
+ /* A temporary table does not have locks. */
+ if (table->s->tmp_table == TMP_TABLE)
+ goto end;
+
+ /* Get command lock or LOCK TABLES lock. Maybe empty for INSERT DELAYED. */
+ if (! (mylock= thd->lock ? thd->lock : thd->locked_tables))
+ goto end;
+
+ /* If we have less than two tables, we cannot have duplicates. */
+ if (mylock->table_count < 2)
+ goto end;
+
+ lock_locks= mylock->locks;
+ lock_tables= mylock->table;
+
+ /* Prepare table related variables that don't change in loop. */
+ DBUG_ASSERT((table->lock_position < mylock->table_count) &&
+ (table == lock_tables[table->lock_position]));
+ table_lock_data= lock_locks + table->lock_data_start;
+ end_data= table_lock_data + table->lock_count;
+
+ for (; haystack; haystack= haystack->next_global)
{
- lock1= (*lock_data1)->lock;
- for (lock_data2= sql_lock2->locks,
- end_data2= lock_data2 + sql_lock2->lock_count;
+ if (haystack->placeholder() || haystack->schema_table)
+ continue;
+ table2= haystack->table;
+ if (table2->s->tmp_table == TMP_TABLE)
+ continue;
+
+ /* All tables in list must be in lock. */
+ DBUG_ASSERT((table2->lock_position < mylock->table_count) &&
+ (table2 == lock_tables[table2->lock_position]));
+
+ for (lock_data2= lock_locks + table2->lock_data_start,
+ end_data2= lock_data2 + table2->lock_count;
lock_data2 < end_data2;
lock_data2++)
{
- if ((*lock_data2)->lock == lock1)
+ THR_LOCK_DATA **lock_data;
+ THR_LOCK *lock2= (*lock_data2)->lock;
+
+ for (lock_data= table_lock_data;
+ lock_data < end_data;
+ lock_data++)
{
- DBUG_PRINT("ingo", ("duplicate lock found"));
- /* Change duplicate position to the real value. */
- dup_pos= lock_data2 - sql_lock2->locks;
- goto end;
+ if ((*lock_data)->lock == lock2)
+ {
+ DBUG_PRINT("info", ("haystack match: '%s'", haystack->table_name));
+ DBUG_RETURN(haystack);
+ }
}
}
}
end:
- tlist_ptr= NULL; /* In case that no duplicate was found. */
- if (dup_pos != UINT_MAX)
- {
- /* Duplicate found. Search the matching TABLE_LIST object. */
- count= 0;
- for (tlist_ptr = haystack; tlist_ptr; tlist_ptr= tlist_ptr->next_global)
- {
- if (! tlist_ptr->placeholder() && ! tlist_ptr->schema_table)
- {
- count+= tlist_ptr->table->file->lock_count();
- if (count > dup_pos)
- break;
- }
- }
- }
- my_free((gptr) sql_lock2, MYF(0));
- my_free((gptr) sql_lock1, MYF(0));
- DBUG_RETURN(tlist_ptr);
-
- err1:
- my_free((gptr) sql_lock1, MYF(0));
- err0:
- /* This non-null but special value indicates error, if caller cares. */
- DBUG_RETURN(needle);
+ DBUG_PRINT("info", ("no duplicate found"));
+ DBUG_RETURN(NULL);
}
@@ -570,17 +623,27 @@ static int unlock_external(THD *thd, TABLE **table,uint count)
/*
-** Get lock structures from table structs and initialize locks
+ Get lock structures from table structs and initialize locks
+
+ SYNOPSIS
+ get_lock_data()
+ thd Thread handler
+ table_ptr Pointer to tables that should be locks
+ flags One of:
+ GET_LOCK_UNLOCK: If we should send TL_IGNORE to
+ store lock
+ GET_LOCK_STORE_LOCKS: Store lock info in TABLE
+ write_lock_used Store pointer to last table with WRITE_ALLOW_WRITE
*/
static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
- bool get_old_locks, TABLE **write_lock_used)
+ uint flags, TABLE **write_lock_used)
{
uint i,tables,lock_count;
MYSQL_LOCK *sql_lock;
- THR_LOCK_DATA **locks;
- TABLE **to;
+ THR_LOCK_DATA **locks, **locks_buf, **locks_start;
+ TABLE **to, **table_buf;
DBUG_ENTER("get_lock_data");
*write_lock_used=0;
@@ -606,23 +669,31 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
}
}
+ /*
+ Allocating twice the number of pointers for lock data for use in
+ thr_mulit_lock(). This function reorders the lock data, but cannot
+ update the table values. So the second part of the array is copied
+ from the first part immediately before calling thr_multi_lock().
+ */
if (!(sql_lock= (MYSQL_LOCK*)
- my_malloc(sizeof(*sql_lock)+
- sizeof(THR_LOCK_DATA*)*tables+sizeof(table_ptr)*lock_count,
+ my_malloc(sizeof(*sql_lock) +
+ sizeof(THR_LOCK_DATA*) * tables * 2 +
+ sizeof(table_ptr) * lock_count,
MYF(0))))
DBUG_RETURN(0);
- locks=sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1);
- to=sql_lock->table=(TABLE**) (locks+tables);
+ locks= locks_buf= sql_lock->locks= (THR_LOCK_DATA**) (sql_lock + 1);
+ to= table_buf= sql_lock->table= (TABLE**) (locks + tables * 2);
sql_lock->table_count=lock_count;
sql_lock->lock_count=tables;
for (i=0 ; i < count ; i++)
{
TABLE *table;
+ enum thr_lock_type lock_type;
+
if ((table=table_ptr[i])->s->tmp_table == TMP_TABLE)
continue;
- *to++=table;
- enum thr_lock_type lock_type= table->reginfo.lock_type;
+ lock_type= table->reginfo.lock_type;
if (lock_type >= TL_WRITE_ALLOW_WRITE)
{
*write_lock_used=table;
@@ -634,8 +705,17 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
}
}
THR_LOCK_DATA **org_locks = locks;
- locks=table->file->store_lock(thd, locks, get_old_locks ? TL_IGNORE :
- lock_type);
+ locks_start= locks;
+ locks= table->file->store_lock(thd, locks,
+ (flags & GET_LOCK_UNLOCK) ? TL_IGNORE :
+ lock_type);
+ if (flags & GET_LOCK_STORE_LOCKS)
+ {
+ table->lock_position= (uint) (to - table_buf);
+ table->lock_data_start= (uint) (locks_start - locks_buf);
+ table->lock_count= (uint) (locks - locks_start);
+ }
+ *to++= table;
if (locks)
for ( ; org_locks != locks ; org_locks++)
(*org_locks)->debug_print_param= (void *) table;
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index ad9606acb83..320b8e1df9d 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -321,7 +321,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
if (!table_list->derived && !table_list->view)
table_list->updatable= 1; // usual table
}
- else
+ else if (thd->net.last_errno != ER_WRONG_OBJECT)
{
/* Too many delayed insert threads; Use a normal insert */
table_list->lock_type= lock_type= TL_WRITE;
@@ -1536,7 +1536,10 @@ TABLE *delayed_insert::get_local_table(THD* client_thd)
/* Adjust in_use for pointing to client thread */
copy->in_use= client_thd;
-
+
+ /* Adjust lock_count. This table object is not part of a lock. */
+ copy->lock_count= 0;
+
return copy;
/* Got fatal error */
diff --git a/sql/table.h b/sql/table.h
index 9d6e4764de3..ce44f797280 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -236,7 +236,10 @@ struct st_table {
*/
timestamp_auto_set_type timestamp_field_type;
table_map map; /* ID bit of table (1,2,4,8,16...) */
-
+
+ uint lock_position; /* Position in MYSQL_LOCK.table */
+ uint lock_data_start; /* Start pos. in MYSQL_LOCK.locks */
+ uint lock_count; /* Number of locks */
uint tablenr,used_fields;
uint temp_pool_slot; /* Used by intern temp tables */
uint status; /* What's in record[0] */
@@ -245,8 +248,8 @@ struct st_table {
uint derived_select_number;
int current_lock; /* Type of lock on table */
my_bool copy_blobs; /* copy_blobs when storing */
-
- /*
+
+ /*
0 or JOIN_TYPE_{LEFT|RIGHT}. Currently this is only compared to 0.
If maybe_null !=0, this table is inner w.r.t. some outer join operation,
and null_row may be true.