summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <monty@mysql.com>2004-10-06 19:14:33 +0300
committerunknown <monty@mysql.com>2004-10-06 19:14:33 +0300
commitbbab9ec678f9e8a0309f0b018cf6d22cd93acf84 (patch)
tree4e2cfa6a6a8032773454e22aa802b2798b2935b8 /sql
parent7d583c5834f420406c9abe8bb9c44518e49e74c3 (diff)
parent95e1c07483005b784aaefa35b5a8597ffb1d3932 (diff)
downloadmariadb-git-bbab9ec678f9e8a0309f0b018cf6d22cd93acf84.tar.gz
Merge with 4.0 for 4.1 release
Noteworthy: - New HANDLER code - New multi-update-grant-check code - Table lock code in ha_innodb.cc was not applied BitKeeper/etc/logging_ok: auto-union BitKeeper/deleted/.del-ctype-latin1_de.c~c5d8f9208bceb98e: Auto merged Build-tools/mysql-copyright-2: Auto merged acinclude.m4: Auto merged client/mysqladmin.c: Auto merged client/mysqldump.c: Auto merged include/config-win.h: Auto merged include/my_global.h: Auto merged include/myisam.h: Auto merged innobase/btr/btr0btr.c: Auto merged innobase/buf/buf0buf.c: Auto merged ltmain.sh: Auto merged innobase/dict/dict0dict.c: Auto merged innobase/fsp/fsp0fsp.c: Auto merged innobase/include/dict0dict.h: Auto merged innobase/include/row0mysql.h: Auto merged innobase/log/log0log.c: Auto merged innobase/log/log0recv.c: Auto merged innobase/pars/pars0opt.c: Auto merged innobase/row/row0row.c: Auto merged innobase/sync/sync0arr.c: Auto merged innobase/ut/ut0dbg.c: Auto merged myisam/mi_check.c: Auto merged myisam/mi_close.c: Auto merged myisam/mi_create.c: Auto merged myisam/mi_locking.c: Auto merged myisam/myisampack.c: Auto merged mysql-test/r/delete.result: Auto merged mysql-test/r/func_if.result: Auto merged Build-tools/mysql-copyright: Merge with 4.0 (too most of the code from 4.0) Makefile.am: merge client/mysql.cc: Used 4.1 code configure.in: merge innobase/os/os0file.c: merge innobase/row/row0mysql.c: merge mysql-test/r/ctype_latin1_de.result: merge mysql-test/r/flush_table.result: merge mysql-test/r/func_str.result: merge mysql-test/r/handler.result: merge mysql-test/r/multi_update.result: merge mysql-test/r/type_timestamp.result: Removed testing of 'new' mode, as this is only relevant for 4.0 mysql-test/r/update.result: merge mysql-test/t/delete.test: merge mysql-test/t/flush_table.test: merge mysql-test/t/func_str.test: merge mysql-test/t/handler.test: merge mysql-test/t/multi_update.test: merge mysql-test/t/type_timestamp.test: Removed testing of 'new' mode, as this is only relevant for 4.0 mysql-test/t/update.test: merge mysys/errors.c: merge mysys/my_fstream.c: merge mysys/my_pread.c: merge mysys/my_write.c: merge mysys/mysys_priv.h: merge scripts/mysqlhotcopy.sh: merge sql/field.cc: Keep code from 4.1 sql/field.h: Keep code from 4.1 sql/ha_innodb.cc: Don't merge lock code from 4.0; Heikki will look at this sql/ha_myisam.cc: merge sql/handler.cc: merge sql/item_cmpfunc.cc: merge sql/item_cmpfunc.h: merge sql/item_strfunc.cc: merge sql/mysql_priv.h: merge sql/mysqld.cc: merge sql/protocol.cc: merge sql/records.cc: merge sql/repl_failsafe.cc: merge mysql-test/r/lock_multi.result: merge mysql-test/t/ctype_latin1_de.test: merge mysql-test/t/func_if.test: merge mysql-test/t/lock_multi.test: merge sql/repl_failsafe.h: merge Remove unnessessary header protection sql/slave.h: merge sql/sql_acl.cc: merge sql/sql_base.cc: merge sql/sql_cache.cc: auto merge sql/sql_class.cc: merge sql/sql_class.h: merge sql/sql_delete.cc: merge sql/sql_handler.cc: Get new HANDLER code into 4.1 sql/sql_parse.cc: Keep old file sql/sql_repl.cc: merge sql/sql_repl.h: merge sql/sql_show.cc: merge sql/sql_table.cc: merge sql/sql_union.cc: Applied the examine_rows bug fix from 4.0 by hand sql/sql_update.cc: New multi-update-grant-check code from 4.0 sql/sql_yacc.yy: New multi-update-grant-check code from 4.0 sql/stacktrace.c: merge sql/table.h: merge
Diffstat (limited to 'sql')
-rw-r--r--sql/ha_innodb.cc23
-rw-r--r--sql/ha_myisam.cc2
-rw-r--r--sql/handler.cc2
-rw-r--r--sql/item_cmpfunc.cc9
-rw-r--r--sql/item_cmpfunc.h1
-rw-r--r--sql/item_strfunc.cc6
-rw-r--r--sql/mysql_priv.h11
-rw-r--r--sql/mysqld.cc5
-rw-r--r--sql/protocol.cc4
-rw-r--r--sql/records.cc30
-rw-r--r--sql/repl_failsafe.cc2
-rw-r--r--sql/repl_failsafe.h19
-rw-r--r--sql/slave.h16
-rw-r--r--sql/sql_base.cc31
-rw-r--r--sql/sql_cache.cc1
-rw-r--r--sql/sql_class.cc9
-rw-r--r--sql/sql_class.h1
-rw-r--r--sql/sql_delete.cc2
-rw-r--r--sql/sql_handler.cc644
-rw-r--r--sql/sql_repl.cc2
-rw-r--r--sql/sql_repl.h16
-rw-r--r--sql/sql_show.cc15
-rw-r--r--sql/sql_table.cc15
-rw-r--r--sql/sql_union.cc5
-rw-r--r--sql/sql_update.cc227
-rw-r--r--sql/sql_yacc.yy9
-rw-r--r--sql/stacktrace.c2
-rw-r--r--sql/table.h8
28 files changed, 794 insertions, 323 deletions
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
index f5da82a8a8c..8ddf00a7568 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -812,6 +812,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 */
@@ -3171,7 +3172,7 @@ ha_innobase::index_last(
{
int error;
- DBUG_ENTER("index_first");
+ DBUG_ENTER("index_last");
statistic_increment(ha_read_last_count, &LOCK_status);
error = index_read(buf, NULL, 0, HA_READ_BEFORE_KEY);
@@ -4255,7 +4256,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
@@ -4816,6 +4817,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) {
@@ -5067,14 +5069,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
@@ -5085,6 +5095,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) {
diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc
index 729ec4c27eb..0b993ebe793 100644
--- a/sql/ha_myisam.cc
+++ b/sql/ha_myisam.cc
@@ -1280,6 +1280,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 ?
@@ -1287,7 +1288,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/handler.cc b/sql/handler.cc
index 23b4fbe4835..e59358d44bf 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -483,7 +483,7 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans)
if (trans == &thd->transaction.all && mysql_bin_log.is_open() &&
my_b_tell(&thd->transaction.trans_log))
{
- if (error= wait_if_global_read_lock(thd, 0, 0))
+ if ((error= wait_if_global_read_lock(thd, 0, 0)))
{
/*
Note that ROLLBACK [TO SAVEPOINT] does not have this test; it's
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index bb1ea09d6bc..5b6484f5017 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1162,6 +1162,15 @@ Item_func_nullif::val_str(String *str)
return res;
}
+
+bool
+Item_func_nullif::is_null()
+{
+ if (!(this->*cmp_func)())
+ return null_value=1;
+ return 0;
+}
+
/*
CASE expression
Return the matching ITEM or NULL if all compares (including else) failed
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index f1a2b11aaa8..ad2b929c19e 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -444,6 +444,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();
};
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 131bd55dc81..78acaafc486 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -1245,7 +1245,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;
}
@@ -1317,8 +1317,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;
}
}
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 5d04d145563..8a81c0690b8 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -677,12 +677,15 @@ 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, int 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 */
void set_item_name(Item *item,char *pos,uint length);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 5e40398574b..b9fc5477449 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -2118,8 +2118,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");
@@ -2147,7 +2146,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);
}
diff --git a/sql/protocol.cc b/sql/protocol.cc
index da2a285fffc..c2d9117b062 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -209,6 +209,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)
{
diff --git a/sql/records.cc b/sql/records.cc
index 5a969ef9c20..e5a0d102b10 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 d7b70fe122c..66c6e7c508d 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
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/slave.h b/sql/slave.h
index 384436fdfcc..20167094453 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/sql_base.cc b/sql/sql_base.cc
index 2770d93bf26..a91371b4248 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -300,7 +300,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",
@@ -852,7 +852,7 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name,
}
/* close handler tables which are marked for flush */
- mysql_ha_close_list(thd, (TABLE_LIST*) NULL, /*flushed*/ 1);
+ 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 ;
@@ -936,6 +936,31 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name,
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;
@@ -1224,7 +1249,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);
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index f69fb3085d3..7aa0ef83238 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -1945,7 +1945,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,
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 6cf01896b03..e061340b0e0 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -172,6 +172,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;
@@ -346,11 +347,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));
diff --git a/sql/sql_class.h b/sql/sql_class.h
index fbbb7fc7383..8896b7b658f 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -750,6 +750,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
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 5965732d2ce..09893970803 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -338,6 +338,8 @@ multi_delete::initialize_tables(JOIN *join)
walk=walk->next;
/* 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;
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 56c1b0a1b51..1e50e1e571e 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,190 +33,410 @@
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, int 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;
- int err=open_tables(thd, tables, &counter);
- HANDLER_TABLES_HACK(thd);
- if (err)
- return -1;
+ *key_len_p= strlen(tables->alias) + 1 ; /* include '\0' in comparisons */
+ return tables->alias;
+}
- // 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;
- }
- send_ok(thd);
- return 0;
+/*
+ Free an hash object.
+
+ SYNOPSIS
+ mysql_ha_hash_free()
+ tables Pointer to the hash object.
+
+ 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 err;
+ DBUG_ENTER("mysql_ha_open");
+ DBUG_PRINT("enter",("mysql_ha_open: '%s'.'%s' as '%s' reopen %d",
+ tables->db, tables->real_name, tables->alias, reopen));
+
+ if (! hash_inited(&thd->handler_tables_hash))
+ {
+ /*
+ HASH entries are of type TABLE_LIST.
+ */
+ if (hash_init(&thd->handler_tables_hash, 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. */
{
- (*table_ptr)->file->ha_index_or_rnd_end();
- if (!dont_lock)
- VOID(pthread_mutex_lock(&LOCK_open));
- if (close_thread_table(thd, table_ptr))
+ 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",("mysql_ha_open: 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);
+ err=open_tables(thd, tables, &counter);
+ HANDLER_TABLES_HACK(thd);
+ if (err)
+ 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)))
+ {
+ DBUG_PRINT("exit",("mysql_ha_open: malloc ERROR"));
+ 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 (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",("mysql_ha_open: OK"));
+ DBUG_RETURN(0);
+
+err:
+ DBUG_PRINT("exit",("mysql_ha_open: 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;
-
- if (tables)
+ bool was_flushed= FALSE;
+ bool not_opened;
+ DBUG_ENTER("mysql_ha_close");
+ DBUG_PRINT("enter",("mysql_ha_close: '%s'.'%s' as '%s'",
+ tables->db, tables->real_name, tables->alias));
+
+ 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)
+ /*
+ 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 MYSQL_VERSION_ID < 40100
+ if (*tables->db && strcmp(hash_tables->db, tables->db))
{
- mysql_ha_close(thd, tl_item, /*dont_send_ok*/ 1,
- /*dont_lock*/ 1, /*no_alias*/ 1);
+ DBUG_PRINT("info",("mysql_ha_close: wrong db"));
+ hash_tables= NULL;
}
- }
- else
- {
- table_ptr= &(thd->handler_tables);
- while (*table_ptr)
+ else
+#endif
{
- if (! flushed || ((*table_ptr)->version != refresh_version))
+ if (*table_ptr)
{
- (*table_ptr)->file->ha_index_or_rnd_end();
+ 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));
}
- continue;
+ VOID(pthread_mutex_unlock(&LOCK_open));
}
- table_ptr= &((*table_ptr)->next);
+
+ hash_delete(&thd->handler_tables_hash, (byte*) hash_tables);
}
}
- return 0;
+
+ if (! hash_tables)
+ {
+#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
+ DBUG_PRINT("exit",("mysql_ha_close: ERROR"));
+ DBUG_RETURN(-1);
+ }
+
+ send_ok(thd);
+ DBUG_PRINT("exit",("mysql_ha_close: 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 err, keyno= -1;
+ uint num_rows;
+ byte *key;
+ uint key_len;
+ bool was_flushed;
+ DBUG_ENTER("mysql_ha_read");
+ DBUG_PRINT("enter",("mysql_ha_read: '%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",("mysql_ha_read: found 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",("mysql_ha_read: reopen failed"));
+ goto err0;
+ }
+
+ table= hash_tables->table;
+ DBUG_PRINT("info",("mysql_ha_read: 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",("mysql_ha_read: 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)
{
@@ -228,34 +444,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);
+ if (insert_fields(thd,tables,tables->db,tables->alias,&it))
+ goto err0;
select_limit+=offset_limit;
protocol->send_fields(&list,1);
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
@@ -378,84 +582,156 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables,
}
ok:
mysql_unlock_tables(thd,lock);
- send_eof(thd);
- return 0;
+ send_eof(&thd->net);
+ DBUG_PRINT("exit",("mysql_ha_read: OK"));
+ DBUG_RETURN(0);
err:
mysql_unlock_tables(thd,lock);
err0:
- return -1;
+ DBUG_PRINT("exit",("mysql_ha_read: 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, int 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_p;
+ TABLE_LIST *tmp_tables;
+ TABLE **table_ptr;
+ bool was_flushed;
+ DBUG_ENTER("mysql_ha_flush");
+ DBUG_PRINT("enter",("mysql_ha_flush: 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)
{
- if (table->version != refresh_version)
+ DBUG_PRINT("info",("mysql_ha_flush: 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));
- if (close_thread_table(thd, table_ptr))
+ if ((! *tmp_tables->db ||
+ ! my_strcasecmp((*table_ptr)->table_cache_key, tmp_tables->db)) &&
+ ! my_strcasecmp((*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",("mysql_ha_flush: *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_PRINT("exit",("mysql_ha_flush: OK"));
+ 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, int mode_flags)
+{
+ TABLE_LIST *hash_tables;
+ TABLE *table= *table_ptr;
+ bool was_flushed;
+ DBUG_ENTER("mysql_ha_flush_table");
+ DBUG_PRINT("info",("mysql_ha_flush_table: '%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,
+ (*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_PRINT("exit",("mysql_ha_flush_table: OK"));
+ DBUG_RETURN(0);
}
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index f71d5a311ab..a84b63c270b 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
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index e7001c1fe1e..3c17540b664 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_show.cc b/sql/sql_show.cc
index bd145a1de59..44bc2a9efc9 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -1,4 +1,5 @@
/* Copyright (C) 2000 MySQL AB
+/* Copyright (C) 2000 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
@@ -387,27 +388,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
{
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 3d5aaf0c2ec..5cb328f530b 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -197,7 +197,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
for (table=tables ; table ; table=table->next)
{
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;
@@ -1762,7 +1762,7 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables,
if (protocol->send_fields(&field_list, 1))
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)
{
char table_name[NAME_LEN*2+2];
@@ -2567,8 +2567,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,
@@ -3307,7 +3306,8 @@ copy_data_between_tables(TABLE *from,TABLE *to,
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);
@@ -3434,12 +3434,13 @@ 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:
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);
}
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 1e8c6576dec..b46cfc05538 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -346,6 +346,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)
@@ -426,6 +427,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);
}
@@ -502,7 +504,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 c6fb3d6e415..25d94d6d039 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -449,6 +449,24 @@ int mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
***************************************************************************/
/*
+ 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;
+}
+
+
+
+/*
Setup multi-update handling and call SELECT to do the join
*/
@@ -465,107 +483,163 @@ int mysql_multi_update(THD *thd,
multi_update *result;
TABLE_LIST *tl;
TABLE_LIST *update_list= (TABLE_LIST*) thd->lex->select_lex.table_list.first;
- table_map item_tables= 0, derived_tables= 0;
+ List<Item> total_list;
DBUG_ENTER("mysql_multi_update");
- if ((res=open_and_lock_tables(thd,table_list)))
- DBUG_RETURN(res);
-
select_lex->select_limit= HA_POS_ERROR;
/*
- Ensure that we have update privilege for all tables and columns in the
- SET part
+ The following loop is here to to ensure that we only lock tables
+ that we are going to update with a write lock
*/
- for (tl= update_list; tl; tl= tl->next)
+ for (;;)
{
- TABLE *table= tl->table;
+ table_map update_tables, derived_tables=0;
+ uint tnr, counter;
+
+ if ((res=open_tables(thd,table_list, &counter)))
+ DBUG_RETURN(res);
+
+ /* Only need to call lock_tables if we are not using LOCK TABLES */
+ if (!using_lock_tables && ((res= lock_tables(thd, table_list))))
+ DBUG_RETURN(res);
+
/*
- Update of derived tables is checked later
- We don't check privileges here, becasue then we would get error
- "UPDATE command denided .. for column N" instead of
- "Target table ... is not updatable"
+ Ensure that we have update privilege for all tables and columns in the
+ SET part
+ While we are here, initialize the table->map field to check which
+ tables are updated and updatability of derived tables
*/
- if (!tl->derived)
- table->grant.want_privilege= (UPDATE_ACL & ~table->grant.privilege);
- }
+ for (tl= update_list, tnr=0 ; tl ; tl=tl->next)
+ {
+ TABLE *table= tl->table;
+ /*
+ Update of derived tables is checked later
+ We don't check privileges here, becasue then we would get error
+ "UPDATE command denided .. for column N" instead of
+ "Target table ... is not updatable"
+ */
+ if (!tl->derived)
+ table->grant.want_privilege= (UPDATE_ACL & ~table->grant.privilege);
+ table->map= (table_map) 1 << (tnr++);
+ }
- /* Assign table map values to check updatability of derived tables */
- {
- uint tablenr=0;
- for (TABLE_LIST *table_list= update_list;
- table_list;
- table_list= table_list->next, tablenr++)
+ if (setup_fields(thd, 0, update_list, *fields, 1, 0, 0))
+ DBUG_RETURN(-1);
+
+ update_tables= get_table_map(fields);
+
+ /* Unlock the tables in preparation for relocking */
+ if (!using_lock_tables)
+ {
+ mysql_unlock_tables(thd, thd->lock);
+ thd->lock= 0;
+ }
+
+ /*
+ Count tables and setup timestamp handling
+ Set also the table locking strategy according to the update map
+ */
+ for (tl= update_list; tl; tl= tl->next)
{
- table_list->table->map= (table_map) 1 << tablenr;
+ TABLE *table= tl->table;
+ /* if table will be updated then check that it is unique */
+ if (table->map & update_tables)
+ {
+ /*
+ Multi-update can't be constructed over-union => we always have
+ single SELECT on top and have to check underlaying SELECTs of it
+ */
+ if (select_lex->check_updateable_in_subqueries(tl->db,
+ tl->real_name))
+ {
+ my_error(ER_UPDATE_TABLE_USED, MYF(0),
+ tl->real_name);
+ DBUG_RETURN(-1);
+ }
+ DBUG_PRINT("info",("setting table `%s` for update", tl->alias));
+ tl->lock_type= thd->lex.lock_option;
+ tl->updating= 1;
+ }
+ else
+ {
+ DBUG_PRINT("info",("setting table `%s` for read-only", tl->alias));
+ tl->lock_type= TL_READ;
+ tl->updating= 0;
+ }
+ if (tl->derived)
+ derived_tables|= table->map;
}
- }
- if (setup_fields(thd, 0, update_list, *fields, 1, 0, 0))
- DBUG_RETURN(-1);
+ if (thd->lex->derived_tables && (update_tables & derived_tables))
+ {
+ // find derived table which cause error
+ for (tl= update_list; tl; tl= tl->next)
+ {
+ if (tl->derived && (update_tables & tl->table->map))
+ {
+ my_printf_error(ER_NON_UPDATABLE_TABLE, ER(ER_NON_UPDATABLE_TABLE),
+ MYF(0), tl->alias, "UPDATE");
+ DBUG_RETURN(-1);
+ }
+ }
+ }
- /* Find tables used in items */
- {
- List_iterator_fast<Item> it(*fields);
- Item *item;
- while ((item= it++))
+ /* Relock the tables with the correct modes */
+ res= lock_tables(thd,table_list);
+ if (using_lock_tables)
{
- item_tables|= item->used_tables();
+ if (res)
+ DBUG_RETURN(res);
+ break; // Don't have to do setup_field()
}
+
+ /*
+ We must setup fields again as the file may have been reopened
+ during lock_tables
+ */
+ {
+ List_iterator_fast<Item> field_it(*fields);
+ Item_field *item;
+
+ while ((item= (Item_field *) field_it++))
+ {
+ item->field->query_id= 0;
+ item->cleanup();
+ }
+ }
+ if (setup_fields(thd, table_list, *fields, 1, 0, 0))
+ DBUG_RETURN(-1);
+ /*
+ If lock succeded and the table map didn't change since the above lock
+ we can continue.
+ */
+ if (!res && update_tables == get_table_map(fields))
+ break;
+
+ /*
+ There was some very unexpected changes in the table definition between
+ open tables and lock tables. Close tables and try again.
+ */
+ close_thread_tables(thd);
}
/*
- Count tables and setup timestamp handling
+ Setup timestamp handling
*/
for (tl= update_list; tl; tl= tl->next)
{
TABLE *table= tl->table;
-
- /* We only need SELECT privilege for columns in the values list */
- table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege);
- // Only set timestamp column if this is not modified
+ /* Only set timestamp column if this is not modified */
if (table->timestamp_field &&
table->timestamp_field->query_id == thd->query_id)
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
-
- /* if table will be updated then check that it is unique */
- if (table->map & item_tables)
- {
- /*
- Multi-update can't be constructed over-union => we always have
- single SELECT on top and have to check underlaying SELECTs of it
- */
- if (select_lex->check_updateable_in_subqueries(tl->db,
- tl->real_name))
- {
- my_error(ER_UPDATE_TABLE_USED, MYF(0),
- tl->real_name);
- DBUG_RETURN(-1);
- }
- }
-
- if (tl->derived)
- derived_tables|= table->map;
- }
- if (thd->lex->derived_tables && (item_tables & derived_tables))
- {
- // find derived table which cause error
- for (tl= update_list; tl; tl= tl->next)
- {
- if (tl->derived && (item_tables & tl->table->map))
- {
- my_printf_error(ER_NON_UPDATABLE_TABLE, ER(ER_NON_UPDATABLE_TABLE),
- MYF(0), tl->alias, "UPDATE");
- DBUG_RETURN(-1);
- }
- }
}
if (!(result=new multi_update(thd, update_list, fields, values,
handle_duplicates)))
DBUG_RETURN(-1);
- List<Item> total_list;
res= mysql_select(thd, &select_lex->ref_pointer_array,
select_lex->get_table_list(), select_lex->with_wild,
total_list,
@@ -597,7 +671,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);
@@ -608,8 +682,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)
{
@@ -672,7 +745,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++;
@@ -918,9 +990,13 @@ 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)
+ {
+ table->file->print_error(error,MYF(0));
+ DBUG_RETURN(1);
+ }
}
}
}
@@ -986,7 +1062,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);
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 0c81c172cf7..7982b501ac4 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -4163,12 +4163,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->lock_option= $3;
+ }
else if (lex->select_lex.get_table_list()->derived)
{
/* it is single table update and it is update of derived table */
@@ -4176,7 +4178,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:
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/table.h b/sql/table.h
index 904038ad029..f25b172a0d9 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -150,6 +150,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 */