diff options
author | unknown <tomas@poseidon.ndb.mysql.com> | 2005-10-06 10:54:07 +0200 |
---|---|---|
committer | unknown <tomas@poseidon.ndb.mysql.com> | 2005-10-06 10:54:07 +0200 |
commit | d04832d195aba5badc42310eaa69c628a4a183bd (patch) | |
tree | cbe58ba165511396b212afaa1654a226d8ae88a4 /sql | |
parent | 282855e90bf052e2cf126adfaada61924cd8dd41 (diff) | |
parent | 3c2dca475dfa91c95f2488831fafacacaf852347 (diff) | |
download | mariadb-git-d04832d195aba5badc42310eaa69c628a4a183bd.tar.gz |
Merge
BitKeeper/etc/ignore:
auto-union
BUILD/autorun.sh:
Auto merged
BitKeeper/deleted/.del-bdb.vcproj~ed8534936e40cefb:
Auto merged
BitKeeper/deleted/.del-heap.vcproj~70c0cc1d680a51c1:
Auto merged
BitKeeper/deleted/.del-innobase.vcproj~71e1de81f11138bf:
Auto merged
BitKeeper/deleted/.del-myisam.vcproj~32bb9e4a163fcb5a:
Auto merged
BitKeeper/deleted/.del-myisammrg.vcproj~6534e59acbfbb63:
Auto merged
BitKeeper/deleted/.del-mysqld.vcproj~703886a28862bb2:
Auto merged
Makefile.am:
Auto merged
client/mysqltest.c:
Auto merged
include/my_global.h:
Auto merged
include/mysql_com.h:
Auto merged
mysql-test/mysql-test-run.pl:
Auto merged
mysql-test/r/ndb_alter_table.result:
Auto merged
mysql-test/r/ps_6bdb.result:
Auto merged
mysql-test/r/ps_7ndb.result:
Auto merged
mysql-test/t/query_cache.test:
Auto merged
mysys/default.c:
Auto merged
scripts/make_win_src_distribution.sh:
Auto merged
scripts/mysql_create_system_tables.sh:
Auto merged
scripts/mysql_fix_privilege_tables.sql:
Auto merged
sql/Makefile.am:
Auto merged
sql/field.cc:
Auto merged
sql/ha_berkeley.cc:
Auto merged
sql/ha_berkeley.h:
Auto merged
sql/ha_federated.cc:
Auto merged
sql/ha_heap.cc:
Auto merged
sql/ha_innodb.cc:
Auto merged
sql/ha_innodb.h:
Auto merged
sql/ha_myisam.cc:
Auto merged
sql/ha_myisammrg.cc:
Auto merged
sql/ha_ndbcluster.h:
Auto merged
sql/handler.cc:
Auto merged
sql/item.cc:
Auto merged
sql/item_subselect.cc:
Auto merged
sql/log.cc:
Auto merged
sql/mysql_priv.h:
Auto merged
sql/opt_sum.cc:
Auto merged
sql/repl_failsafe.cc:
Auto merged
sql/set_var.cc:
Auto merged
sql/sp.cc:
Auto merged
sql/sql_acl.cc:
Auto merged
sql/sql_acl.h:
Auto merged
sql/sql_base.cc:
Auto merged
sql/sql_cache.cc:
Auto merged
sql/sql_class.h:
Auto merged
sql/sql_delete.cc:
Auto merged
sql/sql_insert.cc:
Auto merged
sql/sql_lex.cc:
Auto merged
sql/sql_lex.h:
Auto merged
sql/sql_load.cc:
Auto merged
sql/sql_prepare.cc:
Auto merged
sql/sql_select.cc:
Auto merged
sql/sql_select.h:
Auto merged
sql/sql_show.cc:
Auto merged
sql/sql_table.cc:
Auto merged
sql/sql_update.cc:
Auto merged
sql/sql_yacc.yy:
Auto merged
sql/table.h:
Auto merged
sql/unireg.cc:
Auto merged
storage/innobase/buf/buf0buf.c:
Auto merged
storage/innobase/buf/buf0flu.c:
Auto merged
storage/innobase/data/data0data.c:
Auto merged
storage/innobase/dict/dict0dict.c:
Auto merged
storage/innobase/include/btr0btr.h:
Auto merged
storage/innobase/include/data0type.ic:
Auto merged
storage/innobase/include/dict0dict.h:
Auto merged
storage/innobase/include/dict0mem.h:
Auto merged
storage/innobase/include/mem0mem.h:
Auto merged
storage/innobase/include/mem0mem.ic:
Auto merged
storage/innobase/include/os0file.h:
Auto merged
storage/innobase/include/row0mysql.h:
Auto merged
storage/innobase/include/trx0trx.h:
Auto merged
storage/innobase/include/ut0mem.h:
Auto merged
storage/innobase/mem/mem0mem.c:
Auto merged
storage/innobase/os/os0file.c:
Auto merged
storage/innobase/os/os0proc.c:
Auto merged
storage/innobase/rem/rem0rec.c:
Auto merged
storage/innobase/row/row0ins.c:
Auto merged
storage/innobase/row/row0mysql.c:
Auto merged
storage/innobase/srv/srv0start.c:
Auto merged
storage/innobase/trx/trx0sys.c:
Auto merged
storage/innobase/trx/trx0trx.c:
Auto merged
storage/innobase/ut/ut0mem.c:
Auto merged
storage/myisam/ft_boolean_search.c:
Auto merged
storage/myisam/mi_check.c:
Auto merged
storage/myisam/mi_dbug.c:
Auto merged
storage/myisam/mi_delete.c:
Auto merged
storage/myisam/mi_delete_all.c:
Auto merged
storage/myisam/mi_key.c:
Auto merged
storage/myisam/mi_open.c:
Auto merged
storage/myisam/mi_rkey.c:
Auto merged
storage/myisam/mi_search.c:
Auto merged
storage/myisam/mi_test1.c:
Auto merged
storage/myisam/mi_update.c:
Auto merged
storage/myisam/mi_write.c:
Auto merged
storage/myisam/myisamchk.c:
Auto merged
storage/myisam/myisamdef.h:
Auto merged
storage/myisam/myisampack.c:
Auto merged
storage/myisam/sp_key.c:
Auto merged
storage/myisammrg/myrg_rkey.c:
Auto merged
storage/ndb/include/kernel/GlobalSignalNumbers.h:
Auto merged
storage/ndb/include/kernel/signaldata/ApiVersion.hpp:
Auto merged
storage/ndb/include/kernel/signaldata/EventReport.hpp:
Auto merged
storage/ndb/include/kernel/signaldata/StopReq.hpp:
Auto merged
storage/ndb/include/mgmapi/ndb_logevent.h:
Auto merged
storage/ndb/include/ndbapi/NdbBlob.hpp:
Auto merged
storage/ndb/include/ndbapi/NdbIndexOperation.hpp:
Auto merged
storage/ndb/include/ndbapi/NdbIndexScanOperation.hpp:
Auto merged
storage/ndb/include/ndbapi/NdbOperation.hpp:
Auto merged
storage/ndb/include/ndbapi/NdbRecAttr.hpp:
Auto merged
storage/ndb/include/ndbapi/NdbTransaction.hpp:
Auto merged
storage/ndb/src/common/util/SimpleProperties.cpp:
Auto merged
storage/ndb/src/kernel/blocks/backup/Backup.cpp:
Auto merged
storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp:
Auto merged
storage/ndb/src/kernel/blocks/dbdict/printSchemaFile.cpp:
Auto merged
storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp:
Auto merged
storage/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp:
Auto merged
storage/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp:
Auto merged
storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp:
Auto merged
storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp:
Auto merged
storage/ndb/src/mgmapi/ndb_logevent.cpp:
Auto merged
storage/ndb/src/mgmclient/CommandInterpreter.cpp:
Auto merged
storage/ndb/src/mgmsrv/Config.cpp:
Auto merged
storage/ndb/src/mgmsrv/Config.hpp:
Auto merged
storage/ndb/src/mgmsrv/ConfigInfo.cpp:
Auto merged
storage/ndb/src/mgmsrv/InitConfigFileParser.cpp:
Auto merged
storage/ndb/src/mgmsrv/InitConfigFileParser.hpp:
Auto merged
storage/ndb/src/mgmsrv/MgmtSrvr.hpp:
Auto merged
storage/ndb/src/mgmsrv/MgmtSrvrConfig.cpp:
Auto merged
storage/ndb/src/mgmsrv/MgmtSrvrGeneralSignalHandling.cpp:
Auto merged
storage/ndb/src/mgmsrv/Services.cpp:
Auto merged
storage/ndb/src/mgmsrv/Services.hpp:
Auto merged
storage/ndb/src/mgmsrv/main.cpp:
Auto merged
storage/ndb/src/ndbapi/NdbApiSignal.cpp:
Auto merged
storage/ndb/src/ndbapi/NdbApiSignal.hpp:
Auto merged
storage/ndb/src/ndbapi/NdbBlob.cpp:
Auto merged
storage/ndb/src/ndbapi/NdbImpl.hpp:
Auto merged
storage/ndb/src/ndbapi/NdbRecAttr.cpp:
Auto merged
storage/ndb/src/ndbapi/NdbUtil.cpp:
Auto merged
storage/ndb/src/ndbapi/NdbUtil.hpp:
Auto merged
storage/ndb/src/ndbapi/Ndbif.cpp:
Auto merged
storage/ndb/src/ndbapi/Ndblist.cpp:
Auto merged
storage/ndb/src/ndbapi/ObjectMap.hpp:
Auto merged
storage/ndb/test/include/NDBT_Test.hpp:
Auto merged
storage/ndb/test/ndbapi/bank/BankLoad.cpp:
Auto merged
storage/ndb/test/ndbapi/testOIBasic.cpp:
Auto merged
storage/ndb/test/src/HugoCalculator.cpp:
Auto merged
storage/ndb/test/src/NDBT_Test.cpp:
Auto merged
storage/ndb/test/src/NdbBackup.cpp:
Auto merged
storage/ndb/test/src/NdbRestarts.cpp:
Auto merged
storage/ndb/tools/ndb_condig.cpp:
Auto merged
storage/ndb/tools/restore/Restore.cpp:
Auto merged
storage/ndb/tools/restore/consumer_restore.cpp:
Auto merged
support-files/mysql.spec.sh:
Auto merged
configure.in:
merge
BUILD/SETUP.sh:
merge
BitKeeper/deleted/.del-mysql.sln~f2120278f8a437be:
SCCS merged
libmysqld/Makefile.am:
merge
sql/ha_ndbcluster.cc:
merge
sql/handler.h:
merge
sql/mysqld.cc:
merge
sql/opt_range.cc:
merge
sql/slave.cc:
merge
sql/slave.h:
e
merge
sql/sql_parse.cc:
merge
sql/table.cc:
merge
sql/examples/ha_tina.cc:
merge
sql/examples/ha_tina.h:
merge
sql/share/errmsg.txt:
merge
storage/ndb/include/ndbapi/Ndb.hpp:
merge
storage/ndb/src/common/debugger/EventLogger.cpp:
merge
storage/ndb/src/kernel/blocks/dbdict/Makefile.am:
merge
storage/ndb/src/mgmsrv/MgmtSrvr.cpp:
merge
storage/ndb/src/ndbapi/Ndbinit.cpp:
SCCS merged
Diffstat (limited to 'sql')
79 files changed, 3251 insertions, 1842 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am index e90be7630fa..ada462f5e09 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -61,8 +61,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ tztime.h my_decimal.h\ sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h \ parse_file.h sql_view.h sql_trigger.h \ - sql_array.h \ - examples/ha_example.h examples/ha_archive.h \ + sql_array.h sql_cursor.h \ + examples/ha_example.h ha_archive.h \ examples/ha_tina.h ha_blackhole.h \ ha_federated.h ha_partition.h mysqld_SOURCES = sql_lex.cc sql_handler.cc \ @@ -95,11 +95,11 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \ client.c sql_client.cc mini_client_errors.c pack.c\ stacktrace.c repl_failsafe.h repl_failsafe.cc \ sql_olap.cc sql_view.cc \ - gstream.cc spatial.cc sql_help.cc protocol_cursor.cc \ + gstream.cc spatial.cc sql_help.cc sql_cursor.cc \ tztime.cc my_time.c my_decimal.cc\ sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \ sp_cache.cc parse_file.cc sql_trigger.cc \ - examples/ha_example.cc examples/ha_archive.cc \ + examples/ha_example.cc ha_archive.cc \ examples/ha_tina.cc ha_blackhole.cc \ ha_partition.cc sql_partition.cc \ ha_federated.cc diff --git a/sql/examples/ha_example.cc b/sql/examples/ha_example.cc index dfc2fa7a260..d340b9289ec 100644 --- a/sql/examples/ha_example.cc +++ b/sql/examples/ha_example.cc @@ -73,8 +73,12 @@ #include "ha_example.h" -static handlerton example_hton= { - "CSV", +handlerton example_hton= { + "EXAMPLE", + SHOW_OPTION_YES, + "Example storage engine", + DB_TYPE_EXAMPLE_DB, + NULL, /* We do need to write one! */ 0, /* slot */ 0, /* savepoint size. */ NULL, /* close_connection */ @@ -90,7 +94,7 @@ static handlerton example_hton= { NULL, /* create_cursor_read_view */ NULL, /* set_cursor_read_view */ NULL, /* close_cursor_read_view */ - HTON_NO_FLAGS + HTON_CAN_RECREATE }; /* Variables for example share methods */ diff --git a/sql/examples/ha_tina.cc b/sql/examples/ha_tina.cc index a611466ca1d..2c193f4ce84 100644 --- a/sql/examples/ha_tina.cc +++ b/sql/examples/ha_tina.cc @@ -58,8 +58,12 @@ pthread_mutex_t tina_mutex; static HASH tina_open_tables; static int tina_init= 0; -static handlerton tina_hton= { +handlerton tina_hton= { "CSV", + SHOW_OPTION_YES, + "CSV storage engine", + DB_TYPE_CSV_DB, + NULL, /* One needs to be written! */ 0, /* slot */ 0, /* savepoint size. */ NULL, /* close_connection */ @@ -75,7 +79,7 @@ static handlerton tina_hton= { NULL, /* create_cursor_read_view */ NULL, /* set_cursor_read_view */ NULL, /* close_cursor_read_view */ - HTON_NO_FLAGS + HTON_CAN_RECREATE }; /***************************************************************************** @@ -243,6 +247,16 @@ static int free_share(TINA_SHARE *share) DBUG_RETURN(result_code); } +bool tina_end() +{ + if (tina_init) + { + hash_free(&tina_open_tables); + VOID(pthread_mutex_destroy(&tina_mutex)); + } + tina_init= 0; + return FALSE; +} /* Finds the end of a line. @@ -873,19 +887,7 @@ THR_LOCK_DATA **ha_tina::store_lock(THD *thd, return to; } -/* - Range optimizer calls this. - I need to update the information on this. -*/ -ha_rows ha_tina::records_in_range(uint inx, key_range *min_key, - key_range *max_key) -{ - DBUG_ENTER("ha_tina::records_in_range "); - DBUG_RETURN(records); // Good guess -} - - -/* +/* Create a table. You do not want to leave the table open after a call to this (the database will call ::open() if it needs to). */ diff --git a/sql/examples/ha_tina.h b/sql/examples/ha_tina.h index 43479c7c11c..0ac90a05812 100644 --- a/sql/examples/ha_tina.h +++ b/sql/examples/ha_tina.h @@ -83,11 +83,6 @@ public: */ virtual double scan_time() { return (double) (records+deleted) / 20.0+10; } /* The next method will never be called */ - virtual double read_time(uint index, uint ranges, 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. @@ -119,10 +114,6 @@ public: int reset(void); int external_lock(THD *thd, int lock_type); int delete_all_rows(void); - ha_rows records_in_range(uint inx, key_range *min_key, - key_range *max_key); -// int delete_table(const char *from); -// int rename_table(const char * from, const char * to); int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info); THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, @@ -133,3 +124,6 @@ public: int find_current_row(byte *buf); int chain_append(); }; + +bool tina_end(); + diff --git a/sql/field.cc b/sql/field.cc index 779dae20220..d334dc1ab53 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -3440,7 +3440,7 @@ int Field_long::store(longlong nr, bool unsigned_val) else { if (nr < 0 && unsigned_val) - nr= INT_MAX32+1; // Generate overflow + nr= ((longlong) INT_MAX32) + 1; // Generate overflow if (nr < (longlong) INT_MIN32) { res=(int32) INT_MIN32; diff --git a/sql/examples/ha_archive.cc b/sql/ha_archive.cc index 85104405024..59c56e80cc3 100644 --- a/sql/examples/ha_archive.cc +++ b/sql/ha_archive.cc @@ -18,7 +18,7 @@ #pragma implementation // gcc: Class implementation #endif -#include "../mysql_priv.h" +#include "mysql_priv.h" #ifdef HAVE_ARCHIVE_DB #include "ha_archive.h" @@ -116,7 +116,7 @@ */ /* If the archive storage engine has been inited */ -static bool archive_inited= 0; +static bool archive_inited= FALSE; /* Variables for archive share methods */ pthread_mutex_t archive_mutex; static HASH archive_open_tables; @@ -136,8 +136,12 @@ static HASH archive_open_tables; #define ARCHIVE_CHECK_HEADER 254 // The number we use to determine corruption /* dummy handlerton - only to have something to return from archive_db_init */ -static handlerton archive_hton = { - "archive", +handlerton archive_hton = { + "ARCHIVE", + SHOW_OPTION_YES, + "Archive storage engine", + DB_TYPE_ARCHIVE_DB, + archive_db_init, 0, /* slot */ 0, /* savepoint size. */ NULL, /* close_connection */ @@ -176,18 +180,28 @@ static byte* archive_get_key(ARCHIVE_SHARE *share,uint *length, void RETURN - &archive_hton OK - 0 Error + FALSE OK + TRUE Error */ -handlerton *archive_db_init() +bool archive_db_init() { - archive_inited= 1; - VOID(pthread_mutex_init(&archive_mutex, MY_MUTEX_INIT_FAST)); + DBUG_ENTER("archive_db_init"); + if (pthread_mutex_init(&archive_mutex, MY_MUTEX_INIT_FAST)) + goto error; if (hash_init(&archive_open_tables, system_charset_info, 32, 0, 0, (hash_get_key) archive_get_key, 0, 0)) - return 0; - return &archive_hton; + { + VOID(pthread_mutex_destroy(&archive_mutex)); + } + else + { + archive_inited= TRUE; + DBUG_RETURN(FALSE); + } +error: + have_archive_db= SHOW_OPTION_DISABLED; // If we couldn't use handler + DBUG_RETURN(TRUE); } /* diff --git a/sql/examples/ha_archive.h b/sql/ha_archive.h index e2d8aa49add..849b5b5bd6c 100644 --- a/sql/examples/ha_archive.h +++ b/sql/ha_archive.h @@ -105,6 +105,6 @@ public: enum thr_lock_type lock_type); }; -handlerton *archive_db_init(void); +bool archive_db_init(void); bool archive_db_end(void); diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc index 5bead36600b..3312b60e6c4 100644 --- a/sql/ha_berkeley.cc +++ b/sql/ha_berkeley.cc @@ -108,8 +108,12 @@ static int berkeley_close_connection(THD *thd); static int berkeley_commit(THD *thd, bool all); static int berkeley_rollback(THD *thd, bool all); -static handlerton berkeley_hton = { +handlerton berkeley_hton = { "BerkeleyDB", + SHOW_OPTION_YES, + "Supports transactions and page-level locking", + DB_TYPE_BERKELEY_DB, + berkeley_init, 0, /* slot */ 0, /* savepoint size */ berkeley_close_connection, @@ -136,10 +140,13 @@ typedef struct st_berkeley_trx_data { /* General functions */ -handlerton *berkeley_init(void) +bool berkeley_init(void) { DBUG_ENTER("berkeley_init"); + if (have_berkeley_db != SHOW_OPTION_YES) + goto error; + if (!berkeley_tmpdir) berkeley_tmpdir=mysql_tmpdir; if (!berkeley_home) @@ -165,7 +172,7 @@ handlerton *berkeley_init(void) berkeley_log_file_size= max(berkeley_log_file_size, 10*1024*1024L); if (db_env_create(&db_env,0)) - DBUG_RETURN(0); + goto error; db_env->set_errcall(db_env,berkeley_print_error); db_env->set_errpfx(db_env,"bdb"); db_env->set_noticecall(db_env, berkeley_noticecall); @@ -195,13 +202,16 @@ handlerton *berkeley_init(void) { db_env->close(db_env,0); db_env=0; - DBUG_RETURN(0); + goto error; } (void) hash_init(&bdb_open_tables,system_charset_info,32,0,0, (hash_get_key) bdb_get_key,0,0); pthread_mutex_init(&bdb_mutex,MY_MUTEX_INIT_FAST); - DBUG_RETURN(&berkeley_hton); + DBUG_RETURN(FALSE); +error: + have_berkeley_db= SHOW_OPTION_DISABLED; // If we couldn't use handler + DBUG_RETURN(TRUE); } @@ -1897,7 +1907,7 @@ int ha_berkeley::external_lock(THD *thd, int lock_type) Under LOCK TABLES, each used tables will force a call to start_stmt. */ -int ha_berkeley::start_stmt(THD *thd) +int ha_berkeley::start_stmt(THD *thd, thr_lock_type lock_type) { int error=0; DBUG_ENTER("ha_berkeley::start_stmt"); diff --git a/sql/ha_berkeley.h b/sql/ha_berkeley.h index aab76accefa..95ff64a082a 100644 --- a/sql/ha_berkeley.h +++ b/sql/ha_berkeley.h @@ -124,7 +124,7 @@ class ha_berkeley: public handler int extra(enum ha_extra_function operation); int reset(void); int external_lock(THD *thd, int lock_type); - int start_stmt(THD *thd); + int start_stmt(THD *thd, thr_lock_type lock_type); void position(byte *record); int analyze(THD* thd,HA_CHECK_OPT* check_opt); int optimize(THD* thd, HA_CHECK_OPT* check_opt); @@ -162,7 +162,7 @@ extern char *berkeley_home, *berkeley_tmpdir, *berkeley_logdir; extern long berkeley_lock_scan_time; extern TYPELIB berkeley_lock_typelib; -handlerton *berkeley_init(void); +bool berkeley_init(void); bool berkeley_end(void); bool berkeley_flush_logs(void); int berkeley_show_logs(Protocol *protocol); diff --git a/sql/ha_blackhole.cc b/sql/ha_blackhole.cc index a287d6e446b..2505919af39 100644 --- a/sql/ha_blackhole.cc +++ b/sql/ha_blackhole.cc @@ -26,8 +26,12 @@ /* Blackhole storage engine handlerton */ -static handlerton blackhole_hton= { +handlerton blackhole_hton= { "BLACKHOLE", + SHOW_OPTION_YES, + "/dev/null storage engine (anything you write to it disappears)", + DB_TYPE_BLACKHOLE_DB, + NULL, 0, /* slot */ 0, /* savepoint size. */ NULL, /* close_connection */ @@ -43,7 +47,7 @@ static handlerton blackhole_hton= { NULL, /* create_cursor_read_view */ NULL, /* set_cursor_read_view */ NULL, /* close_cursor_read_view */ - HTON_NO_FLAGS + HTON_CAN_RECREATE }; /***************************************************************************** diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc index fa027ce71b5..66c2431844d 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -363,6 +363,33 @@ pthread_mutex_t federated_mutex; // This is the mutex we use to static int federated_init= FALSE; // Variable for checking the // init state of hash +/* Federated storage engine handlerton */ + +handlerton federated_hton= { + "FEDERATED", + SHOW_OPTION_YES, + "Federated MySQL storage engine", + DB_TYPE_FEDERATED_DB, + federated_db_init, + 0, /* slot */ + 0, /* savepoint size. */ + NULL, /* close_connection */ + NULL, /* savepoint */ + NULL, /* rollback to savepoint */ + NULL, /* release savepoint */ + NULL, /* commit */ + NULL, /* rollback */ + NULL, /* prepare */ + NULL, /* recover */ + NULL, /* commit_by_xid */ + NULL, /* rollback_by_xid */ + NULL, /* create_cursor_read_view */ + NULL, /* set_cursor_read_view */ + NULL, /* close_cursor_read_view */ + HTON_ALTER_NOT_SUPPORTED +}; + + /* Function we use in the creation of our hash to get key. */ static byte *federated_get_key(FEDERATED_SHARE *share, uint *length, @@ -386,10 +413,22 @@ static byte *federated_get_key(FEDERATED_SHARE *share, uint *length, bool federated_db_init() { - federated_init= 1; - VOID(pthread_mutex_init(&federated_mutex, MY_MUTEX_INIT_FAST)); - return (hash_init(&federated_open_tables, system_charset_info, 32, 0, 0, - (hash_get_key) federated_get_key, 0, 0)); + DBUG_ENTER("federated_db_init"); + if (pthread_mutex_init(&federated_mutex, MY_MUTEX_INIT_FAST)) + goto error; + if (hash_init(&federated_open_tables, system_charset_info, 32, 0, 0, + (hash_get_key) federated_get_key, 0, 0)) + { + VOID(pthread_mutex_destroy(&federated_mutex)); + } + else + { + federated_init= TRUE; + DBUG_RETURN(FALSE); + } +error: + have_federated_db= SHOW_OPTION_DISABLED; // If we couldn't use handler + DBUG_RETURN(TRUE); } @@ -588,12 +627,15 @@ static int parse_url(FEDERATED_SHARE *share, TABLE *table, DBUG_ENTER("ha_federated::parse_url"); share->port= 0; + share->socket= 0; DBUG_PRINT("info", ("Length %d \n", table->s->connect_string.length)); DBUG_PRINT("info", ("String %.*s \n", table->s->connect_string.length, table->s->connect_string.str)); - share->scheme= my_strdup_with_length(table->s->connect_string.str, - table->s->connect_string.length+1, - MYF(0)); + share->scheme= my_strdup_with_length((const byte*)table->s-> + connect_string.str, + table->s->connect_string.length, + MYF(0)); + // Add a null for later termination of table name share->scheme[table->s->connect_string.length]= 0; DBUG_PRINT("info",("parse_url alloced share->scheme %lx", share->scheme)); @@ -691,29 +733,6 @@ error: } -/* Federated storage engine handlerton */ - -static handlerton federated_hton= { - "FEDERATED", - 0, /* slot */ - 0, /* savepoint size. */ - NULL, /* close_connection */ - NULL, /* savepoint */ - NULL, /* rollback to savepoint */ - NULL, /* release savepoint */ - NULL, /* commit */ - NULL, /* rollback */ - NULL, /* prepare */ - NULL, /* recover */ - NULL, /* commit_by_xid */ - NULL, /* rollback_by_xid */ - NULL, /* create_cursor_read_view */ - NULL, /* set_cursor_read_view */ - NULL, /* close_cursor_read_view */ - HTON_NO_FLAGS -}; - - /***************************************************************************** ** FEDERATED tables *****************************************************************************/ @@ -1374,13 +1393,9 @@ static int free_share(FEDERATED_SHARE *share) if (!--share->use_count) { - if (share->scheme) - { - my_free((gptr) share->scheme, MYF(0)); - share->scheme= 0; - } - hash_delete(&federated_open_tables, (byte*) share); + my_free((gptr) share->scheme, MYF(MY_ALLOW_ZERO_PTR)); + share->scheme= 0; thr_lock_delete(&share->lock); VOID(pthread_mutex_destroy(&share->mutex)); my_free((gptr) share, MYF(0)); @@ -2601,7 +2616,8 @@ int ha_federated::stash_remote_error() { DBUG_ENTER("ha_federated::stash_remote_error()"); remote_error_number= mysql_errno(mysql); - snprintf(remote_error_buf, FEDERATED_QUERY_BUFFER_SIZE, mysql_error(mysql)); + my_snprintf(remote_error_buf, FEDERATED_QUERY_BUFFER_SIZE, + mysql_error(mysql)); DBUG_RETURN(HA_FEDERATED_ERROR_WITH_REMOTE_SYSTEM); } diff --git a/sql/ha_heap.cc b/sql/ha_heap.cc index 01e693978db..ab13f03825d 100644 --- a/sql/ha_heap.cc +++ b/sql/ha_heap.cc @@ -23,8 +23,12 @@ #include <myisampack.h> #include "ha_heap.h" -static handlerton heap_hton= { +handlerton heap_hton= { "MEMORY", + SHOW_OPTION_YES, + "Hash based, stored in memory, useful for temporary tables", + DB_TYPE_HEAP, + NULL, 0, /* slot */ 0, /* savepoint size. */ NULL, /* close_connection */ @@ -40,7 +44,7 @@ static handlerton heap_hton= { NULL, /* create_cursor_read_view */ NULL, /* set_cursor_read_view */ NULL, /* close_cursor_read_view */ - HTON_NO_FLAGS + HTON_CAN_RECREATE }; /***************************************************************************** diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 638bda49002..dc019bc7a3a 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -206,8 +206,12 @@ static int innobase_rollback_to_savepoint(THD* thd, void *savepoint); static int innobase_savepoint(THD* thd, void *savepoint); static int innobase_release_savepoint(THD* thd, void *savepoint); -static handlerton innobase_hton = { +handlerton innobase_hton = { "InnoDB", + SHOW_OPTION_YES, + "Supports transactions, row-level locking, and foreign keys", + DB_TYPE_INNODB, + innobase_init, 0, /* slot */ sizeof(trx_named_savept_t), /* savepoint size. TODO: use it */ innobase_close_connection, @@ -563,25 +567,29 @@ innobase_mysql_print_thd( use the default max length */ { const THD* thd; + const Security_context *sctx; const char* s; thd = (const THD*) input_thd; + /* We probably want to have original user as part of debug output. */ + sctx = &thd->main_security_ctx; + fprintf(f, "MySQL thread id %lu, query id %lu", thd->thread_id, (ulong) thd->query_id); - if (thd->host) { + if (sctx->host) { putc(' ', f); - fputs(thd->host, f); + fputs(sctx->host, f); } - if (thd->ip) { + if (sctx->ip) { putc(' ', f); - fputs(thd->ip, f); + fputs(sctx->ip, f); } - if (thd->user) { + if (sctx->user) { putc(' ', f); - fputs(thd->user, f); + fputs(sctx->user, f); } if ((s = thd->proc_info)) { @@ -1184,10 +1192,10 @@ ha_innobase::init_table_handle_for_HANDLER(void) /************************************************************************* Opens an InnoDB database. */ -handlerton* +bool innobase_init(void) /*===============*/ - /* out: TRUE if error */ + /* out: &innobase_hton, or NULL on error */ { static char current_dir[3]; /* Set if using current lib */ int err; @@ -1196,6 +1204,9 @@ innobase_init(void) DBUG_ENTER("innobase_init"); + if (have_innodb != SHOW_OPTION_YES) + goto error; + ut_a(DATA_MYSQL_TRUE_VARCHAR == (ulint)MYSQL_TYPE_VARCHAR); os_innodb_umask = (ulint)my_umask; @@ -1248,7 +1259,7 @@ innobase_init(void) copy of it: */ internal_innobase_data_file_path = my_strdup(innobase_data_file_path, - MYF(MY_WME)); + MYF(MY_FAE)); ret = (bool) srv_parse_data_file_paths_and_sizes( internal_innobase_data_file_path, @@ -1263,7 +1274,7 @@ innobase_init(void) "InnoDB: syntax error in innodb_data_file_path"); my_free(internal_innobase_data_file_path, MYF(MY_ALLOW_ZERO_PTR)); - DBUG_RETURN(0); + goto error; } /* -------------- Log files ---------------------------*/ @@ -1294,7 +1305,7 @@ innobase_init(void) my_free(internal_innobase_data_file_path, MYF(MY_ALLOW_ZERO_PTR)); - DBUG_RETURN(0); + goto error; } /* --------------------------------------------------*/ @@ -1382,7 +1393,7 @@ innobase_init(void) if (err != DB_SUCCESS) { my_free(internal_innobase_data_file_path, MYF(MY_ALLOW_ZERO_PTR)); - DBUG_RETURN(0); + goto error; } (void) hash_init(&innobase_open_tables,system_charset_info, 32, 0, 0, @@ -1409,7 +1420,10 @@ innobase_init(void) glob_mi.pos = trx_sys_mysql_master_log_pos; } */ - DBUG_RETURN(&innobase_hton); + DBUG_RETURN(FALSE); +error: + have_innodb= SHOW_OPTION_DISABLED; // If we couldn't use handler + DBUG_RETURN(TRUE); } /*********************************************************************** @@ -2112,7 +2126,7 @@ innobase_rollback_to_savepoint( /* TODO: use provided savepoint data area to store savepoint data */ - longlong2str((ulonglong)savepoint, name, 36); + longlong2str((ulint)savepoint, name, 36); error = (int) trx_rollback_to_savepoint_for_mysql(trx, name, &mysql_binlog_cache_pos); @@ -2141,7 +2155,7 @@ innobase_release_savepoint( /* TODO: use provided savepoint data area to store savepoint data */ - longlong2str((ulonglong)savepoint, name, 36); + longlong2str((ulint)savepoint, name, 36); error = (int) trx_release_savepoint_for_mysql(trx, name); @@ -2182,7 +2196,7 @@ innobase_savepoint( /* TODO: use provided savepoint data area to store savepoint data */ char name[64]; - longlong2str((ulonglong)savepoint,name,36); + longlong2str((ulint)savepoint,name,36); error = (int) trx_savepoint_for_mysql(trx, name, (ib_longlong)0); @@ -2382,7 +2396,7 @@ ha_innobase::open( "how you can resolve the problem.\n", norm_name); free_share(share); - my_free((char*) upd_buff, MYF(0)); + my_free((gptr) upd_buff, MYF(0)); my_errno = ENOENT; DBUG_RETURN(HA_ERR_NO_SUCH_TABLE); @@ -2400,7 +2414,7 @@ ha_innobase::open( "how you can resolve the problem.\n", norm_name); free_share(share); - my_free((char*) upd_buff, MYF(0)); + my_free((gptr) upd_buff, MYF(0)); my_errno = ENOENT; dict_table_decrement_handle_count(ib_table); @@ -2488,13 +2502,13 @@ Closes a handle to an InnoDB table. */ int ha_innobase::close(void) /*====================*/ - /* out: error number */ + /* out: 0 */ { DBUG_ENTER("ha_innobase::close"); row_prebuilt_free((row_prebuilt_t*) innobase_prebuilt); - my_free((char*) upd_buff, MYF(0)); + my_free((gptr) upd_buff, MYF(0)); free_share(share); /* Tell InnoDB server that there might be work for @@ -4490,7 +4504,8 @@ create_index( ulint is_unsigned; ulint i; ulint j; - + ulint* field_lengths; + DBUG_ENTER("create_index"); key = form->key_info + key_num; @@ -4512,6 +4527,10 @@ create_index( index = dict_mem_index_create((char*) table_name, key->name, 0, ind_type, n_fields); + + field_lengths = (ulint*) my_malloc(sizeof(ulint) * n_fields, + MYF(MY_FAE)); + for (i = 0; i < n_fields; i++) { key_part = key->key_part + i; @@ -4566,6 +4585,8 @@ create_index( prefix_len = 0; } + field_lengths[i] = key_part->length; + /* We assume all fields should be sorted in ascending order, hence the '0': */ @@ -4574,10 +4595,12 @@ create_index( 0, prefix_len); } - error = row_create_index_for_mysql(index, trx); + error = row_create_index_for_mysql(index, trx, field_lengths); error = convert_error_code_to_mysql(error, NULL); + my_free((gptr) field_lengths, MYF(0)); + DBUG_RETURN(error); } @@ -4600,7 +4623,7 @@ create_clustered_index_when_no_primary( index = dict_mem_index_create((char*) table_name, (char*) "GEN_CLUST_INDEX", 0, DICT_CLUSTERED, 0); - error = row_create_index_for_mysql(index, trx); + error = row_create_index_for_mysql(index, trx, NULL); error = convert_error_code_to_mysql(error, NULL); @@ -5136,7 +5159,7 @@ ha_innobase::records_in_range( mysql_byte* key_val_buff2 = (mysql_byte*) my_malloc( table->s->reclength + table->s->max_key_length + 100, - MYF(MY_WME)); + MYF(MY_FAE)); ulint buff2_len = table->s->reclength + table->s->max_key_length + 100; dtuple_t* range_start; @@ -5195,7 +5218,7 @@ ha_innobase::records_in_range( dtuple_free_for_mysql(heap1); dtuple_free_for_mysql(heap2); - my_free((char*) key_val_buff2, MYF(0)); + my_free((gptr) key_val_buff2, MYF(0)); prebuilt->trx->op_info = (char*)""; @@ -5989,7 +6012,8 @@ int ha_innobase::start_stmt( /*====================*/ /* out: 0 or error code */ - THD* thd) /* in: handle to the user thread */ + THD* thd, /* in: handle to the user thread */ + thr_lock_type lock_type) { row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; trx_t* trx; @@ -6030,7 +6054,7 @@ ha_innobase::start_stmt( } else { if (trx->isolation_level != TRX_ISO_SERIALIZABLE && thd->lex->sql_command == SQLCOM_SELECT - && thd->lex->lock_option == TL_READ) { + && lock_type == TL_READ) { /* For other than temporary tables, we obtain no lock for consistent read (plain SELECT). */ @@ -6063,6 +6087,8 @@ ha_innobase::start_stmt( } } + trx->detailed_error[0] = '\0'; + /* Set the MySQL flag to mark that there is an active transaction */ if (trx->active_trans == 0) { @@ -6136,6 +6162,8 @@ ha_innobase::external_lock( if (lock_type != F_UNLCK) { /* MySQL is setting a new table lock */ + trx->detailed_error[0] = '\0'; + /* Set the MySQL flag to mark that there is an active transaction */ if (trx->active_trans == 0) { @@ -6673,6 +6701,11 @@ ha_innobase::store_lock( prebuilt->select_lock_type = LOCK_NONE; prebuilt->stored_select_lock_type = LOCK_NONE; + } else if (thd->lex->sql_command == SQLCOM_CHECKSUM) { + /* Use consistent read for checksum table */ + + prebuilt->select_lock_type = LOCK_NONE; + prebuilt->stored_select_lock_type = LOCK_NONE; } else { prebuilt->select_lock_type = LOCK_S; prebuilt->stored_select_lock_type = LOCK_S; @@ -6939,6 +6972,18 @@ ha_innobase::reset_auto_increment(ulonglong value) DBUG_RETURN(0); } +/* See comment in handler.cc */ +bool +ha_innobase::get_error_message(int error, String *buf) +{ + trx_t* trx = check_trx_exists(current_thd); + + buf->copy(trx->detailed_error, strlen(trx->detailed_error), + system_charset_info); + + return FALSE; +} + /*********************************************************************** Compares two 'refs'. A 'ref' is the (internal) primary key value of the row. If there is no explicitly declared non-null unique key or a primary key, then diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index 595ab5ccde2..d687e8bf160 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -150,7 +150,7 @@ class ha_innobase: public handler int extra(enum ha_extra_function operation); int external_lock(THD *thd, int lock_type); int transactional_table_lock(THD *thd, int lock_type); - int start_stmt(THD *thd); + int start_stmt(THD *thd, thr_lock_type lock_type); int ha_retrieve_all_cols() { @@ -184,6 +184,8 @@ class ha_innobase: public handler void init_table_handle_for_HANDLER(); ulonglong get_auto_increment(); int reset_auto_increment(ulonglong value); + + virtual bool get_error_message(int error, String *buf); uint8 table_cache_type() { return HA_CACHE_TBL_ASKTRANSACT; } /* @@ -251,7 +253,7 @@ extern ulong srv_commit_concurrency; extern TYPELIB innobase_lock_typelib; -handlerton *innobase_init(void); +bool innobase_init(void); bool innobase_end(void); bool innobase_flush_logs(void); uint innobase_get_free_space(void); @@ -356,4 +358,4 @@ restored to a transaction read view. */ void innobase_set_cursor_view( /*=====================*/ - void* curview); /* in: Consistent read view to be closed */ + void* curview); /* in: Consistent read view to be set */ diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 715ee3da8b9..689dbf2fc24 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -39,6 +39,12 @@ const char *myisam_recover_names[] = TYPELIB myisam_recover_typelib= {array_elements(myisam_recover_names)-1,"", myisam_recover_names, NULL}; +const char *myisam_stats_method_names[] = {"nulls_unequal", "nulls_equal", + NullS}; +TYPELIB myisam_stats_method_typelib= { + array_elements(myisam_stats_method_names) - 1, "", + myisam_stats_method_names, NULL}; + /***************************************************************************** ** MyISAM tables @@ -46,8 +52,12 @@ TYPELIB myisam_recover_typelib= {array_elements(myisam_recover_names)-1,"", /* MyISAM handlerton */ -static handlerton myisam_hton= { +handlerton myisam_hton= { "MyISAM", + SHOW_OPTION_YES, + "Default engine as of MySQL 3.23 with great performance", + DB_TYPE_MYISAM, + NULL, 0, /* slot */ 0, /* savepoint size. */ NULL, /* close_connection */ @@ -67,7 +77,7 @@ static handlerton myisam_hton= { MyISAM doesn't support transactions and doesn't have transaction-dependent context: cursors can survive a commit. */ - HTON_NO_FLAGS + HTON_CAN_RECREATE }; // collect errors printed by mi_check routines @@ -324,6 +334,7 @@ int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt) param.db_name= table->s->db; param.table_name= table->alias; param.testflag = check_opt->flags | T_CHECK | T_SILENT; + param.stats_method= (enum_mi_stats_method)thd->variables.myisam_stats_method; if (!(table->db_stat & HA_READ_ONLY)) param.testflag|= T_STATISTICS; @@ -413,6 +424,7 @@ int ha_myisam::analyze(THD *thd, HA_CHECK_OPT* check_opt) param.testflag= (T_FAST | T_CHECK | T_SILENT | T_STATISTICS | T_DONT_CHECK_CHECKSUM); param.using_global_keycache = 1; + param.stats_method= (enum_mi_stats_method)thd->variables.myisam_stats_method; if (!(share->state.changed & STATE_NOT_ANALYZED)) return HA_ADMIN_ALREADY_DONE; @@ -967,6 +979,7 @@ int ha_myisam::enable_indexes(uint mode) T_CREATE_MISSING_KEYS); param.myf_rw&= ~MY_WAIT_IF_FULL; param.sort_buffer_length= thd->variables.myisam_sort_buff_size; + param.stats_method= (enum_mi_stats_method)thd->variables.myisam_stats_method; param.tmpdir=&mysql_tmpdir_list; if ((error= (repair(thd,param,0) != HA_ADMIN_OK)) && param.retry_repair) { @@ -1694,7 +1707,7 @@ int ha_myisam::ft_read(byte * buf) uint ha_myisam::checksum() const { - return (uint)file->s->state.checksum; + return (uint)file->state->checksum; } diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index 388ecfb331f..cdea97bcea3 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -34,8 +34,12 @@ /* MyISAM MERGE handlerton */ -static handlerton myisammrg_hton= { - "MRG_MyISAM", +handlerton myisammrg_hton= { + "MRG_MYISAM", + SHOW_OPTION_YES, + "Collection of identical MyISAM tables", + DB_TYPE_MRG_MYISAM, + NULL, 0, /* slot */ 0, /* savepoint size. */ NULL, /* close_connection */ @@ -51,7 +55,7 @@ static handlerton myisammrg_hton= { NULL, /* create_cursor_read_view */ NULL, /* set_cursor_read_view */ NULL, /* close_cursor_read_view */ - HTON_NO_FLAGS + HTON_CAN_RECREATE }; diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index c7d281a1ba0..490d00c55d0 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -51,8 +51,12 @@ static int ndbcluster_close_connection(THD *thd); static int ndbcluster_commit(THD *thd, bool all); static int ndbcluster_rollback(THD *thd, bool all); -static handlerton ndbcluster_hton = { +handlerton ndbcluster_hton = { "ndbcluster", + SHOW_OPTION_YES, + "Clustered, fault-tolerant, memory-based tables", + DB_TYPE_NDBCLUSTER, + ndbcluster_init, 0, /* slot */ 0, /* savepoint size */ ndbcluster_close_connection, @@ -1239,7 +1243,8 @@ inline ulong ha_ndbcluster::index_flags(uint idx_no, uint part, DBUG_ENTER("ha_ndbcluster::index_flags"); DBUG_PRINT("info", ("idx_no: %d", idx_no)); DBUG_ASSERT(get_index_type_from_table(idx_no) < index_flags_size); - DBUG_RETURN(index_type_flags[get_index_type_from_table(idx_no)]); + DBUG_RETURN(index_type_flags[get_index_type_from_table(idx_no)] | + HA_KEY_SCAN_NOT_ROR); } static void shrink_varchar(Field* field, const byte* & ptr, char* buf) @@ -3390,7 +3395,7 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) startTransaction for each transaction/statement. */ -int ha_ndbcluster::start_stmt(THD *thd) +int ha_ndbcluster::start_stmt(THD *thd, thr_lock_type lock_type) { int error=0; DBUG_ENTER("start_stmt"); @@ -4701,11 +4706,14 @@ static int connect_callback() return 0; } -handlerton * -ndbcluster_init() +bool ndbcluster_init() { int res; DBUG_ENTER("ndbcluster_init"); + + if (have_ndbcluster != SHOW_OPTION_YES) + goto ndbcluster_init_error; + // Set connectstring if specified if (opt_ndbcluster_connectstring != 0) DBUG_PRINT("connectstring", ("%s", opt_ndbcluster_connectstring)); @@ -4786,16 +4794,17 @@ ndbcluster_init() } ndbcluster_inited= 1; - DBUG_RETURN(&ndbcluster_hton); + DBUG_RETURN(FALSE); - ndbcluster_init_error: +ndbcluster_init_error: if (g_ndb) delete g_ndb; g_ndb= NULL; if (g_ndb_cluster_connection) delete g_ndb_cluster_connection; g_ndb_cluster_connection= NULL; - DBUG_RETURN(NULL); + have_ndbcluster= SHOW_OPTION_DISABLED; // If we couldn't use handler + DBUG_RETURN(TRUE); } @@ -7472,6 +7481,50 @@ ha_ndbcluster::generate_scan_filter(Ndb_cond_stack *ndb_cond_stack, DBUG_RETURN(0); } +int +ndbcluster_show_status(THD* thd) +{ + Protocol *protocol= thd->protocol; + + DBUG_ENTER("ndbcluster_show_status"); + + if (have_ndbcluster != SHOW_OPTION_YES) + { + my_message(ER_NOT_SUPPORTED_YET, + "Cannot call SHOW NDBCLUSTER STATUS because skip-ndbcluster is defined", + MYF(0)); + DBUG_RETURN(TRUE); + } + + List<Item> field_list; + field_list.push_back(new Item_empty_string("free_list", 255)); + field_list.push_back(new Item_return_int("created", 10,MYSQL_TYPE_LONG)); + field_list.push_back(new Item_return_int("free", 10,MYSQL_TYPE_LONG)); + field_list.push_back(new Item_return_int("sizeof", 10,MYSQL_TYPE_LONG)); + + if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) + DBUG_RETURN(TRUE); + + if (get_thd_ndb(thd) && get_thd_ndb(thd)->ndb) + { + Ndb* ndb= (get_thd_ndb(thd))->ndb; + Ndb::Free_list_usage tmp; tmp.m_name= 0; + while (ndb->get_free_list_usage(&tmp)) + { + protocol->prepare_for_resend(); + + protocol->store(tmp.m_name, &my_charset_bin); + protocol->store((uint)tmp.m_created); + protocol->store((uint)tmp.m_free); + protocol->store((uint)tmp.m_sizeof); + if (protocol->write()) + DBUG_RETURN(TRUE); + } + } + send_eof(thd); + + DBUG_RETURN(FALSE); +} /* Create a table in NDB Cluster diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index 0d99e9c1220..4530e5f1977 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -510,7 +510,7 @@ class ha_ndbcluster: public handler int extra(enum ha_extra_function operation); int extra_opt(enum ha_extra_function operation, ulong cache_size); int external_lock(THD *thd, int lock_type); - int start_stmt(THD *thd); + int start_stmt(THD *thd, thr_lock_type lock_type); const char * table_type() const; const char ** bas_ext() const; ulong table_flags(void) const; @@ -746,7 +746,7 @@ private: extern struct show_var_st ndb_status_variables[]; -handlerton *ndbcluster_init(void); +bool ndbcluster_init(void); bool ndbcluster_end(void); int ndbcluster_discover(THD* thd, const char* dbname, const char* name, @@ -758,3 +758,5 @@ int ndbcluster_table_exists_in_engine(THD* thd, int ndbcluster_drop_database(const char* path); void ndbcluster_print_error(int error, const NdbOperation *error_op); + +int ndbcluster_show_status(THD*); diff --git a/sql/handler.cc b/sql/handler.cc index 8c8904b1df0..5b07c9e9c24 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -25,42 +25,110 @@ #include "ha_heap.h" #include "ha_myisam.h" #include "ha_myisammrg.h" + + +/* + We have dummy hanldertons in case the handler has not been compiled + in. This will be removed in 5.1. +*/ #ifdef HAVE_BERKELEY_DB #include "ha_berkeley.h" +extern handlerton berkeley_hton; +#else +handlerton berkeley_hton = { "BerkeleyDB", SHOW_OPTION_NO, + "Supports transactions and page-level locking", DB_TYPE_BERKELEY_DB, NULL, + 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, HTON_NO_FLAGS }; #endif #ifdef HAVE_BLACKHOLE_DB #include "ha_blackhole.h" +extern handlerton blackhole_hton; +#else +handlerton blackhole_hton = { "BLACKHOLE", SHOW_OPTION_NO, + "/dev/null storage engine (anything you write to it disappears)", + DB_TYPE_BLACKHOLE_DB, NULL, 0, 0, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + HTON_NO_FLAGS }; #endif #ifdef HAVE_EXAMPLE_DB #include "examples/ha_example.h" +extern handlerton example_hton; +#else +handlerton example_hton = { "EXAMPLE", SHOW_OPTION_NO, + "Example storage engine", + DB_TYPE_EXAMPLE_DB, NULL, 0, 0, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + HTON_NO_FLAGS }; #endif #ifdef HAVE_PARTITION_DB #include "ha_partition.h" #endif #ifdef HAVE_ARCHIVE_DB -#include "examples/ha_archive.h" +#include "ha_archive.h" +extern handlerton archive_hton; +#else +handlerton archive_hton = { "ARCHIVE", SHOW_OPTION_NO, + "Archive storage engine", DB_TYPE_ARCHIVE_DB, NULL, 0, 0, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + HTON_NO_FLAGS }; #endif #ifdef HAVE_CSV_DB #include "examples/ha_tina.h" +extern handlerton tina_hton; +#else +handlerton tina_hton = { "CSV", SHOW_OPTION_NO, "CSV storage engine", + DB_TYPE_CSV_DB, NULL, 0, 0, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + HTON_NO_FLAGS }; #endif #ifdef HAVE_INNOBASE_DB #include "ha_innodb.h" +extern handlerton innobase_hton; +#else +handlerton innobase_hton = { "InnoDB", SHOW_OPTION_NO, + "Supports transactions, row-level locking, and foreign keys", + DB_TYPE_INNODB, NULL, 0, 0, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + HTON_NO_FLAGS }; #endif #ifdef HAVE_NDBCLUSTER_DB #include "ha_ndbcluster.h" +extern handlerton ndbcluster_hton; +#else +handlerton ndbcluster_hton = { "ndbcluster", SHOW_OPTION_NO, + "Clustered, fault-tolerant, memory-based tables", + DB_TYPE_NDBCLUSTER, NULL, 0, 0, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + HTON_NO_FLAGS }; #endif #ifdef HAVE_FEDERATED_DB #include "ha_federated.h" +extern handlerton federated_hton; +#else +handlerton federated_hton = { "FEDERATED", SHOW_OPTION_NO, + "Federated MySQL storage engine", DB_TYPE_FEDERATED_DB, NULL, 0, 0, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + HTON_NO_FLAGS }; #endif #include <myisampack.h> #include <errno.h> - /* static functions defined in this file */ +extern handlerton myisam_hton; +extern handlerton myisammrg_hton; +extern handlerton heap_hton; +extern handlerton binlog_hton; -static SHOW_COMP_OPTION have_yes= SHOW_OPTION_YES; +/* + Obsolete +*/ +handlerton isam_hton = { "ISAM", SHOW_OPTION_NO, "Obsolete storage engine", + DB_TYPE_ISAM, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, HTON_NO_FLAGS }; -/* list of all available storage engines (of their handlertons) */ -handlerton *handlertons[MAX_HA]={0}; + +/* static functions defined in this file */ + +static SHOW_COMP_OPTION have_yes= SHOW_OPTION_YES; /* number of entries in handlertons[] */ ulong total_ha; @@ -69,46 +137,35 @@ ulong total_ha_2pc; /* size of savepoint storage area (see ha_init) */ ulong savepoint_alloc_size; -struct show_table_type_st sys_table_types[]= -{ - {"MyISAM", &have_yes, - "Default engine as of MySQL 3.23 with great performance", DB_TYPE_MYISAM}, - {"MEMORY", &have_yes, - "Hash based, stored in memory, useful for temporary tables", DB_TYPE_HEAP}, - {"HEAP", &have_yes, - "Alias for MEMORY", DB_TYPE_HEAP}, - {"MERGE", &have_yes, - "Collection of identical MyISAM tables", DB_TYPE_MRG_MYISAM}, - {"MRG_MYISAM",&have_yes, - "Alias for MERGE", DB_TYPE_MRG_MYISAM}, - {"ISAM", &have_isam, - "Obsolete storage engine, now replaced by MyISAM", DB_TYPE_ISAM}, - {"MRG_ISAM", &have_isam, - "Obsolete storage engine, now replaced by MERGE", DB_TYPE_MRG_ISAM}, - {"InnoDB", &have_innodb, - "Supports transactions, row-level locking, and foreign keys", DB_TYPE_INNODB}, - {"INNOBASE", &have_innodb, - "Alias for INNODB", DB_TYPE_INNODB}, - {"BDB", &have_berkeley_db, - "Supports transactions and page-level locking", DB_TYPE_BERKELEY_DB}, - {"BERKELEYDB",&have_berkeley_db, - "Alias for BDB", DB_TYPE_BERKELEY_DB}, - {"NDBCLUSTER", &have_ndbcluster, - "Clustered, fault-tolerant, memory-based tables", DB_TYPE_NDBCLUSTER}, - {"NDB", &have_ndbcluster, - "Alias for NDBCLUSTER", DB_TYPE_NDBCLUSTER}, - {"EXAMPLE",&have_example_db, - "Example storage engine", DB_TYPE_EXAMPLE_DB}, - {"ARCHIVE",&have_archive_db, - "Archive storage engine", DB_TYPE_ARCHIVE_DB}, - {"CSV",&have_csv_db, - "CSV storage engine", DB_TYPE_CSV_DB}, - {"FEDERATED",&have_federated_db, - "Federated MySQL storage engine", DB_TYPE_FEDERATED_DB}, - {"BLACKHOLE",&have_blackhole_db, - "/dev/null storage engine (anything you write to it disappears)", - DB_TYPE_BLACKHOLE_DB}, - {NullS, NULL, NullS, DB_TYPE_UNKNOWN} +/* + This array is used for processing compiled in engines. +*/ +handlerton *sys_table_types[]= +{ + &myisam_hton, + &heap_hton, + &innobase_hton, + &berkeley_hton, + &blackhole_hton, + &example_hton, + &archive_hton, + &tina_hton, + &ndbcluster_hton, + &federated_hton, + &myisammrg_hton, + &binlog_hton, + &isam_hton, + NULL +}; + +struct show_table_alias_st sys_table_aliases[]= +{ + {"INNOBASE", "InnoDB"}, + {"NDB", "NDBCLUSTER"}, + {"BDB", "BERKELEYDB"}, + {"HEAP", "MEMORY"}, + {"MERGE", "MRG_MYISAM"}, + {NullS, NullS} }; const char *ha_row_type[] = { @@ -127,46 +184,78 @@ uint known_extensions_id= 0; enum db_type ha_resolve_by_name(const char *name, uint namelen) { THD *thd= current_thd; - if (thd && !my_strcasecmp(&my_charset_latin1, name, "DEFAULT")) { + show_table_alias_st *table_alias; + handlerton **types; + const char *ptr= name; + + if (thd && !my_strcasecmp(&my_charset_latin1, ptr, "DEFAULT")) return (enum db_type) thd->variables.table_type; + +retest: + for (types= sys_table_types; *types; types++) + { + if (!my_strcasecmp(&my_charset_latin1, ptr, (*types)->name)) + return (enum db_type) (*types)->db_type; } - show_table_type_st *types; - for (types= sys_table_types; types->type; types++) + /* + We check for the historical aliases. + */ + for (table_alias= sys_table_aliases; table_alias->type; table_alias++) { - if (!my_strcasecmp(&my_charset_latin1, name, types->type)) - return (enum db_type) types->db_type; + if (!my_strcasecmp(&my_charset_latin1, ptr, table_alias->alias)) + { + ptr= table_alias->type; + goto retest; + } } + return DB_TYPE_UNKNOWN; } - const char *ha_get_storage_engine(enum db_type db_type) { - show_table_type_st *types; - for (types= sys_table_types; types->type; types++) + handlerton **types; + for (types= sys_table_types; *types; types++) { - if (db_type == types->db_type) - return types->type; + if (db_type == (*types)->db_type) + return (*types)->name; } return "none"; } +bool ha_check_storage_engine_flag(enum db_type db_type, uint32 flag) +{ + handlerton **types; + for (types= sys_table_types; *types; types++) + { + if (db_type == (*types)->db_type) + { + if ((*types)->flags & flag) + return TRUE; + else + return FALSE; + } + } + + return FALSE; +} + my_bool ha_storage_engine_is_enabled(enum db_type database_type) { - show_table_type_st *types; - for (types= sys_table_types; types->type; types++) + handlerton **types; + for (types= sys_table_types; *types; types++) { - if ((database_type == types->db_type) && - (*types->value == SHOW_OPTION_YES)) + if ((database_type == (*types)->db_type) && + ((*types)->state == SHOW_OPTION_YES)) return TRUE; } return FALSE; } - /* Use other database handler if databasehandler is not incompiled */ +/* Use other database handler if databasehandler is not compiled in */ enum db_type ha_checktype(THD *thd, enum db_type database_type, bool no_substitute, bool report_error) @@ -372,8 +461,8 @@ static int ha_init_errors(void) SETMSG(HA_ERR_READ_ONLY_TRANSACTION, ER(ER_READ_ONLY_TRANSACTION)); SETMSG(HA_ERR_LOCK_DEADLOCK, ER(ER_LOCK_DEADLOCK)); SETMSG(HA_ERR_CANNOT_ADD_FOREIGN, ER(ER_CANNOT_ADD_FOREIGN)); - SETMSG(HA_ERR_NO_REFERENCED_ROW, ER(ER_NO_REFERENCED_ROW)); - SETMSG(HA_ERR_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED)); + SETMSG(HA_ERR_NO_REFERENCED_ROW, ER(ER_NO_REFERENCED_ROW_2)); + SETMSG(HA_ERR_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED_2)); SETMSG(HA_ERR_NO_SAVEPOINT, "No savepoint with that name"); SETMSG(HA_ERR_NON_UNIQUE_BLOCK_SIZE, "Non unique key block size"); SETMSG(HA_ERR_NO_SUCH_TABLE, "No such table: '%.64s'"); @@ -422,81 +511,24 @@ static inline void ha_was_inited_ok(handlerton **ht) int ha_init() { int error= 0; - handlerton **ht= handlertons; + handlerton **types; + show_table_alias_st *table_alias; total_ha= savepoint_alloc_size= 0; if (ha_init_errors()) return 1; - if (opt_bin_log) - { - if (!(*ht= binlog_init())) // Always succeed - { - mysql_bin_log.close(LOG_CLOSE_INDEX); // Never used - opt_bin_log= 0; // Never used - error= 1; // Never used - } - else - ha_was_inited_ok(ht++); - } -#ifdef HAVE_BERKELEY_DB - if (have_berkeley_db == SHOW_OPTION_YES) - { - if (!(*ht= berkeley_init())) - { - have_berkeley_db= SHOW_OPTION_DISABLED; // If we couldn't use handler - error= 1; - } - else - ha_was_inited_ok(ht++); - } -#endif -#ifdef HAVE_INNOBASE_DB - if (have_innodb == SHOW_OPTION_YES) - { - if (!(*ht= innobase_init())) - { - have_innodb= SHOW_OPTION_DISABLED; // If we couldn't use handler - error= 1; - } - else - ha_was_inited_ok(ht++); - } -#endif -#ifdef HAVE_NDBCLUSTER_DB - if (have_ndbcluster == SHOW_OPTION_YES) - { - if (!(*ht= ndbcluster_init())) - { - have_ndbcluster= SHOW_OPTION_DISABLED; - error= 1; - } - else - ha_was_inited_ok(ht++); - } -#endif -#ifdef HAVE_FEDERATED_DB - if (have_federated_db == SHOW_OPTION_YES) - { - if (federated_db_init()) - { - have_federated_db= SHOW_OPTION_DISABLED; - error= 1; - } - } -#endif -#ifdef HAVE_ARCHIVE_DB - if (have_archive_db == SHOW_OPTION_YES) + /* + We now initialize everything here. + */ + for (types= sys_table_types; *types; types++) { - if (!(*ht= archive_db_init())) - { - have_archive_db= SHOW_OPTION_DISABLED; - error= 1; - } + if (!(*types)->init || !(*types)->init()) + ha_was_inited_ok(types); else - ha_was_inited_ok(ht++); + (*types)->state= SHOW_OPTION_DISABLED; } -#endif + DBUG_ASSERT(total_ha < MAX_HA); /* Check if there is a transaction-capable storage engine besides the @@ -544,6 +576,10 @@ int ha_panic(enum ha_panic_function flag) if (have_archive_db == SHOW_OPTION_YES) error|= archive_db_end(); #endif +#ifdef HAVE_CSV_DB + if (have_csv_db == SHOW_OPTION_YES) + error|= tina_end(); +#endif if (ha_finish_errors()) error= 1; return error; @@ -564,9 +600,10 @@ void ha_drop_database(char* path) /* don't bother to rollback here, it's done already */ void ha_close_connection(THD* thd) { - for (uint i=0; i < total_ha; i++) - if (thd->ha_data[i]) - (*handlertons[i]->close_connection)(thd); + handlerton **types; + for (types= sys_table_types; *types; types++) + if (thd->ha_data[(*types)->slot]) + (*types)->close_connection(thd); } /* ======================================================================== @@ -877,13 +914,13 @@ int ha_autocommit_or_rollback(THD *thd, int error) int ha_commit_or_rollback_by_xid(XID *xid, bool commit) { - handlerton **ht= handlertons, **end_ht=ht+total_ha; + handlerton **types; int res= 1; - for ( ; ht < end_ht ; ht++) - if ((*ht)->recover) + for (types= sys_table_types; *types; types++) + if ((*types)->state == SHOW_OPTION_YES && (*types)->recover) res= res && - (*(commit ? (*ht)->commit_by_xid : (*ht)->rollback_by_xid))(xid); + (*(commit ? (*types)->commit_by_xid : (*types)->rollback_by_xid))(xid); return res; } @@ -962,7 +999,7 @@ static char* xid_to_str(char *buf, XID *xid) int ha_recover(HASH *commit_list) { int len, got, found_foreign_xids=0, found_my_xids=0; - handlerton **ht= handlertons, **end_ht=ht+total_ha; + handlerton **types; XID *list=0; bool dry_run=(commit_list==0 && tc_heuristic_recover==0); DBUG_ENTER("ha_recover"); @@ -998,14 +1035,14 @@ int ha_recover(HASH *commit_list) DBUG_RETURN(1); } - for ( ; ht < end_ht ; ht++) + for (types= sys_table_types; *types; types++) { - if (!(*ht)->recover) + if ((*types)->state != SHOW_OPTION_YES || !(*types)->recover) continue; - while ((got=(*(*ht)->recover)(list, len)) > 0 ) + while ((got=(*(*types)->recover)(list, len)) > 0 ) { sql_print_information("Found %d prepared transaction(s) in %s", - got, (*ht)->name); + got, (*types)->name); for (int i=0; i < got; i ++) { my_xid x=list[i].get_my_xid(); @@ -1033,7 +1070,7 @@ int ha_recover(HASH *commit_list) char buf[XIDDATASIZE*4+6]; // see xid_to_str sql_print_information("commit xid %s", xid_to_str(buf, list+i)); #endif - (*(*ht)->commit_by_xid)(list+i); + (*(*types)->commit_by_xid)(list+i); } else { @@ -1041,7 +1078,7 @@ int ha_recover(HASH *commit_list) char buf[XIDDATASIZE*4+6]; // see xid_to_str sql_print_information("rollback xid %s", xid_to_str(buf, list+i)); #endif - (*(*ht)->rollback_by_xid)(list+i); + (*(*types)->rollback_by_xid)(list+i); } } if (got < len) @@ -1401,11 +1438,9 @@ int handler::ha_open(const char *name, int mode, int test_if_locked) table->db_stat|=HA_READ_ONLY; (void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL - if (!alloc_root_inited(&table->mem_root)) // If temporary table - ref=(byte*) sql_alloc(ALIGN_SIZE(ref_length)*2); - else - ref=(byte*) alloc_root(&table->mem_root, ALIGN_SIZE(ref_length)*2); - if (!ref) + DBUG_ASSERT(alloc_root_inited(&table->mem_root)); + + if (!(ref= (byte*) alloc_root(&table->mem_root, ALIGN_SIZE(ref_length)*2))) { close(); error=HA_ERR_OUT_OF_MEM; @@ -1870,11 +1905,19 @@ void handler::print_error(int error, myf errflag) textno=ER_CANNOT_ADD_FOREIGN; break; case HA_ERR_ROW_IS_REFERENCED: - textno=ER_ROW_IS_REFERENCED; - break; + { + String str; + get_error_message(error, &str); + my_error(ER_ROW_IS_REFERENCED_2, MYF(0), str.c_ptr_safe()); + DBUG_VOID_RETURN; + } case HA_ERR_NO_REFERENCED_ROW: - textno=ER_NO_REFERENCED_ROW; - break; + { + String str; + get_error_message(error, &str); + my_error(ER_NO_REFERENCED_ROW_2, MYF(0), str.c_ptr_safe()); + DBUG_VOID_RETURN; + } case HA_ERR_TABLE_DEF_CHANGED: textno=ER_TABLE_DEF_CHANGED; break; @@ -2585,7 +2628,7 @@ TYPELIB *ha_known_exts(void) { if (!known_extensions.type_names || mysys_usage_id != known_extensions_id) { - show_table_type_st *types; + handlerton **types; List<char> found_exts; List_iterator_fast<char> it(found_exts); const char **ext, *old_ext; @@ -2593,11 +2636,11 @@ TYPELIB *ha_known_exts(void) known_extensions_id= mysys_usage_id; found_exts.push_back((char*) triggers_file_ext); found_exts.push_back((char*) trigname_file_ext); - for (types= sys_table_types; types->type; types++) + for (types= sys_table_types; *types; types++) { - if (*types->value == SHOW_OPTION_YES) + if ((*types)->state == SHOW_OPTION_YES) { - handler *file= get_new_handler(0,(enum db_type) types->db_type); + handler *file= get_new_handler(0,(enum db_type) (*types)->db_type); for (ext= file->bas_ext(); *ext; ext++) { while ((old_ext= it++)) diff --git a/sql/handler.h b/sql/handler.h index bd935da2a80..2347c03ae2d 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -107,6 +107,13 @@ #define HA_ONLINE_ADD_EMPTY_PARTITION 1 #define HA_ONLINE_DROP_PARTITION 2 +/* + Index scan will not return records in rowid order. Not guaranteed to be + set for unordered (e.g. HASH) indexes. +*/ +#define HA_KEY_SCAN_NOT_ROR 128 + + /* operations for disable/enable indexes */ #define HA_KEY_SWITCH_NONUNIQ 0 #define HA_KEY_SWITCH_ALL 1 @@ -187,13 +194,6 @@ enum db_type DB_TYPE_DEFAULT // Must be last }; -struct show_table_type_st { - const char *type; - SHOW_COMP_OPTION *value; - const char *comment; - enum db_type db_type; -}; - enum row_type { ROW_TYPE_NOT_USED=-1, ROW_TYPE_DEFAULT, ROW_TYPE_FIXED, ROW_TYPE_DYNAMIC, ROW_TYPE_COMPRESSED, ROW_TYPE_REDUNDANT, ROW_TYPE_COMPACT }; @@ -241,7 +241,7 @@ struct xid_t { char data[XIDDATASIZE]; // not \0-terminated ! bool eq(struct xid_t *xid) - { return !memcmp(this, xid, length()); } + { return eq(xid->gtrid_length, xid->bqual_length, xid->data); } bool eq(long g, long b, const char *d) { return g == gtrid_length && b == bqual_length && !memcmp(d, data, g+b); } void set(struct xid_t *xid) @@ -289,6 +289,14 @@ struct xid_t { return sizeof(formatID)+sizeof(gtrid_length)+sizeof(bqual_length)+ gtrid_length+bqual_length; } + byte *key() + { + return (byte *)>rid_length; + } + uint key_length() + { + return sizeof(gtrid_length)+sizeof(bqual_length)+gtrid_length+bqual_length; + } }; typedef struct xid_t XID; @@ -317,6 +325,27 @@ typedef struct storage engine name as it should be printed to a user */ const char *name; + + /* + Historical marker for if the engine is available of not + */ + SHOW_COMP_OPTION state; + + /* + A comment used by SHOW to describe an engine. + */ + const char *comment; + + /* + Historical number used for frm file to determine the correct storage engine. + This is going away and new engines will just use "name" for this. + */ + enum db_type db_type; + /* + Method that initizlizes a storage engine + */ + bool (*init)(); + /* each storage engine has it's own memory area (actually a pointer) in the thd, for storing per-connection information. @@ -376,9 +405,16 @@ typedef struct uint32 flags; /* global handler flags */ } handlerton; +struct show_table_alias_st { + const char *alias; + const char *type; +}; + /* Possible flags of a handlerton */ -#define HTON_NO_FLAGS 0 -#define HTON_CLOSE_CURSORS_AT_COMMIT 1 +#define HTON_NO_FLAGS 0 +#define HTON_CLOSE_CURSORS_AT_COMMIT (1 << 0) +#define HTON_ALTER_NOT_SUPPORTED (1 << 1) +#define HTON_CAN_RECREATE (1 << 2) typedef struct st_thd_trans { @@ -1121,7 +1157,7 @@ public: { return extra(operation); } virtual int external_lock(THD *thd, int lock_type) { return 0; } virtual void unlock_row() {} - virtual int start_stmt(THD *thd) {return 0;} + virtual int start_stmt(THD *thd, thr_lock_type lock_type) {return 0;} /* This is called to delete all rows in a table If the handler don't support this, then this function will @@ -1338,10 +1374,10 @@ public: /* Some extern variables used with handlers */ -extern struct show_table_type_st sys_table_types[]; +extern handlerton *sys_table_types[]; extern const char *ha_row_type[]; extern TYPELIB tx_isolation_typelib; -extern handlerton *handlertons[MAX_HA]; +extern TYPELIB myisam_stats_method_typelib; extern ulong total_ha, total_ha_2pc; /* Wrapper functions */ @@ -1350,18 +1386,13 @@ extern ulong total_ha, total_ha_2pc; #define ha_commit(thd) (ha_commit_trans((thd), TRUE)) #define ha_rollback(thd) (ha_rollback_trans((thd), TRUE)) -#define ha_supports_generate(T) (T != DB_TYPE_INNODB && \ - T != DB_TYPE_BERKELEY_DB && \ - T != DB_TYPE_ARCHIVE_DB && \ - T != DB_TYPE_FEDERATED_DB && \ - T != DB_TYPE_NDBCLUSTER) - /* lookups */ enum db_type ha_resolve_by_name(const char *name, uint namelen); const char *ha_get_storage_engine(enum db_type db_type); handler *get_new_handler(TABLE *table, enum db_type db_type); enum db_type ha_checktype(THD *thd, enum db_type database_type, bool no_substitute, bool report_error); +bool ha_check_storage_engine_flag(enum db_type db_type, uint32 flag); /* basic stuff */ int ha_init(void); diff --git a/sql/item.cc b/sql/item.cc index 60ea00e1b89..5caac05f6ca 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -904,6 +904,7 @@ bool Item_splocal::fix_fields(THD *, Item **) DBUG_ASSERT(it->fixed); max_length= it->max_length; decimals= it->decimals; + unsigned_flag= it->unsigned_flag; fixed= 1; return FALSE; } @@ -1687,7 +1688,7 @@ bool Item_field::eq(const Item *item, bool binary_cmp) const return 0; Item_field *item_field= (Item_field*) item; - if (item_field->field) + if (item_field->field && field) return item_field->field == field; /* We may come here when we are trying to find a function in a GROUP BY @@ -1701,10 +1702,10 @@ bool Item_field::eq(const Item *item, bool binary_cmp) const */ return (!my_strcasecmp(system_charset_info, item_field->name, field_name) && - (!item_field->table_name || + (!item_field->table_name || !table_name || (!my_strcasecmp(table_alias_charset, item_field->table_name, table_name) && - (!item_field->db_name || + (!item_field->db_name || !db_name || (item_field->db_name && !strcmp(item_field->db_name, db_name)))))); } @@ -1804,6 +1805,7 @@ Item_decimal::Item_decimal(const char *str_arg, uint length, name= (char*) str_arg; decimals= (uint8) decimal_value.frac; fixed= 1; + unsigned_flag= !decimal_value.sign(); max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, decimals, unsigned_flag); } @@ -1813,6 +1815,7 @@ Item_decimal::Item_decimal(longlong val, bool unsig) int2my_decimal(E_DEC_FATAL_ERROR, val, unsig, &decimal_value); decimals= (uint8) decimal_value.frac; fixed= 1; + unsigned_flag= !decimal_value.sign(); max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, decimals, unsigned_flag); } @@ -1823,6 +1826,7 @@ Item_decimal::Item_decimal(double val, int precision, int scale) double2my_decimal(E_DEC_FATAL_ERROR, val, &decimal_value); decimals= (uint8) decimal_value.frac; fixed= 1; + unsigned_flag= !decimal_value.sign(); max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, decimals, unsigned_flag); } @@ -1835,6 +1839,7 @@ Item_decimal::Item_decimal(const char *str, const my_decimal *val_arg, name= (char*) str; decimals= (uint8) decimal_par; max_length= length; + unsigned_flag= !decimal_value.sign(); fixed= 1; } @@ -1844,8 +1849,9 @@ Item_decimal::Item_decimal(my_decimal *value_par) my_decimal2decimal(value_par, &decimal_value); decimals= (uint8) decimal_value.frac; fixed= 1; + unsigned_flag= !decimal_value.sign(); max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, - decimals, !decimal_value.sign()); + decimals, unsigned_flag); } @@ -1855,8 +1861,9 @@ Item_decimal::Item_decimal(const char *bin, int precision, int scale) &decimal_value, precision, scale); decimals= (uint8) decimal_value.frac; fixed= 1; + unsigned_flag= !decimal_value.sign(); max_length= my_decimal_precision_to_length(precision, decimals, - !decimal_value.sign()); + unsigned_flag); } @@ -2966,7 +2973,7 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list) const char *field_name; ORDER *found_group= NULL; int found_match_degree= 0; - Item_field *cur_field; + Item_ident *cur_field; int cur_match_degree= 0; if (find_item->type() == Item::FIELD_ITEM || @@ -2983,9 +2990,9 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list) for (ORDER *cur_group= group_list ; cur_group ; cur_group= cur_group->next) { - if ((*(cur_group->item))->type() == Item::FIELD_ITEM) + if ((*(cur_group->item))->real_item()->type() == Item::FIELD_ITEM) { - cur_field= (Item_field*) *cur_group->item; + cur_field= (Item_ident*) *cur_group->item; cur_match_degree= 0; DBUG_ASSERT(cur_field->field_name != 0); @@ -3448,8 +3455,8 @@ bool Item_field::fix_fields(THD *thd, Item **reference) VIEW_ANY_ACL))) { my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), - "ANY", thd->priv_user, thd->host_or_ip, - field_name, tab); + "ANY", thd->security_ctx->priv_user, + thd->security_ctx->host_or_ip, field_name, tab); goto error; } } @@ -5232,6 +5239,36 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) (Item*) new Item_int(name, result, length)); break; } + case ROW_RESULT: + { + new_item= 0; + /* + If item and comp_item are both Item_rows and have same number of cols + then process items in Item_row one by one. If Item_row contain nulls + substitute it by Item_null. Otherwise just return. + */ + if (item->result_type() == comp_item->result_type() && + ((Item_row*)item)->cols() == ((Item_row*)comp_item)->cols()) + { + Item_row *item_row= (Item_row*)item,*comp_item_row= (Item_row*)comp_item; + if (item_row->null_inside()) + new_item= (Item*) new Item_null(name); + else + { + int i= item_row->cols() - 1; + for (; i >= 0; i--) + { + if (item_row->maybe_null && item_row->el(i)->is_null()) + { + new_item= (Item*) new Item_null(name); + break; + } + resolve_const_item(thd, item_row->addr(i), comp_item_row->el(i)); + } + } + } + break; + } case REAL_RESULT: { // It must REAL_RESULT double result= item->val_real(); @@ -5252,7 +5289,6 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) (Item*) new Item_decimal(name, result, length, decimals)); break; } - case ROW_RESULT: default: DBUG_ASSERT(0); } diff --git a/sql/item.h b/sql/item.h index 381ba98e193..a8f013f60d4 100644 --- a/sql/item.h +++ b/sql/item.h @@ -164,6 +164,7 @@ struct Hybrid_type_traits virtual my_decimal *val_decimal(Hybrid_type *val, my_decimal *buf) const; virtual String *val_str(Hybrid_type *val, String *buf, uint8 decimals) const; static const Hybrid_type_traits *instance(); + Hybrid_type_traits() {}; }; @@ -185,6 +186,7 @@ struct Hybrid_type_traits_decimal: public Hybrid_type_traits { return &val->dec_buf[val->used_dec_buf_no]; } virtual String *val_str(Hybrid_type *val, String *buf, uint8 decimals) const; static const Hybrid_type_traits_decimal *instance(); + Hybrid_type_traits_decimal() {}; }; @@ -215,6 +217,7 @@ struct Hybrid_type_traits_integer: public Hybrid_type_traits virtual String *val_str(Hybrid_type *val, String *buf, uint8 decimals) const { buf->set(val->integer, &my_charset_bin); return buf;} static const Hybrid_type_traits_integer *instance(); + Hybrid_type_traits_integer() {}; }; @@ -1502,6 +1505,7 @@ public: my_decimal *val_decimal(my_decimal *); int save_in_field(Field *field, bool no_conversions); enum Item_result result_type () const { return STRING_RESULT; } + enum Item_result cast_to_int_type() const { return INT_RESULT; } enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } // to prevent drop fixed flag (no need parent cleanup call) void cleanup() {} @@ -1618,7 +1622,7 @@ public: } Item *real_item() { - return (*ref)->real_item(); + return (ref && *ref) ? (*ref)->real_item() : this; } bool walk(Item_processor processor, byte *arg) { return (*ref)->walk(processor, arg); } diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 48839abcb10..85ba8ff794d 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -3072,14 +3072,14 @@ Item_func_regex::fix_fields(THD *thd, Item **ref) return FALSE; } int error; - if ((error= regcomp(&preg,res->c_ptr(), - ((cmp_collation.collation->state & - (MY_CS_BINSORT | MY_CS_CSSORT)) ? - REG_EXTENDED | REG_NOSUB : - REG_EXTENDED | REG_NOSUB | REG_ICASE), - cmp_collation.collation))) + if ((error= my_regcomp(&preg,res->c_ptr(), + ((cmp_collation.collation->state & + (MY_CS_BINSORT | MY_CS_CSSORT)) ? + REG_EXTENDED | REG_NOSUB : + REG_EXTENDED | REG_NOSUB | REG_ICASE), + cmp_collation.collation))) { - (void) regerror(error,&preg,buff,sizeof(buff)); + (void) my_regerror(error,&preg,buff,sizeof(buff)); my_error(ER_REGEXP_ERROR, MYF(0), buff); return TRUE; } @@ -3121,15 +3121,15 @@ longlong Item_func_regex::val_int() prev_regexp.copy(*res2); if (regex_compiled) { - regfree(&preg); + my_regfree(&preg); regex_compiled=0; } - if (regcomp(&preg,res2->c_ptr_safe(), - ((cmp_collation.collation->state & - (MY_CS_BINSORT | MY_CS_CSSORT)) ? - REG_EXTENDED | REG_NOSUB : - REG_EXTENDED | REG_NOSUB | REG_ICASE), - cmp_collation.collation)) + if (my_regcomp(&preg,res2->c_ptr_safe(), + ((cmp_collation.collation->state & + (MY_CS_BINSORT | MY_CS_CSSORT)) ? + REG_EXTENDED | REG_NOSUB : + REG_EXTENDED | REG_NOSUB | REG_ICASE), + cmp_collation.collation)) { null_value=1; return 0; @@ -3138,7 +3138,7 @@ longlong Item_func_regex::val_int() } } null_value=0; - return regexec(&preg,res->c_ptr(),0,(regmatch_t*) 0,0) ? 0 : 1; + return my_regexec(&preg,res->c_ptr(),0,(my_regmatch_t*) 0,0) ? 0 : 1; } @@ -3148,7 +3148,7 @@ void Item_func_regex::cleanup() Item_bool_func::cleanup(); if (regex_compiled) { - regfree(&preg); + my_regfree(&preg); regex_compiled=0; } DBUG_VOID_RETURN; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 09a0fa8c357..aa50593abf4 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1002,11 +1002,11 @@ public: #ifdef USE_REGEX -#include <regex.h> +#include "my_regex.h" class Item_func_regex :public Item_bool_func { - regex_t preg; + my_regex_t preg; bool regex_compiled; bool regex_is_const; String prev_regexp; diff --git a/sql/item_func.cc b/sql/item_func.cc index 518fb011e0f..f07460f2a05 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4711,27 +4711,11 @@ Item_func_sp::execute(Item **itp) THD *thd= current_thd; int res= -1; Sub_statement_state statement_state; + Security_context *save_ctx; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - st_sp_security_context save_ctx; -#endif - - if (! m_sp && ! (m_sp= sp_find_function(thd, m_name, TRUE))) - { - my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str); + if (find_and_check_access(thd, EXECUTE_ACL, &save_ctx)) goto error; - } -#ifndef NO_EMBEDDED_ACCESS_CHECKS - if (check_routine_access(thd, EXECUTE_ACL, - m_sp->m_db.str, m_sp->m_name.str, 0, 0)) - goto error; - sp_change_security_context(thd, m_sp, &save_ctx); - if (save_ctx.changed && - check_routine_access(thd, EXECUTE_ACL, - m_sp->m_db.str, m_sp->m_name.str, 0, 0)) - goto error_check_ctx; -#endif /* Disable the binlogging if this is not a SELECT statement. If this is a SELECT, leave binlogging on, so execute_function() code writes the @@ -4740,7 +4724,7 @@ Item_func_sp::execute(Item **itp) thd->reset_sub_statement_state(&statement_state, SUB_STMT_FUNCTION); res= m_sp->execute_function(thd, args, arg_count, itp); thd->restore_sub_statement_state(&statement_state); - + if (res && mysql_bin_log.is_open() && (m_sp->m_chistics->daccess == SP_CONTAINS_SQL || m_sp->m_chistics->daccess == SP_MODIFIES_SQL_DATA)) @@ -4749,8 +4733,7 @@ Item_func_sp::execute(Item **itp) ER(ER_FAILED_ROUTINE_BREAK_BINLOG)); #ifndef NO_EMBEDDED_ACCESS_CHECKS -error_check_ctx: - sp_restore_security_context(thd, m_sp, &save_ctx); + sp_restore_security_context(thd, save_ctx); #endif error: @@ -4857,3 +4840,79 @@ Item_func_sp::tmp_table_field(TABLE *t_arg) DBUG_RETURN(res); } + + +/* + Find the function and chack access rigths to the function + + SYNOPSIS + find_and_check_access() + thd thread handler + want_access requested access + backup backup of security context or 0 + + RETURN + FALSE Access granted + TRUE Requested access can't be granted or function doesn't exists + + NOTES + Checks if requested access to function can be granted to user. + If function isn't found yet, it searches function first. + If function can't be found or user don't have requested access + error is raised. + If security context sp_ctx is provided and access can be granted then + switch back to previous context isn't performed. + In case of access error or if context is not provided then + find_and_check_access() switches back to previous security context. +*/ + +bool +Item_func_sp::find_and_check_access(THD *thd, ulong want_access, + Security_context **backup) +{ + bool res; + Security_context *local_save, + **save= (backup ? backup : &local_save); + res= TRUE; + if (! m_sp && ! (m_sp= sp_find_function(thd, m_name, TRUE))) + { + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str); + goto error; + } + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_routine_access(thd, want_access, + m_sp->m_db.str, m_sp->m_name.str, 0, FALSE)) + { + goto error; + } + + sp_change_security_context(thd, m_sp, save); + if (*save && + check_routine_access(thd, want_access, + m_sp->m_db.str, m_sp->m_name.str, 0, FALSE)) + { + goto error_check_ctx; + } + res= FALSE; +error_check_ctx: + if (*save && (res || !backup)) + sp_restore_security_context(thd, local_save); +error: +#else + res= 0; +error: +#endif + return res; +} + +bool +Item_func_sp::fix_fields(THD *thd, Item **ref) +{ + bool res; + DBUG_ASSERT(fixed == 0); + res= Item_func::fix_fields(thd, ref); + if (!res && find_and_check_access(thd, EXECUTE_ACL, NULL)) + res= 1; + return res; +} diff --git a/sql/item_func.h b/sql/item_func.h index 019abb0c072..223144a5d51 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -55,7 +55,7 @@ public: NOT_FUNC, NOT_ALL_FUNC, NOW_FUNC, TRIG_COND_FUNC, GUSERVAR_FUNC, COLLATE_FUNC, - EXTRACT_FUNC, CHAR_TYPECAST_FUNC }; + EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP }; enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL, OPTIMIZE_EQUAL }; enum Type type() const { return FUNC_ITEM; } @@ -1286,9 +1286,6 @@ public: { ft_handler->please->close_search(ft_handler); ft_handler=0; - if (join_key) - table->file->ft_handler=0; - table->fulltext_searched=0; } concat= 0; DBUG_VOID_RETURN; @@ -1365,6 +1362,7 @@ public: class sp_head; class sp_name; +struct st_sp_security_context; class Item_func_sp :public Item_func { @@ -1434,7 +1432,11 @@ public: { context= (Name_resolution_context *)cntx; return FALSE; } void fix_length_and_dec(); + bool find_and_check_access(THD * thd, ulong want_access, + Security_context **backup); + virtual enum Functype functype() const { return FUNC_SP; } + bool fix_fields(THD *thd, Item **ref); }; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 4fd33c06095..018afac3812 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -473,7 +473,8 @@ String *Item_func_des_decrypt::val_str(String *str) { uint key_number=(uint) (*res)[0] & 127; // Check if automatic key and that we have privilege to uncompress using it - if (!(current_thd->master_access & SUPER_ACL) || key_number > 9) + if (!(current_thd->security_ctx->master_access & SUPER_ACL) || + key_number > 9) goto error; VOID(pthread_mutex_lock(&LOCK_des_key_file)); @@ -1601,13 +1602,13 @@ String *Item_func_user::val_str(String *str) if (is_current) { - user= thd->priv_user; - host= thd->priv_host; + user= thd->security_ctx->priv_user; + host= thd->security_ctx->priv_host; } else { - user= thd->user; - host= thd->host_or_ip; + user= thd->main_security_ctx.user; + host= thd->main_security_ctx.host_or_ip; } // For system threads (e.g. replication SQL thread) user may be empty @@ -1732,6 +1733,8 @@ String *Item_func_format::val_str(String *str) { my_decimal dec_val, rnd_dec, *res; res= args[0]->val_decimal(&dec_val); + if ((null_value=args[0]->null_value)) + return 0; /* purecov: inspected */ my_decimal_round(E_DEC_FATAL_ERROR, res, decimals, false, &rnd_dec); my_decimal2string(E_DEC_FATAL_ERROR, &rnd_dec, 0, 0, 0, str); str_length= str->length(); @@ -1980,6 +1983,33 @@ b1: str->append((char)(num>>8)); } str->set_charset(collation.collation); str->realloc(str->length()); // Add end 0 (for Purify) + + /* Check whether we got a well-formed string */ + CHARSET_INFO *cs= collation.collation; + int well_formed_error; + uint wlen= cs->cset->well_formed_len(cs, + str->ptr(), str->ptr() + str->length(), + str->length(), &well_formed_error); + if (wlen < str->length()) + { + THD *thd= current_thd; + char hexbuf[7]; + enum MYSQL_ERROR::enum_warning_level level; + uint diff= str->length() - wlen; + set_if_smaller(diff, 3); + octet2hex(hexbuf, (const uchar*) str->ptr() + wlen, diff); + if (thd->variables.sql_mode & + (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)) + { + level= MYSQL_ERROR::WARN_LEVEL_ERROR; + null_value= 1; + str= 0; + } + else + level= MYSQL_ERROR::WARN_LEVEL_WARN; + push_warning_printf(thd, level, ER_INVALID_CHARACTER_STRING, + ER(ER_INVALID_CHARACTER_STRING), cs->csname, hexbuf); + } return str; } @@ -2518,7 +2548,7 @@ String *Item_load_file::val_str(String *str) if (!(file_name= args[0]->val_str(str)) #ifndef NO_EMBEDDED_ACCESS_CHECKS - || !(current_thd->master_access & FILE_ACL) + || !(current_thd->security_ctx->master_access & FILE_ACL) #endif ) goto err; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index aead2d67c2c..0e30bbd8a96 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1468,7 +1468,7 @@ int subselect_single_select_engine::prepare() int subselect_union_engine::prepare() { - return unit->prepare(thd, result, SELECT_NO_UNLOCK, ""); + return unit->prepare(thd, result, SELECT_NO_UNLOCK); } int subselect_uniquesubquery_engine::prepare() diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 880ab4c8cb1..b56d99cf245 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -545,6 +545,7 @@ struct Hybrid_type_traits_fast_decimal: public val->traits->div(val, u); } static const Hybrid_type_traits_fast_decimal *instance(); + Hybrid_type_traits_fast_decimal() {}; }; static const Hybrid_type_traits_fast_decimal fast_decimal_traits_instance; @@ -1367,8 +1368,8 @@ void Item_sum_hybrid::cleanup() void Item_sum_hybrid::no_rows_in_result() { - Item_sum::no_rows_in_result(); was_values= FALSE; + clear(); } diff --git a/sql/log.cc b/sql/log.cc index 9d9f500fe80..0dc0b4d1682 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -39,6 +39,7 @@ ulong sync_binlog_counter= 0; static bool test_if_number(const char *str, long *res, bool allow_wildcards); +static bool binlog_init(); static int binlog_close_connection(THD *thd); static int binlog_savepoint_set(THD *thd, void *sv); static int binlog_savepoint_rollback(THD *thd, void *sv); @@ -46,8 +47,12 @@ static int binlog_commit(THD *thd, bool all); static int binlog_rollback(THD *thd, bool all); static int binlog_prepare(THD *thd, bool all); -static handlerton binlog_hton = { +handlerton binlog_hton = { "binlog", + SHOW_OPTION_YES, + "This is a meta storage engine to represent the binlog in a transaction", + DB_TYPE_UNKNOWN, /* IGNORE for now */ + binlog_init, 0, sizeof(my_off_t), /* savepoint size = binlog offset */ binlog_close_connection, @@ -72,9 +77,9 @@ static handlerton binlog_hton = { should be moved here. */ -handlerton *binlog_init() +bool binlog_init() { - return &binlog_hton; + return false; } static int binlog_close_connection(THD *thd) @@ -1477,7 +1482,7 @@ bool MYSQL_LOG::write(THD *thd,enum enum_server_command command, { // Normal thread if ((thd->options & OPTION_LOG_OFF) #ifndef NO_EMBEDDED_ACCESS_CHECKS - && (thd->master_access & SUPER_ACL) + && (thd->security_ctx->master_access & SUPER_ACL) #endif ) { @@ -1852,7 +1857,9 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event) if (commit_event->write(&log_file)) goto err; +#ifndef DBUG_OFF DBUG_skip_commit: +#endif if (flush_and_sync()) goto err; DBUG_EXECUTE_IF("half_binlogged_transaction", abort();); @@ -1917,6 +1924,7 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, } if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT) || query_start_arg) { + Security_context *sctx= thd->security_ctx; current_time=time(NULL); if (current_time != last_time) { @@ -1937,10 +1945,12 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, tmp_errno=errno; } if (my_b_printf(&log_file, "# User@Host: %s[%s] @ %s [%s]\n", - thd->priv_user ? thd->priv_user : "", - thd->user ? thd->user : "", - thd->host ? thd->host : "", - thd->ip ? thd->ip : "") == (uint) -1) + sctx->priv_user ? + sctx->priv_user : "", + sctx->user ? sctx->user : "", + sctx->host ? sctx->host : "", + sctx->ip ? sctx->ip : "") == + (uint) -1) tmp_errno=errno; } if (query_start_arg) @@ -2792,7 +2802,7 @@ void TC_LOG_MMAP::close() case 3: my_free((gptr)pages, MYF(0)); case 2: - my_munmap(data, (size_t)file_length); + my_munmap((byte*)data, (size_t)file_length); case 1: my_close(fd, MYF(0)); } diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index b15b1682d69..d46d0bd1e81 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -284,7 +284,7 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset; /* Flag set if setup_tables already done */ #define OPTION_SETUP_TABLES_DONE (1L << 30) // intern /* If not set then the thread will ignore all warnings with level notes. */ -#define OPTION_SQL_NOTES (1L << 31) // THD, user +#define OPTION_SQL_NOTES (1UL << 31) // THD, user /* Force the used temporary table to be a MyISAM table (because we will use fulltext functions when reading from it. @@ -491,6 +491,7 @@ typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key, #include "protocol.h" #include "sql_udf.h" class user_var_entry; +class Security_context; enum enum_var_type { OPT_DEFAULT= 0, OPT_SESSION, OPT_GLOBAL @@ -522,7 +523,7 @@ bool delete_precheck(THD *thd, TABLE_LIST *tables); bool insert_precheck(THD *thd, TABLE_LIST *tables); bool create_table_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *create_table); -bool default_view_definer(THD *thd, st_lex_user *definer); +bool default_view_definer(Security_context *sctx, st_lex_user *definer); enum enum_mysql_completiontype { @@ -1097,7 +1098,6 @@ bool fn_format_relative_to_data_home(my_string to, const char *name, const char *dir, const char *extension); File open_binlog(IO_CACHE *log, const char *log_file_name, const char **errmsg); -handlerton *binlog_init(); /* mysqld.cc */ extern void yyerror(const char*); @@ -1182,12 +1182,13 @@ extern my_bool relay_log_purge, opt_innodb_safe_binlog, opt_innodb; 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_endinfo, using_udf_functions; +extern my_bool locked_in_memory; 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; -extern bool opt_character_set_client_handshake; +extern my_bool opt_character_set_client_handshake; extern bool volatile abort_loop, shutdown_in_progress, grant_option; extern bool mysql_proc_table_exists; extern uint volatile thread_count, thread_running, global_read_lock; @@ -1202,7 +1203,7 @@ extern my_bool sp_automatic_privileges, opt_noacl; extern my_bool opt_old_style_user_limits, trust_routine_creators; extern uint opt_crash_binlog_innodb; extern char *shared_memory_base_name, *mysqld_unix_port; -extern bool opt_enable_shared_memory; +extern my_bool opt_enable_shared_memory; extern char *default_tz_name; extern my_bool opt_large_pages; extern uint opt_large_page_size; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 81657a7657f..e79bce9cd9e 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -295,7 +295,7 @@ bool opt_large_files= sizeof(my_off_t) > 4; /* Used with --help for detailed option */ -static bool opt_help= 0, opt_verbose= 0; +static my_bool opt_help= 0, opt_verbose= 0; arg_cmp_func Arg_comparator::comparator_matrix[5][2] = {{&Arg_comparator::compare_string, &Arg_comparator::compare_e_string}, @@ -339,9 +339,10 @@ static my_bool opt_sync_bdb_logs; bool opt_log, opt_update_log, opt_bin_log, opt_slow_log; bool opt_error_log= IF_WIN(1,0); bool opt_disable_networking=0, opt_skip_show_db=0; -bool opt_character_set_client_handshake= 1; +my_bool opt_character_set_client_handshake= 1; bool server_id_supplied = 0; -bool opt_endinfo,using_udf_functions, locked_in_memory; +bool opt_endinfo,using_udf_functions; +my_bool locked_in_memory; bool opt_using_transactions, using_update_log; bool volatile abort_loop; bool volatile shutdown_in_progress, grant_option; @@ -439,6 +440,7 @@ char server_version[SERVER_VERSION_LENGTH]; char *mysqld_unix_port, *opt_mysql_tmpdir; const char **errmesg; /* Error messages */ const char *myisam_recover_options_str="OFF"; +const char *myisam_stats_method_str="nulls_unequal"; /* name of reference on left espression in rewritten IN subquery */ const char *in_left_expr_name= "<left expr>"; /* name of additional condition */ @@ -577,7 +579,7 @@ Query_cache query_cache; #endif #ifdef HAVE_SMEM char *shared_memory_base_name= default_shared_memory_base_name; -bool opt_enable_shared_memory; +my_bool opt_enable_shared_memory; HANDLE smem_event_connect_request= 0; #endif @@ -782,7 +784,9 @@ static void close_connections(void) { if (global_system_variables.log_warnings) sql_print_warning(ER(ER_FORCING_CLOSE),my_progname, - tmp->thread_id,tmp->user ? tmp->user : ""); + tmp->thread_id, + (tmp->main_security_ctx.user ? + tmp->main_security_ctx.user : "")); close_connection(tmp,0,0); } #endif @@ -1098,7 +1102,7 @@ void clean_up(bool print_message) my_free((gptr) ssl_acceptor_fd, MYF(MY_ALLOW_ZERO_PTR)); #endif /* HAVE_OPENSSL */ #ifdef USE_REGEX - regex_end(); + my_regex_end(); #endif if (print_message && errmesg) @@ -2605,7 +2609,7 @@ static int init_common_variables(const char *conf_file_name, int argc, set_var_init(); mysys_uses_curses=0; #ifdef USE_REGEX - regex_init(&my_charset_latin1); + my_regex_init(&my_charset_latin1); #endif if (!(default_charset_info= get_charset_by_csname(default_character_set_name, MY_CS_PRIMARY, @@ -3593,7 +3597,7 @@ static void bootstrap(FILE *file) thd->client_capabilities=0; my_net_init(&thd->net,(st_vio*) 0); thd->max_client_packet_length= thd->net.max_packet; - thd->master_access= ~(ulong)0; + thd->security_ctx->master_access= ~(ulong)0; thd->thread_id=thread_id++; thread_count++; @@ -3933,7 +3937,7 @@ extern "C" pthread_handler_decl(handle_connections_sockets, continue; } if (sock == unix_sock) - thd->host=(char*) my_localhost; + thd->security_ctx->host=(char*) my_localhost; #ifdef __WIN__ /* Set default wait_timeout */ ulong wait_timeout= global_system_variables.net_wait_timeout * 1000; @@ -4024,8 +4028,8 @@ extern "C" pthread_handler_decl(handle_connections_namedpipes,arg) delete thd; continue; } - /* host name is unknown */ - thd->host = my_strdup(my_localhost,MYF(0)); /* Host is unknown */ + /* Host is unknown */ + thd->security_ctx->host= my_strdup(my_localhost, MYF(0)); create_new_thread(thd); } @@ -4216,7 +4220,7 @@ pthread_handler_decl(handle_connections_shared_memory,arg) errmsg= 0; goto errorconn; } - thd->host= my_strdup(my_localhost,MYF(0)); /* Host is unknown */ + thd->security_ctx->host= my_strdup(my_localhost, MYF(0)); /* Host is unknown */ create_new_thread(thd); connect_number++; continue; @@ -4372,6 +4376,7 @@ enum options_mysqld OPT_MAX_ERROR_COUNT, OPT_MULTI_RANGE_COUNT, OPT_MYISAM_DATA_POINTER_SIZE, OPT_MYISAM_BLOCK_SIZE, OPT_MYISAM_MAX_EXTRA_SORT_FILE_SIZE, OPT_MYISAM_MAX_SORT_FILE_SIZE, OPT_MYISAM_SORT_BUFFER_SIZE, + OPT_MYISAM_STATS_METHOD, OPT_NET_BUFFER_LENGTH, OPT_NET_RETRY_COUNT, OPT_NET_READ_TIMEOUT, OPT_NET_WRITE_TIMEOUT, OPT_OPEN_FILES_LIMIT, @@ -5555,6 +5560,11 @@ The minimum value for this variable is 4096.", (gptr*) &global_system_variables.myisam_sort_buff_size, (gptr*) &max_system_variables.myisam_sort_buff_size, 0, GET_ULONG, REQUIRED_ARG, 8192*1024, 4, ~0L, 0, 1, 0}, + {"myisam_stats_method", OPT_MYISAM_STATS_METHOD, + "Specifies how MyISAM index statistics collection code should threat NULLs. " + "Possible values of name are \"nulls_unequal\" (default behavior for 4.1/5.0), and \"nulls_equal\" (emulate 4.0 behavior).", + (gptr*) &myisam_stats_method_str, (gptr*) &myisam_stats_method_str, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"net_buffer_length", OPT_NET_BUFFER_LENGTH, "Buffer length for TCP/IP and socket communication.", (gptr*) &global_system_variables.net_buffer_length, @@ -5863,6 +5873,7 @@ struct show_var_st status_vars[]= { {"Com_show_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_KEYS]), SHOW_LONG_STATUS}, {"Com_show_logs", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_LOGS]), SHOW_LONG_STATUS}, {"Com_show_master_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_MASTER_STAT]), SHOW_LONG_STATUS}, + {"Com_show_ndb_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_NDBCLUSTER_STATUS]), SHOW_LONG_STATUS}, {"Com_show_new_master", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_NEW_MASTER]), SHOW_LONG_STATUS}, {"Com_show_open_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_OPEN_TABLES]), SHOW_LONG_STATUS}, {"Com_show_privileges", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_PRIVILEGES]), SHOW_LONG_STATUS}, @@ -6138,6 +6149,7 @@ static void mysql_init_variables(void) query_id= thread_id= 1L; strmov(server_version, MYSQL_SERVER_VERSION); myisam_recover_options_str= sql_mode_str= "OFF"; + myisam_stats_method_str= "nulls_unequal"; my_bind_addr = htonl(INADDR_ANY); threads.empty(); thread_cache.empty(); @@ -6180,6 +6192,12 @@ static void mysql_init_variables(void) max_system_variables.max_join_size= (ulonglong) HA_POS_ERROR; global_system_variables.old_passwords= 0; global_system_variables.old_alter_table= 0; + + /* + Default behavior for 4.1 and 5.0 is to treat NULL values as unequal + when collecting index statistics for MyISAM tables. + */ + global_system_variables.myisam_stats_method= MI_STATS_METHOD_NULLS_NOT_EQUAL; /* Variables that depends on compile options */ #ifndef DBUG_OFF @@ -6249,7 +6267,7 @@ static void mysql_init_variables(void) #else have_openssl=SHOW_OPTION_NO; #endif -#ifdef HAVE_BROKEN_REALPATH +#if !defined(HAVE_REALPATH) || defined(HAVE_BROKEN_REALPATH) have_symlink=SHOW_OPTION_NO; #else have_symlink=SHOW_OPTION_YES; @@ -6795,6 +6813,17 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), fprintf(stderr, "Unknown option to tc-heuristic-recover: %s\n",argument); exit(1); } + } + case OPT_MYISAM_STATS_METHOD: + { + myisam_stats_method_str= argument; + int method; + if ((method=find_type(argument, &myisam_stats_method_typelib, 2)) <= 0) + { + fprintf(stderr, "Invalid value of myisam_stats_method: %s.\n", argument); + exit(1); + } + global_system_variables.myisam_stats_method= method-1; break; } case OPT_SQL_MODE: @@ -6930,7 +6959,7 @@ static void get_options(int argc,char **argv) usage(); exit(0); } -#if defined(HAVE_BROKEN_REALPATH) +#if !defined(HAVE_REALPATH) || defined(HAVE_BROKEN_REALPATH) my_use_symdir=0; my_disable_symlinks=1; have_symlink=SHOW_OPTION_NO; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index b69822d201b..2f4cf1c4752 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -750,10 +750,9 @@ int QUICK_RANGE_SELECT::init() { DBUG_ENTER("QUICK_RANGE_SELECT::init"); - if (file->inited == handler::NONE) - DBUG_RETURN(error= file->ha_index_init(index, 1)); - error= 0; - DBUG_RETURN(0); + if (file->inited != handler::NONE) + file->ha_index_or_rnd_end(); + DBUG_RETURN(error= file->ha_index_init(index, 1)); } @@ -3539,17 +3538,17 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) switch (cond_func->functype()) { case Item_func::BETWEEN: - if (cond_func->arguments()[0]->type() != Item::FIELD_ITEM) + if (cond_func->arguments()[0]->real_item()->type() != Item::FIELD_ITEM) DBUG_RETURN(0); - field_item= (Item_field*) (cond_func->arguments()[0]); + field_item= (Item_field*) (cond_func->arguments()[0]->real_item()); value= NULL; break; case Item_func::IN_FUNC: { Item_func_in *func=(Item_func_in*) cond_func; - if (func->key_item()->type() != Item::FIELD_ITEM) + if (func->key_item()->real_item()->type() != Item::FIELD_ITEM) DBUG_RETURN(0); - field_item= (Item_field*) (func->key_item()); + field_item= (Item_field*) (func->key_item()->real_item()); value= NULL; break; } @@ -5123,6 +5122,8 @@ check_quick_select(PARAM *param,uint idx,SEL_ARG *tree) if (cpk_scan) param->is_ror_scan= TRUE; } + if (param->table->file->index_flags(key, 0, TRUE) & HA_KEY_SCAN_NOT_ROR) + param->is_ror_scan= FALSE; DBUG_PRINT("exit", ("Records: %lu", (ulong) records)); DBUG_RETURN(records); } diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 9802bbddde6..2e87f9cf0db 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -80,6 +80,8 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) List_iterator_fast<Item> it(all_fields); int const_result= 1; bool recalc_const_item= 0; + longlong count= 1; + bool is_exact_count= TRUE; table_map removed_tables= 0, outer_tables= 0, used_tables= 0; table_map where_tables= 0; Item *item; @@ -88,9 +90,13 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) if (conds) where_tables= conds->used_tables(); - /* Don't replace expression on a table that is part of an outer join */ + /* + Analyze outer join dependencies, and, if possible, compute the number + of returned rows. + */ for (TABLE_LIST *tl= tables; tl; tl= tl->next_leaf) { + /* Don't replace expression on a table that is part of an outer join */ if (tl->on_expr) { outer_tables|= tl->table->map; @@ -106,11 +112,27 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) } else used_tables|= tl->table->map; + + /* + If the storage manager of 'tl' gives exact row count, compute the total + number of rows. If there are no outer table dependencies, this count + may be used as the real count. + */ + if (tl->table->file->table_flags() & HA_NOT_EXACT_COUNT) + { + is_exact_count= FALSE; + count= 1; // ensure count != 0 + } + else + { + tl->table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); + count*= tl->table->file->records; + } } /* - Iterate through item is select part and replace COUNT(), MIN() and MAX() - with constants (if possible) + Iterate through all items in the SELECT clause and replace + COUNT(), MIN() and MAX() with constants (if possible). */ while ((item= it++)) @@ -122,9 +144,11 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) case Item_sum::COUNT_FUNC: /* If the expr in count(expr) can never be null we can change this - to the number of rows in the tables + to the number of rows in the tables if this number is exact and + there are no outer joins. */ - if (!conds && !((Item_sum_count*) item)->args[0]->maybe_null) + if (!conds && !((Item_sum_count*) item)->args[0]->maybe_null && + !outer_tables && is_exact_count) { longlong count= 1; TABLE_LIST *table; @@ -210,12 +234,27 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) } removed_tables|= table->map; } - else if (!expr->const_item()) // This is VERY seldom false + else if (!expr->const_item() || !is_exact_count) { + /* + The optimization is not applicable in both cases: + (a) 'expr' is a non-constant expression. Then we can't + replace 'expr' by a constant. + (b) 'expr' is a costant. According to ANSI, MIN/MAX must return + NULL if the query does not return any rows. Thus, if we are not + able to determine if the query returns any rows, we can't apply + the optimization and replace MIN/MAX with a constant. + */ const_result= 0; break; } - ((Item_sum_min*) item_sum)->reset(); + if (!count) + { + /* If count == 0, then we know that is_exact_count == TRUE. */ + ((Item_sum_min*) item_sum)->clear(); /* Set to NULL. */ + } + else + ((Item_sum_min*) item_sum)->reset(); /* Set to the constant value. */ ((Item_sum_min*) item_sum)->make_const(); recalc_const_item= 1; break; @@ -282,13 +321,28 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) } removed_tables|= table->map; } - else if (!expr->const_item()) // This is VERY seldom false + else if (!expr->const_item() || !is_exact_count) { + /* + The optimization is not applicable in both cases: + (a) 'expr' is a non-constant expression. Then we can't + replace 'expr' by a constant. + (b) 'expr' is a costant. According to ANSI, MIN/MAX must return + NULL if the query does not return any rows. Thus, if we are not + able to determine if the query returns any rows, we can't apply + the optimization and replace MIN/MAX with a constant. + */ const_result= 0; break; } - ((Item_sum_min*) item_sum)->reset(); - ((Item_sum_min*) item_sum)->make_const(); + if (!count) + { + /* If count != 1, then we know that is_exact_count == TRUE. */ + ((Item_sum_max*) item_sum)->clear(); /* Set to NULL. */ + } + else + ((Item_sum_max*) item_sum)->reset(); /* Set to the constant value. */ + ((Item_sum_max*) item_sum)->make_const(); recalc_const_item= 1; break; } diff --git a/sql/parse_file.cc b/sql/parse_file.cc index 82ce2f2d7b5..5c7053e6e2a 100644 --- a/sql/parse_file.cc +++ b/sql/parse_file.cc @@ -333,6 +333,59 @@ err_w_file: DBUG_RETURN(TRUE); } +/* + Renames a frm file (including backups) in same schema + + SYNOPSIS + rename_in_schema_file + schema name of given schema + old_name original file name + new_name new file name + revision revision number + num_view_backups number of backups + + RETURN + 0 - OK + 1 - Error (only if renaming of frm failed) + +*/ +my_bool rename_in_schema_file(const char *schema, const char *old_name, + const char *new_name, ulonglong revision, + uint num_view_backups) +{ + char old_path[FN_REFLEN], new_path[FN_REFLEN], arc_path[FN_REFLEN]; + + strxnmov(old_path, FN_REFLEN, mysql_data_home, "/", schema, "/", + old_name, reg_ext, NullS); + (void) unpack_filename(old_path, old_path); + + strxnmov(new_path, FN_REFLEN, mysql_data_home, "/", schema, "/", + new_name, reg_ext, NullS); + (void) unpack_filename(new_path, new_path); + + if (my_rename(old_path, new_path, MYF(MY_WME))) + return 1; + + /* check if arc_dir exists */ + strxnmov(arc_path, FN_REFLEN, mysql_data_home, "/", schema, "/arc", NullS); + (void) unpack_filename(arc_path, arc_path); + + if (revision > 0 && !access(arc_path, F_OK)) + { + ulonglong limit= (revision > num_view_backups) ? revision - num_view_backups : 0; + while (revision > limit) { + my_snprintf(old_path, FN_REFLEN, "%s/%s%s-%04lu", + arc_path, old_name, reg_ext, (ulong)revision); + (void) unpack_filename(old_path, old_path); + my_snprintf(new_path, FN_REFLEN, "%s/%s%s-%04lu", + arc_path, new_name, reg_ext, (ulong)revision); + (void) unpack_filename(new_path, new_path); + my_rename(old_path, new_path, MYF(0)); + revision--; + } + } + return 0; +} /* Prepare frm to parse (read to memory) diff --git a/sql/parse_file.h b/sql/parse_file.h index cc0aa6556f6..b4199e4fbf1 100644 --- a/sql/parse_file.h +++ b/sql/parse_file.h @@ -48,6 +48,9 @@ my_bool sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name, const LEX_STRING *type, gptr base, File_option *parameters, uint versions); +my_bool rename_in_schema_file(const char *schema, const char *old_name, + const char *new_name, ulonglong revision, + uint num_view_backups); class File_parser: public Sql_alloc { diff --git a/sql/password.c b/sql/password.c index 60cc0ac0c97..aa05be8c740 100644 --- a/sql/password.c +++ b/sql/password.c @@ -318,8 +318,8 @@ void create_random_string(char *to, uint length, struct rand_struct *rand_st) str, len IN the beginning and the length of the input string */ -static void -octet2hex(char *to, const uint8 *str, uint len) +void +octet2hex(char *to, const unsigned char *str, uint len) { const uint8 *str_end= str + len; for (; str != str_end; ++str) diff --git a/sql/protocol.h b/sql/protocol.h index 2717d2258fa..c00bbba4cc9 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -150,30 +150,6 @@ public: virtual bool store(Field *field); }; -class Protocol_cursor :public Protocol_simple -{ -public: - MEM_ROOT *alloc; - MYSQL_FIELD *fields; - MYSQL_ROWS *data; - MYSQL_ROWS **prev_record; - ulong row_count; - - Protocol_cursor() :data(NULL) {} - Protocol_cursor(THD *thd_arg, MEM_ROOT *ini_alloc) :Protocol_simple(thd_arg), alloc(ini_alloc), data(NULL) {} - bool prepare_for_send(List<Item> *item_list) - { - row_count= 0; - fields= NULL; - data= NULL; - prev_record= &data; - return Protocol_simple::prepare_for_send(item_list); - } - bool send_fields(List<Item> *list, uint flags); - bool write(); - uint get_field_count() { return field_count; } -}; - void send_warning(THD *thd, uint sql_errno, const char *err=0); void net_printf_error(THD *thd, uint sql_errno, ...); void net_send_error(THD *thd, uint sql_errno=0, const char *err=0); diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index a30776057b1..93aafbdc0e0 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -67,13 +67,11 @@ static int init_failsafe_rpl_thread(THD* thd) this thread has no other error reporting method). */ thd->system_thread = thd->bootstrap = 1; - thd->host_or_ip= ""; + thd->security_ctx->skip_grants(); thd->client_capabilities = 0; my_net_init(&thd->net, 0); thd->net.read_timeout = slave_net_timeout; thd->max_client_packet_length=thd->net.max_packet; - thd->master_access= ~(ulong)0; - thd->priv_user = 0; pthread_mutex_lock(&LOCK_thread_count); thd->thread_id = thread_id++; pthread_mutex_unlock(&LOCK_thread_count); diff --git a/sql/set_var.cc b/sql/set_var.cc index e7adc7387c0..e1dc23f6032 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -270,6 +270,12 @@ sys_var_long_ptr sys_myisam_data_pointer_size("myisam_data_pointer_size", sys_var_thd_ulonglong sys_myisam_max_sort_file_size("myisam_max_sort_file_size", &SV::myisam_max_sort_file_size, fix_myisam_max_sort_file_size, 1); sys_var_thd_ulong sys_myisam_repair_threads("myisam_repair_threads", &SV::myisam_repair_threads); sys_var_thd_ulong sys_myisam_sort_buffer_size("myisam_sort_buffer_size", &SV::myisam_sort_buff_size); + +sys_var_thd_enum sys_myisam_stats_method("myisam_stats_method", + &SV::myisam_stats_method, + &myisam_stats_method_typelib, + NULL); + sys_var_thd_ulong sys_net_buffer_length("net_buffer_length", &SV::net_buffer_length); sys_var_thd_ulong sys_net_read_timeout("net_read_timeout", @@ -641,6 +647,7 @@ sys_var *sys_variables[]= &sys_myisam_max_sort_file_size, &sys_myisam_repair_threads, &sys_myisam_sort_buffer_size, + &sys_myisam_stats_method, &sys_net_buffer_length, &sys_net_read_timeout, &sys_net_retry_count, @@ -912,6 +919,9 @@ struct show_var_st init_vars[]= { {sys_myisam_repair_threads.name, (char*) &sys_myisam_repair_threads, SHOW_SYS}, {sys_myisam_sort_buffer_size.name, (char*) &sys_myisam_sort_buffer_size, SHOW_SYS}, + + {sys_myisam_stats_method.name, (char*) &sys_myisam_stats_method, SHOW_SYS}, + #ifdef __NT__ {"named_pipe", (char*) &opt_enable_named_pipe, SHOW_MY_BOOL}, #endif @@ -1112,9 +1122,10 @@ static void sys_default_init_slave(THD* thd, enum_var_type type) static int sys_check_ftb_syntax(THD *thd, set_var *var) { - if (thd->master_access & SUPER_ACL) - return ft_boolean_check_syntax_string((byte*) var->value->str_value.c_ptr()) ? - -1 : 0; + if (thd->security_ctx->master_access & SUPER_ACL) + return (ft_boolean_check_syntax_string((byte*) + var->value->str_value.c_ptr()) ? + -1 : 0); else { my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); @@ -2709,7 +2720,7 @@ static bool set_option_autocommit(THD *thd, set_var *var) static int check_log_update(THD *thd, set_var *var) { #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (!(thd->master_access & SUPER_ACL)) + if (!(thd->security_ctx->master_access & SUPER_ACL)) { my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); return 1; @@ -2755,7 +2766,7 @@ static int check_pseudo_thread_id(THD *thd, set_var *var) { var->save_result.ulonglong_value= var->value->val_int(); #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (thd->master_access & SUPER_ACL) + if (thd->security_ctx->master_access & SUPER_ACL) return 0; else { @@ -3120,10 +3131,10 @@ int set_var_password::check(THD *thd) #ifndef NO_EMBEDDED_ACCESS_CHECKS if (!user->host.str) { - if (thd->priv_host != 0) + if (*thd->security_ctx->priv_host != 0) { - user->host.str= (char *) thd->priv_host; - user->host.length= strlen(thd->priv_host); + user->host.str= (char *) thd->security_ctx->priv_host; + user->host.length= strlen(thd->security_ctx->priv_host); } else { diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 61d89545d61..3aabc4e5b1f 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5311,7 +5311,8 @@ ER_XAER_NOTA XAE04 ER_XAER_INVAL XAE05 eng "XAER_INVAL: Invalid arguments (or unsupported command)" ER_XAER_RMFAIL XAE07 - eng "XAER_RMFAIL: The command cannot be executed in the %.64s state" + eng "XAER_RMFAIL: The command cannot be executed when global transaction is in the %.64s state" + rus "XAER_RMFAIL: ÜÔÕ ËÏÍÁÎÄÕ ÎÅÌØÚÑ ×ÙÐÏÌÎÑÔØ ËÏÇÄÁ ÇÌÏÂÁÌØÎÁÑ ÔÒÁÎÚÁËÃÉÑ ÎÁÈÏÄÉÔÓÑ × ÓÏÓÔÏÑÎÉÉ '%.64s'" ER_XAER_OUTSIDE XAE09 eng "XAER_OUTSIDE: Some work is done outside global transaction" ER_XAER_RMERR XAE03 @@ -5366,12 +5367,12 @@ ER_TOO_BIG_SCALE 42000 S1009 eng "Too big scale %d specified for column '%-.64s'. Maximum is %d." ER_TOO_BIG_PRECISION 42000 S1009 eng "Too big precision %d specified for column '%-.64s'. Maximum is %d." -ER_SCALE_BIGGER_THAN_PRECISION 42000 S1009 - eng "Scale may not be larger than the precision (column '%-.64s')." +ER_M_BIGGER_THAN_D 42000 S1009 + eng "For float(M,D), double(M,D) or decimal(M,D), M must be >= D (column '%-.64s')." ER_WRONG_LOCK_OF_SYSTEM_TABLE eng "You can't combine write-locking of system '%-.64s.%-.64s' table with other tables" ER_CONNECT_TO_FOREIGN_DATA_SOURCE - eng "Unable to connect to foreign data source - database '%s'!" + eng "Unable to connect to foreign data source - database '%.64s'!" ER_QUERY_ON_FOREIGN_DATA_SOURCE eng "There was a problem processing the query on the foreign data source. Data source error: '%-.64s'" ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST @@ -5400,11 +5401,11 @@ ER_DATETIME_FUNCTION_OVERFLOW 22008 ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG eng "Can't update table '%-.64s' in stored function/trigger because it is already used by statement which invoked this stored function/trigger." ER_VIEW_PREVENT_UPDATE - eng "The definition of table '%-.64s' prevents operation %s on table '%-.64s'." + eng "The definition of table '%-.64s' prevents operation %.64s on table '%-.64s'." ER_PS_NO_RECURSION eng "The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner" ER_SP_CANT_SET_AUTOCOMMIT - eng "Not allowed to set autocommit from a stored function or trigger" + eng "Not allowed to set autocommit from a stored function or trigger" ER_NO_VIEW_USER eng "View definer is not fully qualified" ER_VIEW_FRM_NO_USER @@ -5413,6 +5414,12 @@ ER_VIEW_OTHER_USER eng "You need the SUPER privilege for creation view with %-.64s@%-.64s definer" ER_NO_SUCH_USER eng "There is not %-.64s@%-.64s registered" +ER_FORBID_SCHEMA_CHANGE + eng "Changing schema from '%-.64s' to '%-.64s' is not allowed." +ER_ROW_IS_REFERENCED_2 23000 + eng "Cannot delete or update a parent row: a foreign key constraint fails (%.192s)" +ER_NO_REFERENCED_ROW_2 23000 + eng "Cannot add or update a child row: a foreign key constraint fails (%.192s)" ER_PARTITION_REQUIRES_VALUES_ERROR eng "%s PARTITIONING requires definition of VALUES %s for each partition" swe "%s PARTITIONering kräver definition av VALUES %s för varje partition" diff --git a/sql/slave.cc b/sql/slave.cc index 757d8bc212d..fb7b9275d0d 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -37,7 +37,7 @@ typedef bool (*CHECK_KILLED_FUNC)(THD*,void*); volatile bool slave_sql_running = 0, slave_io_running = 0; char* slave_load_tmpdir = 0; MASTER_INFO *active_mi; -bool replicate_same_server_id; +my_bool replicate_same_server_id; ulonglong relay_log_space_limit = 0; /* @@ -2426,17 +2426,10 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type) DBUG_ENTER("init_slave_thread"); thd->system_thread = (thd_type == SLAVE_THD_SQL) ? SYSTEM_THREAD_SLAVE_SQL : SYSTEM_THREAD_SLAVE_IO; - /* - The two next lines are needed for replication of SP (CREATE PROCEDURE - needs a valid user to store in mysql.proc). - */ - thd->priv_user= (char *) ""; - thd->priv_host[0]= '\0'; - thd->host_or_ip= ""; + thd->security_ctx->skip_grants(); thd->client_capabilities = 0; my_net_init(&thd->net, 0); thd->net.read_timeout = slave_net_timeout; - thd->master_access= ~(ulong)0; thd->slave_thread = 1; set_slave_thread_options(thd); /* diff --git a/sql/slave.h b/sql/slave.h index ead1aa87ce6..486bb3055f5 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -552,7 +552,7 @@ extern "C" pthread_handler_decl(handle_slave_sql,arg); extern bool volatile abort_loop; extern MASTER_INFO main_mi, *active_mi; /* active_mi for multi-master */ extern LIST master_list; -extern bool replicate_same_server_id; +extern my_bool replicate_same_server_id; extern int disconnect_slave_event_count, abort_slave_event_count ; diff --git a/sql/sp.cc b/sql/sp.cc index e979921492f..18d94a85884 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -494,7 +494,8 @@ db_create_routine(THD *thd, int type, sp_head *sp) else { restore_record(table, s->default_values); // Get default values for fields - strxmov(definer, thd->priv_user, "@", thd->priv_host, NullS); + strxmov(definer, thd->security_ctx->priv_user, "@", + thd->security_ctx->priv_host, NullS); if (table->s->fields != MYSQL_PROC_FIELD_COUNT) { @@ -569,7 +570,7 @@ db_create_routine(THD *thd, int type, sp_head *sp) goto done; } } - if (!(thd->master_access & SUPER_ACL)) + if (!(thd->security_ctx->master_access & SUPER_ACL)) { my_message(ER_BINLOG_CREATE_ROUTINE_NEED_SUPER, ER(ER_BINLOG_CREATE_ROUTINE_NEED_SUPER), MYF(0)); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 1a7599d7bbc..671acbc2a0c 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -199,11 +199,18 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type, Item *it= sp_prepare_func_item(thd, it_addr); uint rsize; Query_arena backup_arena; + Item *old_item_next, *old_free_list, **p_free_list; DBUG_PRINT("info", ("type: %d", type)); if (!it) - { DBUG_RETURN(NULL); + + if (reuse) + { + old_item_next= reuse->next; + p_free_list= use_callers_arena ? &thd->spcont->callers_arena->free_list : + &thd->free_list; + old_free_list= *p_free_list; } switch (sp_map_result_type(type)) { @@ -312,15 +319,23 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type, default: DBUG_ASSERT(0); } - it->rsize= rsize; - - DBUG_RETURN(it); + goto end; return_null_item: CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_null(), use_callers_arena, &backup_arena); +end: it->rsize= rsize; + if (reuse && it == reuse) + { + /* + The Item constructor registered itself in the arena free list, + while the item slot is reused, so we have to restore the list. + */ + it->next= old_item_next; + *p_free_list= old_free_list; + } DBUG_RETURN(it); } @@ -1000,7 +1015,7 @@ int sp_head::execute(THD *thd) ip= hip; ret= 0; ctx->clear_handler(); - ctx->in_handler= TRUE; + ctx->enter_handler(hip); thd->clear_error(); thd->killed= THD::NOT_KILLED; continue; @@ -1358,14 +1373,6 @@ int sp_head::execute_procedure(THD *thd, List<Item> *args) uint offset= static_cast<Item_splocal *>(it)->get_offset(); Item *val= nctx->get_item(i); Item *orig= octx->get_item(offset); - Item *o_item_next; - /* we'll use callers_arena in sp_eval_func_item */ - Item *o_free_list= thd->spcont->callers_arena->free_list; - - LINT_INIT(o_item_next); - - if (orig) - o_item_next= orig->next; /* We might need to allocate new item if we weren't able to @@ -1380,15 +1387,6 @@ int sp_head::execute_procedure(THD *thd, List<Item> *args) } if (copy != orig) octx->set_item(offset, copy); - if (orig && copy == orig) - { - /* - A reused item slot, where the constructor put it in the - free_list, so we have to restore the list. - */ - thd->spcont->callers_arena->free_list= o_free_list; - copy->next= o_item_next; - } } else { @@ -1636,8 +1634,10 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access) tables.db= (char*) "mysql"; tables.table_name= tables.alias= (char*) "proc"; *full_access= (!check_table_access(thd, SELECT_ACL, &tables, 1) || - (!strcmp(sp->m_definer_user.str, thd->priv_user) && - !strcmp(sp->m_definer_host.str, thd->priv_host))); + (!strcmp(sp->m_definer_user.str, + thd->security_ctx->priv_user) && + !strcmp(sp->m_definer_host.str, + thd->security_ctx->priv_host))); if (!*full_access) return check_some_routine_access(thd, sp->m_db.str, sp->m_name.str, sp->m_type == TYPE_ENUM_PROCEDURE); @@ -2378,7 +2378,7 @@ sp_instr_hreturn::execute(THD *thd, uint *nextp) thd->spcont->restore_variables(m_frame); *nextp= thd->spcont->pop_hstack(); } - thd->spcont->in_handler= FALSE; + thd->spcont->exit_handler(); DBUG_RETURN(0); } @@ -2476,6 +2476,10 @@ sp_instr_cpop::backpatch(uint dest, sp_pcontext *dst_ctx) int sp_instr_copen::execute(THD *thd, uint *nextp) { + /* + We don't store a pointer to the cursor in the instruction to be + able to reuse the same instruction among different threads in future. + */ sp_cursor *c= thd->spcont->get_cursor(m_cursor); int res; DBUG_ENTER("sp_instr_copen::execute"); @@ -2484,41 +2488,33 @@ sp_instr_copen::execute(THD *thd, uint *nextp) res= -1; else { - sp_lex_keeper *lex_keeper= c->pre_open(thd); - if (!lex_keeper) // cursor already open or OOM - { - res= -1; - *nextp= m_ip+1; - } - else - { - Query_arena *old_arena= thd->stmt_arena; + sp_lex_keeper *lex_keeper= c->get_lex_keeper(); + Query_arena *old_arena= thd->stmt_arena; - /* - Get the Query_arena from the cpush instruction, which contains - the free_list of the query, so new items (if any) are stored in - the right free_list, and we can cleanup after each open. - */ - thd->stmt_arena= c->get_instr(); - res= lex_keeper->reset_lex_and_exec_core(thd, nextp, FALSE, this); - /* Cleanup the query's items */ - if (thd->stmt_arena->free_list) - cleanup_items(thd->stmt_arena->free_list); - thd->stmt_arena= old_arena; - /* - Work around the fact that errors in selects are not returned properly - (but instead converted into a warning), so if a condition handler - caught, we have lost the result code. - */ - if (!res) - { - uint dummy1, dummy2; + /* + Get the Query_arena from the cpush instruction, which contains + the free_list of the query, so new items (if any) are stored in + the right free_list, and we can cleanup after each open. + */ + thd->stmt_arena= c->get_instr(); + res= lex_keeper->reset_lex_and_exec_core(thd, nextp, FALSE, this); + /* Cleanup the query's items */ + if (thd->stmt_arena->free_list) + cleanup_items(thd->stmt_arena->free_list); + thd->stmt_arena= old_arena; + /* + Work around the fact that errors in selects are not returned properly + (but instead converted into a warning), so if a condition handler + caught, we have lost the result code. + */ + if (!res) + { + uint dummy1, dummy2; - if (thd->spcont->found_handler(&dummy1, &dummy2)) - res= -1; - } - c->post_open(thd, res ? FALSE : TRUE); + if (thd->spcont->found_handler(&dummy1, &dummy2)) + res= -1; } + /* TODO: Assert here that we either have an error or a cursor */ } DBUG_RETURN(res); } @@ -2527,7 +2523,8 @@ sp_instr_copen::execute(THD *thd, uint *nextp) int sp_instr_copen::exec_core(THD *thd, uint *nextp) { - int res= mysql_execute_command(thd); + sp_cursor *c= thd->spcont->get_cursor(m_cursor); + int res= c->open(thd); *nextp= m_ip+1; return res; } @@ -2582,14 +2579,7 @@ sp_instr_cfetch::execute(THD *thd, uint *nextp) Query_arena backup_arena; DBUG_ENTER("sp_instr_cfetch::execute"); - if (! c) - res= -1; - else - { - thd->set_n_backup_active_arena(thd->spcont->callers_arena, &backup_arena); - res= c->fetch(thd, &m_varlist); - thd->restore_active_arena(thd->spcont->callers_arena, &backup_arena); - } + res= c ? c->fetch(thd, &m_varlist) : -1; *nextp= m_ip+1; DBUG_RETURN(res); @@ -2645,54 +2635,36 @@ sp_instr_error::print(String *str) */ #ifndef NO_EMBEDDED_ACCESS_CHECKS -void -sp_change_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp) -{ - ctxp->changed= (sp->m_chistics->suid != SP_IS_NOT_SUID && - (strcmp(sp->m_definer_user.str, thd->priv_user) || - strcmp(sp->m_definer_host.str, thd->priv_host))); - - if (ctxp->changed) +bool +sp_change_security_context(THD *thd, sp_head *sp, Security_context **backup) +{ + *backup= 0; + if (sp->m_chistics->suid != SP_IS_NOT_SUID && + (strcmp(sp->m_definer_user.str, + thd->security_ctx->priv_user) || + my_strcasecmp(system_charset_info, sp->m_definer_host.str, + thd->security_ctx->priv_host))) { - ctxp->master_access= thd->master_access; - ctxp->db_access= thd->db_access; - ctxp->priv_user= thd->priv_user; - strncpy(ctxp->priv_host, thd->priv_host, sizeof(ctxp->priv_host)); - ctxp->user= thd->user; - ctxp->host= thd->host; - ctxp->ip= thd->ip; - - /* Change thise just to do the acl_getroot_no_password */ - thd->user= sp->m_definer_user.str; - thd->host= thd->ip = sp->m_definer_host.str; - - if (acl_getroot_no_password(thd)) - { // Failed, run as invoker for now - ctxp->changed= FALSE; - thd->master_access= ctxp->master_access; - thd->db_access= ctxp->db_access; - thd->priv_user= ctxp->priv_user; - strncpy(thd->priv_host, ctxp->priv_host, sizeof(thd->priv_host)); + if (acl_getroot_no_password(&sp->m_security_ctx, sp->m_definer_user.str, + sp->m_definer_host.str, + sp->m_definer_host.str, + sp->m_db.str)) + { + my_error(ER_NO_SUCH_USER, MYF(0), sp->m_definer_user.str, + sp->m_definer_host.str); + return TRUE; } - - /* Restore these immiediately */ - thd->user= ctxp->user; - thd->host= ctxp->host; - thd->ip= ctxp->ip; + *backup= thd->security_ctx; + thd->security_ctx= &sp->m_security_ctx; } + return FALSE; } void -sp_restore_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp) +sp_restore_security_context(THD *thd, Security_context *backup) { - if (ctxp->changed) - { - ctxp->changed= FALSE; - thd->master_access= ctxp->master_access; - thd->db_access= ctxp->db_access; - thd->priv_user= ctxp->priv_user; - strncpy(thd->priv_host, ctxp->priv_host, sizeof(thd->priv_host)); - } + if (backup) + thd->security_ctx= backup; } #endif /* NO_EMBEDDED_ACCESS_CHECKS */ diff --git a/sql/sp_head.h b/sql/sp_head.h index 9888fe74149..ed0f3987e01 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -151,6 +151,12 @@ public: // Pointers set during parsing uchar *m_param_begin, *m_param_end, *m_body_begin; + /* + Security context for stored routine which should be run under + definer privileges. + */ + Security_context m_security_ctx; + static void * operator new(size_t size); @@ -860,6 +866,12 @@ public: virtual void print(String *str); + /* + This call is used to cleanup the instruction when a sensitive + cursor is closed. For now stored procedures always use materialized + cursors and the call is not used. + */ + virtual void cleanup_stmt() { /* no op */ } private: sp_lex_keeper m_lex_keeper; @@ -1017,23 +1029,12 @@ private: }; // class sp_instr_error : public sp_instr -struct st_sp_security_context -{ - bool changed; - uint master_access; - uint db_access; - char *priv_user; - char priv_host[MAX_HOSTNAME]; - char *user; - char *host; - char *ip; -}; - #ifndef NO_EMBEDDED_ACCESS_CHECKS +bool +sp_change_security_context(THD *thd, sp_head *sp, + Security_context **backup); void -sp_change_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp); -void -sp_restore_security_context(THD *thd, sp_head *sp,st_sp_security_context *ctxp); +sp_restore_security_context(THD *thd, Security_context *backup); #endif /* NO_EMBEDDED_ACCESS_CHECKS */ TABLE_LIST * @@ -1041,4 +1042,7 @@ sp_add_to_query_tables(THD *thd, LEX *lex, const char *db, const char *name, thr_lock_type locktype); +Item *sp_eval_func_item(THD *thd, Item **it, enum_field_types type, + Item *reuse, bool use_callers_arena); + #endif /* _SP_HEAD_H_ */ diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 748c09f56c7..252bd7e5cab 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -25,17 +25,18 @@ #include "mysql.h" #include "sp_head.h" +#include "sql_cursor.h" #include "sp_rcontext.h" #include "sp_pcontext.h" sp_rcontext::sp_rcontext(uint fsize, uint hmax, uint cmax) : m_count(0), m_fsize(fsize), m_result(NULL), m_hcount(0), m_hsp(0), - m_hfound(-1), m_ccount(0) + m_ihsp(0), m_hfound(-1), m_ccount(0) { - in_handler= FALSE; m_frame= (Item **)sql_alloc(fsize * sizeof(Item*)); m_handler= (sp_handler_t *)sql_alloc(hmax * sizeof(sp_handler_t)); m_hstack= (uint *)sql_alloc(hmax * sizeof(uint)); + m_in_handler= (uint *)sql_alloc(hmax * sizeof(uint)); m_cstack= (sp_cursor **)sql_alloc(cmax * sizeof(sp_cursor *)); m_saved.empty(); } @@ -45,31 +46,18 @@ int sp_rcontext::set_item_eval(THD *thd, uint idx, Item **item_addr, enum_field_types type) { - extern Item *sp_eval_func_item(THD *thd, Item **it, enum_field_types type, - Item *reuse, bool use_callers_arena); Item *it; Item *reuse_it; - Item *old_item_next; /* sp_eval_func_item will use callers_arena */ - Item *old_free_list= thd->spcont->callers_arena->free_list; int res; - LINT_INIT(old_item_next); - if ((reuse_it= get_item(idx))) - old_item_next= reuse_it->next; + reuse_it= get_item(idx); it= sp_eval_func_item(thd, item_addr, type, reuse_it, TRUE); if (! it) res= -1; else { res= 0; - if (reuse_it && it == reuse_it) - { - // A reused item slot, where the constructor put it in the free_list, - // so we have to restore the list. - thd->spcont->callers_arena->free_list= old_free_list; - it->next= old_item_next; - } set_item(idx, it); } @@ -80,8 +68,6 @@ bool sp_rcontext::find_handler(uint sql_errno, MYSQL_ERROR::enum_warning_level level) { - if (in_handler) - return 0; // Already executing a handler if (m_hfound >= 0) return 1; // Already got one @@ -91,6 +77,13 @@ sp_rcontext::find_handler(uint sql_errno, while (i--) { sp_cond_type_t *cond= m_handler[i].cond; + int j= m_ihsp; + + while (j--) + if (m_in_handler[j] == m_handler[i].handler) + break; + if (j >= 0) + continue; // Already executing this handler switch (cond->type) { @@ -170,7 +163,8 @@ sp_rcontext::pop_cursors(uint count) */ sp_cursor::sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i) - :m_lex_keeper(lex_keeper), m_prot(NULL), m_isopen(0), m_current_row(NULL), + :m_lex_keeper(lex_keeper), + server_side_cursor(NULL), m_i(i) { /* @@ -182,59 +176,37 @@ sp_cursor::sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i) /* - pre_open cursor + Open an SP cursor SYNOPSIS - pre_open() - THD Thread handler + open() + THD Thread handler - NOTES - We have to open cursor in two steps to make it easy for sp_instr_copen - to reuse the sp_instr::exec_stmt() code. - If this function returns 0, post_open should not be called RETURN - 0 ERROR + 0 in case of success, -1 otherwise */ -sp_lex_keeper* -sp_cursor::pre_open(THD *thd) +int +sp_cursor::open(THD *thd) { - if (m_isopen) + if (server_side_cursor) { my_message(ER_SP_CURSOR_ALREADY_OPEN, ER(ER_SP_CURSOR_ALREADY_OPEN), MYF(0)); - return NULL; + return -1; } - init_alloc_root(&m_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); - if ((m_prot= new Protocol_cursor(thd, &m_mem_root)) == NULL) - return NULL; - - /* Save for execution. Will be restored in post_open */ - m_oprot= thd->protocol; - m_nseof= thd->net.no_send_eof; - - /* Change protocol for execution */ - thd->protocol= m_prot; - thd->net.no_send_eof= TRUE; - return m_lex_keeper; -} - - -void -sp_cursor::post_open(THD *thd, my_bool was_opened) -{ - thd->net.no_send_eof= m_nseof; // Restore the originals - thd->protocol= m_oprot; - if ((m_isopen= was_opened)) - m_current_row= m_prot->data; + if (mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR, &result, + &server_side_cursor)) + return -1; + return 0; } int sp_cursor::close(THD *thd) { - if (! m_isopen) + if (! server_side_cursor) { my_message(ER_SP_CURSOR_NOT_OPEN, ER(ER_SP_CURSOR_NOT_OPEN), MYF(0)); return -1; @@ -247,106 +219,82 @@ sp_cursor::close(THD *thd) void sp_cursor::destroy() { - if (m_prot) - { - delete m_prot; - m_prot= NULL; - free_root(&m_mem_root, MYF(0)); - } - m_isopen= FALSE; + delete server_side_cursor; + server_side_cursor= 0; } + int sp_cursor::fetch(THD *thd, List<struct sp_pvar> *vars) { - List_iterator_fast<struct sp_pvar> li(*vars); - sp_pvar_t *pv; - MYSQL_ROW row; - uint fldcount; - - if (! m_isopen) + if (! server_side_cursor) { my_message(ER_SP_CURSOR_NOT_OPEN, ER(ER_SP_CURSOR_NOT_OPEN), MYF(0)); return -1; } - if (m_current_row == NULL) + if (vars->elements != result.get_field_count()) { - my_message(ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA), MYF(0)); + my_message(ER_SP_WRONG_NO_OF_FETCH_ARGS, + ER(ER_SP_WRONG_NO_OF_FETCH_ARGS), MYF(0)); return -1; } - row= m_current_row->data; - for (fldcount= 0 ; (pv= li++) ; fldcount++) - { - Item *it; - Item *reuse; - uint rsize; - Item *old_item_next; - Item *old_free_list= thd->free_list; - const char *s; - LINT_INIT(old_item_next); - - if (fldcount >= m_prot->get_field_count()) - { - my_message(ER_SP_WRONG_NO_OF_FETCH_ARGS, - ER(ER_SP_WRONG_NO_OF_FETCH_ARGS), MYF(0)); - return -1; - } + result.set_spvar_list(vars); - if ((reuse= thd->spcont->get_item(pv->offset))) - old_item_next= reuse->next; + /* Attempt to fetch one row */ + if (server_side_cursor->is_open()) + server_side_cursor->fetch(1); - s= row[fldcount]; - if (!s) - it= new(reuse, &rsize) Item_null(); - else - { - /* - Length of data can be calculated as: - pointer_to_next_not_null_object - s -1 - where the last -1 is to remove the end \0 - */ - uint len; - MYSQL_ROW next= row+fldcount+1; - while (!*next) // Skip nulls - next++; - len= (*next -s)-1; - switch (sp_map_result_type(pv->type)) { - case INT_RESULT: - it= new(reuse, &rsize) Item_int(s); - break; - case REAL_RESULT: - it= new(reuse, &rsize) Item_float(s, len); - break; - case DECIMAL_RESULT: - it= new(reuse, &rsize) Item_decimal(s, len, thd->db_charset); - break; - case STRING_RESULT: - /* TODO: Document why we do an extra copy of the string 's' here */ - it= new(reuse, &rsize) Item_string(thd->strmake(s, len), len, - thd->db_charset); - break; - case ROW_RESULT: - default: - DBUG_ASSERT(0); - } - } - it->rsize= rsize; - if (reuse && it == reuse) - { - // A reused item slot, where the constructor put it in the free_list, - // so we have to restore the list. - thd->free_list= old_free_list; - it->next= old_item_next; - } - thd->spcont->set_item(pv->offset, it); - } - if (fldcount < m_prot->get_field_count()) + /* + If the cursor was pointing after the last row, the fetch will + close it instead of sending any rows. + */ + if (! server_side_cursor->is_open()) { - my_message(ER_SP_WRONG_NO_OF_FETCH_ARGS, - ER(ER_SP_WRONG_NO_OF_FETCH_ARGS), MYF(0)); + my_message(ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA), MYF(0)); return -1; } - m_current_row= m_current_row->next; + return 0; } + + +/*************************************************************************** + Select_fetch_into_spvars +****************************************************************************/ + +int Select_fetch_into_spvars::prepare(List<Item> &fields, SELECT_LEX_UNIT *u) +{ + /* + Cache the number of columns in the result set in order to easily + return an error if column count does not match value count. + */ + field_count= fields.elements; + return select_result_interceptor::prepare(fields, u); +} + + +bool Select_fetch_into_spvars::send_data(List<Item> &items) +{ + List_iterator_fast<struct sp_pvar> pv_iter(*spvar_list); + List_iterator_fast<Item> item_iter(items); + sp_pvar_t *pv; + Item *item; + + /* Must be ensured by the caller */ + DBUG_ASSERT(spvar_list->elements == items.elements); + + /* + Assign the row fetched from a server side cursor to stored + procedure variables. + */ + for (; pv= pv_iter++, item= item_iter++; ) + { + Item *reuse= thd->spcont->get_item(pv->offset); + /* Evaluate a new item on the arena of the calling instruction */ + Item *it= sp_eval_func_item(thd, &item, pv->type, reuse, TRUE); + + thd->spcont->set_item(pv->offset, it); + } + return FALSE; +} diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 36380952e5d..22fa4f6e865 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -58,7 +58,6 @@ class sp_rcontext : public Sql_alloc public: - bool in_handler; /* Arena used to (re) allocate items on . E.g. reallocate INOUT/OUT SP parameters when they don't fit into prealloced items. This @@ -169,6 +168,18 @@ class sp_rcontext : public Sql_alloc return m_hstack[--m_hsp]; } + inline void + enter_handler(int hid) + { + m_in_handler[m_ihsp++]= hid; + } + + inline void + exit_handler() + { + m_ihsp-= 1; + } + // Save variables starting at fp and up void save_variables(uint fp); @@ -203,12 +214,14 @@ private: Item *m_result; // For FUNCTIONs - sp_handler_t *m_handler; - uint m_hcount; - uint *m_hstack; - uint m_hsp; - int m_hfound; // Set by find_handler; -1 if not found - List<Item> m_saved; // Saved variables + sp_handler_t *m_handler; // Visible handlers + uint m_hcount; // Stack pointer for m_handler + uint *m_hstack; // Return stack for continue handlers + uint m_hsp; // Stack pointer for m_hstack + uint *m_in_handler; // Active handler, for recursion check + uint m_ihsp; // Stack pointer for m_in_handler + int m_hfound; // Set by find_handler; -1 if not found + List<Item> m_saved; // Saved variables during handler exec. sp_cursor **m_cstack; uint m_ccount; @@ -216,6 +229,27 @@ private: }; // class sp_rcontext : public Sql_alloc +/* + An interceptor of cursor result set used to implement + FETCH <cname> INTO <varlist>. +*/ + +class Select_fetch_into_spvars: public select_result_interceptor +{ + List<struct sp_pvar> *spvar_list; + uint field_count; +public: + uint get_field_count() { return field_count; } + void set_spvar_list(List<struct sp_pvar> *vars) { spvar_list= vars; } + + virtual bool send_eof() { return FALSE; } + virtual bool send_data(List<Item> &items); + virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u); +}; + + +/* A mediator between stored procedures and server side cursors */ + class sp_cursor : public Sql_alloc { public: @@ -227,12 +261,11 @@ public: destroy(); } - // We have split this in two to make it easy for sp_instr_copen - // to reuse the sp_instr::exec_stmt() code. sp_lex_keeper * - pre_open(THD *thd); - void - post_open(THD *thd, my_bool was_opened); + get_lex_keeper() { return m_lex_keeper; } + + int + open(THD *thd); int close(THD *thd); @@ -240,7 +273,7 @@ public: inline my_bool is_open() { - return m_isopen; + return test(server_side_cursor); } int @@ -251,18 +284,13 @@ public: { return m_i; } - + private: - MEM_ROOT m_mem_root; // My own mem_root + Select_fetch_into_spvars result; sp_lex_keeper *m_lex_keeper; - Protocol_cursor *m_prot; - my_bool m_isopen; - my_bool m_nseof; // Original no_send_eof - Protocol *m_oprot; // Original protcol - MYSQL_ROWS *m_current_row; + Server_side_cursor *server_side_cursor; sp_instr_cpush *m_i; // My push instruction - void destroy(); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index c7a6387aab1..7a87f01258a 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -692,8 +692,8 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b) SYNOPSIS acl_getroot() thd thread handle. If all checks are OK, - thd->priv_user, thd->master_access are updated. - thd->host, thd->ip, thd->user are used for checks. + thd->security_ctx->priv_user/master_access are updated. + thd->security_ctx->host/ip/user are used for checks. mqh user resources; on success mqh is reset, else unchanged passwd scrambled & crypted password, received from client @@ -718,6 +718,7 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, ulong user_access= NO_ACCESS; int res= 1; ACL_USER *acl_user= 0; + Security_context *sctx= thd->security_ctx; DBUG_ENTER("acl_getroot"); if (!initialized) @@ -725,9 +726,7 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, /* here if mysqld's been started with --skip-grant-tables option. */ - thd->priv_user= (char *) ""; // privileges for - *thd->priv_host= '\0'; // the user are unknown - thd->master_access= ~NO_ACCESS; // everything is allowed + sctx->skip_grants(); bzero((char*) mqh, sizeof(*mqh)); DBUG_RETURN(0); } @@ -743,9 +742,9 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, for (uint i=0 ; i < acl_users.elements ; i++) { ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*); - if (!acl_user_tmp->user || !strcmp(thd->user, acl_user_tmp->user)) + if (!acl_user_tmp->user || !strcmp(sctx->user, acl_user_tmp->user)) { - if (compare_hostname(&acl_user_tmp->host, thd->host, thd->ip)) + if (compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip)) { /* check password: it should be empty or valid */ if (passwd_len == acl_user_tmp->salt_len) @@ -892,14 +891,14 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, break; #endif /* HAVE_OPENSSL */ } - thd->master_access= user_access; - thd->priv_user= acl_user->user ? thd->user : (char *) ""; + sctx->master_access= user_access; + sctx->priv_user= acl_user->user ? sctx->user : (char *) ""; *mqh= acl_user->user_resource; if (acl_user->host.hostname) - strmake(thd->priv_host, acl_user->host.hostname, MAX_HOSTNAME); + strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME); else - *thd->priv_host= 0; + *sctx->priv_host= 0; } VOID(pthread_mutex_unlock(&acl_cache->lock)); DBUG_RETURN(res); @@ -907,47 +906,62 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, /* - * This is like acl_getroot() above, but it doesn't check password, - * and we don't care about the user resources. - * Used to get access rights for SQL SECURITY DEFINER invocation of - * stored procedures. - */ -int acl_getroot_no_password(THD *thd) + This is like acl_getroot() above, but it doesn't check password, + and we don't care about the user resources. + + SYNOPSIS + acl_getroot_no_password() + sctx Context which should be initialized + user user name + host host name + ip IP + db current data base name + + RETURN + FALSE OK + TRUE Error +*/ + +bool acl_getroot_no_password(Security_context *sctx, char *user, char *host, + char *ip, char *db) { int res= 1; uint i; ACL_USER *acl_user= 0; DBUG_ENTER("acl_getroot_no_password"); + sctx->user= user; + sctx->host= host; + sctx->ip= ip; + sctx->host_or_ip= host ? host : (ip ? ip : ""); + if (!initialized) { - /* + /* here if mysqld's been started with --skip-grant-tables option. */ - thd->priv_user= (char *) ""; // privileges for - *thd->priv_host= '\0'; // the user are unknown - thd->master_access= ~NO_ACCESS; // everything is allowed - DBUG_RETURN(0); + sctx->skip_grants(); + DBUG_RETURN(FALSE); } VOID(pthread_mutex_lock(&acl_cache->lock)); - thd->master_access= 0; - thd->db_access= 0; + sctx->master_access= 0; + sctx->db_access= 0; /* Find acl entry in user database. This is specially tailored to suit the check we do for CALL of - a stored procedure; thd->user is set to what is actually a + a stored procedure; user is set to what is actually a priv_user, which can be ''. */ for (i=0 ; i < acl_users.elements ; i++) { acl_user= dynamic_element(&acl_users,i,ACL_USER*); - if ((!acl_user->user && (!thd->user || !thd->user[0])) || - (acl_user->user && strcmp(thd->user, acl_user->user) == 0)) + if ((!acl_user->user && (!user || !user[0])) || + (acl_user->user && strcmp(user, acl_user->user) == 0)) { - if (compare_hostname(&acl_user->host, thd->host, thd->ip)) + if (compare_hostname(&acl_user->host, host, ip)) { res= 0; break; @@ -961,25 +975,25 @@ int acl_getroot_no_password(THD *thd) { ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*); if (!acl_db->user || - (thd->user && thd->user[0] && !strcmp(thd->user, acl_db->user))) + (user && user[0] && !strcmp(user, acl_db->user))) { - if (compare_hostname(&acl_db->host, thd->host, thd->ip)) + if (compare_hostname(&acl_db->host, host, ip)) { - if (!acl_db->db || (thd->db && !strcmp(acl_db->db, thd->db))) + if (!acl_db->db || (db && !strcmp(acl_db->db, db))) { - thd->db_access= acl_db->access; + sctx->db_access= acl_db->access; break; } } } } - thd->master_access= acl_user->access; - thd->priv_user= acl_user->user ? thd->user : (char *) ""; + sctx->master_access= acl_user->access; + sctx->priv_user= acl_user->user ? user : (char *) ""; if (acl_user->host.hostname) - strmake(thd->priv_host, acl_user->host.hostname, MAX_HOSTNAME); + strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME); else - *thd->priv_host= 0; + *sctx->priv_host= 0; } VOID(pthread_mutex_unlock(&acl_cache->lock)); DBUG_RETURN(res); @@ -1333,13 +1347,14 @@ bool check_change_password(THD *thd, const char *host, const char *user, return(1); } if (!thd->slave_thread && - (strcmp(thd->user,user) || - my_strcasecmp(system_charset_info, host, thd->priv_host))) + (strcmp(thd->security_ctx->user, user) || + my_strcasecmp(system_charset_info, host, + thd->security_ctx->priv_host))) { if (check_access(thd, UPDATE_ACL, "mysql",0,1,0,0)) return(1); } - if (!thd->slave_thread && !thd->user[0]) + if (!thd->slave_thread && !thd->security_ctx->user[0]) { my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER), MYF(0)); @@ -1472,7 +1487,7 @@ bool is_acl_user(const char *host, const char *user) { bool res; VOID(pthread_mutex_lock(&acl_cache->lock)); - res= find_acl_user(host, user, TRUE); + res= find_acl_user(host, user, TRUE) != NULL; VOID(pthread_mutex_unlock(&acl_cache->lock)); return res; } @@ -1645,9 +1660,10 @@ static bool update_user_table(THD *thd, TABLE *table, static bool test_if_create_new_users(THD *thd) { - bool create_new_users= test(thd->master_access & INSERT_ACL) || + Security_context *sctx= thd->security_ctx; + bool create_new_users= test(sctx->master_access & INSERT_ACL) || (!opt_safe_user_create && - test(thd->master_access & CREATE_USER_ACL)); + test(sctx->master_access & CREATE_USER_ACL)); if (!create_new_users) { TABLE_LIST tl; @@ -1657,8 +1673,8 @@ static bool test_if_create_new_users(THD *thd) tl.table_name= (char*) "user"; create_new_users= 1; - db_access=acl_get(thd->host, thd->ip, - thd->priv_user, tl.db, 0); + db_access=acl_get(sctx->host, sctx->ip, + sctx->priv_user, tl.db, 0); if (!(db_access & INSERT_ACL)) { if (check_grant(thd, INSERT_ACL, &tl, 0, UINT_MAX, 1)) @@ -1737,7 +1753,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, else if (!can_create_user) { my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0), - thd->user, thd->host_or_ip); + thd->security_ctx->user, thd->security_ctx->host_or_ip); goto end; } old_row_exists = 0; @@ -2449,7 +2465,8 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, byte user_key[MAX_KEY_LENGTH]; DBUG_ENTER("replace_table_table"); - strxmov(grantor, thd->user, "@", thd->host_or_ip, NullS); + strxmov(grantor, thd->security_ctx->user, "@", + thd->security_ctx->host_or_ip, NullS); /* The following should always succeed as new users are created before @@ -2571,7 +2588,8 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, DBUG_RETURN(-1); } - strxmov(grantor, thd->user, "@", thd->host_or_ip, NullS); + strxmov(grantor, thd->security_ctx->user, "@", + thd->security_ctx->host_or_ip, NullS); /* The following should always succeed as new users are created before @@ -2762,7 +2780,8 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list, get_privilege_desc(command, sizeof(command), table_list->grant.want_privilege); my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), - command, thd->priv_user, thd->host_or_ip, table_list->alias); + command, thd->security_ctx->priv_user, + thd->security_ctx->host_or_ip, table_list->alias); DBUG_RETURN(-1); } } @@ -3486,11 +3505,11 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, uint show_table, uint number, bool no_errors) { TABLE_LIST *table; - char *user = thd->priv_user; + Security_context *sctx= thd->security_ctx; DBUG_ENTER("check_grant"); DBUG_ASSERT(number > 0); - want_access&= ~thd->master_access; + want_access&= ~sctx->master_access; if (!want_access) DBUG_RETURN(0); // ok @@ -3508,8 +3527,9 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, table->grant.want_privilege= 0; continue; // Already checked } - if (!(grant_table= table_hash_search(thd->host,thd->ip, - table->db,user, table->table_name,0))) + if (!(grant_table= table_hash_search(sctx->host, sctx->ip, + table->db, sctx->priv_user, + table->table_name,0))) { want_access &= ~table->grant.privilege; goto err; // No grants @@ -3543,8 +3563,8 @@ err: get_privilege_desc(command, sizeof(command), want_access); my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), command, - thd->priv_user, - thd->host_or_ip, + sctx->priv_user, + sctx->host_or_ip, table ? table->table_name : "unknown"); } DBUG_RETURN(1); @@ -3555,6 +3575,7 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant, const char *db_name, const char *table_name, const char *name, uint length, uint show_tables) { + Security_context *sctx= thd->security_ctx; GRANT_TABLE *grant_table; GRANT_COLUMN *grant_column; ulong want_access= grant->want_privilege & ~grant->privilege; @@ -3571,8 +3592,8 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant, if (grant->version != grant_version) { grant->grant_table= - table_hash_search(thd->host, thd->ip, db_name, - thd->priv_user, + table_hash_search(sctx->host, sctx->ip, db_name, + sctx->priv_user, table_name, 0); /* purecov: inspected */ grant->version= grant_version; /* purecov: inspected */ } @@ -3601,8 +3622,8 @@ err: get_privilege_desc(command, sizeof(command), want_access); my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), command, - thd->priv_user, - thd->host_or_ip, + sctx->priv_user, + sctx->host_or_ip, name, table_name); } @@ -3614,6 +3635,7 @@ bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant, const char* db_name, const char *table_name, Field_iterator *fields) { + Security_context *sctx= thd->security_ctx; GRANT_TABLE *grant_table; GRANT_COLUMN *grant_column; @@ -3630,8 +3652,8 @@ bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant, if (grant->version != grant_version) { grant->grant_table= - table_hash_search(thd->host, thd->ip, db_name, - thd->priv_user, + table_hash_search(sctx->host, sctx->ip, db_name, + sctx->priv_user, table_name, 0); /* purecov: inspected */ grant->version= grant_version; /* purecov: inspected */ } @@ -3657,8 +3679,8 @@ err2: get_privilege_desc(command, sizeof(command), want_access); my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), command, - thd->priv_user, - thd->host_or_ip, + sctx->priv_user, + sctx->host_or_ip, fields->name(), table_name); return 1; @@ -3673,11 +3695,12 @@ err2: bool check_grant_db(THD *thd,const char *db) { + Security_context *sctx= thd->security_ctx; char helping [NAME_LEN+USERNAME_LENGTH+2]; uint len; bool error= 1; - len= (uint) (strmov(strmov(helping,thd->priv_user)+1,db)-helping)+ 1; + len= (uint) (strmov(strmov(helping, sctx->priv_user) + 1, db) - helping) + 1; rw_rdlock(&LOCK_grant); for (uint idx=0 ; idx < column_priv_hash.records ; idx++) @@ -3686,7 +3709,7 @@ bool check_grant_db(THD *thd,const char *db) idx); if (len < grant_table->key_length && !memcmp(grant_table->hash_key,helping,len) && - compare_hostname(&grant_table->host, thd->host, thd->ip)) + compare_hostname(&grant_table->host, sctx->host, sctx->ip)) { error=0; // Found match break; @@ -3714,15 +3737,16 @@ bool check_grant_db(THD *thd,const char *db) 1 Error: User did not have the requested privielges ****************************************************************************/ -bool check_grant_routine(THD *thd, ulong want_access, +bool check_grant_routine(THD *thd, ulong want_access, TABLE_LIST *procs, bool is_proc, bool no_errors) { TABLE_LIST *table; - char *user= thd->priv_user; - char *host= thd->priv_host; + Security_context *sctx= thd->security_ctx; + char *user= sctx->priv_user; + char *host= sctx->priv_host; DBUG_ENTER("check_grant_routine"); - want_access&= ~thd->master_access; + want_access&= ~sctx->master_access; if (!want_access) DBUG_RETURN(0); // ok @@ -3730,7 +3754,7 @@ bool check_grant_routine(THD *thd, ulong want_access, for (table= procs; table; table= table->next_global) { GRANT_NAME *grant_proc; - if ((grant_proc= routine_hash_search(host,thd->ip, table->db, user, + if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user, table->table_name, is_proc, 0))) table->grant.privilege|= grant_proc->privs; @@ -3785,9 +3809,12 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name, if (grant_option) { GRANT_NAME *grant_proc; + Security_context *sctx= thd->security_ctx; rw_rdlock(&LOCK_grant); - if ((grant_proc= routine_hash_search(thd->priv_host, thd->ip, db, - thd->priv_user, name, is_proc, 0))) + if ((grant_proc= routine_hash_search(sctx->priv_host, + sctx->ip, db, + sctx->priv_user, + name, is_proc, 0))) no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS); rw_unlock(&LOCK_grant); } @@ -3802,7 +3829,7 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name, ulong get_table_grant(THD *thd, TABLE_LIST *table) { ulong privilege; - char *user = thd->priv_user; + Security_context *sctx= thd->security_ctx; const char *db = table->db ? table->db : thd->db; GRANT_TABLE *grant_table; @@ -3810,7 +3837,7 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table) #ifdef EMBEDDED_LIBRARY grant_table= NULL; #else - grant_table= table_hash_search(thd->host, thd->ip, db, user, + grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user, table->table_name, 0); #endif table->grant.grant_table=grant_table; // Remember for column test @@ -3853,9 +3880,10 @@ ulong get_column_grant(THD *thd, GRANT_INFO *grant, /* reload table if someone has modified any grants */ if (grant->version != grant_version) { + Security_context *sctx= thd->security_ctx; grant->grant_table= - table_hash_search(thd->host, thd->ip, db_name, - thd->priv_user, + table_hash_search(sctx->host, sctx->ip, + db_name, sctx->priv_user, table_name, 0); /* purecov: inspected */ grant->version= grant_version; /* purecov: inspected */ } @@ -5425,22 +5453,24 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, bool is_proc) { + Security_context *sctx= thd->security_ctx; LEX_USER *combo; TABLE_LIST tables[1]; List<LEX_USER> user_list; bool result; - DBUG_ENTER("sp_grant_privileges"); + DBUG_ENTER("sp_grant_privileges"); if (!(combo=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) DBUG_RETURN(TRUE); - combo->user.str= thd->user; + combo->user.str= sctx->user; - if (!find_acl_user(combo->host.str=(char*)thd->host_or_ip, combo->user.str, + if (!find_acl_user(combo->host.str=(char*)sctx->host_or_ip, combo->user.str, + FALSE) && + !find_acl_user(combo->host.str=(char*)sctx->host, combo->user.str, FALSE) && - !find_acl_user(combo->host.str=(char*)thd->host, combo->user.str, + !find_acl_user(combo->host.str=(char*)sctx->ip, combo->user.str, FALSE) && - !find_acl_user(combo->host.str=(char*)thd->ip, combo->user.str, FALSE) && !find_acl_user(combo->host.str=(char*)"%", combo->user.str, FALSE)) DBUG_RETURN(TRUE); @@ -5557,7 +5587,7 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond) char buff[100]; TABLE *table= tables->table; bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0); - char *curr_host= thd->priv_host ? thd->priv_host : (char *) "%"; + char *curr_host= thd->security_ctx->priv_host_name(); DBUG_ENTER("fill_schema_user_privileges"); for (counter=0 ; counter < acl_users.elements ; counter++) @@ -5570,7 +5600,7 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond) host= ""; if (no_global_access && - (strcmp(thd->priv_user, user) || + (strcmp(thd->security_ctx->priv_user, user) || my_strcasecmp(system_charset_info, curr_host, host))) continue; @@ -5610,7 +5640,7 @@ int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond) char buff[100]; TABLE *table= tables->table; bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0); - char *curr_host= thd->priv_host ? thd->priv_host : (char *) "%"; + char *curr_host= thd->security_ctx->priv_host_name(); DBUG_ENTER("fill_schema_schema_privileges"); for (counter=0 ; counter < acl_dbs.elements ; counter++) @@ -5624,7 +5654,7 @@ int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond) host= ""; if (no_global_access && - (strcmp(thd->priv_user, user) || + (strcmp(thd->security_ctx->priv_user, user) || my_strcasecmp(system_charset_info, curr_host, host))) continue; @@ -5665,7 +5695,7 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond) char buff[100]; TABLE *table= tables->table; bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0); - char *curr_host= thd->priv_host ? thd->priv_host : (char *) "%"; + char *curr_host= thd->security_ctx->priv_host_name(); DBUG_ENTER("fill_schema_table_privileges"); for (index=0 ; index < column_priv_hash.records ; index++) @@ -5677,7 +5707,7 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond) user= ""; if (no_global_access && - (strcmp(thd->priv_user, user) || + (strcmp(thd->security_ctx->priv_user, user) || my_strcasecmp(system_charset_info, curr_host, grant_table->host.hostname))) continue; @@ -5727,7 +5757,7 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond) char buff[100]; TABLE *table= tables->table; bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0); - char *curr_host= thd->priv_host ? thd->priv_host : (char *) "%"; + char *curr_host= thd->security_ctx->priv_host_name(); DBUG_ENTER("fill_schema_table_privileges"); for (index=0 ; index < column_priv_hash.records ; index++) @@ -5739,7 +5769,7 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond) user= ""; if (no_global_access && - (strcmp(thd->priv_user, user) || + (strcmp(thd->security_ctx->priv_user, user) || my_strcasecmp(system_charset_info, curr_host, grant_table->host.hostname))) continue; @@ -5803,6 +5833,7 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond) void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, const char *db, const char *table) { + Security_context *sctx= thd->security_ctx; /* --skip-grants */ if (!initialized) { @@ -5811,13 +5842,13 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, } /* global privileges */ - grant->privilege= thd->master_access; + grant->privilege= sctx->master_access; - if (!thd->priv_user) + if (!sctx->priv_user) return; // it is slave /* db privileges */ - grant->privilege|= acl_get(thd->host, thd->ip, thd->priv_user, db, 0); + grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0); if (!grant_option) return; @@ -5827,8 +5858,8 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, { rw_rdlock(&LOCK_grant); grant->grant_table= - table_hash_search(thd->host, thd->ip, db, - thd->priv_user, + table_hash_search(sctx->host, sctx->ip, db, + sctx->priv_user, table, 0); /* purecov: inspected */ grant->version= grant_version; /* purecov: inspected */ rw_unlock(&LOCK_grant); diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 0d739e860e9..0a9c6ba785f 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -182,7 +182,8 @@ ulong acl_get(const char *host, const char *ip, const char *user, const char *db, my_bool db_is_pattern); int acl_getroot(THD *thd, USER_RESOURCES *mqh, const char *passwd, uint passwd_len); -int acl_getroot_no_password(THD *thd); +bool acl_getroot_no_password(Security_context *sctx, char *user, char *host, + char *ip, char *db); bool acl_check_host(const char *host, const char *ip); bool check_change_password(THD *thd, const char *host, const char *user, char *password, uint password_len); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 38a9d259d24..1de9411cad0 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1327,6 +1327,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table->keys_in_use_for_query= table->s->keys_in_use; table->insert_values= 0; table->used_keys= table->s->keys_for_keyread; + table->fulltext_searched= 0; + table->file->ft_handler= 0; if (table->timestamp_field) table->timestamp_field_type= table->timestamp_field->get_auto_set_type(); table_list->updatable= 1; // It is not derived table nor non-updatable VIEW @@ -2146,7 +2148,7 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table, my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0),table->alias); DBUG_RETURN(1); } - if ((error=table->file->start_stmt(thd))) + if ((error=table->file->start_stmt(thd, lock_type))) { table->file->print_error(error,MYF(0)); DBUG_RETURN(1); @@ -2953,6 +2955,18 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, belongs - differs from 'table_list' only for NATURAL_USING joins. + DESCRIPTION + Find a field in a table reference depending on the type of table + reference. There are three types of table references with respect + to the representation of their result columns: + - an array of Field_translator objects for MERGE views and some + information_schema tables, + - an array of Field objects (and possibly a name hash) for stored + tables, + - a list of Natural_join_column objects for NATURAL/USING joins. + This procedure detects the type of the table reference 'table_list' + and calls the corresponding search routine. + RETURN 0 field is not found view_ref_found found value in VIEW (real result is in *ref) @@ -2976,16 +2990,29 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, /* Check that the table and database that qualify the current field name - are the same as the table we are going to search for the field. - This is done differently for NATURAL/USING joins or nested joins that - are operands of NATURAL/USING joins because there we can't simply - compare the qualifying table and database names with the ones of - 'table_list' because each field in such a join may originate from a - different table. + are the same as the table reference we are going to search for the field. + + Exclude from the test below nested joins because the columns in a + nested join generally originate from different tables. Nested joins + also have no table name, except when a nested join is a merge view + or an information schema table. + + We include explicitly table references with a 'field_translation' table, + because if there are views over natural joins we don't want to search + inside the view, but we want to search directly in the view columns + which are represented as a 'field_translation'. + TODO: Ensure that table_name, db_name and tables->db always points to something ! */ - if (!(table_list->nested_join && table_list->join_columns) && + if (/* Exclude nested joins. */ + (!table_list->nested_join || + /* Include merge views and information schema tables. */ + table_list->field_translation) && + /* + Test if the field qualifiers match the table reference we plan + to search. + */ table_name && table_name[0] && (my_strcasecmp(table_alias_charset, table_list->alias, table_name) || (db_name && db_name[0] && table_list->db && table_list->db[0] && @@ -2993,25 +3020,45 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, DBUG_RETURN(0); *actual_table= NULL; + if (table_list->field_translation) { + /* 'table_list' is a view or an information schema table. */ if ((fld= find_field_in_view(thd, table_list, name, item_name, length, ref, check_grants_view, register_tree_change))) *actual_table= table_list; } - else if (table_list->nested_join && table_list->join_columns) + else if (!table_list->nested_join) + { + /* 'table_list' is a stored table. */ + DBUG_ASSERT(table_list->table); + if ((fld= find_field_in_table(thd, table_list->table, name, length, + check_grants_table, allow_rowid, + cached_field_index_ptr))) + *actual_table= table_list; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + /* check for views with temporary table algorithm */ + if (check_grants_view && table_list->view && + fld && fld != WRONG_GRANT && + check_grant_column(thd, &table_list->grant, + table_list->view_db.str, + table_list->view_name.str, + name, length)) + fld= WRONG_GRANT; +#endif + } + else { /* - If this is a NATURAL/USING join, or an operand of such join which is a - join itself, and the field name is qualified, then search for the field - in the operands of the join. + 'table_list' is a NATURAL/USING join, or an operand of such join that + is a nested join itself. + + If the field name we search for is qualified, then search for the field + in the table references used by NATURAL/USING the join. */ if (table_name && table_name[0]) { - /* - Qualified field; Search for it in the tables used by the natural join. - */ List_iterator<TABLE_LIST> it(table_list->nested_join->join_list); TABLE_LIST *table; while ((table= it++)) @@ -3037,23 +3084,6 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, check_grants_table || check_grants_view, register_tree_change, actual_table); } - else - { - if ((fld= find_field_in_table(thd, table_list->table, name, length, - check_grants_table, allow_rowid, - cached_field_index_ptr))) - *actual_table= table_list; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - /* check for views with temporary table algorithm */ - if (check_grants_view && table_list->view && - fld && fld != WRONG_GRANT && - check_grant_column(thd, &table_list->grant, - table_list->view_db.str, - table_list->view_name.str, - name, length)) - fld= WRONG_GRANT; -#endif - } DBUG_RETURN(fld); } @@ -3364,9 +3394,9 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, for (uint i= 0; (item=li++); i++) { - if (field_name && item->type() == Item::FIELD_ITEM) + if (field_name && item->real_item()->type() == Item::FIELD_ITEM) { - Item_field *item_field= (Item_field*) item; + Item_ident *item_field= (Item_ident*) item; /* In case of group_concat() with ORDER BY condition in the QUERY @@ -3466,7 +3496,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, } } } - else if (!table_name && (item->eq(find,0) || + else if (!table_name && (find->eq(item,0) || find->name && item->name && !my_strcasecmp(system_charset_info, item->name,find->name))) @@ -4617,7 +4647,8 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, VIEW_ANY_ACL))) { my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), "ANY", - thd->priv_user, thd->host_or_ip, + thd->security_ctx->priv_user, + thd->security_ctx->host_or_ip, fld->field_name, field_table_name); DBUG_RETURN(TRUE); } @@ -4805,9 +4836,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, } DBUG_RETURN(test(thd->net.report_error)); -err: - if (arena) - thd->restore_active_arena(arena, &backup); err_no_arena: DBUG_RETURN(1); } diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 1ca9db44cc7..f17825e6d2c 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1299,7 +1299,8 @@ void Query_cache::invalidate_locked_for_write(TABLE_LIST *tables_used) DUMP(this); for (; tables_used; tables_used= tables_used->next_local) { - if (tables_used->lock_type & (TL_WRITE_LOW_PRIORITY | TL_WRITE)) + if (tables_used->lock_type & (TL_WRITE_LOW_PRIORITY | TL_WRITE) && + tables_used->table) invalidate_table(tables_used->table); } } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 2699a4fa628..fc7ea6a2794 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -181,9 +181,10 @@ THD::THD() spcont(NULL) { stmt_arena= this; - host= user= priv_user= db= ip= 0; + db= 0; catalog= (char*)"std"; // the only catalog we have for now - host_or_ip= "connecting host"; + main_security_ctx.init(); + security_ctx= &main_security_ctx; locked=some_tables_deleted=no_errors=password= 0; query_start_used= 0; count_cuted_fields= CHECK_FIELD_IGNORE; @@ -236,9 +237,6 @@ THD::THD() server_id = ::server_id; slave_net = 0; command=COM_CONNECT; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - db_access=NO_ACCESS; -#endif *scramble= '\0'; init(); @@ -426,12 +424,8 @@ THD::~THD() ha_close_connection(this); - DBUG_PRINT("info", ("freeing host")); - if (host != my_localhost) // If not pointer to constant - safeFree(host); - if (user != delayed_user) - safeFree(user); - safeFree(ip); + DBUG_PRINT("info", ("freeing security context")); + main_security_ctx.destroy(); safeFree(db); free_root(&warn_root,MYF(0)); #ifdef USING_TRANSACTIONS @@ -1544,6 +1538,19 @@ void Query_arena::free_items() } +void Query_arena::set_query_arena(Query_arena *set) +{ + mem_root= set->mem_root; + free_list= set->free_list; + state= set->state; +} + + +void Query_arena::cleanup_stmt() +{ + DBUG_ASSERT("Query_arena::cleanup_stmt()" == "not implemented"); +} + /* Statement functions */ @@ -1601,12 +1608,6 @@ void Statement::restore_backup_statement(Statement *stmt, Statement *backup) } -void Statement::close_cursor() -{ - DBUG_ASSERT("Statement::close_cursor()" == "not implemented"); -} - - void THD::end_statement() { /* Cleanup SQL processing state to resuse this statement in next query. */ @@ -1648,13 +1649,6 @@ void THD::restore_active_arena(Query_arena *set, Query_arena *backup) DBUG_VOID_RETURN; } -void Query_arena::set_query_arena(Query_arena *set) -{ - mem_root= set->mem_root; - free_list= set->free_list; - state= set->state; -} - Statement::~Statement() { /* @@ -1723,9 +1717,11 @@ int Statement_map::insert(Statement *statement) void Statement_map::close_transient_cursors() { +#ifdef TO_BE_IMPLEMENTED Statement *stmt; while ((stmt= transient_cursor_list.head())) stmt->close_cursor(); /* deletes itself from the list */ +#endif } @@ -1827,6 +1823,38 @@ void THD::set_status_var_init() bzero((char*) &status_var, sizeof(status_var)); } + +void Security_context::init() +{ + host= user= priv_user= ip= 0; + host_or_ip= "connecting host"; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + db_access= NO_ACCESS; +#endif +} + + +void Security_context::destroy() +{ + // If not pointer to constant + if (host != my_localhost) + safeFree(host); + if (user != delayed_user) + safeFree(user); + safeFree(ip); +} + + +void Security_context::skip_grants() +{ + /* privileges for the user are unknown everything is allowed */ + host_or_ip= (char *)""; + master_access= ~NO_ACCESS; + priv_user= (char *)""; + *priv_host= '\0'; +} + + /**************************************************************************** Handling of open and locked tables states. @@ -1954,8 +1982,8 @@ HASH xid_cache; static byte *xid_get_hash_key(const byte *ptr,uint *length, my_bool not_used __attribute__((unused))) { - *length=((XID_STATE*)ptr)->xid.length(); - return (byte *)&((XID_STATE*)ptr)->xid; + *length=((XID_STATE*)ptr)->xid.key_length(); + return ((XID_STATE*)ptr)->xid.key(); } static void xid_free_hash (void *ptr) @@ -1983,7 +2011,7 @@ void xid_cache_free() XID_STATE *xid_cache_search(XID *xid) { pthread_mutex_lock(&LOCK_xid_cache); - XID_STATE *res=(XID_STATE *)hash_search(&xid_cache, (byte *)xid, xid->length()); + XID_STATE *res=(XID_STATE *)hash_search(&xid_cache, xid->key(), xid->key_length()); pthread_mutex_unlock(&LOCK_xid_cache); return res; } @@ -1994,7 +2022,7 @@ bool xid_cache_insert(XID *xid, enum xa_states xa_state) XID_STATE *xs; my_bool res; pthread_mutex_lock(&LOCK_xid_cache); - if (hash_search(&xid_cache, (byte *)xid, xid->length())) + if (hash_search(&xid_cache, xid->key(), xid->key_length())) res=0; else if (!(xs=(XID_STATE *)my_malloc(sizeof(*xs), MYF(MY_WME)))) res=1; @@ -2013,8 +2041,8 @@ bool xid_cache_insert(XID *xid, enum xa_states xa_state) bool xid_cache_insert(XID_STATE *xid_state) { pthread_mutex_lock(&LOCK_xid_cache); - DBUG_ASSERT(hash_search(&xid_cache, (byte *)&xid_state->xid, - xid_state->xid.length())==0); + DBUG_ASSERT(hash_search(&xid_cache, xid_state->xid.key(), + xid_state->xid.key_length())==0); my_bool res=my_hash_insert(&xid_cache, (byte*)xid_state); pthread_mutex_unlock(&LOCK_xid_cache); return res; diff --git a/sql/sql_class.h b/sql/sql_class.h index 59b51a702aa..727ed282836 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -510,6 +510,7 @@ struct system_variables ulong multi_range_count; ulong myisam_repair_threads; ulong myisam_sort_buff_size; + ulong myisam_stats_method; ulong net_buffer_length; ulong net_interactive_timeout; ulong net_read_timeout; @@ -742,10 +743,12 @@ public: void set_query_arena(Query_arena *set); void free_items(); + /* Close the active state associated with execution of this statement */ + virtual void cleanup_stmt(); }; -class Cursor; +class Server_side_cursor; /* State of a single command executed against this connection. @@ -828,7 +831,7 @@ public: */ char *query; uint32 query_length; // current query length - Cursor *cursor; + Server_side_cursor *cursor; public: @@ -845,8 +848,6 @@ public: void restore_backup_statement(Statement *stmt, Statement *backup); /* return class type */ virtual Type type() const; - /* Close the cursor open for this statement, if there is one */ - virtual void close_cursor(); }; @@ -898,9 +899,6 @@ public: } hash_delete(&st_hash, (byte *) statement); } - void add_transient_cursor(Statement *stmt) - { transient_cursor_list.append(stmt); } - void erase_transient_cursor(Statement *stmt) { stmt->unlink(); } /* Close all cursors of this connection that use tables of a storage engine that has transaction-specific state and therefore can not @@ -953,6 +951,34 @@ bool xid_cache_insert(XID *xid, enum xa_states xa_state); bool xid_cache_insert(XID_STATE *xid_state); void xid_cache_delete(XID_STATE *xid_state); + +class Security_context { +public: + /* + host - host of the client + user - user of the client, set to NULL until the user has been read from + the connection + priv_user - The user privilege we are using. May be "" for anonymous user. + ip - client IP + */ + char *host, *user, *priv_user, *ip; + /* The host privilege we are using */ + char priv_host[MAX_HOSTNAME]; + /* points to host if host is available, otherwise points to ip */ + const char *host_or_ip; + ulong master_access; /* Global privileges from mysql.user */ + ulong db_access; /* Privileges for current db */ + + void init(); + void destroy(); + void skip_grants(); + inline char *priv_host_name() + { + return (*priv_host ? priv_host : (char *)"%"); + } +}; + + /* A registry for item tree transformations performed during query optimization. We register only those changes which require @@ -1125,13 +1151,8 @@ public: char *thread_stack; /* - host - host of the client - user - user of the client, set to NULL until the user has been read from - the connection - priv_user - The user privilege we are using. May be '' for anonymous user. db - currently selected database catalog - currently selected catalog - ip - client IP WARNING: some members of THD (currently 'db', 'catalog' and 'query') are set and alloced by the slave SQL thread (for the THD of that thread); that thread is (and must remain, for now) the only responsible for freeing these @@ -1140,8 +1161,10 @@ public: properly. For details see the 'err:' label of the pthread_handler_decl of the slave SQL thread, in sql/slave.cc. */ - char *host,*user,*priv_user,*db,*catalog,*ip; - char priv_host[MAX_HOSTNAME]; + char *db, *catalog; + Security_context main_security_ctx; + Security_context *security_ctx; + /* remote (peer) port */ uint16 peer_port; /* @@ -1150,13 +1173,9 @@ public: a time-consuming piece that MySQL can get stuck in for a long time. */ const char *proc_info; - /* points to host if host is available, otherwise points to ip */ - const char *host_or_ip; ulong client_capabilities; /* What the client supports */ ulong max_client_packet_length; - ulong master_access; /* Global privileges from mysql.user */ - ulong db_access; /* Privileges for current db */ HASH handler_tables_hash; /* @@ -1824,18 +1843,21 @@ public: } }; -class select_union :public select_result_interceptor { - public: - TABLE *table; +class select_union :public select_result_interceptor +{ TMP_TABLE_PARAM tmp_table_param; +public: + TABLE *table; - select_union(TABLE *table_par); - ~select_union(); + select_union() :table(0) {} int prepare(List<Item> &list, SELECT_LEX_UNIT *u); bool send_data(List<Item> &items); bool send_eof(); bool flush(); - void set_table(TABLE *tbl) { table= tbl; } + + bool create_result_table(THD *thd, List<Item> *column_types, + bool is_distinct, ulonglong options, + const char *alias); }; /* Base subselect interface class */ diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc new file mode 100644 index 00000000000..e8da691ea18 --- /dev/null +++ b/sql/sql_cursor.cc @@ -0,0 +1,660 @@ +/* Copyright (C) 2005 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 USE_PRAGMA_IMPLEMENTATION +#pragma implementation /* gcc class implementation */ +#endif + +#include "mysql_priv.h" +#include "sql_cursor.h" +#include "sql_select.h" + +/**************************************************************************** + Declarations. +****************************************************************************/ + +/* + Sensitive_cursor -- a sensitive non-materialized server side + cursor An instance of this class cursor has its own runtime + state -- list of used items and memory root for runtime memory, + open and locked tables, change list for the changes of the + parsed tree. This state is freed when the cursor is closed. +*/ + +class Sensitive_cursor: public Server_side_cursor +{ + MEM_ROOT main_mem_root; + Query_arena *stmt_arena; + JOIN *join; + TABLE *open_tables; + MYSQL_LOCK *lock; + TABLE *derived_tables; + /* List of items created during execution */ + query_id_t query_id; + struct Engine_info + { + const handlerton *ht; + void *read_view; + }; + Engine_info ht_info[MAX_HA]; + Item_change_list change_list; + my_bool close_at_commit; + THR_LOCK_OWNER lock_id; +private: + /* bzero cursor state in THD */ + void reset_thd(THD *thd); +public: + Sensitive_cursor(THD *thd, select_result *result_arg); + + THR_LOCK_OWNER *get_lock_id() { return &lock_id; } + /* Save THD state into cursor */ + void post_open(THD *thd); + + virtual bool is_open() const { return join != 0; } + virtual int open(JOIN *join); + virtual void fetch(ulong num_rows); + virtual void close(); + virtual ~Sensitive_cursor(); +}; + + +/* + Materialized_cursor -- an insensitive materialized server-side + cursor. The result set of this cursor is saved in a temporary + table at open. The cursor itself is simply an interface for the + handler of the temporary table. +*/ + +class Materialized_cursor: public Server_side_cursor +{ + MEM_ROOT main_mem_root; + /* A fake unit to supply to select_send when fetching */ + SELECT_LEX_UNIT fake_unit; + TABLE *table; + List<Item> item_list; + ulong fetch_limit; + ulong fetch_count; +public: + Materialized_cursor(select_result *result, TABLE *table); + + virtual bool is_open() const { return table != 0; } + virtual int open(JOIN *join __attribute__((unused))); + virtual void fetch(ulong num_rows); + virtual void close(); + virtual ~Materialized_cursor(); +}; + + +/* + Select_materialize -- a mediator between a cursor query and the + protocol. In case we were not able to open a non-materialzed + cursor, it creates an internal temporary HEAP table, and insert + all rows into it. When the table reaches max_heap_table_size, + it's converted to a MyISAM table. Later this table is used to + create a Materialized_cursor. +*/ + +class Select_materialize: public select_union +{ + select_result *result; /* the result object of the caller (PS or SP) */ +public: + Select_materialize(select_result *result_arg) :result(result_arg) {} + virtual bool send_fields(List<Item> &list, uint flags); +}; + + +/**************************************************************************/ + +/* + Attempt to open a materialized or non-materialized cursor. + + SYNOPSIS + mysql_open_cursor() + thd thread handle + flags [in] create a materialized cursor or not + result [in] result class of the caller used as a destination + for the rows fetched from the cursor + pcursor [out] a pointer to store a pointer to cursor in + + RETURN VALUE + 0 the query has been successfully executed; in this + case pcursor may or may not contain + a pointer to an open cursor. + non-zero an error, 'pcursor' has been left intact. +*/ + +int mysql_open_cursor(THD *thd, uint flags, select_result *result, + Server_side_cursor **pcursor) +{ + Sensitive_cursor *sensitive_cursor; + select_result *save_result; + Select_materialize *result_materialize; + LEX *lex= thd->lex; + int rc; + + /* + The lifetime of the sensitive cursor is the same or less as the + lifetime of the runtime memory of the statement it's opened for. + */ + if (! (result_materialize= new (thd->mem_root) Select_materialize(result))) + return 1; + + if (! (sensitive_cursor= new (thd->mem_root) Sensitive_cursor(thd, result))) + { + delete result; + return 1; + } + + save_result= lex->result; + + lex->result= result_materialize; + if (! (flags & (uint) ALWAYS_MATERIALIZED_CURSOR)) + { + thd->lock_id= sensitive_cursor->get_lock_id(); + thd->cursor= sensitive_cursor; + } + + rc= mysql_execute_command(thd); + + lex->result= save_result; + thd->lock_id= &thd->main_lock_id; + thd->cursor= 0; + + /* + Possible options here: + - a sensitive cursor is open. In this case rc is 0 and + result_materialize->table is NULL, or + - a materialized cursor is open. In this case rc is 0 and + result_materialize->table is not NULL + - an error occured during materializaton. + result_materialize->table is not NULL, but rc != 0 + - successful completion of mysql_execute_command without + a cursor: rc is 0, result_materialize->table is NULL, + sensitive_cursor is not open. + This is possible if some command writes directly to the + network, bypassing select_result mechanism. An example of + such command is SHOW VARIABLES or SHOW STATUS. + */ + if (rc) + goto err_open; + + if (sensitive_cursor->is_open()) + { + DBUG_ASSERT(!result_materialize->table); + /* + It's safer if we grab THD state after mysql_execute_command + is finished and not in Sensitive_cursor::open(), because + currently the call to Sensitive_cursor::open is buried deep + in JOIN::exec of the top level join. + */ + sensitive_cursor->post_open(thd); + *pcursor= sensitive_cursor; + goto end; + } + else if (result_materialize->table) + { + Materialized_cursor *materialized_cursor; + TABLE *table= result_materialize->table; + MEM_ROOT *mem_root= &table->mem_root; + + if (!(materialized_cursor= new (mem_root) + Materialized_cursor(result, table))) + { + rc= 1; + goto err_open; + } + + if ((rc= materialized_cursor->open(0))) + { + delete materialized_cursor; + goto err_open; + } + + *pcursor= materialized_cursor; + thd->stmt_arena->cleanup_stmt(); + goto end; + } + +err_open: + DBUG_ASSERT(! (sensitive_cursor && sensitive_cursor->is_open())); + delete sensitive_cursor; + if (result_materialize->table) + free_tmp_table(thd, result_materialize->table); +end: + delete result_materialize; + return rc; +} + +/**************************************************************************** + Server_side_cursor +****************************************************************************/ + +Server_side_cursor::~Server_side_cursor() +{ +} + + +void Server_side_cursor::operator delete(void *ptr, size_t size) +{ + Server_side_cursor *cursor= (Server_side_cursor*) ptr; + MEM_ROOT own_root= *cursor->mem_root; + + DBUG_ENTER("Server_side_cursor::operator delete"); + TRASH(ptr, size); + /* + If this cursor has never been opened mem_root is empty. Otherwise + mem_root points to the memory the cursor object was allocated in. + In this case it's important to call free_root last, and free a copy + instead of *mem_root to avoid writing into freed memory. + */ + free_root(&own_root, MYF(0)); + DBUG_VOID_RETURN; +} + +/**************************************************************************** + Sensitive_cursor +****************************************************************************/ + +Sensitive_cursor::Sensitive_cursor(THD *thd, select_result *result_arg) + :Server_side_cursor(&main_mem_root, result_arg), + stmt_arena(0), + join(0), + close_at_commit(FALSE) +{ + /* We will overwrite it at open anyway. */ + init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0); + thr_lock_owner_init(&lock_id, &thd->lock_info); + bzero((void*) ht_info, sizeof(ht_info)); +} + + +void +Sensitive_cursor::post_open(THD *thd) +{ + Engine_info *info; + /* + We need to save and reset thd->mem_root, otherwise it'll be + freed later in mysql_parse. + + We can't just change thd->mem_root here as we want to keep the + things that are already allocated in thd->mem_root for + Sensitive_cursor::fetch() + */ + *mem_root= *thd->mem_root; + stmt_arena= thd->stmt_arena; + state= stmt_arena->state; + /* Allocate a new memory root for thd */ + init_sql_alloc(thd->mem_root, + thd->variables.query_alloc_block_size, + thd->variables.query_prealloc_size); + + /* + Save tables and zero THD pointers to prevent table close in + close_thread_tables. + */ + derived_tables= thd->derived_tables; + open_tables= thd->open_tables; + lock= thd->lock; + query_id= thd->query_id; + free_list= thd->free_list; + change_list= thd->change_list; + reset_thd(thd); + /* Now we have an active cursor and can cause a deadlock */ + thd->lock_info.n_cursors++; + + close_at_commit= FALSE; /* reset in case we're reusing the cursor */ + info= &ht_info[0]; + for (handlerton **pht= thd->transaction.stmt.ht; *pht; pht++) + { + const handlerton *ht= *pht; + close_at_commit|= test(ht->flags & HTON_CLOSE_CURSORS_AT_COMMIT); + if (ht->create_cursor_read_view) + { + info->ht= ht; + info->read_view= (ht->create_cursor_read_view)(); + ++info; + } + } + /* + XXX: thd->locked_tables is not changed. + What problems can we have with it if cursor is open? + TODO: must be fixed because of the prelocked mode. + */ +} + + +void +Sensitive_cursor::reset_thd(THD *thd) +{ + thd->derived_tables= 0; + thd->open_tables= 0; + thd->lock= 0; + thd->free_list= 0; + thd->change_list.empty(); +} + + +int +Sensitive_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("Sensitive_cursor::open"); + + join->change_result(result); + /* + Send fields description to the client; server_status is sent + in 'EOF' packet, which follows send_fields(). + We don't simply use SEND_EOF flag of send_fields because we also + want to flush the network buffer, which is done only in a standalone + send_eof(). + */ + result->send_fields(*join->fields, Protocol::SEND_NUM_ROWS); + thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; + result->send_eof(); + thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; + + /* Prepare JOIN for reading rows. */ + join->tmp_table= 0; + join->join_tab[join->tables-1].next_select= setup_end_select_func(join); + join->send_records= 0; + 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++) + { + if (tab->next_select == sub_select_cache) + tab->next_select= sub_select; + } + + DBUG_ASSERT(join_tab->table->reginfo.not_exists_optimize == 0); + DBUG_ASSERT(join_tab->not_used_in_distinct == 0); + /* + null_row is set only if row not found and it's outer join: should never + happen for the first table in join_tab list + */ + DBUG_ASSERT(join_tab->table->null_row == 0); + DBUG_RETURN(0); +} + + +/* + SYNOPSIS + Sensitive_cursor::fetch() + num_rows fetch up to this number of rows (maybe less) + + DESCRIPTION + Fetch next num_rows rows from the cursor and send them to the client + + Precondition: + Sensitive_cursor is open + + RETURN VALUES: + none, this function will send OK to the clinet or set an error + message in THD +*/ + +void +Sensitive_cursor::fetch(ulong num_rows) +{ + THD *thd= join->thd; + JOIN_TAB *join_tab= join->join_tab + join->const_tables; + enum_nested_loop_state error= NESTED_LOOP_OK; + Query_arena backup_arena; + Engine_info *info; + DBUG_ENTER("Sensitive_cursor::fetch"); + DBUG_PRINT("enter",("rows: %lu", num_rows)); + + DBUG_ASSERT(thd->derived_tables == 0 && thd->open_tables == 0 && + thd->lock == 0); + + thd->derived_tables= derived_tables; + thd->open_tables= open_tables; + thd->lock= lock; + thd->query_id= query_id; + thd->change_list= change_list; + /* save references to memory allocated during fetch */ + thd->set_n_backup_active_arena(this, &backup_arena); + + for (info= ht_info; info->read_view ; info++) + (info->ht->set_cursor_read_view)(info->read_view); + + join->fetch_limit+= num_rows; + + error= sub_select(join, join_tab, 0); + if (error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS) + error= sub_select(join,join_tab,1); + if (error == NESTED_LOOP_QUERY_LIMIT) + error= NESTED_LOOP_OK; /* select_limit used */ + if (error == NESTED_LOOP_CURSOR_LIMIT) + join->resume_nested_loop= TRUE; + +#ifdef USING_TRANSACTIONS + ha_release_temporary_latches(thd); +#endif + /* Grab free_list here to correctly free it in close */ + thd->restore_active_arena(this, &backup_arena); + + change_list= thd->change_list; + reset_thd(thd); + + for (info= ht_info; info->read_view; info++) + (info->ht->set_cursor_read_view)(0); + + if (error == NESTED_LOOP_CURSOR_LIMIT) + { + /* Fetch limit worked, possibly more rows are there */ + thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; + result->send_eof(); + thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; + } + else + { + close(); + if (error == NESTED_LOOP_OK) + { + thd->server_status|= SERVER_STATUS_LAST_ROW_SENT; + result->send_eof(); + thd->server_status&= ~SERVER_STATUS_LAST_ROW_SENT; + } + else if (error != NESTED_LOOP_KILLED) + my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); + } + DBUG_VOID_RETURN; +} + + +void +Sensitive_cursor::close() +{ + THD *thd= join->thd; + DBUG_ENTER("Sensitive_cursor::close"); + + for (Engine_info *info= ht_info; info->read_view; info++) + { + (info->ht->close_cursor_read_view)(info->read_view); + info->read_view= 0; + info->ht= 0; + } + + thd->change_list= change_list; + { + /* + XXX: Another hack: we need to set THD state as if in a fetch to be + able to call stmt close. + */ + DBUG_ASSERT(lock || open_tables || derived_tables); + + TABLE *tmp_derived_tables= thd->derived_tables; + MYSQL_LOCK *tmp_lock= thd->lock; + + thd->open_tables= open_tables; + thd->derived_tables= derived_tables; + thd->lock= lock; + + /* Is expected to at least close tables and empty thd->change_list */ + stmt_arena->cleanup_stmt(); + + thd->open_tables= tmp_derived_tables; + thd->derived_tables= tmp_derived_tables; + thd->lock= tmp_lock; + } + thd->lock_info.n_cursors--; /* Decrease the number of active cursors */ + join= 0; + stmt_arena= 0; + free_items(); + change_list.empty(); + DBUG_VOID_RETURN; +} + + +Sensitive_cursor::~Sensitive_cursor() +{ + if (is_open()) + close(); +} + +/*************************************************************************** + Materialized_cursor +****************************************************************************/ + +Materialized_cursor::Materialized_cursor(select_result *result_arg, + TABLE *table_arg) + :Server_side_cursor(&table_arg->mem_root, result_arg), + table(table_arg), + fetch_limit(0), + fetch_count(0) +{ + fake_unit.init_query(); + fake_unit.thd= table->in_use; +} + + +int Materialized_cursor::open(JOIN *join __attribute__((unused))) +{ + THD *thd= fake_unit.thd; + int rc; + Query_arena backup_arena; + + thd->set_n_backup_active_arena(this, &backup_arena); + /* Create a list of fields and start sequential scan */ + rc= (table->fill_item_list(&item_list) || + result->prepare(item_list, &fake_unit) || + table->file->ha_rnd_init(TRUE)); + thd->restore_active_arena(this, &backup_arena); + return rc; +} + + +/* + Fetch up to the given number of rows from a materialized cursor. + + DESCRIPTION + Precondition: the cursor is open. + + If the cursor points after the last row, the fetch will automatically + close the cursor and not send any data (except the 'EOF' packet + with SERVER_STATUS_LAST_ROW_SENT). This is an extra round trip + and probably should be improved to return + SERVER_STATUS_LAST_ROW_SENT along with the last row. + + RETURN VALUE + none, in case of success the row is sent to the client, otherwise + an error message is set in THD +*/ + +void Materialized_cursor::fetch(ulong num_rows) +{ + THD *thd= table->in_use; + + int res= 0; + for (fetch_limit+= num_rows; fetch_count < fetch_limit; fetch_count++) + { + if ((res= table->file->rnd_next(table->record[0]))) + break; + /* Send data only if the read was successful. */ + result->send_data(item_list); + } + + switch (res) { + case 0: + thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; + result->send_eof(); + thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; + break; + case HA_ERR_END_OF_FILE: + thd->server_status|= SERVER_STATUS_LAST_ROW_SENT; + result->send_eof(); + thd->server_status&= ~SERVER_STATUS_LAST_ROW_SENT; + close(); + break; + default: + table->file->print_error(res, MYF(0)); + close(); + break; + } +} + + +void Materialized_cursor::close() +{ + /* Free item_list items */ + free_items(); + (void) table->file->ha_rnd_end(); + /* + We need to grab table->mem_root to prevent free_tmp_table from freeing: + the cursor object was allocated in this memory. + */ + main_mem_root= table->mem_root; + mem_root= &main_mem_root; + clear_alloc_root(&table->mem_root); + free_tmp_table(table->in_use, table); + table= 0; +} + + +Materialized_cursor::~Materialized_cursor() +{ + if (is_open()) + close(); +} + + +/*************************************************************************** + Select_materialize +****************************************************************************/ + +bool Select_materialize::send_fields(List<Item> &list, uint flags) +{ + bool rc; + DBUG_ASSERT(table == 0); + if (create_result_table(unit->thd, unit->get_unit_column_types(), + FALSE, thd->options | TMP_TABLE_ALL_COLUMNS, "")) + return TRUE; + /* + We can't simply supply SEND_EOF flag to send_fields, because send_fields + doesn't flush the network buffer. + */ + rc= result->send_fields(list, Protocol::SEND_NUM_ROWS); + thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; + result->send_eof(); + thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; + return rc; +} + diff --git a/sql/sql_cursor.h b/sql/sql_cursor.h new file mode 100644 index 00000000000..d1156dfba8d --- /dev/null +++ b/sql/sql_cursor.h @@ -0,0 +1,65 @@ +#ifndef _sql_cursor_h_ +#define _sql_cursor_h_ +/* Copyright (C) 2005 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 USE_PRAGMA_INTERFACE +#pragma interface /* gcc class interface */ +#endif + +/* + Declarations for implementation of server side cursors. Only + read-only non-scrollable cursors are currently implemented. +*/ + +/* + Server_side_cursor -- an interface for materialized and + sensitive (non-materialized) implementation of cursors. All + cursors are self-contained (created in their own memory root). + For that reason they must be deleted only using a pointer to + Server_side_cursor, not to its base class. +*/ + +class Server_side_cursor: protected Query_arena, public Sql_alloc +{ +protected: + /* Row destination used for fetch */ + select_result *result; +public: + Server_side_cursor(MEM_ROOT *mem_root_arg, select_result *result_arg) + :Query_arena(mem_root_arg, INITIALIZED), result(result_arg) + {} + + virtual bool is_open() const= 0; + + virtual int open(JOIN *top_level_join)= 0; + virtual void fetch(ulong num_rows)= 0; + virtual void close()= 0; + virtual ~Server_side_cursor(); + + static void operator delete(void *ptr, size_t size); +}; + + +int mysql_open_cursor(THD *thd, uint flags, + select_result *result, + Server_side_cursor **res); + +/* Possible values for flags */ + +enum { ANY_CURSOR= 1, ALWAYS_MATERIALIZED_CURSOR= 2 }; + +#endif /* _sql_cusor_h_ */ diff --git a/sql/sql_db.cc b/sql/sql_db.cc index e8fb8d9ce16..a5dabc8140c 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -1094,6 +1094,7 @@ bool mysql_change_db(THD *thd, const char *name, bool no_access_check) bool system_db= 0; #ifndef NO_EMBEDDED_ACCESS_CHECKS ulong db_access; + Security_context *sctx= thd->security_ctx; #endif DBUG_ENTER("mysql_change_db"); DBUG_PRINT("enter",("name: '%s'",name)); @@ -1131,22 +1132,20 @@ bool mysql_change_db(THD *thd, const char *name, bool no_access_check) #ifndef NO_EMBEDDED_ACCESS_CHECKS if (!no_access_check) { - if (test_all_bits(thd->master_access,DB_ACLS)) + if (test_all_bits(sctx->master_access, DB_ACLS)) db_access=DB_ACLS; else - db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) | - thd->master_access); + db_access= (acl_get(sctx->host, sctx->ip, sctx->priv_user, dbname, 0) | + sctx->master_access); if (!(db_access & DB_ACLS) && (!grant_option || check_grant_db(thd,dbname))) { my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), - thd->priv_user, - thd->priv_host, + sctx->priv_user, + sctx->priv_host, dbname); - mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR), - thd->priv_user, - thd->priv_host, - dbname); + mysql_log.write(thd, COM_INIT_DB, ER(ER_DBACCESS_DENIED_ERROR), + sctx->priv_user, sctx->priv_host, dbname); my_free(dbname,MYF(0)); DBUG_RETURN(1); } @@ -1168,7 +1167,7 @@ end: thd->db_length=db_length; #ifndef NO_EMBEDDED_ACCESS_CHECKS if (!no_access_check) - thd->db_access=db_access; + sctx->db_access= db_access; #endif if (system_db) { diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 00d82bcdfda..584601fe202 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -266,19 +266,12 @@ cleanup: delete select; transactional_table= table->file->has_transactions(); - /* - We write to the binary log even if we deleted no row, because maybe the - user is using this command to ensure that a table is clean on master *and - on slave*. Think of the case of a user having played separately with the - master's table and slave's table and wanting to take a fresh identical - start now. - error < 0 means "really no error". error <= 0 means "maybe some error". - */ - if ((deleted || (error < 0)) && (error <= 0 || !transactional_table)) + /* See similar binlogging code in sql_update.cc, for comments */ + if ((error < 0) || (deleted && !transactional_table)) { if (mysql_bin_log.is_open()) { - if (error <= 0) + if (error < 0) thd->clear_error(); Query_log_event qinfo(thd, thd->query, thd->query_length, transactional_table, FALSE); @@ -328,6 +321,7 @@ bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) SELECT_LEX *select_lex= &thd->lex->select_lex; DBUG_ENTER("mysql_prepare_delete"); + thd->allow_sum_func= 0; if (setup_tables(thd, &thd->lex->select_lex.context, &thd->lex->select_lex.top_join_list, table_list, conds, &select_lex->leaf_tables, @@ -736,6 +730,9 @@ bool multi_delete::send_eof() /* Does deletes for the last n - 1 tables, returns 0 if ok */ int local_error= do_deletes(); // returns 0 if success + /* compute a total error to know if something failed */ + local_error= local_error || error; + /* reset used flags */ thd->proc_info="end"; @@ -748,19 +745,11 @@ bool multi_delete::send_eof() query_cache_invalidate3(thd, delete_tables, 1); } - /* - Write the SQL statement to the binlog if we deleted - rows and we succeeded, or also in an error case when there - was a non-transaction-safe table involved, since - modifications in it cannot be rolled back. - Note that if we deleted nothing we don't write to the binlog (TODO: - fix this). - */ - if (deleted && ((error <= 0 && !local_error) || normal_tables)) + if ((local_error == 0) || (deleted && normal_tables)) { if (mysql_bin_log.is_open()) { - if (error <= 0 && !local_error) + if (local_error == 0) thd->clear_error(); Query_log_event qinfo(thd, thd->query, thd->query_length, transactional_tables, FALSE); @@ -816,7 +805,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) TABLE *table= *table_ptr; table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK); db_type table_type= table->s->db_type; - if (!ha_supports_generate(table_type)) + if (!ha_check_storage_engine_flag(table_type, HTON_CAN_RECREATE)) goto trunc_by_del; strmov(path, table->s->path); *table_ptr= table->next; // Unlink table from list @@ -847,7 +836,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) table_list->db, table_list->table_name); DBUG_RETURN(TRUE); } - if (!ha_supports_generate(table_type) || thd->lex->sphead) + if (!ha_check_storage_engine_flag(table_type, HTON_CAN_RECREATE) + || thd->lex->sphead) goto trunc_by_del; if (lock_and_wait_for_table_name(thd, table_list)) DBUG_RETURN(TRUE); diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 7b9191cd841..74b239e1637 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -27,7 +27,7 @@ /* - call given derived table processor (preparing or filling tables) + Call given derived table processor (preparing or filling tables) SYNOPSIS mysql_handle_derived() @@ -36,7 +36,6 @@ RETURN 0 ok - -1 Error 1 Error and error message given */ @@ -97,14 +96,14 @@ out: RETURN 0 ok - 1 Error - -1 Error and error message given - */ + 1 Error and an error message was given +*/ int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list) { SELECT_LEX_UNIT *unit= orig_table_list->derived; int res= 0; + ulonglong create_options; DBUG_ENTER("mysql_derived_prepare"); if (unit) { @@ -118,21 +117,18 @@ int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list) for (SELECT_LEX *sl= first_select; sl; sl= sl->next_select()) sl->context.outer_context= 0; - if (!(derived_result= new select_union(0))) + if (!(derived_result= new select_union)) DBUG_RETURN(1); // out of memory // st_select_lex_unit::prepare correctly work for single select - if ((res= unit->prepare(thd, derived_result, 0, orig_table_list->alias))) + if ((res= unit->prepare(thd, derived_result, 0))) goto exit; - if (check_duplicate_names(unit->types, 0)) - { - res= -1; + if ((res= check_duplicate_names(unit->types, 0))) goto exit; - } - derived_result->tmp_table_param.init(); - derived_result->tmp_table_param.field_count= unit->types.elements; + create_options= (first_select->options | thd->options | + TMP_TABLE_ALL_COLUMNS); /* Temp table is created so that it hounours if UNION without ALL is to be processed @@ -143,18 +139,12 @@ int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list) !unit->union_distinct->next_select() (i.e. it is union and last distinct SELECT is last SELECT of UNION). */ - if (!(table= create_tmp_table(thd, &derived_result->tmp_table_param, - unit->types, (ORDER*) 0, - FALSE, 1, - (first_select->options | thd->options | - TMP_TABLE_ALL_COLUMNS), - HA_POS_ERROR, - orig_table_list->alias))) - { - res= -1; + if ((res= derived_result->create_result_table(thd, &unit->types, FALSE, + create_options, + orig_table_list->alias))) goto exit; - } - derived_result->set_table(table); + + table= derived_result->table; exit: /* Hide "Unknown column" or "Unknown function" error */ @@ -231,9 +221,8 @@ exit: RETURN 0 ok - 1 Error - -1 Error and error message given - */ + 1 Error and an error message was given +*/ int mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *orig_table_list) { diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 3d7ca8059b9..d1122cf2ac4 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -285,7 +285,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, By default, both logs are enabled (this won't cause problems if the server runs without --log-update or --log-bin). */ - bool log_on= (thd->options & OPTION_BIN_LOG) || (!(thd->master_access & SUPER_ACL)); + bool log_on= (thd->options & OPTION_BIN_LOG) || + (!(thd->security_ctx->master_access & SUPER_ACL)); bool transactional_table; uint value_count; ulong counter = 1; @@ -1277,8 +1278,8 @@ public: table(0),tables_in_use(0),stacked_inserts(0), status(0), dead(0), group_count(0) { - thd.user=thd.priv_user=(char*) delayed_user; - thd.host=(char*) my_localhost; + thd.security_ctx->user=thd.security_ctx->priv_user=(char*) delayed_user; + thd.security_ctx->host=(char*) my_localhost; thd.current_tablenr=0; thd.version=refresh_version; thd.command=COM_DELAYED_INSERT; @@ -1288,7 +1289,7 @@ public: bzero((char*) &thd.net, sizeof(thd.net)); // Safety bzero((char*) &table_list, sizeof(table_list)); // Safety thd.system_thread= SYSTEM_THREAD_DELAYED_INSERT; - thd.host_or_ip= ""; + thd.security_ctx->host_or_ip= ""; bzero((char*) &info,sizeof(info)); pthread_mutex_init(&mutex,MY_MUTEX_INIT_FAST); pthread_cond_init(&cond,NULL); @@ -1311,7 +1312,7 @@ public: pthread_cond_destroy(&cond_client); thd.unlink(); // Must be unlinked under lock x_free(thd.query); - thd.user=thd.host=0; + thd.security_ctx->user= thd.security_ctx->host=0; thread_count--; delayed_insert_threads--; VOID(pthread_mutex_unlock(&LOCK_thread_count)); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 381d6573a09..7640b6eb569 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -54,7 +54,7 @@ enum enum_sql_command { SQLCOM_SHOW_DATABASES, SQLCOM_SHOW_TABLES, SQLCOM_SHOW_FIELDS, SQLCOM_SHOW_KEYS, SQLCOM_SHOW_VARIABLES, SQLCOM_SHOW_LOGS, SQLCOM_SHOW_STATUS, - SQLCOM_SHOW_INNODB_STATUS, SQLCOM_SHOW_MUTEX_STATUS, + SQLCOM_SHOW_INNODB_STATUS, SQLCOM_SHOW_NDBCLUSTER_STATUS, SQLCOM_SHOW_MUTEX_STATUS, SQLCOM_SHOW_PROCESSLIST, SQLCOM_SHOW_MASTER_STAT, SQLCOM_SHOW_SLAVE_STAT, SQLCOM_SHOW_GRANTS, SQLCOM_SHOW_CREATE, SQLCOM_SHOW_CHARSETS, SQLCOM_SHOW_COLLATIONS, SQLCOM_SHOW_CREATE_DB, SQLCOM_SHOW_TABLE_STATUS, @@ -433,10 +433,6 @@ public: { return my_reinterpret_cast(st_select_lex*)(slave); } - st_select_lex* first_select_in_union() - { - return my_reinterpret_cast(st_select_lex*)(slave); - } st_select_lex_unit* next_unit() { return my_reinterpret_cast(st_select_lex_unit*)(next); @@ -446,8 +442,7 @@ public: void exclude_tree(); /* UNION methods */ - bool prepare(THD *thd, select_result *result, ulong additional_options, - const char *tmp_table_alias); + bool prepare(THD *thd, select_result *result, ulong additional_options); bool exec(); bool cleanup(); inline void unclean() { cleaned= 0; } @@ -463,7 +458,10 @@ public: friend void lex_start(THD *thd, uchar *buf, uint length); friend int subselect_union_engine::exec(); + + List<Item> *get_unit_column_types(); }; + typedef class st_select_lex_unit SELECT_LEX_UNIT; /* diff --git a/sql/sql_list.h b/sql/sql_list.h index e4a34cc0aa1..285f1d6e501 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -32,6 +32,8 @@ public: { 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 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); } diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 8eec970b4df..895065241f4 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -147,6 +147,10 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, MYF(0)); DBUG_RETURN(TRUE); } + /* + This needs to be done before external_lock + */ + ha_enable_transaction(thd, FALSE); if (open_and_lock_tables(thd, table_list)) DBUG_RETURN(TRUE); if (setup_tables(thd, &thd->lex->select_lex.context, @@ -363,7 +367,6 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if (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; @@ -383,10 +386,10 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, *enclosed, skip_lines, ignore); 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; } + ha_enable_transaction(thd, TRUE); if (file >= 0) my_close(file,MYF(0)); free_blobs(table); /* if pack_blob was used */ @@ -552,6 +555,8 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, while ((sql_field= (Item_field*) it++)) { Field *field= sql_field->field; + if (field == table->next_number_field) + table->auto_increment_field_not_null= TRUE; /* No fields specified in fields_vars list can be null in this format. Mark field as not null, we should do this for each row because of @@ -570,6 +575,8 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, { uint length; byte save_chr; + if (field == table->next_number_field) + table->auto_increment_field_not_null= TRUE; if ((length=(uint) (read_info.row_end-pos)) > field->field_length) length=field->field_length; @@ -692,6 +699,8 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, Field *field= ((Item_field *)item)->field; field->reset(); field->set_null(); + if (field == table->next_number_field) + table->auto_increment_field_not_null= TRUE; if (!field->maybe_null()) { if (field->type() == FIELD_TYPE_TIMESTAMP) @@ -709,9 +718,12 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, if (item->type() == Item::FIELD_ITEM) { + Field *field= ((Item_field *)item)->field; field->set_notnull(); read_info.row_end[0]=0; // Safe to change end marker + if (field == table->next_number_field) + table->auto_increment_field_not_null= TRUE; field->store((char*) pos, length, read_info.read_charset); } else diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index d9f5c178370..48dc8f68707 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -26,6 +26,10 @@ #include "ha_innodb.h" #endif +#ifdef HAVE_NDBCLUSTER_DB +#include "ha_ndbcluster.h" +#endif + #include "sp_head.h" #include "sp.h" #include "sp_cache.h" @@ -131,6 +135,12 @@ static bool end_active_trans(THD *thd) my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); DBUG_RETURN(1); } + if (thd->transaction.xid_state.xa_state != XA_NOTR) + { + my_error(ER_XAER_RMFAIL, MYF(0), + xa_state_names[thd->transaction.xid_state.xa_state]); + DBUG_RETURN(1); + } if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN | OPTION_TABLE_LOCK)) { @@ -180,13 +190,8 @@ static bool begin_trans(THD *thd) */ inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables) { - return (rpl_filter->is_on() && tables && - !(thd->spcont || rpl_filter->tables_ok(thd->db, tables)) && - ((thd->lex->sql_command != SQLCOM_DELETE_MULTI) || - !(thd->spcont || - rpl_filter->tables_ok(thd->db, - (TABLE_LIST *) - thd->lex->auxilliary_table_list.first)))); + return rpl_filter->is_on() && tables && + !rpl_filter->tables_ok(thd->db, tables); } #endif @@ -249,7 +254,7 @@ end: SYNOPSIS check_user() - thd thread handle, thd->{host,user,ip} are used + thd thread handle, thd->security_ctx->{host,user,ip} are used command originator of the check: now check_user is called during connect and change user procedures; used for logging. @@ -264,8 +269,8 @@ end: are 'IN'. RETURN VALUE - 0 OK; thd->user, thd->master_access, thd->priv_user, thd->db and - thd->db_access are updated; OK is sent to client; + 0 OK; thd->security_ctx->user/master_access/priv_user/db_access and + thd->db are updated; OK is sent to client; -1 access denied or handshake error; error is sent to client; >0 error, not sent to client */ @@ -277,7 +282,7 @@ int check_user(THD *thd, enum enum_server_command command, DBUG_ENTER("check_user"); #ifdef NO_EMBEDDED_ACCESS_CHECKS - thd->master_access= GLOBAL_ACLS; // Full rights + thd->main_security_ctx.master_access= GLOBAL_ACLS; // Full rights /* Change database if necessary */ if (db && db[0]) { @@ -344,15 +349,17 @@ int check_user(THD *thd, enum enum_server_command command, if (opt_secure_auth_local) { net_printf_error(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE, - thd->user, thd->host_or_ip); + thd->main_security_ctx.user, + thd->main_security_ctx.host_or_ip); mysql_log.write(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE), - thd->user, thd->host_or_ip); + thd->main_security_ctx.user, + thd->main_security_ctx.host_or_ip); DBUG_RETURN(-1); } /* We have to read very specific packet size */ if (send_old_password_request(thd) || my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) - { + { inc_host_errors(&thd->remote.sin_addr); DBUG_RETURN(ER_HANDSHAKE_ERROR); } @@ -364,22 +371,27 @@ int check_user(THD *thd, enum enum_server_command command, /* here res is always >= 0 */ if (res == 0) { - if (!(thd->master_access & NO_ACCESS)) // authentication is OK + if (!(thd->main_security_ctx.master_access & + NO_ACCESS)) // authentication is OK { DBUG_PRINT("info", ("Capabilities: %d packet_length: %ld Host: '%s' " "Login user: '%s' Priv_user: '%s' Using password: %s " "Access: %u db: '%s'", - thd->client_capabilities, thd->max_client_packet_length, - thd->host_or_ip, thd->user, thd->priv_user, + thd->client_capabilities, + thd->max_client_packet_length, + thd->main_security_ctx.host_or_ip, + thd->main_security_ctx.user, + thd->main_security_ctx.priv_user, passwd_len ? "yes": "no", - thd->master_access, thd->db ? thd->db : "*none*")); + thd->main_security_ctx.master_access, + (thd->db ? thd->db : "*none*"))); if (check_count) { VOID(pthread_mutex_lock(&LOCK_thread_count)); bool count_ok= thread_count <= max_connections + delayed_insert_threads - || (thd->master_access & SUPER_ACL); + || (thd->main_security_ctx.master_access & SUPER_ACL); VOID(pthread_mutex_unlock(&LOCK_thread_count)); if (!count_ok) { // too many connections @@ -389,11 +401,13 @@ int check_user(THD *thd, enum enum_server_command command, } /* Why logging is performed before all checks've passed? */ - mysql_log.write(thd,command, - (thd->priv_user == thd->user ? + mysql_log.write(thd, command, + (thd->main_security_ctx.priv_user == + thd->main_security_ctx.user ? (char*) "%s@%s on %s" : (char*) "%s@%s as anonymous on %s"), - thd->user, thd->host_or_ip, + thd->main_security_ctx.user, + thd->main_security_ctx.host_or_ip, db ? db : (char*) ""); /* @@ -401,14 +415,16 @@ int check_user(THD *thd, enum enum_server_command command, set to 0 here because we don't have an active database yet (and we may not have an active database to set. */ - thd->db_access=0; + thd->main_security_ctx.db_access=0; /* Don't allow user to connect if he has done too many queries */ if ((ur.questions || ur.updates || ur.conn_per_hour || ur.user_conn || max_user_connections) && get_or_create_user_conn(thd, - opt_old_style_user_limits ? thd->user : thd->priv_user, - opt_old_style_user_limits ? thd->host_or_ip : thd->priv_host, + (opt_old_style_user_limits ? thd->main_security_ctx.user : + thd->main_security_ctx.priv_user), + (opt_old_style_user_limits ? thd->main_security_ctx.host_or_ip : + thd->main_security_ctx.priv_host), &ur)) DBUG_RETURN(-1); if (thd->user_connect && @@ -443,12 +459,12 @@ int check_user(THD *thd, enum enum_server_command command, DBUG_RETURN(-1); } net_printf_error(thd, ER_ACCESS_DENIED_ERROR, - thd->user, - thd->host_or_ip, + thd->main_security_ctx.user, + thd->main_security_ctx.host_or_ip, passwd_len ? ER(ER_YES) : ER(ER_NO)); mysql_log.write(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR), - thd->user, - thd->host_or_ip, + thd->main_security_ctx.user, + thd->main_security_ctx.host_or_ip, passwd_len ? ER(ER_YES) : ER(ER_NO)); DBUG_RETURN(-1); #endif /* NO_EMBEDDED_ACCESS_CHECKS */ @@ -772,41 +788,45 @@ static int check_connection(THD *thd) DBUG_PRINT("info", ("New connection received on %s", vio_description(net->vio))); - if (!thd->host) // If TCP/IP connection + if (!thd->main_security_ctx.host) // If TCP/IP connection { char ip[30]; if (vio_peer_addr(net->vio, ip, &thd->peer_port)) return (ER_BAD_HOST_ERROR); - if (!(thd->ip= my_strdup(ip,MYF(0)))) + if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(0)))) return (ER_OUT_OF_RESOURCES); - thd->host_or_ip= thd->ip; + thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip; vio_in_addr(net->vio,&thd->remote.sin_addr); if (!(specialflag & SPECIAL_NO_RESOLVE)) { vio_in_addr(net->vio,&thd->remote.sin_addr); - thd->host=ip_to_hostname(&thd->remote.sin_addr,&connect_errors); + thd->main_security_ctx.host= + ip_to_hostname(&thd->remote.sin_addr, &connect_errors); /* Cut very long hostnames to avoid possible overflows */ - if (thd->host) + if (thd->main_security_ctx.host) { - if (thd->host != my_localhost) - thd->host[min(strlen(thd->host), HOSTNAME_LENGTH)]= 0; - thd->host_or_ip= thd->host; + if (thd->main_security_ctx.host != my_localhost) + thd->main_security_ctx.host[min(strlen(thd->main_security_ctx.host), + HOSTNAME_LENGTH)]= 0; + thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host; } if (connect_errors > max_connect_errors) return(ER_HOST_IS_BLOCKED); } DBUG_PRINT("info",("Host: %s ip: %s", - thd->host ? thd->host : "unknown host", - thd->ip ? thd->ip : "unknown ip")); - if (acl_check_host(thd->host,thd->ip)) + (thd->main_security_ctx.host ? + thd->main_security_ctx.host : "unknown host"), + (thd->main_security_ctx.ip ? + thd->main_security_ctx.ip : "unknown ip"))); + if (acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip)) return(ER_HOST_NOT_PRIVILEGED); } else /* Hostname given means that the connection was on a socket */ { - DBUG_PRINT("info",("Host: %s",thd->host)); - thd->host_or_ip= thd->host; - thd->ip= 0; + DBUG_PRINT("info",("Host: %s", thd->main_security_ctx.host)); + thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host; + thd->main_security_ctx.ip= 0; /* Reset sin_addr */ bzero((char*) &thd->remote, sizeof(thd->remote)); } @@ -989,9 +1009,9 @@ static int check_connection(THD *thd) thd->charset(), &dummy_errors)]= '\0'; user= user_buff; - if (thd->user) - x_free(thd->user); - if (!(thd->user= my_strdup(user, MYF(0)))) + if (thd->main_security_ctx.user) + x_free(thd->main_security_ctx.user); + if (!(thd->main_security_ctx.user= my_strdup(user, MYF(0)))) return (ER_OUT_OF_RESOURCES); return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE); } @@ -1079,13 +1099,14 @@ pthread_handler_decl(handle_one_connection,arg) { int error; NET *net= &thd->net; + Security_context *sctx= thd->security_ctx; thd->thread_stack= (char*) &thd; net->no_send_error= 0; if ((error=check_connection(thd))) { // Wrong permissions if (error > 0) - net_printf_error(thd, error, thd->host_or_ip); + net_printf_error(thd, error, sctx->host_or_ip); #ifdef __NT__ if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE) my_sleep(1000); /* must wait after eof() */ @@ -1094,7 +1115,7 @@ pthread_handler_decl(handle_one_connection,arg) goto end_thread; } #ifdef __NETWARE__ - netware_reg_user(thd->ip, thd->user, "MySQL"); + netware_reg_user(sctx->ip, sctx->user, "MySQL"); #endif if (thd->variables.max_join_size == HA_POS_ERROR) thd->options |= OPTION_BIG_SELECTS; @@ -1107,7 +1128,7 @@ pthread_handler_decl(handle_one_connection,arg) thd->set_time(); thd->init_for_queries(); - if (sys_init_connect.value_length && !(thd->master_access & SUPER_ACL)) + if (sys_init_connect.value_length && !(sctx->master_access & SUPER_ACL)) { execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect); if (thd->query_error) @@ -1131,8 +1152,8 @@ pthread_handler_decl(handle_one_connection,arg) if (!thd->killed && thd->variables.log_warnings > 1) 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, + sctx->user ? sctx->user : "unauthenticated", + sctx->host_or_ip, (net->last_errno ? ER(net->last_errno) : ER(ER_UNKNOWN_ERROR))); net_send_error(thd, net->last_errno, NullS); @@ -1195,7 +1216,8 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg) thd->proc_info=0; thd->version=refresh_version; - thd->priv_user=thd->user=(char*) my_strdup("boot", MYF(MY_WME)); + thd->security_ctx->priv_user= + thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME)); buff= (char*) thd->net.buff; thd->init_for_queries(); @@ -1354,6 +1376,12 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion) my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); DBUG_RETURN(1); } + if (thd->transaction.xid_state.xa_state != XA_NOTR) + { + my_error(ER_XAER_RMFAIL, MYF(0), + xa_state_names[thd->transaction.xid_state.xa_state]); + DBUG_RETURN(1); + } switch (completion) { case COMMIT: /* @@ -1590,17 +1618,14 @@ bool dispatch_command(enum enum_server_command command, THD *thd, db= db_buff; /* Save user and privileges */ - uint save_master_access= thd->master_access; - uint save_db_access= thd->db_access; uint save_db_length= thd->db_length; - char *save_user= thd->user; - char *save_priv_user= thd->priv_user; char *save_db= thd->db; + Security_context save_security_ctx= *thd->security_ctx; USER_CONN *save_user_connect= thd->user_connect; - - if (!(thd->user= my_strdup(user, MYF(0)))) + + if (!(thd->security_ctx->user= my_strdup(user, MYF(0)))) { - thd->user= save_user; + thd->security_ctx->user= save_security_ctx.user; my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); break; } @@ -1614,12 +1639,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, /* authentication failure, we shall restore old user */ if (res > 0) my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); - x_free(thd->user); - thd->user= save_user; - thd->priv_user= save_priv_user; + x_free(thd->security_ctx->user); + *thd->security_ctx= save_security_ctx; thd->user_connect= save_user_connect; - thd->master_access= save_master_access; - thd->db_access= save_db_access; thd->db= save_db; thd->db_length= save_db_length; } @@ -1629,7 +1651,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (save_user_connect) decrease_user_connections(save_user_connect); x_free((gptr) save_db); - x_free((gptr) save_user); + x_free((gptr) save_security_ctx.user); } break; } @@ -1971,12 +1993,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_PROCESS_INFO: statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST], &LOCK_status); - if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL)) + if (!thd->security_ctx->priv_user[0] && + check_global_access(thd, PROCESS_ACL)) break; mysql_log.write(thd,command,NullS); mysqld_list_processes(thd, - thd->master_access & PROCESS_ACL ? - NullS : thd->priv_user, 0); + thd->security_ctx->master_access & PROCESS_ACL ? + NullS : thd->security_ctx->priv_user, 0); break; case COM_PROCESS_KILL: { @@ -2144,7 +2167,8 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, if (!thd->col_access && check_grant_db(thd,db)) { my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), - thd->priv_user, thd->priv_host, db); + thd->security_ctx->priv_user, thd->security_ctx->priv_host, + db); DBUG_RETURN(1); } /* @@ -2402,7 +2426,8 @@ mysql_execute_command(THD *thd) Except for the replication thread and the 'super' users. */ if (opt_readonly && - !(thd->slave_thread || (thd->master_access & SUPER_ACL)) && + !(thd->slave_thread || + (thd->security_ctx->master_access & SUPER_ACL)) && uc_update_queries[lex->sql_command]) { my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); @@ -2673,6 +2698,13 @@ mysql_execute_command(THD *thd) res = load_master_data(thd); break; #endif /* HAVE_REPLICATION */ +#ifdef HAVE_NDBCLUSTER_DB + case SQLCOM_SHOW_NDBCLUSTER_STATUS: + { + res = ndbcluster_show_status(thd); + break; + } +#endif #ifdef HAVE_INNOBASE_DB case SQLCOM_SHOW_INNODB_STATUS: { @@ -3381,11 +3413,14 @@ end_with_restore_list: res = mysql_drop_index(thd, first_table, &lex->alter_info); break; case SQLCOM_SHOW_PROCESSLIST: - if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL)) + if (!thd->security_ctx->priv_user[0] && + check_global_access(thd,PROCESS_ACL)) break; mysqld_list_processes(thd, - thd->master_access & PROCESS_ACL ? NullS : - thd->priv_user,lex->verbose); + (thd->security_ctx->master_access & PROCESS_ACL ? + NullS : + thd->security_ctx->priv_user), + lex->verbose); break; case SQLCOM_SHOW_STORAGE_ENGINES: res= mysqld_show_storage_engines(thd); @@ -3723,7 +3758,7 @@ end_with_restore_list: select_lex->db ? is_schema_db(select_lex->db) : 0)) goto error; - if (thd->user) // If not replication + if (thd->security_ctx->user) // If not replication { LEX_USER *user; uint counter; @@ -3739,9 +3774,9 @@ end_with_restore_list: user->host.str); // Are we trying to change a password of another user DBUG_ASSERT(user->host.str != 0); - if (strcmp(thd->user, user->user.str) || + if (strcmp(thd->security_ctx->user, user->user.str) || my_strcasecmp(system_charset_info, - user->host.str, thd->host_or_ip)) + user->host.str, thd->security_ctx->host_or_ip)) { // TODO: use check_change_password() if (check_acl_user(user, &counter) && user->password.str && @@ -3868,8 +3903,8 @@ end_with_restore_list: } #ifndef NO_EMBEDDED_ACCESS_CHECKS case SQLCOM_SHOW_GRANTS: - if ((thd->priv_user && - !strcmp(thd->priv_user,lex->grant_user->user.str)) || + if ((thd->security_ctx->priv_user && + !strcmp(thd->security_ctx->priv_user, lex->grant_user->user.str)) || !check_access(thd, SELECT_ACL, "mysql",0,1,0,0)) { res = mysql_show_grants(thd,lex->grant_user); @@ -3905,6 +3940,12 @@ end_with_restore_list: break; case SQLCOM_BEGIN: + if (thd->transaction.xid_state.xa_state != XA_NOTR) + { + my_error(ER_XAER_RMFAIL, MYF(0), + xa_state_names[thd->transaction.xid_state.xa_state]); + break; + } if (begin_trans(thd)) goto error; send_ok(thd); @@ -4143,7 +4184,7 @@ end_with_restore_list: else { #ifndef NO_EMBEDDED_ACCESS_CHECKS - st_sp_security_context save_ctx; + Security_context *save_ctx; #endif ha_rows select_limit; /* bits that should be cleared in thd->server_status */ @@ -4189,23 +4230,23 @@ end_with_restore_list: } #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (check_routine_access(thd, EXECUTE_ACL, - sp->m_db.str, sp->m_name.str, TRUE, 0)) + if (check_routine_access(thd, EXECUTE_ACL, + sp->m_db.str, sp->m_name.str, TRUE, 0) || + sp_change_security_context(thd, sp, &save_ctx)) { #ifndef EMBEDDED_LIBRARY thd->net.no_send_ok= nsok; #endif goto error; } - sp_change_security_context(thd, sp, &save_ctx); - if (save_ctx.changed && - check_routine_access(thd, EXECUTE_ACL, - sp->m_db.str, sp->m_name.str, TRUE, 0)) + if (save_ctx && + check_routine_access(thd, EXECUTE_ACL, + sp->m_db.str, sp->m_name.str, TRUE, 0)) { #ifndef EMBEDDED_LIBRARY thd->net.no_send_ok= nsok; #endif - sp_restore_security_context(thd, sp, &save_ctx); + sp_restore_security_context(thd, save_ctx); goto error; } @@ -4245,7 +4286,7 @@ end_with_restore_list: thd->variables.select_limit= select_limit; #ifndef NO_EMBEDDED_ACCESS_CHECKS - sp_restore_security_context(thd, sp, &save_ctx); + sp_restore_security_context(thd, save_ctx); #endif #ifndef EMBEDDED_LIBRARY @@ -4477,10 +4518,10 @@ end_with_restore_list: mysql_bin_log.is_open()) { String buff; - LEX_STRING command[3]= - {{STRING_WITH_LEN("CREATE ")}, - {STRING_WITH_LEN("ALTER ")}, - {STRING_WITH_LEN("CREATE OR REPLACE ")}}; + const LEX_STRING command[3]= + {{(char *)STRING_WITH_LEN("CREATE ")}, + {(char *)STRING_WITH_LEN("ALTER ")}, + {(char *)STRING_WITH_LEN("CREATE OR REPLACE ")}}; thd->clear_error(); buff.append(command[thd->lex->create_view_mode].str, @@ -4807,6 +4848,7 @@ bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, bool dont_check_global_grants, bool no_errors, bool schema_db) { + Security_context *sctx= thd->security_ctx; #ifndef NO_EMBEDDED_ACCESS_CHECKS ulong db_access; bool db_is_pattern= test(want_access & GRANT_ACL); @@ -4815,7 +4857,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, const char *db_name; DBUG_ENTER("check_access"); DBUG_PRINT("enter",("db: %s want_access: %lu master_access: %lu", - db ? db : "", want_access, thd->master_access)); + db ? db : "", want_access, sctx->master_access)); if (save_priv) *save_priv=0; else @@ -4837,7 +4879,8 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, { if (!no_errors) my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), - thd->priv_user, thd->priv_host, db_name); + sctx->priv_user, + sctx->priv_host, db_name); DBUG_RETURN(TRUE); } else @@ -4850,28 +4893,29 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, #ifdef NO_EMBEDDED_ACCESS_CHECKS DBUG_RETURN(0); #else - if ((thd->master_access & want_access) == want_access) + if ((sctx->master_access & want_access) == want_access) { /* If we don't have a global SELECT privilege, we have to get the database specific access rights to be able to handle queries of type UPDATE t1 SET a=1 WHERE b > 0 */ - db_access= thd->db_access; - if (!(thd->master_access & SELECT_ACL) && + db_access= sctx->db_access; + if (!(sctx->master_access & SELECT_ACL) && (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))) - db_access=acl_get(thd->host, thd->ip, thd->priv_user, db, db_is_pattern); - *save_priv=thd->master_access | db_access; + db_access=acl_get(sctx->host, sctx->ip, sctx->priv_user, db, + db_is_pattern); + *save_priv=sctx->master_access | db_access; DBUG_RETURN(FALSE); } - if (((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL)) || + if (((want_access & ~sctx->master_access) & ~(DB_ACLS | EXTRA_ACL)) || ! db && dont_check_global_grants) { // We can never grant this DBUG_PRINT("error",("No possible access")); if (!no_errors) my_error(ER_ACCESS_DENIED_ERROR, MYF(0), - thd->priv_user, - thd->priv_host, + sctx->priv_user, + sctx->priv_host, (thd->password ? ER(ER_YES) : ER(ER_NO))); /* purecov: tested */ @@ -4882,15 +4926,16 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, DBUG_RETURN(FALSE); // Allow select on anything if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db))) - db_access=acl_get(thd->host, thd->ip, thd->priv_user, db, db_is_pattern); + db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, + db_is_pattern); else - db_access=thd->db_access; + db_access= sctx->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); + want_access &= ~(sctx->master_access | EXTRA_ACL); DBUG_PRINT("info",("db_access: %lu want_access: %lu", db_access, want_access)); - db_access= ((*save_priv=(db_access | thd->master_access)) & want_access); + db_access= ((*save_priv=(db_access | sctx->master_access)) & want_access); /* grant_option is set if there exists a single table or column grant */ if (db_access == want_access || @@ -4901,8 +4946,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, DBUG_PRINT("error",("Access denied")); if (!no_errors) my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), - thd->priv_user, - thd->priv_host, + sctx->priv_user, sctx->priv_host, (db ? db : (thd->db ? thd->db : "unknown"))); /* purecov: tested */ @@ -4936,7 +4980,7 @@ bool check_global_access(THD *thd, ulong want_access) return 0; #else char command[128]; - if ((thd->master_access & want_access)) + if ((thd->security_ctx->master_access & want_access)) return 0; get_privilege_desc(command, sizeof(command), want_access); my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command); @@ -4964,7 +5008,7 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, { if (!no_errors) my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), - thd->priv_user, thd->priv_host, + thd->security_ctx->priv_user, thd->security_ctx->priv_host, information_schema_name.str); return TRUE; } @@ -4973,7 +5017,8 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, 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) && + if ((thd->security_ctx->master_access & want_access) == + (want_access & ~EXTRA_ACL) && thd->db) tables->grant.privilege= want_access; else if (tables->db && tables->db == thd->db) @@ -5010,7 +5055,8 @@ check_routine_access(THD *thd, ulong want_access,char *db, char *name, tables->db= db; tables->table_name= tables->alias= name; - if ((thd->master_access & want_access) == want_access && !thd->db) + if ((thd->security_ctx->master_access & want_access) == want_access && + !thd->db) tables->grant.privilege= want_access; else if (check_access(thd,want_access,db,&tables->grant.privilege, 0, no_errors, test(tables->schema_table))) @@ -5043,7 +5089,7 @@ bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc) { ulong save_priv; - if (thd->master_access & SHOW_PROC_ACLS) + if (thd->security_ctx->master_access & SHOW_PROC_ACLS) return FALSE; /* There are no routines in information_schema db. So we can safely @@ -5240,6 +5286,7 @@ void mysql_reset_thd_for_next_command(THD *thd) thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED); + DBUG_ASSERT(thd->security_ctx== &thd->main_security_ctx); thd->tmp_table_used= 0; if (!thd->in_sub_stmt) { @@ -5617,8 +5664,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, and so on, the display width is ignored. */ char buf[32]; - my_snprintf(buf, sizeof(buf), - "TIMESTAMP(%s)", length, system_charset_info); + my_snprintf(buf, sizeof(buf), "TIMESTAMP(%s)", length); push_warning_printf(thd,MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), @@ -5724,7 +5770,7 @@ new_create_field(THD *thd, char *field_name, enum_field_types type, } if (new_field->length < new_field->decimals) { - my_error(ER_SCALE_BIGGER_THAN_PRECISION, MYF(0), field_name); + my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name); DBUG_RETURN(NULL); } new_field->length= @@ -5787,19 +5833,31 @@ new_create_field(THD *thd, char *field_name, enum_field_types type, new_field->decimals= NOT_FIXED_DEC; break; } - if (!length) + if (!length && !decimals) { new_field->length = FLT_DIG+6; new_field->decimals= NOT_FIXED_DEC; } + if (new_field->length < new_field->decimals && + new_field->decimals != NOT_FIXED_DEC) + { + my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name); + DBUG_RETURN(NULL); + } break; case FIELD_TYPE_DOUBLE: allowed_type_modifier= AUTO_INCREMENT_FLAG; - if (!length) + if (!length && !decimals) { new_field->length = DBL_DIG+7; new_field->decimals=NOT_FIXED_DEC; } + if (new_field->length < new_field->decimals && + new_field->decimals != NOT_FIXED_DEC) + { + my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name); + DBUG_RETURN(NULL); + } break; case FIELD_TYPE_TIMESTAMP: if (!length) @@ -6764,8 +6822,8 @@ void kill_one_thread(THD *thd, ulong id, bool only_kill_query) VOID(pthread_mutex_unlock(&LOCK_thread_count)); if (tmp) { - if ((thd->master_access & SUPER_ACL) || - !strcmp(thd->user,tmp->user)) + if ((thd->security_ctx->master_access & SUPER_ACL) || + !strcmp(thd->security_ctx->user, tmp->security_ctx->user)) { tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION); error=0; @@ -7182,6 +7240,12 @@ bool multi_delete_set_locks_and_link_aux_tables(LEX *lex) target_tbl->table_name, "MULTI DELETE"); DBUG_RETURN(TRUE); } + if (!walk->derived) + { + target_tbl->table_name= walk->table_name; + target_tbl->table_name_length= walk->table_name_length; + } + walk->updating= target_tbl->updating; walk->lock_type= target_tbl->lock_type; target_tbl->correspondent_table= walk; // Remember corresponding table } @@ -7390,7 +7454,7 @@ Item *negate_expression(THD *thd, Item *expr) SYNOPSIS default_definer() - thd thread handler + Secytity_context current decurity context definer structure where it should be assigned RETURN @@ -7398,14 +7462,14 @@ Item *negate_expression(THD *thd, Item *expr) TRUE Error */ -bool default_view_definer(THD *thd, st_lex_user *definer) +bool default_view_definer(Security_context *sctx, st_lex_user *definer) { - definer->user.str= thd->priv_user; - definer->user.length= strlen(thd->priv_user); - if (*thd->priv_host != 0) + definer->user.str= sctx->priv_user; + definer->user.length= strlen(sctx->priv_user); + if (*sctx->priv_host != 0) { - definer->host.str= thd->priv_host; - definer->host.length= strlen(thd->priv_host); + definer->host.str= sctx->priv_host; + definer->host.length= strlen(sctx->priv_host); } else { diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 4cda4ed108a..16433e42e3c 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -71,6 +71,7 @@ When one supplies long data for a placeholder: #include "mysql_priv.h" #include "sql_select.h" // for JOIN +#include "sql_cursor.h" #include "sp_head.h" #include "sp.h" #include "sp_cache.h" @@ -81,6 +82,18 @@ When one supplies long data for a placeholder: #include <mysql_com.h> #endif +/* A result class used to send cursor rows using the binary protocol. */ + +class Select_fetch_protocol_prep: public select_send +{ + Protocol_prep protocol; +public: + Select_fetch_protocol_prep(THD *thd); + virtual bool send_fields(List<Item> &list, uint flags); + virtual bool send_data(List<Item> &items); + virtual bool send_eof(); +}; + /****************************************************************************** Prepared_statement: a statement that can contain placeholders ******************************************************************************/ @@ -89,6 +102,7 @@ class Prepared_statement: public Statement { public: THD *thd; + Select_fetch_protocol_prep result; Protocol *protocol; Item_param **param_array; uint param_count; @@ -109,8 +123,9 @@ public: virtual ~Prepared_statement(); void setup_set_params(); virtual Query_arena::Type type() const; - virtual void close_cursor(); + virtual void cleanup_stmt(); bool set_name(LEX_STRING *name); + inline void close_cursor() { delete cursor; cursor= 0; } bool prepare(const char *packet, uint packet_length); bool execute(String *expanded_query, bool open_cursor); @@ -140,8 +155,6 @@ inline bool is_param_null(const uchar *pos, ulong param_no) return pos[param_no/8] & (1 << (param_no & 7)); } -enum { STMT_QUERY_LOG_LENGTH= 8192 }; - /* Find a prepared statement in the statement map by id. @@ -1264,7 +1277,7 @@ static int mysql_test_select(Prepared_statement *stmt, It is not SELECT COMMAND for sure, so setup_tables will be called as usual, and we pass 0 as setup_tables_done_option */ - if (unit->prepare(thd, 0, 0, "")) + if (unit->prepare(thd, 0, 0)) goto error; if (!lex->describe && !text_protocol) { @@ -1395,7 +1408,7 @@ static bool select_like_stmt_test(Prepared_statement *stmt, thd->used_tables= 0; // Updated by setup_fields /* Calls JOIN::prepare */ - DBUG_RETURN(lex->unit.prepare(thd, 0, setup_tables_done_option, "")); + DBUG_RETURN(lex->unit.prepare(thd, 0, setup_tables_done_option)); } /* @@ -1785,19 +1798,6 @@ static bool init_param_array(Prepared_statement *stmt) } -/* Cleanup PS after execute/prepare and restore THD state */ - -static void cleanup_stmt_and_thd_after_use(Statement *stmt, THD *thd) -{ - DBUG_ENTER("cleanup_stmt_and_thd_after_use"); - stmt->lex->unit.cleanup(); - cleanup_items(stmt->free_list); - thd->rollback_item_tree_changes(); - thd->cleanup_after_query(); - DBUG_VOID_RETURN; -} - - /* COM_STMT_PREPARE handler. @@ -2129,8 +2129,8 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) lex->result->cleanup(); lex->result->set_thd(thd); } - - DBUG_VOID_RETURN; + thd->allow_sum_func= 0; + DBUG_VOID_RETURN; } @@ -2221,16 +2221,14 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) test(flags & (ulong) CURSOR_TYPE_READ_ONLY)); if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(), WAIT_PRIOR); - if (rc) - goto err; - mysql_log.write(thd, COM_STMT_EXECUTE, "[%lu] %s", stmt->id, thd->query); + if (rc == 0) + mysql_log.write(thd, COM_STMT_EXECUTE, "[%lu] %s", stmt->id, thd->query); DBUG_VOID_RETURN; set_params_data_err: my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute"); -err: reset_stmt_params(stmt); DBUG_VOID_RETURN; } @@ -2285,14 +2283,16 @@ void mysql_sql_stmt_execute(THD *thd) if (stmt->set_params_from_vars(stmt, lex->prepared_stmt_params, &expanded_query)) - { - my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE"); - DBUG_VOID_RETURN; - } + goto set_params_data_err; (void) stmt->execute(&expanded_query, FALSE); DBUG_VOID_RETURN; + +set_params_data_err: + my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE"); + reset_stmt_params(stmt); + DBUG_VOID_RETURN; } @@ -2313,7 +2313,7 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) ulong num_rows= uint4korr(packet+4); Prepared_statement *stmt; Statement stmt_backup; - Cursor *cursor; + Server_side_cursor *cursor; DBUG_ENTER("mysql_stmt_fetch"); statistic_increment(thd->status_var.com_stmt_fetch, &LOCK_status); @@ -2321,7 +2321,7 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) DBUG_VOID_RETURN; cursor= stmt->cursor; - if (!cursor || !cursor->is_open()) + if (!cursor) { my_error(ER_STMT_HAS_NO_OPEN_CURSOR, MYF(0), stmt_id); DBUG_VOID_RETURN; @@ -2333,25 +2333,16 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(), QUERY_PRIOR); - thd->protocol= stmt->protocol; // Switch to binary protocol cursor->fetch(num_rows); - thd->protocol= &thd->protocol_simple; // Use normal protocol if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(), WAIT_PRIOR); if (!cursor->is_open()) { - /* We're done with the fetch: reset PS for next execution */ - cleanup_stmt_and_thd_after_use(stmt, thd); + stmt->close_cursor(); + thd->cursor= 0; reset_stmt_params(stmt); - /* - Must be the last, as some memory is still needed for - the previous calls. - */ - free_root(cursor->mem_root, MYF(0)); - if (cursor->close_at_commit) - thd->stmt_map.erase_transient_cursor(stmt); } thd->restore_backup_statement(stmt, &stmt_backup); @@ -2384,14 +2375,19 @@ void mysql_stmt_reset(THD *thd, char *packet) /* There is always space for 4 bytes in buffer */ ulong stmt_id= uint4korr(packet); Prepared_statement *stmt; - Cursor *cursor; DBUG_ENTER("mysql_stmt_reset"); statistic_increment(thd->status_var.com_stmt_reset, &LOCK_status); if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_reset"))) DBUG_VOID_RETURN; - stmt->close_cursor(); /* will reset statement params */ + stmt->close_cursor(); + + /* + Clear parameters from data which could be set by + mysql_stmt_send_long_data() call. + */ + reset_stmt_params(stmt); stmt->state= Query_arena::PREPARED; @@ -2533,11 +2529,65 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length) } +/*************************************************************************** + Select_fetch_protocol_prep +****************************************************************************/ + +Select_fetch_protocol_prep::Select_fetch_protocol_prep(THD *thd) + :protocol(thd) +{} + +bool Select_fetch_protocol_prep::send_fields(List<Item> &list, uint flags) +{ + bool rc; + Protocol *save_protocol= thd->protocol; + + /* + Protocol::send_fields caches the information about column types: + this information is later used to send data. Therefore, the same + dedicated Protocol object must be used for all operations with + a cursor. + */ + thd->protocol= &protocol; + rc= select_send::send_fields(list, flags); + thd->protocol= save_protocol; + + return rc; +} + +bool Select_fetch_protocol_prep::send_eof() +{ + Protocol *save_protocol= thd->protocol; + + thd->protocol= &protocol; + ::send_eof(thd); + thd->protocol= save_protocol; + return FALSE; +} + + +bool +Select_fetch_protocol_prep::send_data(List<Item> &fields) +{ + Protocol *save_protocol= thd->protocol; + bool rc; + + thd->protocol= &protocol; + rc= select_send::send_data(fields); + thd->protocol= save_protocol; + return rc; +} + +/*************************************************************************** + Prepared_statement +****************************************************************************/ + Prepared_statement::Prepared_statement(THD *thd_arg, Protocol *protocol_arg) :Statement(INITIALIZED, ++thd_arg->statement_id_counter, thd_arg->variables.query_alloc_block_size, thd_arg->variables.query_prealloc_size), thd(thd_arg), + result(thd_arg), protocol(protocol_arg), param_array(0), param_count(0), @@ -2585,17 +2635,7 @@ Prepared_statement::~Prepared_statement() { DBUG_ENTER("Prepared_statement::~Prepared_statement"); DBUG_PRINT("enter",("stmt: %p cursor: %p", this, cursor)); - if (cursor) - { - if (cursor->is_open()) - { - cursor->close(FALSE); - cleanup_items(free_list); - thd->rollback_item_tree_changes(); - free_root(cursor->mem_root, MYF(0)); - } - cursor->Cursor::~Cursor(); - } + delete cursor; /* We have to call free on the items even if cleanup is called as some items, like Item_param, don't free everything until free_items() @@ -2612,25 +2652,18 @@ Query_arena::Type Prepared_statement::type() const } -void Prepared_statement::close_cursor() +void Prepared_statement::cleanup_stmt() { - DBUG_ENTER("Prepared_statement::close_cursor"); + DBUG_ENTER("Prepared_statement::cleanup_stmt"); DBUG_PRINT("enter",("stmt: %p", this)); - if (cursor && cursor->is_open()) - { - thd->change_list= cursor->change_list; - cursor->close(FALSE); - cleanup_stmt_and_thd_after_use(this, thd); - free_root(cursor->mem_root, MYF(0)); - if (cursor->close_at_commit) - thd->stmt_map.erase_transient_cursor(this); - } - /* - Clear parameters from data which could be set by - mysql_stmt_send_long_data() call. - */ - reset_stmt_params(this); + /* The order is important */ + lex->unit.cleanup(); + cleanup_items(free_list); + thd->cleanup_after_query(); + close_thread_tables(thd); + thd->rollback_item_tree_changes(); + DBUG_VOID_RETURN; } @@ -2734,14 +2767,13 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) if (rc == 0) rc= check_prepared_statement(this, name.str != 0); - if (rc && thd->lex->sphead) + if (rc && lex->sphead) { - delete thd->lex->sphead; - thd->lex->sphead= NULL; + delete lex->sphead; + lex->sphead= NULL; } lex_end(lex); - close_thread_tables(thd); - cleanup_stmt_and_thd_after_use(this, thd); + cleanup_stmt(); thd->restore_backup_statement(this, &stmt_backup); thd->stmt_arena= old_stmt_arena; @@ -2781,7 +2813,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) Statement stmt_backup; Query_arena *old_stmt_arena; Item *old_free_list; - bool rc= 1; + bool rc= TRUE; statistic_increment(thd->status_var.com_stmt_execute, &LOCK_status); @@ -2789,18 +2821,35 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) if (state == Query_arena::ERROR) { my_message(last_errno, last_error, MYF(0)); - return 1; + return TRUE; } if (flags & IS_IN_USE) { my_error(ER_PS_NO_RECURSION, MYF(0)); - return 1; + return TRUE; } + + /* + For SHOW VARIABLES lex->result is NULL, as it's a non-SELECT + command. For such queries we don't return an error and don't + open a cursor -- the client library will recognize this case and + materialize the result set. + For SELECT statements lex->result is created in + check_prepared_statement. lex->result->simple_select() is FALSE + in INSERT ... SELECT and similar commands. + */ + + if (open_cursor && lex->result && !lex->result->simple_select()) + { + DBUG_PRINT("info",("Cursor asked for not SELECT stmt")); + my_error(ER_SP_BAD_CURSOR_QUERY, MYF(0)); + return TRUE; + } + /* In case the command has a call to SP which re-uses this statement name */ flags|= IS_IN_USE; - if (cursor && cursor->is_open()) - close_cursor(); + close_cursor(); /* If the free_list is not empty, we'll wrongly free some externally @@ -2808,32 +2857,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) */ DBUG_ASSERT(thd->change_list.is_empty()); DBUG_ASSERT(thd->free_list == NULL); - if (open_cursor) - { - if (!lex->result || !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. - */ - my_error(ER_SP_BAD_CURSOR_QUERY, MYF(0)); - goto error; - } - - DBUG_PRINT("info",("Using READ_ONLY cursor")); - if (!cursor && !(cursor= new (mem_root) Cursor(thd))) - goto error; - /* If lex->result is set, mysql_execute_command will use it */ - lex->result= &cursor->result; - protocol= &cursor->protocol; - thd->lock_id= &cursor->lock_id; - /* - Currently cursors can be used only from C API, so - we don't have to create an own memory root for them: - the one in THD is clean and can be used. - */ - } thd->set_n_backup_statement(this, &stmt_backup); if (expanded_query->length() && alloc_query(thd, (char*) expanded_query->ptr(), @@ -2862,38 +2885,27 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) reinit_stmt_before_use(thd, lex); thd->protocol= protocol; /* activate stmt protocol */ - mysql_execute_command(thd); + rc= open_cursor ? mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR, + &result, &cursor) : + mysql_execute_command(thd); thd->protocol= &thd->protocol_simple; /* use normal protocol */ - if (cursor && cursor->is_open()) - { - /* - It's safer if we grab THD state after mysql_execute_command is - finished and not in Cursor::open(), because currently the call to - Cursor::open is buried deep in JOIN::exec of the top level join. - */ - cursor->init_from_thd(thd); + /* Assert that if an error, no cursor is open */ + DBUG_ASSERT(! (rc && cursor)); - if (cursor->close_at_commit) - thd->stmt_map.add_transient_cursor(this); - } - else + if (! cursor) { - close_thread_tables(thd); - cleanup_stmt_and_thd_after_use(this, thd); + cleanup_stmt(); reset_stmt_params(this); } thd->set_statement(&stmt_backup); - thd->lock_id= &thd->main_lock_id; thd->stmt_arena= old_stmt_arena; if (state == Query_arena::PREPARED) state= Query_arena::EXECUTED; - rc= 0; error: - thd->lock_id= &thd->main_lock_id; flags&= ~IS_IN_USE; return rc; } diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 3880aa428b9..154e828b47e 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -133,11 +133,12 @@ static TABLE_LIST * rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) { TABLE_LIST *ren_table,*new_table; + frm_type_enum frm_type; DBUG_ENTER("rename_tables"); for (ren_table= table_list; ren_table; ren_table= new_table->next_local) { - db_type table_type; + int rc= 1; char name[FN_REFLEN]; const char *new_alias, *old_alias; @@ -164,19 +165,36 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) ren_table->db, old_alias, reg_ext); unpack_filename(name, name); - if ((table_type=get_table_type(thd, name)) == DB_TYPE_UNKNOWN) - { - my_error(ER_FILE_NOT_FOUND, MYF(0), name, my_errno); - if (!skip_error) - DBUG_RETURN(ren_table); - } - else if (mysql_rename_table(table_type, - ren_table->db, old_alias, - new_table->db, new_alias)) + + frm_type= mysql_frm_type(name); + switch (frm_type) { - if (!skip_error) - DBUG_RETURN(ren_table); + case FRMTYPE_TABLE: + { + db_type table_type; + if ((table_type= get_table_type(thd, name)) == DB_TYPE_UNKNOWN) + my_error(ER_FILE_NOT_FOUND, MYF(0), name, my_errno); + else + rc= mysql_rename_table(table_type, ren_table->db, old_alias, + new_table->db, new_alias); + break; + } + case FRMTYPE_VIEW: + /* change of schema is not allowed */ + if (strcmp(ren_table->db, new_table->db)) + my_error(ER_FORBID_SCHEMA_CHANGE, MYF(0), ren_table->db, + new_table->db); + else + rc= mysql_rename_view(thd, new_alias, ren_table); + break; + default: + DBUG_ASSERT(0); // should never happen + case FRMTYPE_ERROR: + my_error(ER_FILE_NOT_FOUND, MYF(0), name, my_errno); + break; } + if (rc && !skip_error) + DBUG_RETURN(ren_table); } DBUG_RETURN(0); } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b8f9d30faab..a02a61b7a86 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -23,6 +23,7 @@ #include "mysql_priv.h" #include "sql_select.h" +#include "sql_cursor.h" #include <m_ctype.h> #include <hash.h> @@ -107,20 +108,15 @@ static bool const_expression_in_where(COND *conds,Item *item, Item **comp_item); static bool open_tmp_table(TABLE *table); static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param, ulong options); -static Next_select_func setup_end_select_func(JOIN *join); static int do_select(JOIN *join,List<Item> *fields,TABLE *tmp_table, Procedure *proc); static enum_nested_loop_state -sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool end_of_records); -static enum_nested_loop_state evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, int error, my_bool *report_error); static enum_nested_loop_state evaluate_null_complemented_join_record(JOIN *join, JOIN_TAB *join_tab); static enum_nested_loop_state -sub_select(JOIN *join,JOIN_TAB *join_tab, bool end_of_records); -static enum_nested_loop_state flush_cached_records(JOIN *join, JOIN_TAB *join_tab, bool skip_last); static enum_nested_loop_state end_send(JOIN *join, JOIN_TAB *join_tab, bool end_of_records); @@ -1728,262 +1724,6 @@ JOIN::destroy() DBUG_RETURN(error); } - -/************************* Cursor ******************************************/ - -Cursor::Cursor(THD *thd) - :Query_arena(&main_mem_root, INITIALIZED), - join(0), unit(0), - protocol(thd), - close_at_commit(FALSE) -{ - /* We will overwrite it at open anyway. */ - init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0); - thr_lock_owner_init(&lock_id, &thd->lock_info); - bzero((void*) ht_info, sizeof(ht_info)); -} - - -void -Cursor::init_from_thd(THD *thd) -{ - Engine_info *info; - /* - We need to save and reset thd->mem_root, otherwise it'll be freed - later in mysql_parse. - - We can't just change the thd->mem_root here as we want to keep the - things that are already allocated in thd->mem_root for Cursor::fetch() - */ - main_mem_root= *thd->mem_root; - state= thd->stmt_arena->state; - /* Allocate new memory root for thd */ - init_sql_alloc(thd->mem_root, - thd->variables.query_alloc_block_size, - thd->variables.query_prealloc_size); - - /* - The same is true for open tables and lock: save tables and zero THD - pointers to prevent table close in close_thread_tables (This is a part - of the temporary solution to make cursors work with minimal changes to - the current source base). - */ - derived_tables= thd->derived_tables; - open_tables= thd->open_tables; - lock= thd->lock; - query_id= thd->query_id; - free_list= thd->free_list; - change_list= thd->change_list; - reset_thd(thd); - /* Now we have an active cursor and can cause a deadlock */ - thd->lock_info.n_cursors++; - - close_at_commit= FALSE; /* reset in case we're reusing the cursor */ - info= &ht_info[0]; - for (handlerton **pht= thd->transaction.stmt.ht; *pht; pht++) - { - const handlerton *ht= *pht; - close_at_commit|= test(ht->flags & HTON_CLOSE_CURSORS_AT_COMMIT); - if (ht->create_cursor_read_view) - { - info->ht= ht; - info->read_view= (ht->create_cursor_read_view)(); - ++info; - } - } - /* - XXX: thd->locked_tables is not changed. - What problems can we have with it if cursor is open? - TODO: must be fixed because of the prelocked mode. - */ -} - - -void -Cursor::reset_thd(THD *thd) -{ - thd->derived_tables= 0; - thd->open_tables= 0; - thd->lock= 0; - thd->free_list= 0; - thd->change_list.empty(); -} - - -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 - in 'EOF' packet, which ends send_fields(). - */ - thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; - join->result->send_fields(*join->fields, Protocol::SEND_NUM_ROWS); - ::send_eof(thd); - thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; - - /* Prepare JOIN for reading rows. */ - join->tmp_table= 0; - join->join_tab[join->tables-1].next_select= setup_end_select_func(join); - join->send_records= 0; - 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++) - { - if (tab->next_select == sub_select_cache) - tab->next_select= sub_select; - } - - DBUG_ASSERT(join_tab->table->reginfo.not_exists_optimize == 0); - DBUG_ASSERT(join_tab->not_used_in_distinct == 0); - /* - null_row is set only if row not found and it's outer join: should never - happen for the first table in join_tab list - */ - DBUG_ASSERT(join_tab->table->null_row == 0); - DBUG_RETURN(0); -} - - -/* - DESCRIPTION - Fetch next num_rows rows from the cursor and sent them to the client - PRECONDITION: - Cursor is open - RETURN VALUES: - none, this function will send error or OK to network if necessary. -*/ - -void -Cursor::fetch(ulong num_rows) -{ - THD *thd= join->thd; - JOIN_TAB *join_tab= join->join_tab + join->const_tables; - enum_nested_loop_state error= NESTED_LOOP_OK; - Query_arena backup_arena; - Engine_info *info; - DBUG_ENTER("Cursor::fetch"); - DBUG_PRINT("enter",("rows: %lu", num_rows)); - - DBUG_ASSERT(thd->derived_tables == 0 && thd->open_tables == 0 && - thd->lock == 0); - - thd->derived_tables= derived_tables; - thd->open_tables= open_tables; - thd->lock= lock; - thd->query_id= query_id; - thd->change_list= change_list; - /* save references to memory, allocated during fetch */ - thd->set_n_backup_active_arena(this, &backup_arena); - - for (info= ht_info; info->read_view ; info++) - (info->ht->set_cursor_read_view)(info->read_view); - - join->fetch_limit+= num_rows; - - error= sub_select(join, join_tab, 0); - if (error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS) - error= sub_select(join,join_tab,1); - if (error == NESTED_LOOP_QUERY_LIMIT) - error= NESTED_LOOP_OK; /* select_limit used */ - if (error == NESTED_LOOP_CURSOR_LIMIT) - join->resume_nested_loop= TRUE; - -#ifdef USING_TRANSACTIONS - ha_release_temporary_latches(thd); -#endif - /* Grab free_list here to correctly free it in close */ - thd->restore_active_arena(this, &backup_arena); - - for (info= ht_info; info->read_view; info++) - (info->ht->set_cursor_read_view)(0); - - if (error == NESTED_LOOP_CURSOR_LIMIT) - { - /* Fetch limit worked, possibly more rows are there */ - thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; - ::send_eof(thd); - thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; - change_list= thd->change_list; - reset_thd(thd); - } - else - { - close(TRUE); - if (error == NESTED_LOOP_OK) - { - thd->server_status|= SERVER_STATUS_LAST_ROW_SENT; - ::send_eof(thd); - thd->server_status&= ~SERVER_STATUS_LAST_ROW_SENT; - } - else if (error != NESTED_LOOP_KILLED) - my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); - } - DBUG_VOID_RETURN; -} - - -void -Cursor::close(bool is_active) -{ - THD *thd= join->thd; - DBUG_ENTER("Cursor::close"); - - /* - In case of UNIONs JOIN is freed inside of unit->cleanup(), - otherwise in select_lex->cleanup(). - */ - if (unit) - (void) unit->cleanup(); - else - (void) join->select_lex->cleanup(); - - for (Engine_info *info= ht_info; info->read_view; info++) - { - (info->ht->close_cursor_read_view)(info->read_view); - info->read_view= 0; - info->ht= 0; - } - - if (is_active) - close_thread_tables(thd); - else - { - /* XXX: Another hack: closing tables used in the cursor */ - DBUG_ASSERT(lock || open_tables || derived_tables); - - TABLE *tmp_derived_tables= thd->derived_tables; - MYSQL_LOCK *tmp_lock= thd->lock; - - thd->open_tables= open_tables; - thd->derived_tables= derived_tables; - thd->lock= lock; - close_thread_tables(thd); - - thd->open_tables= tmp_derived_tables; - thd->derived_tables= tmp_derived_tables; - thd->lock= tmp_lock; - } - thd->lock_info.n_cursors--; /* Decrease the number of active cursors */ - join= 0; - unit= 0; - free_items(); - change_list.empty(); - DBUG_VOID_RETURN; -} - - -/*********************************************************************/ - /* An entry point to single-unit select (a select without UNION). @@ -2063,9 +1803,9 @@ mysql_select(THD *thd, Item ***rref_pointer_array, } else { - if (join->prepare(rref_pointer_array, tables, wild_num, - conds, og_num, order, group, having, proc_param, - select_lex, unit)) + if (err= join->prepare(rref_pointer_array, tables, wild_num, + conds, og_num, order, group, having, proc_param, + select_lex, unit)) { goto err; } @@ -2080,9 +1820,9 @@ mysql_select(THD *thd, Item ***rref_pointer_array, DBUG_RETURN(TRUE); thd->proc_info="init"; thd->used_tables=0; // Updated by setup_fields - if (join->prepare(rref_pointer_array, tables, wild_num, - conds, og_num, order, group, having, proc_param, - select_lex, unit)) + if (err= join->prepare(rref_pointer_array, tables, wild_num, + conds, og_num, order, group, having, proc_param, + select_lex, unit)) { goto err; } @@ -2124,7 +1864,7 @@ err: if (free_join) { thd->proc_info="end"; - err= select_lex->cleanup(); + err|= select_lex->cleanup(); DBUG_RETURN(err || thd->net.report_error); } DBUG_RETURN(join->error); @@ -2658,7 +2398,6 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, field Field used in comparision eq_func True if we used =, <=> or IS NULL value Value used for comparison with field - Is NULL for BETWEEN and IN usable_tables Tables which can be used for key optimization NOTES @@ -2682,6 +2421,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, !field->table->maybe_null || field->null_ptr) return; // Not a key. Skip it exists_optimize= KEY_OPTIMIZE_EXISTS; + DBUG_ASSERT(num_values == 1); } else { @@ -2733,7 +2473,26 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, eq_func is NEVER true when num_values > 1 */ if (!eq_func) - return; + { + /* + Additional optimization: if we're processing + "t.key BETWEEN c1 AND c1" then proceed as if we were processing + "t.key = c1". + TODO: This is a very limited fix. A more generic fix is possible. + There are 2 options: + A) Make equality propagation code be able to handle BETWEEN + (including cases like t1.key BETWEEN t2.key AND t3.key) + B) Make range optimizer to infer additional "t.key = c" equalities + and use them in equality propagation process (see details in + OptimizerKBAndTodo) + */ + if ((cond->functype() == Item_func::BETWEEN) && + value[0]->eq(value[1], field->binary())) + eq_func= TRUE; + else + return; + } + if (field->result_type() == STRING_RESULT) { if ((*value)->result_type() != STRING_RESULT) @@ -2760,7 +2519,6 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, } } } - DBUG_ASSERT(num_values == 1); /* For the moment eq_func is always true. This slot is reserved for future extensions where we want to remembers other things than just eq comparisons @@ -5991,7 +5749,7 @@ void JOIN::join_free(bool full) cleanup(full); for (unit= select_lex->first_inner_unit(); unit; unit= unit->next_unit()) - for (sl= unit->first_select_in_union(); sl; sl= sl->next_select()) + for (sl= unit->first_select(); sl; sl= sl->next_select()) { JOIN *join= sl->join; if (join) @@ -8092,18 +7850,24 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, if (field->maybe_null && !field->field->maybe_null()) { result= create_tmp_field_from_item(thd, item, table, NULL, - modify_item, convert_blob_length); + modify_item, convert_blob_length); *from_field= field->field; if (result && modify_item) - ((Item_field*)item)->result_field= result; + field->result_field= result; } - else if (table_cant_handle_bit_fields && field->field->type() == FIELD_TYPE_BIT) + else if (table_cant_handle_bit_fields && field->field->type() == + FIELD_TYPE_BIT) + { + *from_field= field->field; result= create_tmp_field_from_item(thd, item, table, copy_func, modify_item, convert_blob_length); + if (result && modify_item) + field->result_field= result; + } else result= create_tmp_field_from_field(thd, (*from_field= field->field), item->name, table, - modify_item ? (Item_field*) item : + modify_item ? field : NULL, convert_blob_length); if (orig_type == Item::REF_ITEM && orig_modify) @@ -8137,9 +7901,31 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, /* Create a temp table according to a field list. - Set distinct if duplicates could be removed - Given fields field pointers are changed to point at tmp_table - for send_fields + + SYNOPSIS + create_tmp_table() + thd thread handle + param a description used as input to create the table + fields list of items that will be used to define + column types of the table (also see NOTES) + group TODO document + distinct should table rows be distinct + save_sum_fields see NOTES + select_options + rows_limit + table_alias possible name of the temporary table that can be used + for name resolving; can be "". + + DESCRIPTION + Given field pointers are changed to point at tmp_table for + send_fields. The table object is self contained: it's + allocated in its own memory root, as well as Field objects + created for table columns. + This function will replace Item_sum items in 'fields' list with + corresponding Item_field items, pointing at the fields in the + temporary table, unless this was prohibited by TRUE + value of argument save_sum_fields. The Item_field objects + are created in THD memory root. */ #define STRING_TOTAL_LENGTH_TO_PACK_ROWS 128 @@ -8153,6 +7939,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, ulonglong select_options, ha_rows rows_limit, char *table_alias) { + MEM_ROOT *mem_root_save, own_root; TABLE *table; uint i,field_count,null_count,null_pack_length; uint hidden_null_count, hidden_null_pack_length, hidden_field_count; @@ -8217,29 +8004,33 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, field_count=param->field_count+param->func_count+param->sum_func_count; hidden_field_count=param->hidden_field_count; - if (!my_multi_malloc(MYF(MY_WME), - &table,sizeof(*table), - ®_field, sizeof(Field*)*(field_count+1), - &blob_field, sizeof(uint)*(field_count+1), - &from_field, sizeof(Field*)*field_count, - ©_func,sizeof(*copy_func)*(param->func_count+1), - ¶m->keyinfo,sizeof(*param->keyinfo), - &key_part_info, - sizeof(*key_part_info)*(param->group_parts+1), - ¶m->start_recinfo, - sizeof(*param->recinfo)*(field_count*2+4), - &tmpname,(uint) strlen(path)+1, - &group_buff,group && ! using_unique_constraint ? - param->group_length : 0, - NullS)) + + init_sql_alloc(&own_root, TABLE_ALLOC_BLOCK_SIZE, 0); + + if (!multi_alloc_root(&own_root, + &table, sizeof(*table), + ®_field, sizeof(Field*) * (field_count+1), + &blob_field, sizeof(uint)*(field_count+1), + &from_field, sizeof(Field*)*field_count, + ©_func, sizeof(*copy_func)*(param->func_count+1), + ¶m->keyinfo, sizeof(*param->keyinfo), + &key_part_info, + sizeof(*key_part_info)*(param->group_parts+1), + ¶m->start_recinfo, + sizeof(*param->recinfo)*(field_count*2+4), + &tmpname, (uint) strlen(path)+1, + &group_buff, group && ! using_unique_constraint ? + param->group_length : 0, + NullS)) { bitmap_lock_clear_bit(&temp_pool, temp_pool_slot); DBUG_RETURN(NULL); /* purecov: inspected */ } - if (!(param->copy_field=copy=new Copy_field[field_count])) + /* Copy_field belongs to TMP_TABLE_PARAM, allocate it in THD mem_root */ + if (!(param->copy_field= copy= new (thd->mem_root) Copy_field[field_count])) { bitmap_lock_clear_bit(&temp_pool, temp_pool_slot); - my_free((gptr) table,MYF(0)); /* purecov: inspected */ + free_root(&own_root, MYF(0)); /* purecov: inspected */ DBUG_RETURN(NULL); /* purecov: inspected */ } param->items_to_copy= copy_func; @@ -8249,6 +8040,11 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, bzero((char*) table,sizeof(*table)); bzero((char*) reg_field,sizeof(Field*)*(field_count+1)); bzero((char*) from_field,sizeof(Field*)*field_count); + + table->mem_root= own_root; + mem_root_save= thd->mem_root; + thd->mem_root= &table->mem_root; + table->field=reg_field; table->alias= table_alias; table->reginfo.lock_type=TL_WRITE; /* Will be updated */ @@ -8315,7 +8111,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, Field *new_field= create_tmp_field(thd, table, arg, arg->type(), ©_func, tmp_from_field, group != 0,not_all_columns, - group || distinct, + distinct, param->convert_blob_length); if (!new_field) goto err; // Should be OOM @@ -8326,6 +8122,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, *blob_field++= (uint) (reg_field - table->field); blob_count++; } + if (new_field->type() == FIELD_TYPE_BIT) + total_uneven_bit_length+= new_field->field_length & 7; new_field->field_index= (uint) (reg_field - table->field); *(reg_field++)= new_field; if (new_field->real_type() == MYSQL_TYPE_STRING || @@ -8334,7 +8132,9 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, string_count++; string_total_length+= new_field->pack_length(); } + thd->mem_root= mem_root_save; thd->change_item_tree(argp, new Item_field(new_field)); + thd->mem_root= &table->mem_root; if (!(new_field->flags & NOT_NULL_FLAG)) { null_count++; @@ -8360,12 +8160,16 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, write rows to the temporary table. We here distinguish between UNION and multi-table-updates by the fact that in the later case group is set to the row pointer. + + The test for item->marker == 4 is ensure we don't create a group-by + key over a bit field as heap tables can't handle that. */ Field *new_field= (param->schema_table) ? create_tmp_field_for_schema(thd, item, table) : create_tmp_field(thd, table, item, type, ©_func, tmp_from_field, group != 0, - not_all_columns || group != 0, 0, + not_all_columns || group != 0, + item->marker == 4, param->convert_blob_length); if (!new_field) @@ -8398,7 +8202,14 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, *(reg_field++) =new_field; } if (!--hidden_field_count) + { + /* + This was the last hidden field; Remember how many hidden fields could + have null + */ hidden_null_count=null_count; + null_count= 0; + } } DBUG_ASSERT(field_count >= (uint) (reg_field - table->field)); field_count= (uint) (reg_field - table->field); @@ -8438,8 +8249,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, null_count++; } hidden_null_pack_length=(hidden_null_count+7)/8; - null_pack_length= hidden_null_count + - (null_count + total_uneven_bit_length + 7) / 8; + null_pack_length= (hidden_null_pack_length + + (null_count + total_uneven_bit_length + 7) / 8); reclength+=null_pack_length; if (!reclength) reclength=1; // Dummy select @@ -8454,7 +8265,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, { uint alloc_length=ALIGN_SIZE(reclength+MI_UNIQUE_HASH_LENGTH+1); table->s->rec_buff_length= alloc_length; - if (!(table->record[0]= (byte *) my_malloc(alloc_length*3, MYF(MY_WME)))) + if (!(table->record[0]= (byte*) + alloc_root(&table->mem_root, alloc_length*3))) goto err; table->record[1]= table->record[0]+alloc_length; table->s->default_values= table->record[1]+alloc_length; @@ -8640,8 +8452,10 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, table->s->uniques= 1; } if (!(key_part_info= (KEY_PART_INFO*) - sql_calloc((keyinfo->key_parts)*sizeof(KEY_PART_INFO)))) + alloc_root(&table->mem_root, + keyinfo->key_parts * sizeof(KEY_PART_INFO)))) goto err; + bzero((void*) key_part_info, keyinfo->key_parts * sizeof(KEY_PART_INFO)); table->key_info=keyinfo; keyinfo->key_part=key_part_info; keyinfo->flags=HA_NOSAME | HA_NULL_ARE_EQUAL; @@ -8689,10 +8503,15 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, if (create_myisam_tmp_table(table,param,select_options)) goto err; } - if (!open_tmp_table(table)) - DBUG_RETURN(table); + if (open_tmp_table(table)) + goto err; - err: + thd->mem_root= mem_root_save; + + DBUG_RETURN(table); + +err: + thd->mem_root= mem_root_save; free_tmp_table(thd,table); /* purecov: inspected */ bitmap_lock_clear_bit(&temp_pool, temp_pool_slot); DBUG_RETURN(NULL); /* purecov: inspected */ @@ -8837,11 +8656,12 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param, if (table->s->keys) { // Get keys for ni_create bool using_unique_constraint=0; - HA_KEYSEG *seg= (HA_KEYSEG*) sql_calloc(sizeof(*seg) * - keyinfo->key_parts); + HA_KEYSEG *seg= (HA_KEYSEG*) alloc_root(&table->mem_root, + sizeof(*seg) * keyinfo->key_parts); if (!seg) goto err; + bzero(seg, sizeof(*seg) * keyinfo->key_parts); if (keyinfo->key_length >= table->file->max_key_length() || keyinfo->key_parts > table->file->max_key_parts() || table->s->uniques) @@ -8938,13 +8758,14 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param, void free_tmp_table(THD *thd, TABLE *entry) { + MEM_ROOT own_root= entry->mem_root; const char *save_proc_info; DBUG_ENTER("free_tmp_table"); DBUG_PRINT("enter",("table: %s",entry->alias)); save_proc_info=thd->proc_info; thd->proc_info="removing tmp table"; - free_blobs(entry); + if (entry->file) { if (entry->db_stat) @@ -8965,12 +8786,11 @@ free_tmp_table(THD *thd, TABLE *entry) /* free blobs */ for (Field **ptr=entry->field ; *ptr ; ptr++) (*ptr)->free(); - my_free((gptr) entry->record[0],MYF(0)); free_io_cache(entry); bitmap_lock_clear_bit(&temp_pool, entry->temp_pool_slot); - my_free((gptr) entry,MYF(0)); + free_root(&own_root, MYF(0)); /* the table is allocated in its own root */ thd->proc_info=save_proc_info; DBUG_VOID_RETURN; @@ -9090,7 +8910,7 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param, end_select function to use. This function can't fail. */ -static Next_select_func setup_end_select_func(JOIN *join) +Next_select_func setup_end_select_func(JOIN *join) { TABLE *table= join->tmp_table; Next_select_func end_select; @@ -9245,7 +9065,7 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) } -static enum_nested_loop_state +enum_nested_loop_state sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) { enum_nested_loop_state rc; @@ -9386,7 +9206,7 @@ sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) return one of enum_nested_loop_state, except NESTED_LOOP_NO_MORE_ROWS. */ -static enum_nested_loop_state +enum_nested_loop_state sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) { join_tab->table->null_row=0; @@ -12409,6 +12229,11 @@ calc_group_buffer(JOIN *join,ORDER *group) key_length+=MAX_BLOB_WIDTH; // Can't be used as a key else if (field->type() == MYSQL_TYPE_VARCHAR) key_length+= field->field_length + HA_KEY_BLOB_LENGTH; + else if (field->type() == FIELD_TYPE_BIT) + { + /* Bit is usually stored as a longlong key for group fields */ + key_length+= 8; // Big enough + } else key_length+= field->pack_length(); } @@ -13813,8 +13638,7 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result) unit->fake_select_lex->select_number= UINT_MAX; // jost for initialization unit->fake_select_lex->type= "UNION RESULT"; unit->fake_select_lex->options|= SELECT_DESCRIBE; - if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE, - ""))) + if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE))) res= unit->exec(); res|= unit->cleanup(); } diff --git a/sql/sql_select.h b/sql/sql_select.h index ce40f657a8e..be51851e760 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -101,7 +101,7 @@ enum enum_nested_loop_state typedef enum_nested_loop_state (*Next_select_func)(JOIN *, struct st_join_table *, bool); typedef int (*Read_record_func)(struct st_join_table *tab); - +Next_select_func setup_end_select_func(JOIN *join); typedef struct st_join_table { TABLE *table; @@ -141,6 +141,11 @@ typedef struct st_join_table { void cleanup(); } JOIN_TAB; +enum_nested_loop_state sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool + end_of_records); +enum_nested_loop_state sub_select(JOIN *join,JOIN_TAB *join_tab, bool + end_of_records); + typedef struct st_position /* Used in find_best */ { @@ -373,58 +378,6 @@ class JOIN :public Sql_alloc }; -/* - Server-side cursor (now stands only for basic read-only cursor) - See class implementation in sql_select.cc - A cursor has its own runtime state - list of used items and memory root of - used memory - which is different from Prepared statement runtime: it must - be different at least for the purpose of reusing the same prepared - statement for many cursors. -*/ - -class Cursor: public Sql_alloc, public Query_arena -{ - MEM_ROOT main_mem_root; - JOIN *join; - SELECT_LEX_UNIT *unit; - - TABLE *open_tables; - MYSQL_LOCK *lock; - TABLE *derived_tables; - /* List of items created during execution */ - query_id_t query_id; - struct Engine_info - { - const handlerton *ht; - void *read_view; - }; - Engine_info ht_info[MAX_HA]; -public: - Protocol_prep protocol; - Item_change_list change_list; - select_send result; - THR_LOCK_OWNER lock_id; - my_bool close_at_commit; - - /* Temporary implementation as now we replace THD state by value */ - /* Save THD state into cursor */ - void init_from_thd(THD *thd); - /* bzero cursor state in THD */ - void reset_thd(THD *thd); - - int open(JOIN *join); - void fetch(ulong num_rows); - void reset() { join= 0; } - bool is_open() const { return join != 0; } - - void close(bool is_active); - - void set_unit(SELECT_LEX_UNIT *unit_arg) { unit= unit_arg; } - Cursor(THD *thd); - ~Cursor() {} -}; - - typedef struct st_select_check { uint const_ref,reg_ref; } SELECT_CHECK; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index c0bb29e035b..24acb1fb6d1 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -67,18 +67,18 @@ bool mysqld_show_storage_engines(THD *thd) const char *default_type_name= ha_get_storage_engine((enum db_type)thd->variables.table_type); - show_table_type_st *types; - for (types= sys_table_types; types->type; types++) + handlerton **types; + for (types= sys_table_types; *types; types++) { protocol->prepare_for_resend(); - protocol->store(types->type, system_charset_info); - const char *option_name= show_comp_option_name[(int) *types->value]; + protocol->store((*types)->name, system_charset_info); + const char *option_name= show_comp_option_name[(int) (*types)->state]; - if (*types->value == SHOW_OPTION_YES && - !my_strcasecmp(system_charset_info, default_type_name, types->type)) + if ((*types)->state == SHOW_OPTION_YES && + !my_strcasecmp(system_charset_info, default_type_name, (*types)->name)) option_name= "DEFAULT"; protocol->store(option_name, system_charset_info); - protocol->store(types->comment, system_charset_info); + protocol->store((*types)->comment, system_charset_info); if (protocol->write()) DBUG_RETURN(TRUE); } @@ -415,8 +415,9 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) bool mysqld_show_create_db(THD *thd, char *dbname, HA_CREATE_INFO *create_info) { + Security_context *sctx= thd->security_ctx; int length; - char path[FN_REFLEN]; + char path[FN_REFLEN]; char buff[2048]; String buffer(buff, sizeof(buff), system_charset_info); #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -435,17 +436,17 @@ bool mysqld_show_create_db(THD *thd, char *dbname, } #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (test_all_bits(thd->master_access,DB_ACLS)) + if (test_all_bits(sctx->master_access, DB_ACLS)) db_access=DB_ACLS; else - db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) | - thd->master_access); + db_access= (acl_get(sctx->host, sctx->ip, sctx->priv_user, dbname, 0) | + sctx->master_access); if (!(db_access & DB_ACLS) && (!grant_option || check_grant_db(thd,dbname))) { my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), - thd->priv_user, thd->host_or_ip, dbname); + sctx->priv_user, sctx->host_or_ip, dbname); mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR), - thd->priv_user, thd->host_or_ip, dbname); + sctx->priv_user, sctx->host_or_ip, dbname); DBUG_RETURN(TRUE); } #endif @@ -793,7 +794,8 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet) field->sql_type(type); packet->append(type.ptr(), type.length(), system_charset_info); - if (field->has_charset() && !limited_mysql_mode && !foreign_db_mode) + if (field->has_charset() && + !(thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40))) { if (field->charset() != share->table_charset) { @@ -832,7 +834,7 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet) has_default= (field->type() != FIELD_TYPE_BLOB && !(field->flags & NO_DEFAULT_VALUE_FLAG) && field->unireg_check != Field::NEXT_NUMBER && - !((foreign_db_mode || limited_mysql_mode) && + !((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) && has_now_default)); if (has_default) @@ -862,12 +864,13 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet) packet->append(tmp); } - if (!foreign_db_mode && !limited_mysql_mode && + if (!(thd->variables.sql_mode & MODE_NO_FIELD_OPTIONS) && table->timestamp_field == field && field->unireg_check != Field::TIMESTAMP_DN_FIELD) packet->append(" on update CURRENT_TIMESTAMP",28); - if (field->unireg_check == Field::NEXT_NUMBER && !foreign_db_mode) + if (field->unireg_check == Field::NEXT_NUMBER && + !(thd->variables.sql_mode & MODE_NO_FIELD_OPTIONS)) packet->append(" auto_increment", 15 ); if (field->comment.length) @@ -1210,24 +1213,26 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) THD *tmp; while ((tmp=it++)) { + Security_context *tmp_sctx= tmp->security_ctx; struct st_my_thread_var *mysys_var; if ((tmp->vio_ok() || tmp->system_thread) && - (!user || (tmp->user && !strcmp(tmp->user,user)))) + (!user || (tmp_sctx->user && !strcmp(tmp_sctx->user, user)))) { - thread_info *thd_info=new thread_info; + thread_info *thd_info= new thread_info; thd_info->thread_id=tmp->thread_id; - thd_info->user=thd->strdup(tmp->user ? tmp->user : - (tmp->system_thread ? - "system user" : "unauthenticated user")); - if (tmp->peer_port && (tmp->host || tmp->ip) && thd->host_or_ip[0]) + thd_info->user= thd->strdup(tmp_sctx->user ? tmp_sctx->user : + (tmp->system_thread ? + "system user" : "unauthenticated user")); + if (tmp->peer_port && (tmp_sctx->host || tmp_sctx->ip) && + thd->security_ctx->host_or_ip[0]) { if ((thd_info->host= thd->alloc(LIST_PROCESS_HOST_LEN+1))) my_snprintf((char *) thd_info->host, LIST_PROCESS_HOST_LEN, - "%s:%u", tmp->host_or_ip, tmp->peer_port); + "%s:%u", tmp_sctx->host_or_ip, tmp->peer_port); } else - thd_info->host= thd->strdup(tmp->host_or_ip); + thd_info->host= thd->strdup(tmp_sctx->host_or_ip); if ((thd_info->db=tmp->db)) // Safe test thd_info->db=thd->strdup(thd_info->db); thd_info->command=(int) tmp->command; @@ -1278,6 +1283,9 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) VOID(pthread_mutex_unlock(&LOCK_thread_count)); thread_info *thd_info; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + Security_context *sctx; +#endif time_t now= time(0); while ((thd_info=thread_infos.get())) { @@ -2014,7 +2022,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) enum enum_schema_tables schema_table_idx; List<char> bases; List_iterator_fast<char> it(bases); - COND *partial_cond; + COND *partial_cond; + Security_context *sctx= thd->security_ctx; uint derived_tables= lex->derived_tables; int error= 1; Open_tables_state open_tables_state_backup; @@ -2086,8 +2095,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) #ifndef NO_EMBEDDED_ACCESS_CHECKS if (!check_access(thd,SELECT_ACL, base_name, &thd->col_access, 0, 1, with_i_schema) || - thd->master_access & (DB_ACLS | SHOW_DB_ACL) || - acl_get(thd->host, thd->ip, thd->priv_user, base_name,0) || + sctx->master_access & (DB_ACLS | SHOW_DB_ACL) || + acl_get(sctx->host, sctx->ip, sctx->priv_user, base_name,0) || (grant_option && !check_grant_db(thd, base_name))) #endif { @@ -2219,6 +2228,7 @@ int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond) bool with_i_schema; HA_CREATE_INFO create; TABLE *table= tables->table; + Security_context *sctx= thd->security_ctx; DBUG_ENTER("fill_schema_shemata"); if (make_db_list(thd, &files, &idx_field_vals, @@ -2237,8 +2247,8 @@ int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond) continue; } #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (thd->master_access & (DB_ACLS | SHOW_DB_ACL) || - acl_get(thd->host, thd->ip, thd->priv_user, file_name,0) || + if (sctx->master_access & (DB_ACLS | SHOW_DB_ACL) || + acl_get(sctx->host, sctx->ip, sctx->priv_user, file_name,0) || (grant_option && !check_grant_db(thd, file_name))) #endif { @@ -2839,7 +2849,8 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond) Open_tables_state open_tables_state_backup; DBUG_ENTER("fill_schema_proc"); - strxmov(definer, thd->priv_user, "@", thd->priv_host, NullS); + strxmov(definer, thd->security_ctx->priv_user, "@", + thd->security_ctx->priv_host, NullS); /* We use this TABLE_LIST instance only for checking of privileges. */ bzero((char*) &proc_tables,sizeof(proc_tables)); proc_tables.db= (char*) "mysql"; @@ -2977,7 +2988,7 @@ static int get_schema_views_record(THD *thd, struct st_table_list *tables, CHARSET_INFO *cs= system_charset_info; DBUG_ENTER("get_schema_views_record"); char definer[HOSTNAME_LENGTH + USERNAME_LENGTH + 2]; - uint defiler_len; + uint definer_len; if (!res) { if (tables->view) @@ -3002,9 +3013,9 @@ static int get_schema_views_record(THD *thd, struct st_table_list *tables, table->field[5]->store(STRING_WITH_LEN("YES"), cs); else table->field[5]->store(STRING_WITH_LEN("NO"), cs); - defiler_len= (strxmov(definer, tables->definer.user.str, "@", - tables->definer.host.str, NullS) - definer) - 1; - table->field[6]->store(definer, defiler_len, cs); + definer_len= (strxmov(definer, tables->definer.user.str, "@", + tables->definer.host.str, NullS) - definer); + table->field[6]->store(definer, definer_len, cs); if (tables->view_suid) table->field[7]->store(STRING_WITH_LEN("DEFINER"), cs); else diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 59e23f8b972..a1f2be7e11c 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3867,6 +3867,16 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, if (create_info->row_type == ROW_TYPE_NOT_USED) create_info->row_type= table->s->row_type; + DBUG_PRINT("info", ("old type: %d new type: %d", old_db_type, new_db_type)); + if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) + || ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED)) + { + DBUG_PRINT("info", ("doesn't support alter")); + my_error(ER_ILLEGAL_HA, MYF(0), table_name); + DBUG_RETURN(TRUE); + } + DBUG_PRINT("info", ("supports alter")); + thd->proc_info="setup"; if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) && !table->s->tmp_table) // no need to touch frm @@ -4880,7 +4890,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt) strxmov(table_name, table->db ,".", table->table_name, NullS); - t= table->table= open_ltable(thd, table, TL_READ_NO_INSERT); + t= table->table= open_ltable(thd, table, TL_READ); thd->clear_error(); // these errors shouldn't get client protocol->prepare_for_resend(); @@ -4906,6 +4916,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt) { /* calculating table's checksum */ ha_checksum crc= 0; + uchar null_mask=256 - (1 << t->s->last_null_bit_pos); /* Set all bits in read set and inform InnoDB that we are reading all @@ -4927,9 +4938,15 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt) continue; break; } - if (t->record[0] != (byte*) t->field[0]->ptr) - row_crc= my_checksum(row_crc, t->record[0], - ((byte*) t->field[0]->ptr) - t->record[0]); + if (t->s->null_bytes) + { + /* fix undefined null bits */ + t->record[0][t->s->null_bytes-1] |= null_mask; + if (!(t->s->db_create_options & HA_OPTION_PACK_RECORD)) + t->record[0][0] |= 1; + + row_crc= my_checksum(row_crc, t->record[0], t->s->null_bytes); + } for (uint i= 0; i < t->s->fields; i++ ) { @@ -4973,9 +4990,9 @@ static bool check_engine(THD *thd, const char *table_name, enum db_type *new_engine) { enum db_type req_engine= *new_engine; - bool no_substitution= + bool no_substitution= test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION); - if ((*new_engine= + if ((*new_engine= ha_checktype(thd, req_engine, no_substitution, 1)) == DB_TYPE_UNKNOWN) return TRUE; diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 7342c146045..df8de59508d 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -172,7 +172,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) stronger test will be removed, the test below will hold. */ if (!trust_routine_creators && mysql_bin_log.is_open() && - !(thd->master_access & SUPER_ACL)) + !(thd->security_ctx->master_access & SUPER_ACL)) { my_message(ER_BINLOG_CREATE_ROUTINE_NEED_SUPER, ER(ER_BINLOG_CREATE_ROUTINE_NEED_SUPER), MYF(0)); diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 556493f4fc8..951248e8cd8 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -23,6 +23,7 @@ #include "mysql_priv.h" #include "sql_select.h" +#include "sql_cursor.h" bool mysql_union(THD *thd, LEX *lex, select_result *result, SELECT_LEX_UNIT *unit, ulong setup_tables_done_option) @@ -30,13 +31,9 @@ bool mysql_union(THD *thd, LEX *lex, select_result *result, DBUG_ENTER("mysql_union"); bool res; if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | - setup_tables_done_option, ""))) + setup_tables_done_option))) res= unit->exec(); - if (!res && thd->cursor && thd->cursor->is_open()) - { - thd->cursor->set_unit(unit); - } - else + if (res || !thd->cursor || !thd->cursor->is_open()) res|= unit->cleanup(); DBUG_RETURN(res); } @@ -46,16 +43,6 @@ bool mysql_union(THD *thd, LEX *lex, select_result *result, ** store records in temporary table for UNION ***************************************************************************/ -select_union::select_union(TABLE *table_par) - :table(table_par) -{ -} - -select_union::~select_union() -{ -} - - int select_union::prepare(List<Item> &list, SELECT_LEX_UNIT *u) { unit= u; @@ -103,6 +90,45 @@ bool select_union::flush() return 0; } +/* + Create a temporary table to store the result of select_union. + + SYNOPSIS + select_union::create_result_table() + thd thread handle + column_types a list of items used to define columns of the + temporary table + is_union_distinct if set, the temporary table will eliminate + duplicates on insert + options create options + + DESCRIPTION + Create a temporary table that is used to store the result of a UNION, + derived table, or a materialized cursor. + + RETURN VALUE + 0 The table has been created successfully. + 1 create_tmp_table failed. +*/ + +bool +select_union::create_result_table(THD *thd, List<Item> *column_types, + bool is_union_distinct, ulonglong options, + const char *alias) +{ + DBUG_ASSERT(table == 0); + tmp_table_param.init(); + tmp_table_param.field_count= column_types->elements; + + if (! (table= create_tmp_table(thd, &tmp_table_param, *column_types, + (ORDER*) 0, is_union_distinct, 1, + options, HA_POS_ERROR, (char*) alias))) + return TRUE; + table->file->extra(HA_EXTRA_WRITE_CACHE); + table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); + return FALSE; +} + /* initialization procedures before fake_select_lex preparation() @@ -133,11 +159,10 @@ st_select_lex_unit::init_prepare_fake_select_lex(THD *thd) bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, - ulong additional_options, - const char *tmp_table_alias) + ulong additional_options) { SELECT_LEX *lex_select_save= thd_arg->lex->current_select; - SELECT_LEX *sl, *first_select; + SELECT_LEX *sl, *first_sl= first_select(); select_result *tmp_result; bool is_union; TABLE *empty_table= 0; @@ -156,7 +181,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, if (describe) { /* fast reinit for EXPLAIN */ - for (sl= first_select_in_union(); sl; sl= sl->next_select()) + for (sl= first_sl; sl; sl= sl->next_select()) { sl->join->result= result; select_limit_cnt= HA_POS_ERROR; @@ -175,17 +200,16 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, prepared= 1; res= FALSE; - 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()); + thd_arg->lex->current_select= sl= first_sl; + found_rows_for_union= first_sl->options & OPTION_FOUND_ROWS; + is_union= test(first_sl->next_select()); /* Global option */ if (is_union) { - if (!(tmp_result= union_result= new select_union(0))) + if (!(tmp_result= union_result= new select_union)) goto err; - union_result->tmp_table_param.init(); if (describe) tmp_result= sel_result; } @@ -238,8 +262,8 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, information about fields lengths and exact types */ if (!is_union) - types= first_select_in_union()->item_list; - else if (sl == first_select) + types= first_sl->item_list; + else if (sl == first_sl) { /* We need to create an empty table object. It is used @@ -287,7 +311,6 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, all collations together for UNION. */ List_iterator_fast<Item> tp(types); - Query_arena *arena= thd->stmt_arena; Item *type; ulonglong create_options; @@ -301,7 +324,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, } } - create_options= (first_select_in_union()->options | thd_arg->options | + create_options= (first_sl->options | thd_arg->options | TMP_TABLE_ALL_COLUMNS); /* Force the temporary table to be a MyISAM table if we're going to use @@ -312,47 +335,35 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, if (global_parameters->ftfunc_list->elements) create_options= create_options | TMP_TABLE_FORCE_MYISAM; - union_result->tmp_table_param.field_count= types.elements; - if (!(table= create_tmp_table(thd_arg, - &union_result->tmp_table_param, types, - (ORDER*) 0, (bool) union_distinct, 1, - create_options, HA_POS_ERROR, - (char *) tmp_table_alias))) + if (union_result->create_result_table(thd, &types, test(union_distinct), + create_options, "")) goto err; - table->file->extra(HA_EXTRA_WRITE_CACHE); - table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); bzero((char*) &result_table_list, sizeof(result_table_list)); result_table_list.db= (char*) ""; result_table_list.table_name= result_table_list.alias= (char*) "union"; - result_table_list.table= table; - union_result->set_table(table); + result_table_list.table= table= union_result->table; thd_arg->lex->current_select= lex_select_save; if (!item_list.elements) { - Field **field; - Query_arena *tmp_arena,backup; - tmp_arena= thd->activate_stmt_arena_if_needed(&backup); + Query_arena *arena, backup_arena; - for (field= table->field; *field; field++) - { - Item_field *item= new Item_field(*field); - if (!item || item_list.push_back(item)) - { - if (tmp_arena) - thd->restore_active_arena(tmp_arena, &backup); - DBUG_RETURN(TRUE); - } - } - if (tmp_arena) - thd->restore_active_arena(tmp_arena, &backup); - if (arena->is_stmt_prepare_or_first_sp_execute()) + arena= thd->activate_stmt_arena_if_needed(&backup_arena); + + res= table->fill_item_list(&item_list); + + if (arena) + thd->restore_active_arena(arena, &backup_arena); + + if (res) + goto err; + + if (thd->stmt_arena->is_stmt_prepare()) { - /* prepare fake select to initialize it correctly */ + /* Validate the global parameters of this union */ + init_prepare_fake_select_lex(thd); - /* - Should be done only once (the only item_list per statement). - */ + /* Should be done only once (the only item_list per statement) */ DBUG_ASSERT(fake_select_lex->join == 0); if (!(fake_select_lex->join= new JOIN(thd, item_list, thd->options, result))) @@ -375,19 +386,14 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, fake_select_lex->table_list.empty(); } } - else if (!arena->is_conventional()) + else { + DBUG_ASSERT(!thd->stmt_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 != 0); - item_field->reset_field(*field); - } + table->reset_item_list(&item_list); } } @@ -404,7 +410,7 @@ err: bool st_select_lex_unit::exec() { SELECT_LEX *lex_select_save= thd->lex->current_select; - SELECT_LEX *select_cursor=first_select_in_union(); + SELECT_LEX *select_cursor=first_select(); ulonglong add_rows=0; ha_rows examined_rows= 0; DBUG_ENTER("st_select_lex_unit::exec"); @@ -595,7 +601,7 @@ bool st_select_lex_unit::cleanup() table= 0; // Safety } - for (SELECT_LEX *sl= first_select_in_union(); sl; sl= sl->next_select()) + for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) error|= sl->cleanup(); if (fake_select_lex) @@ -652,7 +658,7 @@ bool st_select_lex_unit::change_result(select_subselect *result, select_subselect *old_result) { bool res= FALSE; - for (SELECT_LEX *sl= first_select_in_union(); sl; sl= sl->next_select()) + for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select()) { if (sl->join && sl->join->result == old_result) if (sl->join->change_result(result)) @@ -663,6 +669,36 @@ bool st_select_lex_unit::change_result(select_subselect *result, return (res); } +/* + Get column type information for this unit. + + SYNOPSIS + st_select_lex_unit::get_unit_column_types() + + DESCRIPTION + For a single-select the column types are taken + from the list of selected items. For a union this function + assumes that st_select_lex_unit::prepare has been called + and returns the type holders that were created for unioned + column types of all selects. + + NOTES + The implementation of this function should be in sync with + st_select_lex_unit::prepare() +*/ + +List<Item> *st_select_lex_unit::get_unit_column_types() +{ + bool is_union= test(first_select()->next_select()); + + if (is_union) + { + DBUG_ASSERT(prepared); + /* Types are generated during prepare */ + return &types; + } + return &first_select()->item_list; +} bool st_select_lex::cleanup() { diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 7e20817c7c7..72a8eef9a55 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -580,11 +580,20 @@ int mysql_update(THD *thd, query_cache_invalidate3(thd, table_list, 1); } - if ((updated || (error < 0)) && (error <= 0 || !transactional_table)) + /* + error < 0 means really no error at all: we processed all rows until the + last one without error. error > 0 means an error (e.g. unique key + violation and no IGNORE or REPLACE). error == 0 is also an error (if + preparing the record or invoking before triggers fails). See + ha_autocommit_or_rollback(error>=0) and DBUG_RETURN(error>=0) below. + Sometimes we want to binlog even if we updated no rows, in case user used + it to be sure master and slave are in same state. + */ + if ((error < 0) || (updated && !transactional_table)) { if (mysql_bin_log.is_open()) { - if (error <= 0) + if (error < 0) thd->clear_error(); Query_log_event qinfo(thd, thd->query, thd->query_length, transactional_table, FALSE); @@ -667,6 +676,7 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, bzero((char*) &tables,sizeof(tables)); // For ORDER BY tables.table= table; tables.alias= table_list->alias; + thd->allow_sum_func= 0; if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, table_list, conds, &select_lex->leaf_tables, @@ -1544,16 +1554,14 @@ bool multi_update::send_eof() /* Write the SQL statement to the binlog if we updated rows and we succeeded or if we updated some non - transacational tables. - Note that if we updated nothing we don't write to the binlog (TODO: - fix this). + transactional tables. */ - if (updated && (local_error <= 0 || !trans_safe)) + if ((local_error == 0) || (updated && !trans_safe)) { if (mysql_bin_log.is_open()) { - if (local_error <= 0) + if (local_error == 0) thd->clear_error(); Query_log_event qinfo(thd, thd->query, thd->query_length, transactional_tables, FALSE); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index c26f9cc4d71..f956d9d4928 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -214,12 +214,13 @@ bool mysql_create_view(THD *thd, - same as current user - current user has SUPER_ACL */ - if (strcmp(lex->create_view_definer->user.str, thd->priv_user) != 0 || + if (strcmp(lex->create_view_definer->user.str, + thd->security_ctx->priv_user) != 0 || my_strcasecmp(system_charset_info, lex->create_view_definer->host.str, - thd->priv_host) != 0) + thd->security_ctx->priv_host) != 0) { - if (!(thd->master_access & SUPER_ACL)) + if (!(thd->security_ctx->master_access & SUPER_ACL)) { my_error(ER_VIEW_OTHER_USER, MYF(0), lex->create_view_definer->user.str, lex->create_view_definer->host.str); @@ -275,7 +276,8 @@ bool mysql_create_view(THD *thd, if (check_some_access(thd, VIEW_ANY_ACL, tbl)) { my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), - "ANY", thd->priv_user, thd->host_or_ip, tbl->table_name); + "ANY", thd->security_ctx->priv_user, + thd->security_ctx->priv_host, tbl->table_name); res= TRUE; goto err; } @@ -378,7 +380,7 @@ bool mysql_create_view(THD *thd, /* prepare select to resolve all fields */ lex->view_prepare_mode= 1; - if (unit->prepare(thd, 0, 0, view->view_name.str)) + if (unit->prepare(thd, 0, 0)) { /* some errors from prepare are reported to user, if is not then @@ -441,7 +443,8 @@ bool mysql_create_view(THD *thd, { /* VIEW column has more privileges */ my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), - "create view", thd->priv_user, thd->host_or_ip, item->name, + "create view", thd->security_ctx->priv_user, + thd->security_ctx->priv_host, item->name, view->table_name); res= TRUE; goto err; @@ -481,6 +484,8 @@ err: static const int revision_number_position= 8; /* index of last required parameter for making view */ static const int required_view_parameters= 10; +/* number of backups */ +static const int num_view_backups= 3; /* table of VIEW .frm field descriptors @@ -708,7 +713,7 @@ loop_out: } if (sql_create_definition_file(&dir, &file, view_file_type, - (gptr)view, view_parameters, 3)) + (gptr)view, view_parameters, num_view_backups)) { DBUG_RETURN(thd->net.report_error? -1 : 1); } @@ -786,7 +791,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_VIEW_FRM_NO_USER, ER(ER_VIEW_FRM_NO_USER), table->db, table->table_name); - if (default_view_definer(thd, &table->definer)) + if (default_view_definer(thd->security_ctx, &table->definer)) goto err; } @@ -1165,7 +1170,7 @@ frm_type_enum mysql_frm_type(char *path) int length; DBUG_ENTER("mysql_frm_type"); - if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(MY_WME))) < 0) + if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(0))) < 0) { DBUG_RETURN(FRMTYPE_ERROR); } @@ -1369,3 +1374,94 @@ int view_checksum(THD *thd, TABLE_LIST *view) HA_ADMIN_WRONG_CHECKSUM : HA_ADMIN_OK); } + +/* + rename view + + Synopsis: + renames a view + + Parameters: + thd thread handler + new_name new name of view + view view + + Return values: + FALSE Ok + TRUE Error +*/ +bool +mysql_rename_view(THD *thd, + const char *new_name, + TABLE_LIST *view) +{ + LEX_STRING pathstr, file; + File_parser *parser; + char view_path[FN_REFLEN]; + bool error= TRUE; + + DBUG_ENTER("mysql_rename_view"); + + strxnmov(view_path, FN_REFLEN, mysql_data_home, "/", view->db, "/", + view->table_name, reg_ext, NullS); + (void) unpack_filename(view_path, view_path); + + pathstr.str= (char *)view_path; + pathstr.length= strlen(view_path); + + if ((parser= sql_parse_prepare(&pathstr, thd->mem_root, 1)) && + is_equal(&view_type, parser->type())) + { + TABLE_LIST view_def; + char dir_buff[FN_REFLEN], file_buff[FN_REFLEN]; + + /* + To be PS-friendly we should either to restore state of + TABLE_LIST object pointed by 'view' after using it for + view definition parsing or use temporary 'view_def' + object for it. + */ + bzero(&view_def, sizeof(view_def)); + view_def.timestamp.str= view_def.timestamp_buffer; + view_def.view_suid= TRUE; + + /* get view definition and source */ + if (parser->parse((gptr)&view_def, thd->mem_root, view_parameters, + sizeof(view_parameters)/sizeof(view_parameters[0])-1)) + goto err; + + /* rename view and it's backups */ + if (rename_in_schema_file(view->db, view->table_name, new_name, + view_def.revision - 1, num_view_backups)) + goto err; + + strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", view->db, "/", NullS); + (void) unpack_filename(dir_buff, dir_buff); + + pathstr.str= (char*)dir_buff; + pathstr.length= strlen(dir_buff); + + file.str= file_buff; + file.length= (strxnmov(file_buff, FN_REFLEN, new_name, reg_ext, NullS) + - file_buff); + + if (sql_create_definition_file(&pathstr, &file, view_file_type, + (gptr)&view_def, view_parameters, + num_view_backups)) + { + /* restore renamed view in case of error */ + rename_in_schema_file(view->db, new_name, view->table_name, + view_def.revision - 1, num_view_backups); + goto err; + } + } else + DBUG_RETURN(1); + + /* remove cache entries */ + query_cache_invalidate3(thd, view, 0); + sp_cache_invalidate(); + error= FALSE; + +err: + DBUG_RETURN(error); +} diff --git a/sql/sql_view.h b/sql/sql_view.h index 9d961feb143..4cc9eb454fb 100644 --- a/sql/sql_view.h +++ b/sql/sql_view.h @@ -34,6 +34,7 @@ int view_checksum(THD *thd, TABLE_LIST *view); extern TYPELIB updatable_views_with_limit_typelib; bool check_duplicate_names(List<Item>& item_list, bool gen_unique_view_names); +bool mysql_rename_view(THD *thd, const char *new_name, TABLE_LIST *view); #define VIEW_ANY_ACL (SELECT_ACL | UPDATE_ACL | INSERT_ACL | DELETE_ACL) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 49c385dd1be..77c1a37e16e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5370,27 +5370,48 @@ simple_expr: $$= new Item_func_sp(Lex->current_context(), name); lex->safe_to_cache_query=0; } - | IDENT_sys '(' udf_expr_list ')' + | IDENT_sys '(' { #ifdef HAVE_DLOPEN - udf_func *udf; + udf_func *udf= 0; + if (using_udf_functions && + (udf= find_udf($1.str, $1.length)) && + udf->type == UDFTYPE_AGGREGATE) + { + LEX *lex= Lex; + if (lex->current_select->inc_in_sum_expr()) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + } + $<udf>$= udf; +#endif + } + udf_expr_list ')' + { +#ifdef HAVE_DLOPEN + udf_func *udf= $<udf>3; SELECT_LEX *sel= Select; - if (using_udf_functions && (udf=find_udf($1.str, $1.length))) + if (udf) { + if (udf->type == UDFTYPE_AGGREGATE) + Select->in_sum_expr--; + switch (udf->returns) { case STRING_RESULT: if (udf->type == UDFTYPE_FUNCTION) { - if ($3 != NULL) - $$ = new Item_func_udf_str(udf, *$3); + if ($4 != NULL) + $$ = new Item_func_udf_str(udf, *$4); else $$ = new Item_func_udf_str(udf); } else { - if ($3 != NULL) - $$ = new Item_sum_udf_str(udf, *$3); + if ($4 != NULL) + $$ = new Item_sum_udf_str(udf, *$4); else $$ = new Item_sum_udf_str(udf); } @@ -5398,15 +5419,15 @@ simple_expr: case REAL_RESULT: if (udf->type == UDFTYPE_FUNCTION) { - if ($3 != NULL) - $$ = new Item_func_udf_float(udf, *$3); + if ($4 != NULL) + $$ = new Item_func_udf_float(udf, *$4); else $$ = new Item_func_udf_float(udf); } else { - if ($3 != NULL) - $$ = new Item_sum_udf_float(udf, *$3); + if ($4 != NULL) + $$ = new Item_sum_udf_float(udf, *$4); else $$ = new Item_sum_udf_float(udf); } @@ -5414,15 +5435,15 @@ simple_expr: case INT_RESULT: if (udf->type == UDFTYPE_FUNCTION) { - if ($3 != NULL) - $$ = new Item_func_udf_int(udf, *$3); + if ($4 != NULL) + $$ = new Item_func_udf_int(udf, *$4); else $$ = new Item_func_udf_int(udf); } else { - if ($3 != NULL) - $$ = new Item_sum_udf_int(udf, *$3); + if ($4 != NULL) + $$ = new Item_sum_udf_int(udf, *$4); else $$ = new Item_sum_udf_int(udf); } @@ -5430,15 +5451,15 @@ simple_expr: case DECIMAL_RESULT: if (udf->type == UDFTYPE_FUNCTION) { - if ($3 != NULL) - $$ = new Item_func_udf_decimal(udf, *$3); + if ($4 != NULL) + $$ = new Item_func_udf_decimal(udf, *$4); else $$ = new Item_func_udf_decimal(udf); } else { - if ($3 != NULL) - $$ = new Item_sum_udf_decimal(udf, *$3); + if ($4 != NULL) + $$ = new Item_sum_udf_decimal(udf, *$4); else $$ = new Item_sum_udf_decimal(udf); } @@ -5454,8 +5475,8 @@ simple_expr: sp_name *name= sp_name_current_db_new(YYTHD, $1); sp_add_used_routine(lex, YYTHD, name, TYPE_ENUM_FUNCTION); - if ($3) - $$= new Item_func_sp(Lex->current_context(), name, *$3); + if ($4) + $$= new Item_func_sp(Lex->current_context(), name, *$4); else $$= new Item_func_sp(Lex->current_context(), name); lex->safe_to_cache_query=0; @@ -7145,15 +7166,16 @@ show_param: LEX *lex=Lex; lex->sql_command= SQLCOM_SHOW_GRANTS; THD *thd= lex->thd; + Security_context *sctx= thd->security_ctx; LEX_USER *curr_user; if (!(curr_user= (LEX_USER*) thd->alloc(sizeof(st_lex_user)))) YYABORT; - curr_user->user.str= thd->priv_user; - curr_user->user.length= strlen(thd->priv_user); - if (*thd->priv_host != 0) + curr_user->user.str= sctx->priv_user; + curr_user->user.length= strlen(sctx->priv_user); + if (*sctx->priv_host != 0) { - curr_user->host.str= thd->priv_host; - curr_user->host.length= strlen(thd->priv_host); + curr_user->host.str= sctx->priv_host; + curr_user->host.length= strlen(sctx->priv_host); } else { @@ -7239,6 +7261,9 @@ show_engine_param: STATUS_SYM { switch (Lex->create_info.db_type) { + case DB_TYPE_NDBCLUSTER: + Lex->sql_command = SQLCOM_SHOW_NDBCLUSTER_STATUS; + break; case DB_TYPE_INNODB: Lex->sql_command = SQLCOM_SHOW_INNODB_STATUS; break; @@ -8054,14 +8079,15 @@ user: | CURRENT_USER optional_braces { THD *thd= YYTHD; + Security_context *sctx= thd->security_ctx; if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) YYABORT; - $$->user.str= thd->priv_user; - $$->user.length= strlen(thd->priv_user); - if (*thd->priv_host != 0) + $$->user.str= sctx->priv_user; + $$->user.length= strlen(sctx->priv_user); + if (*sctx->priv_host != 0) { - $$->host.str= thd->priv_host; - $$->host.length= strlen(thd->priv_host); + $$->host.str= sctx->priv_host; + $$->host.length= strlen(sctx->priv_host); } else { @@ -8588,7 +8614,7 @@ option_value: if (!(user=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))) YYABORT; user->host=null_lex_str; - user->user.str=thd->priv_user; + user->user.str=thd->security_ctx->priv_user; thd->lex->var_list.push_back(new set_var_password(user, $3)); } | PASSWORD FOR_SYM user equal text_or_password @@ -9521,7 +9547,8 @@ view_user: if (!(thd->lex->create_view_definer= (LEX_USER*) thd->alloc(sizeof(st_lex_user)))) YYABORT; - if (default_view_definer(thd, thd->lex->create_view_definer)) + if (default_view_definer(thd->security_ctx, + thd->lex->create_view_definer)) YYABORT; } | CURRENT_USER optional_braces @@ -9530,7 +9557,8 @@ view_user: if (!(thd->lex->create_view_definer= (LEX_USER*) thd->alloc(sizeof(st_lex_user)))) YYABORT; - if (default_view_definer(thd, thd->lex->create_view_definer)) + if (default_view_definer(thd->security_ctx, + thd->lex->create_view_definer)) YYABORT; } | DEFINER_SYM EQ ident_or_text '@' ident_or_text diff --git a/sql/table.cc b/sql/table.cc index 9d91cb51ee1..d3f7d2b3b9f 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -290,8 +290,6 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, keynames=(char*) key_part; strpos+= (strmov(keynames, (char *) strpos) - keynames)+1; - share->null_bytes= null_pos - (uchar*) outparam->null_flags + (null_bit_pos + 7) / 8; - share->reclength = uint2korr((head+16)); if (*(head+26) == 1) share->system= 1; /* one-record-database */ @@ -351,7 +349,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, char *buff; if (!(buff= alloc_root(&outparam->mem_root, n_length))) goto err; - if (my_pread(file, buff, n_length, record_offset + share->reclength, + if (my_pread(file, (byte*)buff, n_length, record_offset + share->reclength, MYF(MY_NABP))) goto err; share->connect_string.length= uint2korr(buff); @@ -473,6 +471,9 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, { outparam->null_flags=null_pos=(uchar*) record+1; null_bit_pos= (db_create_options & HA_OPTION_PACK_RECORD) ? 0 : 1; + /* null_bytes below is only correct under the condition that + there are no bit fields. Correct values is set below after the + table struct is initialized */ share->null_bytes= (share->null_fields + null_bit_pos + 7) / 8; } else @@ -885,6 +886,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, (*save++)= i; } } + if (outparam->file->ha_allocate_read_write_set(share->fields)) goto err; @@ -895,6 +897,10 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, #endif goto err; + /* the correct null_bytes can now be set, since bitfields have been taken into account */ + share->null_bytes= null_pos - (uchar*) outparam->null_flags + (null_bit_pos + 7) / 8; + share->last_null_bit_pos= null_bit_pos; + /* The table struct is now initialized; Open the table */ error=2; if (db_stat) @@ -1725,6 +1731,63 @@ db_type get_table_type(THD *thd, const char *name) DBUG_RETURN(ha_checktype(thd,(enum db_type) (uint) *(head+3),0,0)); } +/* + Create Item_field for each column in the table. + + SYNPOSIS + st_table::fill_item_list() + item_list a pointer to an empty list used to store items + + DESCRIPTION + Create Item_field object for each column in the table and + initialize it with the corresponding Field. New items are + created in the current THD memory root. + + RETURN VALUE + 0 success + 1 out of memory +*/ + +bool st_table::fill_item_list(List<Item> *item_list) const +{ + /* + All Item_field's created using a direct pointer to a field + are fixed in Item_field constructor. + */ + for (Field **ptr= field; *ptr; ptr++) + { + Item_field *item= new Item_field(*ptr); + if (!item || item_list->push_back(item)) + return TRUE; + } + return FALSE; +} + +/* + Reset an existing list of Item_field items to point to the + Fields of this table. + + SYNPOSIS + st_table::fill_item_list() + item_list a non-empty list with Item_fields + + DESCRIPTION + This is a counterpart of fill_item_list used to redirect + Item_fields to the fields of a newly created table. + The caller must ensure that number of items in the item_list + is the same as the number of columns in the table. +*/ + +void st_table::reset_item_list(List<Item> *item_list) const +{ + List_iterator_fast<Item> it(*item_list); + for (Field **ptr= field; *ptr; ptr++) + { + Item_field *item_field= (Item_field*) it++; + DBUG_ASSERT(item_field != 0); + item_field->reset_field(*ptr); + } +} /* calculate md5 of query @@ -2268,8 +2331,10 @@ TABLE_LIST *st_table_list::first_leaf_for_name_resolution() List_iterator_fast<TABLE_LIST> it(cur_nested_join->join_list); cur_table_ref= it++; /* - If 'this' is a RIGHT JOIN, the operands in 'join_list' are in reverse - order, thus the first operand is already at the front of the list. + If the current nested join is a RIGHT JOIN, the operands in + 'join_list' are in reverse order, thus the first operand is + already at the front of the list. Otherwise the first operand + is in the end of the list of join operands. */ if (!(cur_table_ref->outer_join & JOIN_TYPE_RIGHT)) { @@ -2320,9 +2385,11 @@ TABLE_LIST *st_table_list::last_leaf_for_name_resolution() cur_nested_join; cur_nested_join= cur_table_ref->nested_join) { + cur_table_ref= cur_nested_join->join_list.head(); /* - If 'this' is a RIGHT JOIN, the operands in 'join_list' are in reverse - order, thus the last operand is in the end of the list. + If the current nested is a RIGHT JOIN, the operands in + 'join_list' are in reverse order, thus the last operand is in the + end of the list. */ if ((cur_table_ref->outer_join & JOIN_TYPE_RIGHT)) { @@ -2332,8 +2399,6 @@ TABLE_LIST *st_table_list::last_leaf_for_name_resolution() while ((next= it++)) cur_table_ref= next; } - else - cur_table_ref= cur_nested_join->join_list.head(); if (cur_table_ref->is_leaf_for_name_resolution()) break; } @@ -2401,22 +2466,6 @@ Field *Natural_join_column::field() const char *Natural_join_column::table_name() { return table_ref->alias; - /* - TODO: - I think that it is sufficient to return just - table->alias, which is correctly set to either - the view name, the table name, or the alias to - the table reference (view or stored table). - */ -#ifdef NOT_YET - if (view_field) - return table_ref->view_name.str; - - DBUG_ASSERT(!strcmp(table_ref->table_name, - table_ref->table->s->table_name)); - return table_ref->table_name; -} -#endif } @@ -2552,7 +2601,7 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref, DBUG_RETURN(field); } Item *item= new Item_direct_view_ref(&view->view->select_lex.context, - field_ref, view->view_name.str, + field_ref, view->alias, name); DBUG_RETURN(item); } diff --git a/sql/table.h b/sql/table.h index f3f1444a797..c1de61ef273 100644 --- a/sql/table.h +++ b/sql/table.h @@ -145,7 +145,7 @@ typedef struct st_table_share enum tmp_table_type tmp_table; uint blob_ptr_size; /* 4 or 8 */ - uint null_bytes; + uint null_bytes, last_null_bit_pos; uint key_length; /* Length of table_cache_key */ uint fields; /* Number of fields */ uint rec_buff_length; /* Size of table->record[] buffer */ @@ -274,6 +274,9 @@ struct st_table { GRANT_INFO grant; FILESORT_INFO sort; TABLE_SHARE share_not_to_be_used; /* To be deleted when true shares */ + + bool fill_item_list(List<Item> *item_list) const; + void reset_item_list(List<Item> *item_list) const; }; diff --git a/sql/unireg.cc b/sql/unireg.cc index 0279f224940..592093350f1 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -167,8 +167,8 @@ bool mysql_create_frm(THD *thd, my_string file_name, { char buff[2]; int2store(buff,create_info->connect_string.length); - if (my_write(file, buff, sizeof(buff), MYF(MY_NABP)) || - my_write(file, create_info->connect_string.str, + if (my_write(file, (const byte*)buff, sizeof(buff), MYF(MY_NABP)) || + my_write(file, (const byte*)create_info->connect_string.str, create_info->connect_string.length, MYF(MY_NABP))) goto err; } |