2003-01-29 18:22:22 +01:00
|
|
|
/* Copyright (C) 2000-2003 MySQL AB
|
2000-08-21 13:35:27 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
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
|
2006-12-23 20:17:15 +01:00
|
|
|
the Free Software Foundation; version 2 of the License.
|
2000-08-21 13:35:27 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
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.
|
2000-08-21 13:35:27 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
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 */
|
|
|
|
|
2006-03-09 19:09:52 +01:00
|
|
|
#define MYSQL_LEX 1
|
2000-07-31 21:29:14 +02:00
|
|
|
#include "mysql_priv.h"
|
2000-09-30 01:20:26 +02:00
|
|
|
#include "sql_repl.h"
|
2006-05-02 19:56:43 +02:00
|
|
|
#include "rpl_filter.h"
|
2001-11-11 06:24:12 +01:00
|
|
|
#include "repl_failsafe.h"
|
2000-07-31 21:29:14 +02:00
|
|
|
#include <m_ctype.h>
|
|
|
|
#include <myisam.h>
|
|
|
|
#include <my_dir.h>
|
|
|
|
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
#include "sp_head.h"
|
2002-12-12 13:14:23 +01:00
|
|
|
#include "sp.h"
|
2005-07-30 10:19:57 +02:00
|
|
|
#include "sp_cache.h"
|
2006-06-08 23:07:11 +02:00
|
|
|
#include "events.h"
|
2006-06-27 08:48:50 +02:00
|
|
|
#include "event_data_objects.h"
|
2007-02-26 12:25:43 +01:00
|
|
|
#include "sql_trigger.h"
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
|
2003-03-05 19:45:17 +01:00
|
|
|
/* Used in error handling only */
|
|
|
|
#define SP_TYPE_STRING(LP) \
|
|
|
|
((LP)->sphead->m_type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE")
|
|
|
|
#define SP_COM_STRING(LP) \
|
|
|
|
((LP)->sql_command == SQLCOM_CREATE_SPFUNCTION || \
|
|
|
|
(LP)->sql_command == SQLCOM_ALTER_FUNCTION || \
|
2005-01-03 19:53:54 +01:00
|
|
|
(LP)->sql_command == SQLCOM_SHOW_CREATE_FUNC || \
|
2003-03-05 19:45:17 +01:00
|
|
|
(LP)->sql_command == SQLCOM_DROP_FUNCTION ? \
|
|
|
|
"FUNCTION" : "PROCEDURE")
|
|
|
|
|
2006-06-20 12:20:32 +02:00
|
|
|
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2000-09-30 01:20:26 +02:00
|
|
|
const char *any_db="*any*"; // Special symbol for check_access
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2006-08-17 18:13:45 +02:00
|
|
|
const LEX_STRING command_name[]={
|
|
|
|
C_STRING_WITH_LEN("Sleep"),
|
|
|
|
C_STRING_WITH_LEN("Quit"),
|
|
|
|
C_STRING_WITH_LEN("Init DB"),
|
|
|
|
C_STRING_WITH_LEN("Query"),
|
|
|
|
C_STRING_WITH_LEN("Field List"),
|
|
|
|
C_STRING_WITH_LEN("Create DB"),
|
|
|
|
C_STRING_WITH_LEN("Drop DB"),
|
|
|
|
C_STRING_WITH_LEN("Refresh"),
|
|
|
|
C_STRING_WITH_LEN("Shutdown"),
|
|
|
|
C_STRING_WITH_LEN("Statistics"),
|
|
|
|
C_STRING_WITH_LEN("Processlist"),
|
|
|
|
C_STRING_WITH_LEN("Connect"),
|
|
|
|
C_STRING_WITH_LEN("Kill"),
|
|
|
|
C_STRING_WITH_LEN("Debug"),
|
|
|
|
C_STRING_WITH_LEN("Ping"),
|
|
|
|
C_STRING_WITH_LEN("Time"),
|
|
|
|
C_STRING_WITH_LEN("Delayed insert"),
|
|
|
|
C_STRING_WITH_LEN("Change user"),
|
|
|
|
C_STRING_WITH_LEN("Binlog Dump"),
|
|
|
|
C_STRING_WITH_LEN("Table Dump"),
|
|
|
|
C_STRING_WITH_LEN("Connect Out"),
|
|
|
|
C_STRING_WITH_LEN("Register Slave"),
|
|
|
|
C_STRING_WITH_LEN("Prepare"),
|
|
|
|
C_STRING_WITH_LEN("Execute"),
|
|
|
|
C_STRING_WITH_LEN("Long Data"),
|
|
|
|
C_STRING_WITH_LEN("Close stmt"),
|
|
|
|
C_STRING_WITH_LEN("Reset stmt"),
|
|
|
|
C_STRING_WITH_LEN("Set option"),
|
|
|
|
C_STRING_WITH_LEN("Fetch"),
|
|
|
|
C_STRING_WITH_LEN("Daemon"),
|
|
|
|
C_STRING_WITH_LEN("Error") // Last command number
|
2000-07-31 21:29:14 +02:00
|
|
|
};
|
|
|
|
|
2005-01-27 22:38:56 +01:00
|
|
|
const char *xa_state_names[]={
|
|
|
|
"NON-EXISTING", "ACTIVE", "IDLE", "PREPARED"
|
|
|
|
};
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2002-11-16 19:19:10 +01:00
|
|
|
static void unlock_locked_tables(THD *thd)
|
|
|
|
{
|
|
|
|
if (thd->locked_tables)
|
|
|
|
{
|
|
|
|
thd->lock=thd->locked_tables;
|
2004-10-07 00:45:06 +02:00
|
|
|
thd->locked_tables=0; // Will be automatically closed
|
2002-11-16 19:19:10 +01:00
|
|
|
close_thread_tables(thd); // Free tables
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-03-12 08:15:08 +01:00
|
|
|
|
2006-05-05 19:08:40 +02:00
|
|
|
bool end_active_trans(THD *thd)
|
2000-11-13 22:55:10 +01:00
|
|
|
{
|
2000-12-24 14:19:00 +01:00
|
|
|
int error=0;
|
2004-12-06 01:00:37 +01:00
|
|
|
DBUG_ENTER("end_active_trans");
|
2005-07-30 10:19:57 +02:00
|
|
|
if (unlikely(thd->in_sub_stmt))
|
2005-06-07 12:53:08 +02:00
|
|
|
{
|
|
|
|
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
2005-10-05 19:58:16 +02:00
|
|
|
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);
|
|
|
|
}
|
2002-07-23 17:31:22 +02:00
|
|
|
if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN |
|
2001-09-08 00:02:41 +02:00
|
|
|
OPTION_TABLE_LOCK))
|
2000-11-13 22:55:10 +01:00
|
|
|
{
|
2004-12-06 01:00:37 +01:00
|
|
|
DBUG_PRINT("info",("options: 0x%lx", (ulong) thd->options));
|
|
|
|
/* Safety if one did "drop table" on locked tables */
|
|
|
|
if (!thd->locked_tables)
|
|
|
|
thd->options&= ~OPTION_TABLE_LOCK;
|
2000-11-24 00:51:18 +01:00
|
|
|
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
2000-11-13 22:55:10 +01:00
|
|
|
if (ha_commit(thd))
|
2000-12-24 14:19:00 +01:00
|
|
|
error=1;
|
2000-11-13 22:55:10 +01:00
|
|
|
}
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
thd->options&= ~(OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE |
|
|
|
|
OPTION_KEEP_LOG);
|
2004-12-06 01:00:37 +01:00
|
|
|
DBUG_RETURN(error);
|
2000-11-13 22:55:10 +01:00
|
|
|
}
|
|
|
|
|
2007-03-01 21:47:28 +01:00
|
|
|
|
2006-05-02 19:56:43 +02:00
|
|
|
bool begin_trans(THD *thd)
|
2005-02-01 20:48:05 +01:00
|
|
|
{
|
|
|
|
int error=0;
|
2005-07-30 10:19:57 +02:00
|
|
|
if (unlikely(thd->in_sub_stmt))
|
2005-06-07 12:53:08 +02:00
|
|
|
{
|
|
|
|
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
|
|
|
|
return 1;
|
|
|
|
}
|
2005-02-01 20:48:05 +01:00
|
|
|
if (thd->locked_tables)
|
|
|
|
{
|
|
|
|
thd->lock=thd->locked_tables;
|
|
|
|
thd->locked_tables=0; // Will be automatically closed
|
|
|
|
close_thread_tables(thd); // Free tables
|
|
|
|
}
|
|
|
|
if (end_active_trans(thd))
|
|
|
|
error= -1;
|
|
|
|
else
|
|
|
|
{
|
2005-02-02 00:43:09 +01:00
|
|
|
LEX *lex= thd->lex;
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
thd->options|= OPTION_BEGIN;
|
2005-02-01 20:48:05 +01:00
|
|
|
thd->server_status|= SERVER_STATUS_IN_TRANS;
|
|
|
|
if (lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT)
|
2005-02-14 21:50:09 +01:00
|
|
|
error= ha_start_consistent_snapshot(thd);
|
2005-02-01 20:48:05 +01:00
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
2000-11-13 22:55:10 +01:00
|
|
|
|
2004-03-17 23:09:13 +01:00
|
|
|
#ifdef HAVE_REPLICATION
|
2005-08-31 18:08:45 +02:00
|
|
|
/*
|
|
|
|
Returns true if all tables should be ignored
|
|
|
|
*/
|
2004-03-12 08:15:08 +01:00
|
|
|
inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables)
|
|
|
|
{
|
2006-05-02 19:56:43 +02:00
|
|
|
return rpl_filter->is_on() && tables && !thd->spcont &&
|
|
|
|
!rpl_filter->tables_ok(thd->db, tables);
|
2004-03-12 08:15:08 +01:00
|
|
|
}
|
2004-03-17 23:09:13 +01:00
|
|
|
#endif
|
2004-03-12 08:15:08 +01:00
|
|
|
|
|
|
|
|
2005-10-17 10:52:34 +02:00
|
|
|
static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables)
|
|
|
|
{
|
|
|
|
for (TABLE_LIST *table= tables; table; table= table->next_global)
|
|
|
|
{
|
|
|
|
DBUG_ASSERT(table->db && table->table_name);
|
|
|
|
if (table->updating &&
|
|
|
|
!find_temporary_table(thd, table->db, table->table_name))
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-06-01 14:06:42 +02:00
|
|
|
|
2002-09-16 14:55:19 +02:00
|
|
|
/*
|
|
|
|
Mark all commands that somehow changes a table
|
|
|
|
This is used to check number of updates / hour
|
2004-10-22 08:54:14 +02:00
|
|
|
|
|
|
|
sql_command is actually set to SQLCOM_END sometimes
|
|
|
|
so we need the +1 to include it in the array.
|
2005-01-27 22:38:56 +01:00
|
|
|
|
2006-06-20 12:20:32 +02:00
|
|
|
See COMMAND_FLAG_xxx for different type of commands
|
2005-01-27 22:38:56 +01:00
|
|
|
2 - query that returns meaningful ROW_COUNT() -
|
|
|
|
a number of modified rows
|
2002-09-16 14:55:19 +02:00
|
|
|
*/
|
|
|
|
|
2006-06-20 12:20:32 +02:00
|
|
|
uint sql_command_flags[SQLCOM_END+1];
|
2002-09-16 14:55:19 +02:00
|
|
|
|
|
|
|
void init_update_queries(void)
|
|
|
|
{
|
2006-06-20 12:20:32 +02:00
|
|
|
bzero((gptr) &sql_command_flags, sizeof(sql_command_flags));
|
|
|
|
|
|
|
|
sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA;
|
|
|
|
sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA;
|
|
|
|
sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA;
|
|
|
|
sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA;
|
|
|
|
sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA;
|
|
|
|
sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA;
|
|
|
|
sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA;
|
|
|
|
sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA;
|
|
|
|
sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA;
|
|
|
|
sql_command_flags[SQLCOM_BACKUP_TABLE]= CF_CHANGES_DATA;
|
|
|
|
sql_command_flags[SQLCOM_RESTORE_TABLE]= CF_CHANGES_DATA;
|
|
|
|
sql_command_flags[SQLCOM_DROP_INDEX]= CF_CHANGES_DATA;
|
|
|
|
sql_command_flags[SQLCOM_CREATE_VIEW]= CF_CHANGES_DATA;
|
|
|
|
sql_command_flags[SQLCOM_DROP_VIEW]= CF_CHANGES_DATA;
|
|
|
|
sql_command_flags[SQLCOM_CREATE_EVENT]= CF_CHANGES_DATA;
|
|
|
|
sql_command_flags[SQLCOM_ALTER_EVENT]= CF_CHANGES_DATA;
|
|
|
|
sql_command_flags[SQLCOM_DROP_EVENT]= CF_CHANGES_DATA;
|
|
|
|
|
|
|
|
sql_command_flags[SQLCOM_UPDATE]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
|
|
|
|
sql_command_flags[SQLCOM_UPDATE_MULTI]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
|
|
|
|
sql_command_flags[SQLCOM_INSERT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
|
|
|
|
sql_command_flags[SQLCOM_INSERT_SELECT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
|
|
|
|
sql_command_flags[SQLCOM_DELETE]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
|
|
|
|
sql_command_flags[SQLCOM_DELETE_MULTI]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
|
|
|
|
sql_command_flags[SQLCOM_REPLACE]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
|
|
|
|
sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
|
|
|
|
|
|
|
|
sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND;
|
|
|
|
sql_command_flags[SQLCOM_SHOW_STATUS_FUNC]= CF_STATUS_COMMAND;
|
|
|
|
sql_command_flags[SQLCOM_SHOW_STATUS]= CF_STATUS_COMMAND;
|
|
|
|
sql_command_flags[SQLCOM_SHOW_DATABASES]= CF_STATUS_COMMAND;
|
|
|
|
sql_command_flags[SQLCOM_SHOW_TRIGGERS]= CF_STATUS_COMMAND;
|
|
|
|
sql_command_flags[SQLCOM_SHOW_EVENTS]= CF_STATUS_COMMAND;
|
|
|
|
sql_command_flags[SQLCOM_SHOW_OPEN_TABLES]= CF_STATUS_COMMAND;
|
|
|
|
sql_command_flags[SQLCOM_SHOW_PLUGINS]= CF_STATUS_COMMAND;
|
|
|
|
sql_command_flags[SQLCOM_SHOW_FIELDS]= CF_STATUS_COMMAND;
|
|
|
|
sql_command_flags[SQLCOM_SHOW_KEYS]= CF_STATUS_COMMAND;
|
|
|
|
sql_command_flags[SQLCOM_SHOW_VARIABLES]= CF_STATUS_COMMAND;
|
|
|
|
sql_command_flags[SQLCOM_SHOW_CHARSETS]= CF_STATUS_COMMAND;
|
|
|
|
sql_command_flags[SQLCOM_SHOW_COLLATIONS]= CF_STATUS_COMMAND;
|
|
|
|
sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND;
|
|
|
|
|
|
|
|
sql_command_flags[SQLCOM_SHOW_TABLES]= (CF_STATUS_COMMAND |
|
|
|
|
CF_SHOW_TABLE_COMMAND);
|
|
|
|
sql_command_flags[SQLCOM_SHOW_TABLE_STATUS]= (CF_STATUS_COMMAND |
|
|
|
|
CF_SHOW_TABLE_COMMAND);
|
|
|
|
|
|
|
|
/*
|
|
|
|
The following is used to preserver CF_ROW_COUNT during the
|
|
|
|
a CALL or EXECUTE statement, so the value generated by the
|
|
|
|
last called (or executed) statement is preserved.
|
|
|
|
See mysql_execute_command() for how CF_ROW_COUNT is used.
|
|
|
|
*/
|
|
|
|
sql_command_flags[SQLCOM_CALL]= CF_HAS_ROW_COUNT;
|
|
|
|
sql_command_flags[SQLCOM_EXECUTE]= CF_HAS_ROW_COUNT;
|
2002-09-16 14:55:19 +02:00
|
|
|
}
|
|
|
|
|
2006-06-20 12:20:32 +02:00
|
|
|
|
2003-01-30 18:39:54 +01:00
|
|
|
bool is_update_query(enum enum_sql_command command)
|
|
|
|
{
|
2004-10-22 08:54:14 +02:00
|
|
|
DBUG_ASSERT(command >= 0 && command <= SQLCOM_END);
|
2006-06-21 12:24:06 +02:00
|
|
|
return (sql_command_flags[command] & CF_CHANGES_DATA) != 0;
|
2003-01-30 18:39:54 +01:00
|
|
|
}
|
2002-09-16 14:55:19 +02:00
|
|
|
|
2003-02-14 10:47:41 +01:00
|
|
|
|
2003-12-08 06:13:14 +01:00
|
|
|
void execute_init_command(THD *thd, sys_var_str *init_command_var,
|
|
|
|
rw_lock_t *var_mutex)
|
2003-07-18 11:11:01 +02:00
|
|
|
{
|
|
|
|
Vio* save_vio;
|
|
|
|
ulong save_client_capabilities;
|
|
|
|
|
2003-12-08 06:13:14 +01:00
|
|
|
thd->proc_info= "Execution of init_command";
|
|
|
|
/*
|
|
|
|
We need to lock init_command_var because
|
|
|
|
during execution of init_command_var query
|
|
|
|
values of init_command_var can't be changed
|
|
|
|
*/
|
|
|
|
rw_rdlock(var_mutex);
|
|
|
|
thd->query= init_command_var->value;
|
|
|
|
thd->query_length= init_command_var->value_length;
|
2003-07-18 11:11:01 +02:00
|
|
|
save_client_capabilities= thd->client_capabilities;
|
|
|
|
thd->client_capabilities|= CLIENT_MULTI_QUERIES;
|
2003-12-08 06:13:14 +01:00
|
|
|
/*
|
|
|
|
We don't need return result of execution to client side.
|
|
|
|
To forbid this we should set thd->net.vio to 0.
|
|
|
|
*/
|
2003-07-18 11:11:01 +02:00
|
|
|
save_vio= thd->net.vio;
|
|
|
|
thd->net.vio= 0;
|
2005-01-20 09:41:37 +01:00
|
|
|
thd->net.no_send_error= 0;
|
2003-07-18 11:11:01 +02:00
|
|
|
dispatch_command(COM_QUERY, thd, thd->query, thd->query_length+1);
|
2003-12-08 06:13:14 +01:00
|
|
|
rw_unlock(var_mutex);
|
2003-07-18 11:11:01 +02:00
|
|
|
thd->client_capabilities= save_client_capabilities;
|
|
|
|
thd->net.vio= save_vio;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-11-28 03:47:47 +01:00
|
|
|
/*
|
|
|
|
Execute commands from bootstrap_file.
|
|
|
|
Used when creating the initial grant tables
|
|
|
|
*/
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2005-10-08 16:39:55 +02:00
|
|
|
pthread_handler_t handle_bootstrap(void *arg)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2000-11-28 03:47:47 +01:00
|
|
|
THD *thd=(THD*) arg;
|
|
|
|
FILE *file=bootstrap_file;
|
|
|
|
char *buff;
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2001-03-06 14:24:08 +01:00
|
|
|
/* The following must be called before DBUG_ENTER */
|
2005-11-24 01:36:28 +01:00
|
|
|
thd->thread_stack= (char*) &thd;
|
2000-11-28 03:47:47 +01:00
|
|
|
if (my_thread_init() || thd->store_globals())
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2003-09-29 18:07:53 +02:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2003-05-26 18:01:20 +02:00
|
|
|
close_connection(thd, ER_OUT_OF_RESOURCES, 1);
|
2003-09-29 18:07:53 +02:00
|
|
|
#endif
|
2003-01-30 21:15:44 +01:00
|
|
|
thd->fatal_error();
|
2000-11-28 03:47:47 +01:00
|
|
|
goto end;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2001-03-06 14:24:08 +01:00
|
|
|
DBUG_ENTER("handle_bootstrap");
|
|
|
|
|
2003-09-29 18:07:53 +02:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2001-03-06 14:24:08 +01:00
|
|
|
pthread_detach_this_thread();
|
|
|
|
thd->thread_stack= (char*) &thd;
|
2003-09-29 18:07:53 +02:00
|
|
|
#endif /* EMBEDDED_LIBRARY */
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2002-12-20 13:58:27 +01:00
|
|
|
if (thd->variables.max_join_size == HA_POS_ERROR)
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->options |= OPTION_BIG_SELECTS;
|
|
|
|
|
|
|
|
thd->proc_info=0;
|
|
|
|
thd->version=refresh_version;
|
2005-09-15 21:29:07 +02:00
|
|
|
thd->security_ctx->priv_user=
|
|
|
|
thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
|
2006-05-02 19:56:43 +02:00
|
|
|
thd->security_ctx->priv_host[0]=0;
|
2006-07-04 21:46:15 +02:00
|
|
|
/*
|
|
|
|
Make the "client" handle multiple results. This is necessary
|
|
|
|
to enable stored procedures with SELECTs and Dynamic SQL
|
|
|
|
in init-file.
|
|
|
|
*/
|
|
|
|
thd->client_capabilities|= CLIENT_MULTI_RESULTS;
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2000-11-28 03:47:47 +01:00
|
|
|
buff= (char*) thd->net.buff;
|
2003-12-21 20:26:45 +01:00
|
|
|
thd->init_for_queries();
|
2000-07-31 21:29:14 +02:00
|
|
|
while (fgets(buff, thd->net.max_packet, file))
|
|
|
|
{
|
2006-10-16 18:57:33 +02:00
|
|
|
/* strlen() can't be deleted because fgets() doesn't return length */
|
|
|
|
ulong length= (ulong) strlen(buff);
|
|
|
|
while (buff[length-1] != '\n' && !feof(file))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
We got only a part of the current string. Will try to increase
|
|
|
|
net buffer then read the rest of the current string.
|
|
|
|
*/
|
|
|
|
/* purecov: begin tested */
|
|
|
|
if (net_realloc(&(thd->net), 2 * thd->net.max_packet))
|
|
|
|
{
|
|
|
|
net_send_error(thd, ER_NET_PACKET_TOO_LARGE, NullS);
|
|
|
|
thd->fatal_error();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
buff= (char*) thd->net.buff;
|
|
|
|
fgets(buff + length, thd->net.max_packet - length, file);
|
|
|
|
length+= (ulong) strlen(buff + length);
|
|
|
|
/* purecov: end */
|
|
|
|
}
|
|
|
|
if (thd->is_fatal_error)
|
|
|
|
break; /* purecov: inspected */
|
2004-12-22 12:54:39 +01:00
|
|
|
|
2003-03-15 14:24:21 +01:00
|
|
|
while (length && (my_isspace(thd->charset(), buff[length-1]) ||
|
2006-10-16 18:57:33 +02:00
|
|
|
buff[length-1] == ';'))
|
2000-07-31 21:29:14 +02:00
|
|
|
length--;
|
|
|
|
buff[length]=0;
|
2001-10-08 22:20:19 +02:00
|
|
|
thd->query_length=length;
|
2005-08-24 01:45:15 +02:00
|
|
|
thd->query= thd->memdup_w_gap(buff, length+1,
|
|
|
|
thd->db_length+1+QUERY_CACHE_FLAGS_SIZE);
|
2001-12-21 06:00:58 +01:00
|
|
|
thd->query[length] = '\0';
|
2007-02-20 12:48:15 +01:00
|
|
|
DBUG_PRINT("query",("%-.4096s",thd->query));
|
2004-03-01 07:15:33 +01:00
|
|
|
/*
|
|
|
|
We don't need to obtain LOCK_thread_count here because in bootstrap
|
|
|
|
mode we have only one thread.
|
|
|
|
*/
|
2005-01-16 13:16:23 +01:00
|
|
|
thd->query_id=next_query_id();
|
2007-02-19 14:57:54 +01:00
|
|
|
thd->set_time();
|
2000-07-31 21:29:14 +02:00
|
|
|
mysql_parse(thd,thd->query,length);
|
|
|
|
close_thread_tables(thd); // Free tables
|
2007-02-20 12:48:15 +01:00
|
|
|
|
2003-01-30 21:15:44 +01:00
|
|
|
if (thd->is_fatal_error)
|
2000-11-28 03:47:47 +01:00
|
|
|
break;
|
2007-02-20 12:48:15 +01:00
|
|
|
|
|
|
|
if (thd->net.report_error)
|
|
|
|
{
|
|
|
|
/* The query failed, send error to log and abort bootstrap */
|
|
|
|
net_send_error(thd);
|
|
|
|
thd->fatal_error();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2004-11-08 00:13:54 +01:00
|
|
|
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
|
2005-01-16 13:16:23 +01:00
|
|
|
#ifdef USING_TRANSACTIONS
|
2002-03-15 22:57:31 +01:00
|
|
|
free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC));
|
2005-01-16 13:16:23 +01:00
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2000-11-28 03:47:47 +01:00
|
|
|
|
|
|
|
end:
|
2007-02-20 12:48:15 +01:00
|
|
|
/* Remember the exit code of bootstrap */
|
2005-01-16 13:16:23 +01:00
|
|
|
bootstrap_error= thd->is_fatal_error;
|
|
|
|
|
|
|
|
net_end(&thd->net);
|
|
|
|
thd->cleanup();
|
|
|
|
delete thd;
|
|
|
|
|
2003-09-29 18:07:53 +02:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2000-11-28 03:47:47 +01:00
|
|
|
(void) pthread_mutex_lock(&LOCK_thread_count);
|
|
|
|
thread_count--;
|
|
|
|
(void) pthread_mutex_unlock(&LOCK_thread_count);
|
2002-08-22 15:50:58 +02:00
|
|
|
(void) pthread_cond_broadcast(&COND_thread_count);
|
2000-11-28 03:47:47 +01:00
|
|
|
my_thread_end();
|
|
|
|
pthread_exit(0);
|
2003-09-29 18:07:53 +02:00
|
|
|
#endif
|
2005-01-16 13:16:23 +01:00
|
|
|
DBUG_RETURN(0);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-07-07 01:07:45 +02:00
|
|
|
/* This works because items are allocated with sql_alloc() */
|
|
|
|
|
|
|
|
void free_items(Item *item)
|
|
|
|
{
|
|
|
|
Item *next;
|
|
|
|
DBUG_ENTER("free_items");
|
|
|
|
for (; item ; item=next)
|
|
|
|
{
|
|
|
|
next=item->next;
|
|
|
|
item->delete_self();
|
|
|
|
}
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This works because items are allocated with sql_alloc() */
|
2003-11-18 12:47:27 +01:00
|
|
|
|
|
|
|
void cleanup_items(Item *item)
|
|
|
|
{
|
2005-05-20 15:14:35 +02:00
|
|
|
DBUG_ENTER("cleanup_items");
|
2003-11-18 12:47:27 +01:00
|
|
|
for (; item ; item=item->next)
|
|
|
|
item->cleanup();
|
2005-05-20 15:14:35 +02:00
|
|
|
DBUG_VOID_RETURN;
|
2003-11-18 12:47:27 +01:00
|
|
|
}
|
|
|
|
|
A fix and a test case for
Bug#19022 "Memory bug when switching db during trigger execution"
Bug#17199 "Problem when view calls function from another database."
Bug#18444 "Fully qualified stored function names don't work correctly in
SELECT statements"
Documentation note: this patch introduces a change in behaviour of prepared
statements.
This patch adds a few new invariants with regard to how THD::db should
be used. These invariants should be preserved in future:
- one should never refer to THD::db by pointer and always make a deep copy
(strmake, strdup)
- one should never compare two databases by pointer, but use strncmp or
my_strncasecmp
- TABLE_LIST object table->db should be always initialized in the parser or
by creator of the object.
For prepared statements it means that if the current database is changed
after a statement is prepared, the database that was current at prepare
remains active. This also means that you can not prepare a statement that
implicitly refers to the current database if the latter is not set.
This is not documented, and therefore needs documentation. This is NOT a
change in behavior for almost all SQL statements except:
- ALTER TABLE t1 RENAME t2
- OPTIMIZE TABLE t1
- ANALYZE TABLE t1
- TRUNCATE TABLE t1 --
until this patch t1 or t2 could be evaluated at the first execution of
prepared statement.
CURRENT_DATABASE() still works OK and is evaluated at every execution
of prepared statement.
Note, that in stored routines this is not an issue as the default
database is the database of the stored procedure and "use" statement
is prohibited in stored routines.
This patch makes obsolete the use of check_db_used (it was never used in the
old code too) and all other places that check for table->db and assign it
from THD::db if it's NULL, except the parser.
How this patch was created: THD::{db,db_length} were replaced with a
LEX_STRING, THD::db. All the places that refer to THD::{db,db_length} were
manually checked and:
- if the place uses thd->db by pointer, it was fixed to make a deep copy
- if a place compared two db pointers, it was fixed to compare them by value
(via strcmp/my_strcasecmp, whatever was approproate)
Then this intermediate patch was used to write a smaller patch that does the
same thing but without a rename.
TODO in 5.1:
- remove check_db_used
- deploy THD::set_db in mysql_change_db
See also comments to individual files.
2006-06-26 22:47:52 +02:00
|
|
|
/*
|
|
|
|
Handle COM_TABLE_DUMP command
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
mysql_table_dump
|
|
|
|
thd thread handle
|
|
|
|
db database name or an empty string. If empty,
|
|
|
|
the current database of the connection is used
|
|
|
|
tbl_name name of the table to dump
|
|
|
|
|
|
|
|
NOTES
|
|
|
|
This function is written to handle one specific command only.
|
|
|
|
|
|
|
|
RETURN VALUE
|
|
|
|
0 success
|
|
|
|
1 error, the error message is set in THD
|
|
|
|
*/
|
|
|
|
|
|
|
|
static
|
2006-10-16 18:57:33 +02:00
|
|
|
int mysql_table_dump(THD *thd, LEX_STRING *db, char *tbl_name)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
TABLE* table;
|
|
|
|
TABLE_LIST* table_list;
|
|
|
|
int error = 0;
|
|
|
|
DBUG_ENTER("mysql_table_dump");
|
2006-10-16 18:57:33 +02:00
|
|
|
if (db->length == 0)
|
|
|
|
{
|
|
|
|
db->str= thd->db; /* purecov: inspected */
|
|
|
|
db->length= thd->db_length; /* purecov: inspected */
|
|
|
|
}
|
2001-07-11 09:36:22 +02:00
|
|
|
if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST))))
|
2001-02-21 00:11:32 +01:00
|
|
|
DBUG_RETURN(1); // out of memory
|
2006-10-16 18:57:33 +02:00
|
|
|
table_list->db= db->str;
|
2005-01-06 12:00:13 +01:00
|
|
|
table_list->table_name= table_list->alias= tbl_name;
|
2004-07-16 00:15:55 +02:00
|
|
|
table_list->lock_type= TL_READ_NO_INSERT;
|
|
|
|
table_list->prev_global= &table_list; // can be removed after merge with 4.1
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2006-10-16 18:57:33 +02:00
|
|
|
if (check_db_name(db))
|
2001-01-31 03:47:25 +01:00
|
|
|
{
|
2006-10-16 18:57:33 +02:00
|
|
|
/* purecov: begin inspected */
|
|
|
|
my_error(ER_WRONG_DB_NAME ,MYF(0), db->str ? db->str : "NULL");
|
2001-01-31 03:47:25 +01:00
|
|
|
goto err;
|
2006-10-16 18:57:33 +02:00
|
|
|
/* purecov: end */
|
2001-01-31 03:47:25 +01:00
|
|
|
}
|
2003-01-29 17:56:34 +01:00
|
|
|
if (lower_case_table_names)
|
2003-02-07 14:47:24 +01:00
|
|
|
my_casedn_str(files_charset_info, tbl_name);
|
2003-01-29 17:56:34 +01:00
|
|
|
|
|
|
|
if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT)))
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
|
2004-04-10 00:14:32 +02:00
|
|
|
if (check_one_table_access(thd, SELECT_ACL, table_list))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto err;
|
|
|
|
thd->free_list = 0;
|
2001-12-05 12:03:00 +01:00
|
|
|
thd->query_length=(uint) strlen(tbl_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->query = tbl_name;
|
2004-11-18 10:16:06 +01:00
|
|
|
if ((error = mysqld_dump_create_info(thd, table_list, -1)))
|
2001-10-08 22:20:19 +02:00
|
|
|
{
|
2003-02-07 14:47:24 +01:00
|
|
|
my_error(ER_GET_ERRNO, MYF(0), my_errno);
|
2001-10-08 22:20:19 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
net_flush(&thd->net);
|
A fix and a test case for
Bug#19022 "Memory bug when switching db during trigger execution"
Bug#17199 "Problem when view calls function from another database."
Bug#18444 "Fully qualified stored function names don't work correctly in
SELECT statements"
Documentation note: this patch introduces a change in behaviour of prepared
statements.
This patch adds a few new invariants with regard to how THD::db should
be used. These invariants should be preserved in future:
- one should never refer to THD::db by pointer and always make a deep copy
(strmake, strdup)
- one should never compare two databases by pointer, but use strncmp or
my_strncasecmp
- TABLE_LIST object table->db should be always initialized in the parser or
by creator of the object.
For prepared statements it means that if the current database is changed
after a statement is prepared, the database that was current at prepare
remains active. This also means that you can not prepare a statement that
implicitly refers to the current database if the latter is not set.
This is not documented, and therefore needs documentation. This is NOT a
change in behavior for almost all SQL statements except:
- ALTER TABLE t1 RENAME t2
- OPTIMIZE TABLE t1
- ANALYZE TABLE t1
- TRUNCATE TABLE t1 --
until this patch t1 or t2 could be evaluated at the first execution of
prepared statement.
CURRENT_DATABASE() still works OK and is evaluated at every execution
of prepared statement.
Note, that in stored routines this is not an issue as the default
database is the database of the stored procedure and "use" statement
is prohibited in stored routines.
This patch makes obsolete the use of check_db_used (it was never used in the
old code too) and all other places that check for table->db and assign it
from THD::db if it's NULL, except the parser.
How this patch was created: THD::{db,db_length} were replaced with a
LEX_STRING, THD::db. All the places that refer to THD::{db,db_length} were
manually checked and:
- if the place uses thd->db by pointer, it was fixed to make a deep copy
- if a place compared two db pointers, it was fixed to compare them by value
(via strcmp/my_strcasecmp, whatever was approproate)
Then this intermediate patch was used to write a smaller patch that does the
same thing but without a rename.
TODO in 5.1:
- remove check_db_used
- deploy THD::set_db in mysql_change_db
See also comments to individual files.
2006-06-26 22:47:52 +02:00
|
|
|
if ((error= table->file->dump(thd,-1)))
|
2004-06-10 21:18:57 +02:00
|
|
|
my_error(ER_GET_ERRNO, MYF(0), error);
|
2000-08-21 23:39:08 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
err:
|
2000-08-21 23:39:08 +02:00
|
|
|
DBUG_RETURN(error);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2005-02-01 20:48:05 +01:00
|
|
|
/*
|
|
|
|
Ends the current transaction and (maybe) begin the next
|
|
|
|
|
|
|
|
SYNOPSIS
|
2005-02-17 13:52:16 +01:00
|
|
|
end_trans()
|
2005-02-01 20:48:05 +01:00
|
|
|
thd Current thread
|
|
|
|
completion Completion type
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 - OK
|
|
|
|
*/
|
|
|
|
|
2005-02-17 13:52:16 +01:00
|
|
|
int end_trans(THD *thd, enum enum_mysql_completiontype completion)
|
2005-02-01 20:48:05 +01:00
|
|
|
{
|
|
|
|
bool do_release= 0;
|
|
|
|
int res= 0;
|
2005-02-17 13:52:16 +01:00
|
|
|
DBUG_ENTER("end_trans");
|
2005-02-01 20:48:05 +01:00
|
|
|
|
2005-07-30 10:19:57 +02:00
|
|
|
if (unlikely(thd->in_sub_stmt))
|
2005-06-07 12:53:08 +02:00
|
|
|
{
|
|
|
|
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
2005-10-05 19:58:16 +02:00
|
|
|
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);
|
|
|
|
}
|
2005-02-01 20:48:05 +01:00
|
|
|
switch (completion) {
|
|
|
|
case COMMIT:
|
|
|
|
/*
|
|
|
|
We don't use end_active_trans() here to ensure that this works
|
|
|
|
even if there is a problem with the OPTION_AUTO_COMMIT flag
|
|
|
|
(Which of course should never happen...)
|
|
|
|
*/
|
|
|
|
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
2005-02-17 13:52:16 +01:00
|
|
|
res= ha_commit(thd);
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE |
|
|
|
|
OPTION_KEEP_LOG);
|
2005-02-01 20:48:05 +01:00
|
|
|
break;
|
|
|
|
case COMMIT_RELEASE:
|
2005-02-14 21:50:09 +01:00
|
|
|
do_release= 1; /* fall through */
|
2005-02-01 20:48:05 +01:00
|
|
|
case COMMIT_AND_CHAIN:
|
|
|
|
res= end_active_trans(thd);
|
|
|
|
if (!res && completion == COMMIT_AND_CHAIN)
|
|
|
|
res= begin_trans(thd);
|
|
|
|
break;
|
|
|
|
case ROLLBACK_RELEASE:
|
2005-02-14 21:50:09 +01:00
|
|
|
do_release= 1; /* fall through */
|
2005-02-01 20:48:05 +01:00
|
|
|
case ROLLBACK:
|
|
|
|
case ROLLBACK_AND_CHAIN:
|
|
|
|
{
|
|
|
|
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
2005-02-16 17:34:02 +01:00
|
|
|
if (ha_rollback(thd))
|
2005-02-01 20:48:05 +01:00
|
|
|
res= -1;
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE |
|
|
|
|
OPTION_KEEP_LOG);
|
2005-02-01 20:48:05 +01:00
|
|
|
if (!res && (completion == ROLLBACK_AND_CHAIN))
|
|
|
|
res= begin_trans(thd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
res= -1;
|
|
|
|
my_error(ER_UNKNOWN_COM_ERROR, MYF(0));
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2005-02-14 21:50:09 +01:00
|
|
|
|
2005-02-01 20:48:05 +01:00
|
|
|
if (res < 0)
|
|
|
|
my_error(thd->killed_errno(), MYF(0));
|
|
|
|
else if ((res == 0) && do_release)
|
2005-02-14 21:50:09 +01:00
|
|
|
thd->killed= THD::KILL_CONNECTION;
|
|
|
|
|
2005-02-01 20:48:05 +01:00
|
|
|
DBUG_RETURN(res);
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2003-02-07 14:47:24 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2004-03-08 15:47:42 +01:00
|
|
|
|
|
|
|
/*
|
2007-02-16 02:47:39 +01:00
|
|
|
Read one command from connection and execute it (query or simple command).
|
2004-03-08 15:47:42 +01:00
|
|
|
This function is called in loop from thread function.
|
|
|
|
SYNOPSIS
|
|
|
|
do_command()
|
|
|
|
RETURN VALUE
|
|
|
|
0 success
|
|
|
|
1 request of thread shutdown (see dispatch_command() description)
|
|
|
|
*/
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
bool do_command(THD *thd)
|
|
|
|
{
|
2007-02-16 02:47:39 +01:00
|
|
|
char *packet= 0;
|
2001-12-05 12:03:00 +01:00
|
|
|
ulong packet_length;
|
2007-02-16 02:47:39 +01:00
|
|
|
NET *net= &thd->net;
|
2000-07-31 21:29:14 +02:00
|
|
|
enum enum_server_command command;
|
|
|
|
DBUG_ENTER("do_command");
|
|
|
|
|
2003-07-01 18:05:08 +02:00
|
|
|
/*
|
|
|
|
indicator of uninitialized lex => normal flow of errors handling
|
|
|
|
(see my_message_sql)
|
|
|
|
*/
|
2003-07-09 17:07:12 +02:00
|
|
|
thd->lex->current_select= 0;
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2007-02-16 02:47:39 +01:00
|
|
|
/*
|
|
|
|
This thread will do a blocking read from the client which
|
|
|
|
will be interrupted when the next command is received from
|
|
|
|
the client, the connection is closed or "net_wait_timeout"
|
|
|
|
number of seconds has passed
|
|
|
|
*/
|
|
|
|
net_set_read_timeout(net, thd->variables.net_wait_timeout);
|
|
|
|
|
2002-11-03 23:56:25 +01:00
|
|
|
thd->clear_error(); // Clear error message
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
net_new_transaction(net);
|
|
|
|
if ((packet_length=my_net_read(net)) == packet_error)
|
|
|
|
{
|
2003-01-04 14:17:16 +01:00
|
|
|
DBUG_PRINT("info",("Got error %d reading command from socket %s",
|
|
|
|
net->error,
|
|
|
|
vio_description(net->vio)));
|
|
|
|
/* Check if we can continue without closing the connection */
|
|
|
|
if (net->error != 3)
|
2003-06-05 11:29:13 +02:00
|
|
|
{
|
|
|
|
statistic_increment(aborted_threads,&LOCK_status);
|
2003-01-04 14:17:16 +01:00
|
|
|
DBUG_RETURN(TRUE); // We have to close it.
|
2003-06-05 11:29:13 +02:00
|
|
|
}
|
2004-10-20 15:06:54 +02:00
|
|
|
net_send_error(thd, net->last_errno, NullS);
|
2003-01-04 14:17:16 +01:00
|
|
|
net->error= 0;
|
2003-01-02 15:21:22 +01:00
|
|
|
DBUG_RETURN(FALSE);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
packet=(char*) net->read_pos;
|
|
|
|
command = (enum enum_server_command) (uchar) packet[0];
|
2003-01-04 14:17:16 +01:00
|
|
|
if (command >= COM_END)
|
|
|
|
command= COM_END; // Wrong command
|
2001-10-11 17:58:40 +02:00
|
|
|
DBUG_PRINT("info",("Command on %s = %d (%s)",
|
|
|
|
vio_description(net->vio), command,
|
2006-11-27 00:47:38 +01:00
|
|
|
command_name[command].str));
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2007-02-16 02:47:39 +01:00
|
|
|
|
|
|
|
/* Restore read timeout value */
|
|
|
|
net_set_read_timeout(net, thd->variables.net_read_timeout);
|
|
|
|
|
2004-03-02 20:39:50 +01:00
|
|
|
/*
|
|
|
|
packet_length contains length of data, as it was stored in packet
|
|
|
|
header. In case of malformed header, packet_length can be zero.
|
|
|
|
If packet_length is not zero, my_net_read ensures that this number
|
|
|
|
of bytes was actually read from network. Additionally my_net_read
|
|
|
|
sets packet[packet_length]= 0 (thus if packet_length == 0,
|
|
|
|
command == packet[0] == COM_SLEEP).
|
|
|
|
In dispatch_command packet[packet_length] points beyond the end of packet.
|
|
|
|
*/
|
2001-12-05 12:03:00 +01:00
|
|
|
DBUG_RETURN(dispatch_command(command,thd, packet+1, (uint) packet_length));
|
2001-04-11 13:04:03 +02:00
|
|
|
}
|
2002-06-17 13:24:51 +02:00
|
|
|
#endif /* EMBEDDED_LIBRARY */
|
2001-04-11 13:04:03 +02:00
|
|
|
|
2005-02-13 20:47:00 +01:00
|
|
|
|
2004-03-02 20:39:50 +01:00
|
|
|
/*
|
|
|
|
Perform one connection-level (COM_XXXX) command.
|
2005-08-15 17:15:12 +02:00
|
|
|
|
2004-03-02 20:39:50 +01:00
|
|
|
SYNOPSIS
|
|
|
|
dispatch_command()
|
|
|
|
thd connection handle
|
|
|
|
command type of command to perform
|
|
|
|
packet data for the command, packet is always null-terminated
|
|
|
|
packet_length length of packet + 1 (to show that data is
|
|
|
|
null-terminated) except for COM_SLEEP, where it
|
|
|
|
can be zero.
|
|
|
|
RETURN VALUE
|
|
|
|
0 ok
|
|
|
|
1 request of thread shutdown, i. e. if command is
|
|
|
|
COM_QUIT/COM_SHUTDOWN
|
|
|
|
*/
|
2003-02-14 10:47:41 +01:00
|
|
|
|
2001-04-11 13:04:03 +02:00
|
|
|
bool dispatch_command(enum enum_server_command command, THD *thd,
|
|
|
|
char* packet, uint packet_length)
|
|
|
|
{
|
|
|
|
NET *net= &thd->net;
|
2003-02-14 10:47:41 +01:00
|
|
|
bool error= 0;
|
2003-11-25 13:28:43 +01:00
|
|
|
DBUG_ENTER("dispatch_command");
|
|
|
|
|
2005-05-09 13:26:06 +02:00
|
|
|
if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA)
|
2007-03-15 09:51:35 +01:00
|
|
|
{
|
2005-05-09 13:26:06 +02:00
|
|
|
thd->killed= THD::NOT_KILLED;
|
2007-03-15 09:51:35 +01:00
|
|
|
thd->mysys_var->abort= 0;
|
|
|
|
}
|
2005-05-09 13:26:06 +02:00
|
|
|
|
2003-11-25 13:28:43 +01:00
|
|
|
thd->command=command;
|
2002-02-13 21:37:19 +01:00
|
|
|
/*
|
2005-06-16 21:05:38 +02:00
|
|
|
Commands which always take a long time are logged into
|
|
|
|
the slow log only if opt_log_slow_admin_statements is set.
|
2002-02-13 21:37:19 +01:00
|
|
|
*/
|
2005-06-16 21:05:38 +02:00
|
|
|
thd->enable_slow_log= TRUE;
|
2004-09-07 14:50:56 +02:00
|
|
|
thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */
|
2002-07-23 17:31:22 +02:00
|
|
|
thd->set_time();
|
2000-07-31 21:29:14 +02:00
|
|
|
VOID(pthread_mutex_lock(&LOCK_thread_count));
|
2007-02-16 02:47:39 +01:00
|
|
|
thd->query_id= global_query_id;
|
2000-07-31 21:29:14 +02:00
|
|
|
if (command != COM_STATISTICS && command != COM_PING)
|
2005-01-16 13:16:23 +01:00
|
|
|
next_query_id();
|
2000-07-31 21:29:14 +02:00
|
|
|
thread_running++;
|
2004-06-18 23:50:04 +02:00
|
|
|
/* TODO: set thd->lex->sql_command to SQLCOM_END here */
|
2000-07-31 21:29:14 +02:00
|
|
|
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
2002-07-23 17:31:22 +02:00
|
|
|
|
2003-12-06 23:21:09 +01:00
|
|
|
thd->server_status&=
|
|
|
|
~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
|
2001-04-11 13:04:03 +02:00
|
|
|
switch (command) {
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_INIT_DB:
|
2003-08-18 23:08:08 +02:00
|
|
|
{
|
|
|
|
LEX_STRING tmp;
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB],
|
|
|
|
&LOCK_status);
|
2003-08-18 23:08:08 +02:00
|
|
|
thd->convert_string(&tmp, system_charset_info,
|
2006-10-16 18:57:33 +02:00
|
|
|
packet, packet_length-1, thd->charset());
|
2005-08-12 02:04:16 +02:00
|
|
|
if (!mysql_change_db(thd, tmp.str, FALSE))
|
|
|
|
{
|
2006-05-02 19:56:43 +02:00
|
|
|
general_log_print(thd, command, "%s",thd->db);
|
2005-08-12 02:04:16 +02:00
|
|
|
send_ok(thd);
|
|
|
|
}
|
2003-08-18 23:08:08 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-09-08 12:08:53 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2001-05-31 02:50:56 +02:00
|
|
|
case COM_REGISTER_SLAVE:
|
|
|
|
{
|
2002-08-21 21:04:22 +02:00
|
|
|
if (!register_slave(thd, (uchar*)packet, packet_length))
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2001-05-31 02:50:56 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_TABLE_DUMP:
|
2003-08-19 15:00:12 +02:00
|
|
|
{
|
2006-10-16 18:57:33 +02:00
|
|
|
char *tbl_name;
|
|
|
|
LEX_STRING db;
|
2003-08-19 15:00:12 +02:00
|
|
|
uint db_len= *(uchar*) packet;
|
2006-04-25 23:06:04 +02:00
|
|
|
if (db_len >= packet_length || db_len > NAME_LEN)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2006-04-26 07:39:59 +02:00
|
|
|
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
|
2006-04-25 23:06:04 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-08-19 15:00:12 +02:00
|
|
|
uint tbl_len= *(uchar*) (packet + db_len + 1);
|
2006-04-25 23:06:04 +02:00
|
|
|
if (db_len+tbl_len+2 > packet_length || tbl_len > NAME_LEN)
|
|
|
|
{
|
2006-04-26 07:39:59 +02:00
|
|
|
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-08-19 15:00:12 +02:00
|
|
|
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_other, &LOCK_status);
|
2005-06-16 21:05:38 +02:00
|
|
|
thd->enable_slow_log= opt_log_slow_admin_statements;
|
2006-10-16 18:57:33 +02:00
|
|
|
db.str= thd->alloc(db_len + tbl_len + 2);
|
|
|
|
db.length= db_len;
|
|
|
|
if (!db.str)
|
2006-01-03 17:54:54 +01:00
|
|
|
{
|
|
|
|
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
|
|
|
|
break;
|
|
|
|
}
|
2006-10-16 18:57:33 +02:00
|
|
|
tbl_name= strmake(db.str, packet + 1, db_len)+1;
|
2003-08-19 15:00:12 +02:00
|
|
|
strmake(tbl_name, packet + db_len + 2, tbl_len);
|
2006-10-16 18:57:33 +02:00
|
|
|
mysql_table_dump(thd, &db, tbl_name);
|
2003-08-19 15:00:12 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_CHANGE_USER:
|
|
|
|
{
|
2006-10-16 18:57:33 +02:00
|
|
|
statistic_increment(thd->status_var.com_other, &LOCK_status);
|
|
|
|
char *user= (char*) packet, *packet_end= packet+ packet_length;
|
|
|
|
char *passwd= strend(user)+1;
|
|
|
|
|
2002-11-16 19:19:10 +01:00
|
|
|
thd->change_user();
|
2003-07-01 21:40:59 +02:00
|
|
|
thd->clear_error(); // if errors from rollback
|
2002-11-16 19:19:10 +01:00
|
|
|
|
2006-01-03 17:54:54 +01:00
|
|
|
/*
|
2003-07-04 18:52:04 +02:00
|
|
|
Old clients send null-terminated string ('\0' for empty string) for
|
|
|
|
password. New clients send the size (1 byte) + string (not null
|
|
|
|
terminated, so also '\0' for empty string).
|
|
|
|
*/
|
2006-01-03 17:54:54 +01:00
|
|
|
char db_buff[NAME_LEN+1]; // buffer to store db in utf8
|
2003-07-04 18:52:04 +02:00
|
|
|
char *db= passwd;
|
2006-10-16 18:57:33 +02:00
|
|
|
char *save_db;
|
|
|
|
uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
|
|
|
|
*passwd++ : strlen(passwd));
|
|
|
|
uint dummy_errors, save_db_length, db_length, res;
|
|
|
|
Security_context save_security_ctx= *thd->security_ctx;
|
|
|
|
USER_CONN *save_user_connect;
|
|
|
|
|
2003-07-04 18:52:04 +02:00
|
|
|
db+= passwd_len + 1;
|
2004-02-14 17:26:21 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2004-10-07 00:45:06 +02:00
|
|
|
/* Small check for incoming packet */
|
2000-07-31 21:29:14 +02:00
|
|
|
if ((uint) ((uchar*) db - net->read_pos) > packet_length)
|
2003-07-01 21:40:59 +02:00
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
|
2003-07-01 21:40:59 +02:00
|
|
|
break;
|
|
|
|
}
|
2004-02-14 17:26:21 +01:00
|
|
|
#endif
|
2003-09-03 15:29:51 +02:00
|
|
|
/* Convert database name to utf8 */
|
2006-10-16 18:57:33 +02:00
|
|
|
/*
|
|
|
|
Handle problem with old bug in client protocol where db had an extra
|
|
|
|
\0
|
|
|
|
*/
|
|
|
|
db_length= (packet_end - db);
|
|
|
|
if (db_length > 0 && db[db_length-1] == 0)
|
|
|
|
db_length--;
|
2003-09-16 00:39:25 +02:00
|
|
|
db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
|
2006-10-16 18:57:33 +02:00
|
|
|
system_charset_info, db, db_length,
|
2004-10-29 14:00:39 +02:00
|
|
|
thd->charset(), &dummy_errors)]= 0;
|
2003-09-16 00:39:25 +02:00
|
|
|
db= db_buff;
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2003-07-01 21:40:59 +02:00
|
|
|
/* Save user and privileges */
|
2006-10-16 18:57:33 +02:00
|
|
|
save_db_length= thd->db_length;
|
|
|
|
save_db= thd->db;
|
|
|
|
save_user_connect= thd->user_connect;
|
2005-09-15 21:29:07 +02:00
|
|
|
|
|
|
|
if (!(thd->security_ctx->user= my_strdup(user, MYF(0))))
|
2003-07-01 21:40:59 +02:00
|
|
|
{
|
2005-09-15 21:29:07 +02:00
|
|
|
thd->security_ctx->user= save_security_ctx.user;
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
|
2003-07-01 21:40:59 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2003-10-30 00:01:53 +01:00
|
|
|
/* Clear variables that are allocated */
|
|
|
|
thd->user_connect= 0;
|
2006-10-16 18:57:33 +02:00
|
|
|
res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, FALSE);
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2003-07-01 21:40:59 +02:00
|
|
|
if (res)
|
|
|
|
{
|
2004-10-07 00:45:06 +02:00
|
|
|
/* authentication failure, we shall restore old user */
|
2003-07-01 21:40:59 +02:00
|
|
|
if (res > 0)
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
|
2005-09-15 21:29:07 +02:00
|
|
|
x_free(thd->security_ctx->user);
|
|
|
|
*thd->security_ctx= save_security_ctx;
|
2003-10-30 00:01:53 +01:00
|
|
|
thd->user_connect= save_user_connect;
|
2003-07-01 21:40:59 +02:00
|
|
|
thd->db= save_db;
|
|
|
|
thd->db_length= save_db_length;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-06-01 14:06:42 +02:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2003-07-01 21:40:59 +02:00
|
|
|
/* we've authenticated new user */
|
2003-11-04 08:40:36 +01:00
|
|
|
if (save_user_connect)
|
|
|
|
decrease_user_connections(save_user_connect);
|
2006-06-01 14:06:42 +02:00
|
|
|
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
|
2003-07-01 21:40:59 +02:00
|
|
|
x_free((gptr) save_db);
|
2005-09-15 21:29:07 +02:00
|
|
|
x_free((gptr) save_security_ctx.user);
|
2003-07-01 21:40:59 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2005-06-17 21:26:25 +02:00
|
|
|
case COM_STMT_EXECUTE:
|
2002-06-12 23:13:12 +02:00
|
|
|
{
|
2004-03-15 18:20:47 +01:00
|
|
|
mysql_stmt_execute(thd, packet, packet_length);
|
2002-06-12 23:13:12 +02:00
|
|
|
break;
|
|
|
|
}
|
2005-06-17 21:26:25 +02:00
|
|
|
case COM_STMT_FETCH:
|
2004-08-03 12:32:21 +02:00
|
|
|
{
|
|
|
|
mysql_stmt_fetch(thd, packet, packet_length);
|
|
|
|
break;
|
|
|
|
}
|
2005-06-17 21:26:25 +02:00
|
|
|
case COM_STMT_SEND_LONG_DATA:
|
2002-06-12 23:13:12 +02:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
mysql_stmt_get_longdata(thd, packet, packet_length);
|
2002-06-12 23:13:12 +02:00
|
|
|
break;
|
|
|
|
}
|
2005-06-17 21:26:25 +02:00
|
|
|
case COM_STMT_PREPARE:
|
2002-06-12 23:13:12 +02:00
|
|
|
{
|
Implement WL#2661 "Prepared Statements: Dynamic SQL in Stored Procedures".
The idea of the patch is to separate statement processing logic,
such as parsing, validation of the parsed tree, execution and cleanup,
from global query processing logic, such as logging, resetting
priorities of a thread, resetting stored procedure cache, resetting
thread count of errors and warnings.
This makes PREPARE and EXECUTE behave similarly to the rest of SQL
statements and allows their use in stored procedures.
This patch contains a change in behaviour:
until recently for each SQL prepared statement command, 2 queries
were written to the general log, e.g.
[Query] prepare stmt from @stmt_text;
[Prepare] select * from t1 <-- contents of @stmt_text
The chagne was necessary to prevent [Prepare] commands from being written
to the general log when executing a stored procedure with Dynamic SQL.
We should consider whether the old behavior is preferrable and probably
restore it.
This patch refixes Bug#7115, Bug#10975 (partially), Bug#10605 (various bugs
in Dynamic SQL reported before it was disabled).
2005-09-03 01:13:18 +02:00
|
|
|
mysql_stmt_prepare(thd, packet, packet_length);
|
2002-06-12 23:13:12 +02:00
|
|
|
break;
|
|
|
|
}
|
2005-06-17 21:26:25 +02:00
|
|
|
case COM_STMT_CLOSE:
|
2002-11-27 03:51:38 +01:00
|
|
|
{
|
2005-06-17 21:26:25 +02:00
|
|
|
mysql_stmt_close(thd, packet);
|
2002-11-27 03:51:38 +01:00
|
|
|
break;
|
|
|
|
}
|
2005-06-17 21:26:25 +02:00
|
|
|
case COM_STMT_RESET:
|
2003-07-08 11:27:21 +02:00
|
|
|
{
|
|
|
|
mysql_stmt_reset(thd, packet);
|
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_QUERY:
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
if (alloc_query(thd, packet, packet_length))
|
|
|
|
break; // fatal error is set
|
2005-02-14 23:47:17 +01:00
|
|
|
char *packet_end= thd->query + thd->query_length;
|
2006-11-20 21:42:06 +01:00
|
|
|
/* 'b' stands for 'buffer' parameter', special for 'my_snprintf' */
|
|
|
|
const char *format= "%.*b";
|
2006-11-27 00:47:38 +01:00
|
|
|
general_log_print(thd, command, format, thd->query_length, thd->query);
|
2003-01-04 14:17:16 +01:00
|
|
|
DBUG_PRINT("query",("%-.4096s",thd->query));
|
Implement WL#2661 "Prepared Statements: Dynamic SQL in Stored Procedures".
The idea of the patch is to separate statement processing logic,
such as parsing, validation of the parsed tree, execution and cleanup,
from global query processing logic, such as logging, resetting
priorities of a thread, resetting stored procedure cache, resetting
thread count of errors and warnings.
This makes PREPARE and EXECUTE behave similarly to the rest of SQL
statements and allows their use in stored procedures.
This patch contains a change in behaviour:
until recently for each SQL prepared statement command, 2 queries
were written to the general log, e.g.
[Query] prepare stmt from @stmt_text;
[Prepare] select * from t1 <-- contents of @stmt_text
The chagne was necessary to prevent [Prepare] commands from being written
to the general log when executing a stored procedure with Dynamic SQL.
We should consider whether the old behavior is preferrable and probably
restore it.
This patch refixes Bug#7115, Bug#10975 (partially), Bug#10605 (various bugs
in Dynamic SQL reported before it was disabled).
2005-09-03 01:13:18 +02:00
|
|
|
|
|
|
|
if (!(specialflag & SPECIAL_NO_PRIOR))
|
|
|
|
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
|
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
mysql_parse(thd,thd->query, thd->query_length);
|
2003-01-20 23:00:50 +01:00
|
|
|
|
2005-01-16 13:16:23 +01:00
|
|
|
while (!thd->killed && thd->lex->found_semicolon && !thd->net.report_error)
|
2003-01-20 23:00:50 +01:00
|
|
|
{
|
2007-02-16 02:47:39 +01:00
|
|
|
char *next_packet= thd->lex->found_semicolon;
|
2005-05-27 15:01:09 +02:00
|
|
|
net->no_send_error= 0;
|
2003-11-14 13:50:19 +01:00
|
|
|
/*
|
2003-01-20 23:00:50 +01:00
|
|
|
Multiple queries exits, execute them individually
|
|
|
|
*/
|
2005-03-04 14:35:28 +01:00
|
|
|
if (thd->lock || thd->open_tables || thd->derived_tables ||
|
|
|
|
thd->prelocked_mode)
|
2003-11-14 13:50:19 +01:00
|
|
|
close_thread_tables(thd);
|
2007-02-16 02:47:39 +01:00
|
|
|
ulong length= (ulong)(packet_end - next_packet);
|
2003-11-14 13:50:19 +01:00
|
|
|
|
2005-06-16 22:11:48 +02:00
|
|
|
log_slow_statement(thd);
|
2005-02-17 20:48:44 +01:00
|
|
|
|
2003-01-20 23:00:50 +01:00
|
|
|
/* Remove garbage at start of query */
|
2007-02-16 02:47:39 +01:00
|
|
|
while (my_isspace(thd->charset(), *next_packet) && length > 0)
|
2003-01-20 23:00:50 +01:00
|
|
|
{
|
2007-02-16 02:47:39 +01:00
|
|
|
next_packet++;
|
2003-01-20 23:00:50 +01:00
|
|
|
length--;
|
|
|
|
}
|
2004-05-17 00:52:13 +02:00
|
|
|
VOID(pthread_mutex_lock(&LOCK_thread_count));
|
2003-01-20 23:00:50 +01:00
|
|
|
thd->query_length= length;
|
2007-02-16 02:47:39 +01:00
|
|
|
thd->query= next_packet;
|
2005-01-16 13:16:23 +01:00
|
|
|
thd->query_id= next_query_id();
|
2005-02-17 20:48:44 +01:00
|
|
|
thd->set_time(); /* Reset the query start time. */
|
2004-06-18 23:50:04 +02:00
|
|
|
/* TODO: set thd->lex->sql_command to SQLCOM_END here */
|
2003-01-23 20:49:28 +01:00
|
|
|
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
2007-02-16 02:47:39 +01:00
|
|
|
mysql_parse(thd, next_packet, length);
|
2003-01-20 23:00:50 +01:00
|
|
|
}
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!(specialflag & SPECIAL_NO_PRIOR))
|
|
|
|
my_pthread_setprio(pthread_self(),WAIT_PRIOR);
|
|
|
|
DBUG_PRINT("info",("query ready"));
|
|
|
|
break;
|
|
|
|
}
|
2001-09-27 20:45:48 +02:00
|
|
|
case COM_FIELD_LIST: // This isn't actually needed
|
2000-07-31 21:29:14 +02:00
|
|
|
#ifdef DONT_ALLOW_SHOW_COMMANDS
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
|
|
|
|
MYF(0)); /* purecov: inspected */
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
#else
|
|
|
|
{
|
2006-10-16 18:57:33 +02:00
|
|
|
char *fields, *packet_end= packet + packet_length - 1, *arg_end;
|
2005-02-13 20:47:00 +01:00
|
|
|
/* Locked closure of all tables */
|
2000-07-31 21:29:14 +02:00
|
|
|
TABLE_LIST table_list;
|
2003-08-18 23:08:08 +02:00
|
|
|
LEX_STRING conv_name;
|
2006-10-16 18:57:33 +02:00
|
|
|
uint dummy;
|
2006-05-02 19:56:43 +02:00
|
|
|
|
2005-11-02 14:17:57 +01:00
|
|
|
/* used as fields initializator */
|
|
|
|
lex_start(thd, 0, 0);
|
2005-02-13 20:47:00 +01:00
|
|
|
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS],
|
|
|
|
&LOCK_status);
|
2000-07-31 21:29:14 +02:00
|
|
|
bzero((char*) &table_list,sizeof(table_list));
|
2006-10-16 18:57:33 +02:00
|
|
|
if (thd->copy_db_to(&table_list.db, &dummy))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2006-10-16 18:57:33 +02:00
|
|
|
/*
|
|
|
|
We have name + wildcard in packet, separated by endzero
|
|
|
|
*/
|
|
|
|
arg_end= strend(packet);
|
2003-08-18 23:08:08 +02:00
|
|
|
thd->convert_string(&conv_name, system_charset_info,
|
2006-10-16 18:57:33 +02:00
|
|
|
packet, (uint) (arg_end - packet), thd->charset());
|
2005-01-06 12:00:13 +01:00
|
|
|
table_list.alias= table_list.table_name= conv_name.str;
|
2006-10-16 18:57:33 +02:00
|
|
|
packet= arg_end + 1;
|
2004-12-18 10:48:01 +01:00
|
|
|
|
|
|
|
if (!my_strcasecmp(system_charset_info, table_list.db,
|
|
|
|
information_schema_name.str))
|
|
|
|
{
|
|
|
|
ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, table_list.alias);
|
|
|
|
if (schema_table)
|
|
|
|
table_list.schema_table= schema_table;
|
|
|
|
}
|
|
|
|
|
2006-10-16 18:57:33 +02:00
|
|
|
thd->query_length= (uint) (packet_end - packet); // Don't count end \0
|
2001-12-17 02:02:58 +01:00
|
|
|
if (!(thd->query=fields=thd->memdup(packet,thd->query_length+1)))
|
|
|
|
break;
|
2006-05-02 19:56:43 +02:00
|
|
|
general_log_print(thd, command, "%s %s", table_list.table_name, fields);
|
2003-01-29 17:56:34 +01:00
|
|
|
if (lower_case_table_names)
|
2005-01-06 12:00:13 +01:00
|
|
|
my_casedn_str(files_charset_info, table_list.table_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2004-02-16 09:03:25 +01:00
|
|
|
if (check_access(thd,SELECT_ACL,table_list.db,&table_list.grant.privilege,
|
2005-09-13 13:07:38 +02:00
|
|
|
0, 0, test(table_list.schema_table)))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2004-04-10 00:14:32 +02:00
|
|
|
if (grant_option &&
|
|
|
|
check_grant(thd, SELECT_ACL, &table_list, 2, UINT_MAX, 0))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2004-09-07 14:50:56 +02:00
|
|
|
/* init structures for VIEW processing */
|
|
|
|
table_list.select_lex= &(thd->lex->select_lex);
|
|
|
|
mysql_init_query(thd, (uchar*)"", 0);
|
|
|
|
thd->lex->
|
|
|
|
select_lex.table_list.link_in_list((byte*) &table_list,
|
|
|
|
(byte**) &table_list.next_local);
|
2005-05-11 01:31:13 +02:00
|
|
|
thd->lex->add_to_query_tables(&table_list);
|
2004-09-07 14:50:56 +02:00
|
|
|
|
2004-10-10 10:01:05 +02:00
|
|
|
/* switch on VIEW optimisation: do not fill temporary tables */
|
|
|
|
thd->lex->sql_command= SQLCOM_SHOW_FIELDS;
|
2000-07-31 21:29:14 +02:00
|
|
|
mysqld_list_fields(thd,&table_list,fields);
|
2004-09-07 14:50:56 +02:00
|
|
|
thd->lex->unit.cleanup();
|
2004-09-15 21:10:31 +02:00
|
|
|
thd->cleanup_after_query();
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
case COM_QUIT:
|
2001-12-13 19:06:44 +01:00
|
|
|
/* We don't calculate statistics for this command */
|
2006-05-02 19:56:43 +02:00
|
|
|
general_log_print(thd, command, NullS);
|
2000-07-31 21:29:14 +02:00
|
|
|
net->error=0; // Don't give 'abort' message
|
|
|
|
error=TRUE; // End server
|
|
|
|
break;
|
|
|
|
|
2006-10-16 18:57:33 +02:00
|
|
|
#ifdef REMOVED
|
2001-09-03 04:16:15 +02:00
|
|
|
case COM_CREATE_DB: // QQ: To be removed
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2006-10-16 18:57:33 +02:00
|
|
|
LEX_STRING db, alias;
|
2004-10-17 11:59:46 +02:00
|
|
|
HA_CREATE_INFO create_info;
|
2003-12-30 12:14:21 +01:00
|
|
|
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_stat[SQLCOM_CREATE_DB],
|
|
|
|
&LOCK_status);
|
2006-10-16 18:57:33 +02:00
|
|
|
if (thd->LEX_STRING_make(&db, packet, packet_length -1) ||
|
|
|
|
thd->LEX_STRING_make(&alias, db.str, db.length) ||
|
|
|
|
check_db_name(&db))
|
2001-01-31 03:47:25 +01:00
|
|
|
{
|
2006-10-16 18:57:33 +02:00
|
|
|
my_error(ER_WRONG_DB_NAME, MYF(0), db.str ? db.str : "NULL");
|
2001-01-31 03:47:25 +01:00
|
|
|
break;
|
|
|
|
}
|
2006-10-16 18:57:33 +02:00
|
|
|
if (check_access(thd, CREATE_ACL, db.str , 0, 1, 0,
|
|
|
|
is_schema_db(db.str)))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2006-05-02 19:56:43 +02:00
|
|
|
general_log_print(thd, command, packet);
|
2004-10-17 11:59:46 +02:00
|
|
|
bzero(&create_info, sizeof(create_info));
|
2006-10-16 18:57:33 +02:00
|
|
|
mysql_create_db(thd, (lower_case_table_names == 2 ? alias.str : db.str),
|
2004-11-16 12:39:10 +01:00
|
|
|
&create_info, 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2001-09-03 04:16:15 +02:00
|
|
|
case COM_DROP_DB: // QQ: To be removed
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_stat[SQLCOM_DROP_DB],
|
|
|
|
&LOCK_status);
|
2006-10-16 18:57:33 +02:00
|
|
|
LEX_STRING db;
|
|
|
|
|
|
|
|
if (thd->LEX_STRING_make(&db, packet, packet_length - 1) ||
|
|
|
|
check_db_name(&db))
|
2001-01-31 03:47:25 +01:00
|
|
|
{
|
2006-10-16 18:57:33 +02:00
|
|
|
my_error(ER_WRONG_DB_NAME, MYF(0), db.str ? db.str : "NULL");
|
2001-01-31 03:47:25 +01:00
|
|
|
break;
|
|
|
|
}
|
2006-10-16 18:57:33 +02:00
|
|
|
if (check_access(thd, DROP_ACL, db.str, 0, 1, 0, is_schema_db(db.str)))
|
2002-12-20 13:58:27 +01:00
|
|
|
break;
|
2001-09-03 04:16:15 +02:00
|
|
|
if (thd->locked_tables || thd->active_transaction())
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
|
|
|
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2001-09-03 04:16:15 +02:00
|
|
|
}
|
2006-10-16 18:57:33 +02:00
|
|
|
general_log_print(thd, command, db.str);
|
|
|
|
mysql_rm_db(thd, db.str, 0, 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2006-10-16 18:57:33 +02:00
|
|
|
#endif
|
2002-12-16 14:33:29 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_BINLOG_DUMP:
|
|
|
|
{
|
2004-11-10 17:07:39 +01:00
|
|
|
ulong pos;
|
|
|
|
ushort flags;
|
|
|
|
uint32 slave_server_id;
|
|
|
|
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_other,&LOCK_status);
|
2005-06-16 21:05:38 +02:00
|
|
|
thd->enable_slow_log= opt_log_slow_admin_statements;
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd, REPL_SLAVE_ACL))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2000-08-21 23:39:08 +02:00
|
|
|
|
2002-08-21 21:04:22 +02:00
|
|
|
/* TODO: The following has to be changed to an 8 byte integer */
|
2001-04-11 13:04:03 +02:00
|
|
|
pos = uint4korr(packet);
|
|
|
|
flags = uint2korr(packet + 4);
|
2001-10-12 17:37:25 +02:00
|
|
|
thd->server_id=0; /* avoid suicide */
|
2003-07-03 01:08:31 +02:00
|
|
|
if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0
|
2003-07-02 22:56:27 +02:00
|
|
|
kill_zombie_dump_threads(slave_server_id);
|
2000-09-30 01:20:26 +02:00
|
|
|
thd->server_id = slave_server_id;
|
2004-11-10 17:07:39 +01:00
|
|
|
|
2006-05-02 19:56:43 +02:00
|
|
|
general_log_print(thd, command, "Log: '%s' Pos: %ld", packet+10,
|
2004-11-10 17:07:39 +01:00
|
|
|
(long) pos);
|
2002-08-21 21:04:22 +02:00
|
|
|
mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags);
|
2001-10-12 17:37:25 +02:00
|
|
|
unregister_slave(thd,1,1);
|
2004-07-07 10:29:39 +02:00
|
|
|
/* fake COM_QUIT -- if we get here, the thread needs to terminate */
|
2000-10-03 01:59:12 +02:00
|
|
|
error = TRUE;
|
|
|
|
net->error = 0;
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_REFRESH:
|
2005-08-11 14:58:15 +02:00
|
|
|
{
|
|
|
|
bool not_used;
|
|
|
|
statistic_increment(thd->status_var.com_stat[SQLCOM_FLUSH],
|
|
|
|
&LOCK_status);
|
|
|
|
ulong options= (ulong) (uchar) packet[0];
|
|
|
|
if (check_global_access(thd,RELOAD_ACL))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2006-05-02 19:56:43 +02:00
|
|
|
general_log_print(thd, command, NullS);
|
2005-08-11 14:58:15 +02:00
|
|
|
if (!reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, ¬_used))
|
|
|
|
send_ok(thd);
|
|
|
|
break;
|
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_SHUTDOWN:
|
2004-06-15 11:35:23 +02:00
|
|
|
{
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_other, &LOCK_status);
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd,SHUTDOWN_ACL))
|
2000-07-31 21:29:14 +02:00
|
|
|
break; /* purecov: inspected */
|
2004-06-15 11:35:23 +02:00
|
|
|
/*
|
2004-06-18 23:50:04 +02:00
|
|
|
If the client is < 4.1.3, it is going to send us no argument; then
|
|
|
|
packet_length is 1, packet[0] is the end 0 of the packet. Note that
|
|
|
|
SHUTDOWN_DEFAULT is 0. If client is >= 4.1.3, the shutdown level is in
|
|
|
|
packet[0].
|
2004-06-15 11:35:23 +02:00
|
|
|
*/
|
2004-08-19 20:48:00 +02:00
|
|
|
enum mysql_enum_shutdown_level level=
|
|
|
|
(enum mysql_enum_shutdown_level) (uchar) packet[0];
|
2004-06-15 11:35:23 +02:00
|
|
|
if (level == SHUTDOWN_DEFAULT)
|
|
|
|
level= SHUTDOWN_WAIT_ALL_BUFFERS; // soon default will be configurable
|
|
|
|
else if (level != SHUTDOWN_WAIT_ALL_BUFFERS)
|
|
|
|
{
|
|
|
|
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "this shutdown level");
|
|
|
|
break;
|
|
|
|
}
|
2004-06-18 23:50:04 +02:00
|
|
|
DBUG_PRINT("quit",("Got shutdown command for level %u", level));
|
2006-05-02 19:56:43 +02:00
|
|
|
general_log_print(thd, command, NullS);
|
2002-10-02 12:33:08 +02:00
|
|
|
send_eof(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
#ifdef __WIN__
|
|
|
|
sleep(1); // must wait after eof()
|
|
|
|
#endif
|
2006-11-29 21:51:09 +01:00
|
|
|
/*
|
|
|
|
The client is next going to send a COM_QUIT request (as part of
|
|
|
|
mysql_close()). Make the life simpler for the client by sending
|
|
|
|
the response for the coming COM_QUIT in advance
|
|
|
|
*/
|
|
|
|
send_eof(thd);
|
2003-05-26 18:01:20 +02:00
|
|
|
close_connection(thd, 0, 1);
|
2000-07-31 21:29:14 +02:00
|
|
|
close_thread_tables(thd); // Free before kill
|
|
|
|
kill_mysql();
|
|
|
|
error=TRUE;
|
|
|
|
break;
|
2004-06-15 11:35:23 +02:00
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_STATISTICS:
|
|
|
|
{
|
2006-10-16 18:57:33 +02:00
|
|
|
STATUS_VAR current_global_status_var;
|
|
|
|
ulong uptime;
|
|
|
|
uint length;
|
2003-10-04 16:28:08 +02:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2006-10-16 18:57:33 +02:00
|
|
|
char buff[250];
|
|
|
|
uint buff_len= sizeof(buff);
|
2003-10-04 16:28:08 +02:00
|
|
|
#else
|
|
|
|
char *buff= thd->net.last_error;
|
2006-10-16 18:57:33 +02:00
|
|
|
uint buff_len= sizeof(thd->net.last_error);
|
2003-10-04 16:28:08 +02:00
|
|
|
#endif
|
2006-06-06 13:21:07 +02:00
|
|
|
|
2006-10-16 18:57:33 +02:00
|
|
|
general_log_print(thd, command, NullS);
|
|
|
|
statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS],
|
|
|
|
&LOCK_status);
|
2006-06-06 13:21:07 +02:00
|
|
|
calc_sum_of_all_status(¤t_global_status_var);
|
2007-02-16 02:47:39 +01:00
|
|
|
uptime= (ulong) (thd->start_time - server_start_time);
|
2006-10-16 18:57:33 +02:00
|
|
|
length= my_snprintf((char*) buff, buff_len - 1,
|
|
|
|
"Uptime: %lu Threads: %d Questions: %lu "
|
|
|
|
"Slow queries: %lu Opens: %lu Flush tables: %lu "
|
|
|
|
"Open tables: %u Queries per second avg: %.3f",
|
|
|
|
uptime,
|
|
|
|
(int) thread_count, (ulong) thd->query_id,
|
|
|
|
current_global_status_var.long_query_count,
|
|
|
|
current_global_status_var.opened_tables,
|
|
|
|
refresh_version,
|
|
|
|
cached_open_tables(),
|
|
|
|
(uptime ? (ulonglong2double(thd->query_id) /
|
|
|
|
(double) uptime) : (double) 0));
|
2000-07-31 21:29:14 +02:00
|
|
|
#ifdef SAFEMALLOC
|
2003-06-12 21:39:45 +02:00
|
|
|
if (sf_malloc_cur_memory) // Using SAFEMALLOC
|
2006-10-16 18:57:33 +02:00
|
|
|
{
|
|
|
|
char *end= buff + length;
|
|
|
|
length+= my_snprintf(end, buff_len - length - 1,
|
|
|
|
end," Memory in use: %ldK Max memory used: %ldK",
|
|
|
|
(sf_malloc_cur_memory+1023L)/1024L,
|
|
|
|
(sf_malloc_max_memory+1023L)/1024L);
|
|
|
|
}
|
2003-10-04 16:28:08 +02:00
|
|
|
#endif
|
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2006-10-16 18:57:33 +02:00
|
|
|
VOID(my_net_write(net, buff, length));
|
|
|
|
VOID(net_flush(net));
|
2003-10-04 16:28:08 +02:00
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case COM_PING:
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_other, &LOCK_status);
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd); // Tell client we are alive
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
case COM_PROCESS_INFO:
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST],
|
|
|
|
&LOCK_status);
|
2005-09-15 21:29:07 +02:00
|
|
|
if (!thd->security_ctx->priv_user[0] &&
|
|
|
|
check_global_access(thd, PROCESS_ACL))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2006-05-02 19:56:43 +02:00
|
|
|
general_log_print(thd, command, NullS);
|
2003-09-26 12:33:13 +02:00
|
|
|
mysqld_list_processes(thd,
|
2005-09-15 21:29:07 +02:00
|
|
|
thd->security_ctx->master_access & PROCESS_ACL ?
|
|
|
|
NullS : thd->security_ctx->priv_user, 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
case COM_PROCESS_KILL:
|
|
|
|
{
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_stat[SQLCOM_KILL], &LOCK_status);
|
2001-04-11 13:04:03 +02:00
|
|
|
ulong id=(ulong) uint4korr(packet);
|
2006-05-22 20:46:13 +02:00
|
|
|
sql_kill(thd,id,false);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-11-18 12:47:27 +01:00
|
|
|
case COM_SET_OPTION:
|
|
|
|
{
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_stat[SQLCOM_SET_OPTION],
|
|
|
|
&LOCK_status);
|
2007-02-16 02:47:39 +01:00
|
|
|
uint opt_command= uint2korr(packet);
|
|
|
|
|
|
|
|
switch (opt_command) {
|
|
|
|
case (int) MYSQL_OPTION_MULTI_STATEMENTS_ON:
|
2003-11-18 12:47:27 +01:00
|
|
|
thd->client_capabilities|= CLIENT_MULTI_STATEMENTS;
|
2004-01-09 21:28:29 +01:00
|
|
|
send_eof(thd);
|
2003-11-18 12:47:27 +01:00
|
|
|
break;
|
2007-02-16 02:47:39 +01:00
|
|
|
case (int) MYSQL_OPTION_MULTI_STATEMENTS_OFF:
|
2003-11-18 12:47:27 +01:00
|
|
|
thd->client_capabilities&= ~CLIENT_MULTI_STATEMENTS;
|
2004-01-09 21:28:29 +01:00
|
|
|
send_eof(thd);
|
2003-11-18 12:47:27 +01:00
|
|
|
break;
|
|
|
|
default:
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
|
2003-11-18 12:47:27 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_DEBUG:
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_other, &LOCK_status);
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd, SUPER_ACL))
|
2000-07-31 21:29:14 +02:00
|
|
|
break; /* purecov: inspected */
|
2005-02-17 13:07:28 +01:00
|
|
|
mysql_print_status();
|
2006-05-02 19:56:43 +02:00
|
|
|
general_log_print(thd, command, NullS);
|
2002-10-02 12:33:08 +02:00
|
|
|
send_eof(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
case COM_SLEEP:
|
|
|
|
case COM_CONNECT: // Impossible here
|
|
|
|
case COM_TIME: // Impossible from client
|
|
|
|
case COM_DELAYED_INSERT:
|
2003-01-04 14:17:16 +01:00
|
|
|
case COM_END:
|
2000-07-31 21:29:14 +02:00
|
|
|
default:
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2005-03-04 14:35:28 +01:00
|
|
|
if (thd->lock || thd->open_tables || thd->derived_tables ||
|
|
|
|
thd->prelocked_mode)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
thd->proc_info="closing tables";
|
|
|
|
close_thread_tables(thd); /* Free tables */
|
|
|
|
}
|
2005-01-16 13:16:23 +01:00
|
|
|
/*
|
|
|
|
assume handlers auto-commit (if some doesn't - transaction handling
|
|
|
|
in MySQL should be redesigned to support it; it's a big change,
|
|
|
|
and it's not worth it - better to commit explicitly only writing
|
|
|
|
transactions, read-only ones should better take care of themselves.
|
|
|
|
saves some work in 2pc too)
|
|
|
|
see also sql_base.cc - close_thread_tables()
|
|
|
|
*/
|
|
|
|
bzero(&thd->transaction.stmt, sizeof(thd->transaction.stmt));
|
|
|
|
if (!thd->active_transaction())
|
2005-08-12 21:15:01 +02:00
|
|
|
thd->transaction.xid_state.xid.null();
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
/* report error issued during command execution */
|
|
|
|
if (thd->killed_errno() && !thd->net.report_error)
|
|
|
|
thd->send_kill_message();
|
2004-11-12 13:34:00 +01:00
|
|
|
if (thd->net.report_error)
|
2004-10-20 15:06:54 +02:00
|
|
|
net_send_error(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2005-06-16 22:11:48 +02:00
|
|
|
log_slow_statement(thd);
|
2005-02-17 20:48:44 +01:00
|
|
|
|
|
|
|
thd->proc_info="cleaning up";
|
|
|
|
VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list
|
|
|
|
thd->proc_info=0;
|
|
|
|
thd->command=COM_SLEEP;
|
|
|
|
thd->query=0;
|
|
|
|
thd->query_length=0;
|
|
|
|
thread_running--;
|
|
|
|
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
|
|
|
thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
|
|
|
|
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
|
|
|
|
DBUG_RETURN(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-06-16 22:11:48 +02:00
|
|
|
void log_slow_statement(THD *thd)
|
2005-02-17 20:48:44 +01:00
|
|
|
{
|
2005-08-15 17:15:12 +02:00
|
|
|
time_t start_of_query;
|
2006-05-02 19:56:43 +02:00
|
|
|
DBUG_ENTER("log_slow_statement");
|
2005-08-15 17:15:12 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
The following should never be true with our current code base,
|
|
|
|
but better to keep this here so we don't accidently try to log a
|
|
|
|
statement in a trigger or stored function
|
|
|
|
*/
|
|
|
|
if (unlikely(thd->in_sub_stmt))
|
2006-05-02 19:56:43 +02:00
|
|
|
DBUG_VOID_RETURN; // Don't set time for sub stmt
|
2005-08-15 17:15:12 +02:00
|
|
|
|
|
|
|
start_of_query= thd->start_time;
|
2000-10-14 02:16:35 +02:00
|
|
|
thd->end_time(); // Set start time
|
2001-02-16 22:46:52 +01:00
|
|
|
|
2005-06-16 21:05:38 +02:00
|
|
|
/*
|
|
|
|
Do not log administrative statements unless the appropriate option is
|
|
|
|
set; do not log into slow log if reading from backup.
|
|
|
|
*/
|
|
|
|
if (thd->enable_slow_log && !thd->user_time)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2001-02-16 22:46:52 +01:00
|
|
|
thd->proc_info="logging slow query";
|
|
|
|
|
2002-06-28 18:30:09 +02:00
|
|
|
if ((ulong) (thd->start_time - thd->time_after_lock) >
|
|
|
|
thd->variables.long_query_time ||
|
2003-12-06 23:21:09 +01:00
|
|
|
((thd->server_status &
|
|
|
|
(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
|
2003-10-07 09:05:35 +02:00
|
|
|
(specialflag & SPECIAL_LOG_QUERIES_NOT_USING_INDEXES)))
|
2000-10-14 02:16:35 +02:00
|
|
|
{
|
2004-09-13 15:48:01 +02:00
|
|
|
thd->status_var.long_query_count++;
|
2006-05-02 19:56:43 +02:00
|
|
|
slow_log_print(thd, thd->query, thd->query_length, start_of_query);
|
2000-10-14 02:16:35 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2006-05-02 19:56:43 +02:00
|
|
|
DBUG_VOID_RETURN;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
|
A fix and a test case for
Bug#19022 "Memory bug when switching db during trigger execution"
Bug#17199 "Problem when view calls function from another database."
Bug#18444 "Fully qualified stored function names don't work correctly in
SELECT statements"
Documentation note: this patch introduces a change in behaviour of prepared
statements.
This patch adds a few new invariants with regard to how THD::db should
be used. These invariants should be preserved in future:
- one should never refer to THD::db by pointer and always make a deep copy
(strmake, strdup)
- one should never compare two databases by pointer, but use strncmp or
my_strncasecmp
- TABLE_LIST object table->db should be always initialized in the parser or
by creator of the object.
For prepared statements it means that if the current database is changed
after a statement is prepared, the database that was current at prepare
remains active. This also means that you can not prepare a statement that
implicitly refers to the current database if the latter is not set.
This is not documented, and therefore needs documentation. This is NOT a
change in behavior for almost all SQL statements except:
- ALTER TABLE t1 RENAME t2
- OPTIMIZE TABLE t1
- ANALYZE TABLE t1
- TRUNCATE TABLE t1 --
until this patch t1 or t2 could be evaluated at the first execution of
prepared statement.
CURRENT_DATABASE() still works OK and is evaluated at every execution
of prepared statement.
Note, that in stored routines this is not an issue as the default
database is the database of the stored procedure and "use" statement
is prohibited in stored routines.
This patch makes obsolete the use of check_db_used (it was never used in the
old code too) and all other places that check for table->db and assign it
from THD::db if it's NULL, except the parser.
How this patch was created: THD::{db,db_length} were replaced with a
LEX_STRING, THD::db. All the places that refer to THD::{db,db_length} were
manually checked and:
- if the place uses thd->db by pointer, it was fixed to make a deep copy
- if a place compared two db pointers, it was fixed to compare them by value
(via strcmp/my_strcasecmp, whatever was approproate)
Then this intermediate patch was used to write a smaller patch that does the
same thing but without a rename.
TODO in 5.1:
- remove check_db_used
- deploy THD::set_db in mysql_change_db
See also comments to individual files.
2006-06-26 22:47:52 +02:00
|
|
|
/*
|
|
|
|
Create a TABLE_LIST object for an INFORMATION_SCHEMA table.
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
prepare_schema_table()
|
|
|
|
thd thread handle
|
|
|
|
lex current lex
|
|
|
|
table_ident table alias if it's used
|
|
|
|
schema_table_idx the type of the INFORMATION_SCHEMA table to be
|
|
|
|
created
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
This function is used in the parser to convert a SHOW or DESCRIBE
|
|
|
|
table_name command to a SELECT from INFORMATION_SCHEMA.
|
|
|
|
It prepares a SELECT_LEX and a TABLE_LIST object to represent the
|
|
|
|
given command as a SELECT parse tree.
|
|
|
|
|
|
|
|
NOTES
|
|
|
|
Due to the way this function works with memory and LEX it cannot
|
|
|
|
be used outside the parser (parse tree transformations outside
|
|
|
|
the parser break PS and SP).
|
|
|
|
|
|
|
|
RETURN VALUE
|
|
|
|
0 success
|
|
|
|
1 out of memory or SHOW commands are not allowed
|
|
|
|
in this version of the server.
|
|
|
|
*/
|
|
|
|
|
2004-11-13 11:56:39 +01:00
|
|
|
int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
|
|
|
|
enum enum_schema_tables schema_table_idx)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("prepare_schema_table");
|
|
|
|
SELECT_LEX *sel= 0;
|
2005-01-12 02:38:53 +01:00
|
|
|
switch (schema_table_idx) {
|
2004-11-13 11:56:39 +01:00
|
|
|
case SCH_SCHEMATA:
|
|
|
|
#if defined(DONT_ALLOW_SHOW_COMMANDS)
|
2004-11-13 22:26:15 +01:00
|
|
|
my_message(ER_NOT_ALLOWED_COMMAND,
|
|
|
|
ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
|
2004-11-13 11:56:39 +01:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
#else
|
|
|
|
if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
|
|
|
|
check_global_access(thd, SHOW_DB_ACL))
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case SCH_TABLE_NAMES:
|
|
|
|
case SCH_TABLES:
|
|
|
|
case SCH_VIEWS:
|
2005-07-19 18:06:49 +02:00
|
|
|
case SCH_TRIGGERS:
|
2006-05-02 19:56:43 +02:00
|
|
|
case SCH_EVENTS:
|
2004-11-13 11:56:39 +01:00
|
|
|
#ifdef DONT_ALLOW_SHOW_COMMANDS
|
2004-11-13 22:26:15 +01:00
|
|
|
my_message(ER_NOT_ALLOWED_COMMAND,
|
|
|
|
ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
|
2004-11-13 11:56:39 +01:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
#else
|
|
|
|
{
|
2006-10-16 18:57:33 +02:00
|
|
|
LEX_STRING db;
|
|
|
|
uint dummy;
|
A fix and a test case for
Bug#19022 "Memory bug when switching db during trigger execution"
Bug#17199 "Problem when view calls function from another database."
Bug#18444 "Fully qualified stored function names don't work correctly in
SELECT statements"
Documentation note: this patch introduces a change in behaviour of prepared
statements.
This patch adds a few new invariants with regard to how THD::db should
be used. These invariants should be preserved in future:
- one should never refer to THD::db by pointer and always make a deep copy
(strmake, strdup)
- one should never compare two databases by pointer, but use strncmp or
my_strncasecmp
- TABLE_LIST object table->db should be always initialized in the parser or
by creator of the object.
For prepared statements it means that if the current database is changed
after a statement is prepared, the database that was current at prepare
remains active. This also means that you can not prepare a statement that
implicitly refers to the current database if the latter is not set.
This is not documented, and therefore needs documentation. This is NOT a
change in behavior for almost all SQL statements except:
- ALTER TABLE t1 RENAME t2
- OPTIMIZE TABLE t1
- ANALYZE TABLE t1
- TRUNCATE TABLE t1 --
until this patch t1 or t2 could be evaluated at the first execution of
prepared statement.
CURRENT_DATABASE() still works OK and is evaluated at every execution
of prepared statement.
Note, that in stored routines this is not an issue as the default
database is the database of the stored procedure and "use" statement
is prohibited in stored routines.
This patch makes obsolete the use of check_db_used (it was never used in the
old code too) and all other places that check for table->db and assign it
from THD::db if it's NULL, except the parser.
How this patch was created: THD::{db,db_length} were replaced with a
LEX_STRING, THD::db. All the places that refer to THD::{db,db_length} were
manually checked and:
- if the place uses thd->db by pointer, it was fixed to make a deep copy
- if a place compared two db pointers, it was fixed to compare them by value
(via strcmp/my_strcasecmp, whatever was approproate)
Then this intermediate patch was used to write a smaller patch that does the
same thing but without a rename.
TODO in 5.1:
- remove check_db_used
- deploy THD::set_db in mysql_change_db
See also comments to individual files.
2006-06-26 22:47:52 +02:00
|
|
|
if (lex->select_lex.db == NULL &&
|
2006-10-16 18:57:33 +02:00
|
|
|
thd->copy_db_to(&lex->select_lex.db, &dummy))
|
2004-11-13 11:56:39 +01:00
|
|
|
{
|
A fix and a test case for
Bug#19022 "Memory bug when switching db during trigger execution"
Bug#17199 "Problem when view calls function from another database."
Bug#18444 "Fully qualified stored function names don't work correctly in
SELECT statements"
Documentation note: this patch introduces a change in behaviour of prepared
statements.
This patch adds a few new invariants with regard to how THD::db should
be used. These invariants should be preserved in future:
- one should never refer to THD::db by pointer and always make a deep copy
(strmake, strdup)
- one should never compare two databases by pointer, but use strncmp or
my_strncasecmp
- TABLE_LIST object table->db should be always initialized in the parser or
by creator of the object.
For prepared statements it means that if the current database is changed
after a statement is prepared, the database that was current at prepare
remains active. This also means that you can not prepare a statement that
implicitly refers to the current database if the latter is not set.
This is not documented, and therefore needs documentation. This is NOT a
change in behavior for almost all SQL statements except:
- ALTER TABLE t1 RENAME t2
- OPTIMIZE TABLE t1
- ANALYZE TABLE t1
- TRUNCATE TABLE t1 --
until this patch t1 or t2 could be evaluated at the first execution of
prepared statement.
CURRENT_DATABASE() still works OK and is evaluated at every execution
of prepared statement.
Note, that in stored routines this is not an issue as the default
database is the database of the stored procedure and "use" statement
is prohibited in stored routines.
This patch makes obsolete the use of check_db_used (it was never used in the
old code too) and all other places that check for table->db and assign it
from THD::db if it's NULL, except the parser.
How this patch was created: THD::{db,db_length} were replaced with a
LEX_STRING, THD::db. All the places that refer to THD::{db,db_length} were
manually checked and:
- if the place uses thd->db by pointer, it was fixed to make a deep copy
- if a place compared two db pointers, it was fixed to compare them by value
(via strcmp/my_strcasecmp, whatever was approproate)
Then this intermediate patch was used to write a smaller patch that does the
same thing but without a rename.
TODO in 5.1:
- remove check_db_used
- deploy THD::set_db in mysql_change_db
See also comments to individual files.
2006-06-26 22:47:52 +02:00
|
|
|
DBUG_RETURN(1);
|
2004-11-13 11:56:39 +01:00
|
|
|
}
|
2006-10-16 18:57:33 +02:00
|
|
|
db.str= lex->select_lex.db;
|
|
|
|
db.length= strlen(db.str);
|
|
|
|
if (check_db_name(&db))
|
2004-11-13 11:56:39 +01:00
|
|
|
{
|
2006-10-16 18:57:33 +02:00
|
|
|
my_error(ER_WRONG_DB_NAME, MYF(0), db.str);
|
2004-11-13 11:56:39 +01:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
2006-10-16 18:57:33 +02:00
|
|
|
if (check_access(thd, SELECT_ACL, db.str, &thd->col_access, 0, 0,
|
|
|
|
is_schema_db(db.str)))
|
2004-11-13 11:56:39 +01:00
|
|
|
DBUG_RETURN(1); /* purecov: inspected */
|
2006-10-16 18:57:33 +02:00
|
|
|
if (!thd->col_access && check_grant_db(thd, db.str))
|
2004-11-13 11:56:39 +01:00
|
|
|
{
|
2004-11-13 22:26:15 +01:00
|
|
|
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
|
2005-09-15 21:29:07 +02:00
|
|
|
thd->security_ctx->priv_user, thd->security_ctx->priv_host,
|
2006-10-16 18:57:33 +02:00
|
|
|
db.str);
|
2004-11-13 11:56:39 +01:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
case SCH_COLUMNS:
|
|
|
|
case SCH_STATISTICS:
|
|
|
|
#ifdef DONT_ALLOW_SHOW_COMMANDS
|
2004-11-13 22:26:15 +01:00
|
|
|
my_message(ER_NOT_ALLOWED_COMMAND,
|
|
|
|
ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
|
2004-11-13 11:56:39 +01:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
#else
|
|
|
|
if (table_ident)
|
|
|
|
{
|
|
|
|
TABLE_LIST **query_tables_last= lex->query_tables_last;
|
|
|
|
sel= new SELECT_LEX();
|
2005-08-12 16:57:19 +02:00
|
|
|
/* 'parent_lex' is used in init_query() so it must be before it. */
|
|
|
|
sel->parent_lex= lex;
|
2004-11-13 11:56:39 +01:00
|
|
|
sel->init_query();
|
2007-03-05 18:08:41 +01:00
|
|
|
if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ))
|
2004-11-13 11:56:39 +01:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
lex->query_tables_last= query_tables_last;
|
|
|
|
TABLE_LIST *table_list= (TABLE_LIST*) sel->table_list.first;
|
|
|
|
char *db= table_list->db;
|
|
|
|
if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
|
2005-09-13 13:07:38 +02:00
|
|
|
&table_list->grant.privilege, 0, 0,
|
|
|
|
test(table_list->schema_table)))
|
2004-11-13 11:56:39 +01:00
|
|
|
DBUG_RETURN(1); /* purecov: inspected */
|
|
|
|
if (grant_option && check_grant(thd, SELECT_ACL, table_list, 2,
|
|
|
|
UINT_MAX, 0))
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
2004-12-30 13:20:40 +01:00
|
|
|
case SCH_OPEN_TABLES:
|
|
|
|
case SCH_VARIABLES:
|
|
|
|
case SCH_STATUS:
|
2004-11-13 11:56:39 +01:00
|
|
|
case SCH_PROCEDURES:
|
|
|
|
case SCH_CHARSETS:
|
2006-05-02 19:56:43 +02:00
|
|
|
case SCH_ENGINES:
|
2004-11-13 11:56:39 +01:00
|
|
|
case SCH_COLLATIONS:
|
|
|
|
case SCH_COLLATION_CHARACTER_SET_APPLICABILITY:
|
|
|
|
case SCH_USER_PRIVILEGES:
|
|
|
|
case SCH_SCHEMA_PRIVILEGES:
|
|
|
|
case SCH_TABLE_PRIVILEGES:
|
|
|
|
case SCH_COLUMN_PRIVILEGES:
|
|
|
|
case SCH_TABLE_CONSTRAINTS:
|
|
|
|
case SCH_KEY_COLUMN_USAGE:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
SELECT_LEX *select_lex= lex->current_select;
|
|
|
|
if (make_schema_select(thd, select_lex, schema_table_idx))
|
|
|
|
{
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first;
|
|
|
|
table_list->schema_select_lex= sel;
|
2005-01-24 16:44:54 +01:00
|
|
|
table_list->schema_table_reformed= 1;
|
2004-11-13 11:56:39 +01:00
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
/*
|
|
|
|
Read query from packet and store in thd->query
|
2005-06-17 21:26:25 +02:00
|
|
|
Used in COM_QUERY and COM_STMT_PREPARE
|
2002-10-02 12:33:08 +02:00
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
Sets the following THD variables:
|
|
|
|
query
|
|
|
|
query_length
|
|
|
|
|
|
|
|
RETURN VALUES
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE ok
|
|
|
|
TRUE error; In this case thd->fatal_error is set
|
2002-10-02 12:33:08 +02:00
|
|
|
*/
|
|
|
|
|
Implement WL#2661 "Prepared Statements: Dynamic SQL in Stored Procedures".
The idea of the patch is to separate statement processing logic,
such as parsing, validation of the parsed tree, execution and cleanup,
from global query processing logic, such as logging, resetting
priorities of a thread, resetting stored procedure cache, resetting
thread count of errors and warnings.
This makes PREPARE and EXECUTE behave similarly to the rest of SQL
statements and allows their use in stored procedures.
This patch contains a change in behaviour:
until recently for each SQL prepared statement command, 2 queries
were written to the general log, e.g.
[Query] prepare stmt from @stmt_text;
[Prepare] select * from t1 <-- contents of @stmt_text
The chagne was necessary to prevent [Prepare] commands from being written
to the general log when executing a stored procedure with Dynamic SQL.
We should consider whether the old behavior is preferrable and probably
restore it.
This patch refixes Bug#7115, Bug#10975 (partially), Bug#10605 (various bugs
in Dynamic SQL reported before it was disabled).
2005-09-03 01:13:18 +02:00
|
|
|
bool alloc_query(THD *thd, const char *packet, uint packet_length)
|
2002-10-02 12:33:08 +02:00
|
|
|
{
|
|
|
|
packet_length--; // Remove end null
|
2003-01-20 23:00:50 +01:00
|
|
|
/* Remove garbage at start and end of query */
|
2003-03-15 14:24:21 +01:00
|
|
|
while (my_isspace(thd->charset(),packet[0]) && packet_length > 0)
|
2002-10-02 12:33:08 +02:00
|
|
|
{
|
|
|
|
packet++;
|
|
|
|
packet_length--;
|
|
|
|
}
|
Implement WL#2661 "Prepared Statements: Dynamic SQL in Stored Procedures".
The idea of the patch is to separate statement processing logic,
such as parsing, validation of the parsed tree, execution and cleanup,
from global query processing logic, such as logging, resetting
priorities of a thread, resetting stored procedure cache, resetting
thread count of errors and warnings.
This makes PREPARE and EXECUTE behave similarly to the rest of SQL
statements and allows their use in stored procedures.
This patch contains a change in behaviour:
until recently for each SQL prepared statement command, 2 queries
were written to the general log, e.g.
[Query] prepare stmt from @stmt_text;
[Prepare] select * from t1 <-- contents of @stmt_text
The chagne was necessary to prevent [Prepare] commands from being written
to the general log when executing a stored procedure with Dynamic SQL.
We should consider whether the old behavior is preferrable and probably
restore it.
This patch refixes Bug#7115, Bug#10975 (partially), Bug#10605 (various bugs
in Dynamic SQL reported before it was disabled).
2005-09-03 01:13:18 +02:00
|
|
|
const char *pos= packet + packet_length; // Point at end null
|
2002-11-30 18:58:53 +01:00
|
|
|
while (packet_length > 0 &&
|
2003-03-15 14:24:21 +01:00
|
|
|
(pos[-1] == ';' || my_isspace(thd->charset() ,pos[-1])))
|
2002-10-02 12:33:08 +02:00
|
|
|
{
|
|
|
|
pos--;
|
|
|
|
packet_length--;
|
|
|
|
}
|
|
|
|
/* We must allocate some extra memory for query cache */
|
2004-05-17 00:52:13 +02:00
|
|
|
thd->query_length= 0; // Extra safety: Avoid races
|
2002-10-02 12:33:08 +02:00
|
|
|
if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet),
|
|
|
|
packet_length,
|
2003-10-02 11:02:05 +02:00
|
|
|
thd->db_length+ 1 +
|
|
|
|
QUERY_CACHE_FLAGS_SIZE)))
|
2004-10-20 03:04:37 +02:00
|
|
|
return TRUE;
|
2002-10-02 12:33:08 +02:00
|
|
|
thd->query[packet_length]=0;
|
|
|
|
thd->query_length= packet_length;
|
2004-05-25 00:03:49 +02:00
|
|
|
|
|
|
|
/* Reclaim some memory */
|
|
|
|
thd->packet.shrink(thd->variables.net_buffer_length);
|
|
|
|
thd->convert_buffer.shrink(thd->variables.net_buffer_length);
|
2002-10-02 12:33:08 +02:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
return FALSE;
|
2002-10-02 12:33:08 +02:00
|
|
|
}
|
|
|
|
|
2005-08-31 18:08:45 +02:00
|
|
|
static void reset_one_shot_variables(THD *thd)
|
|
|
|
{
|
|
|
|
thd->variables.character_set_client=
|
|
|
|
global_system_variables.character_set_client;
|
|
|
|
thd->variables.collation_connection=
|
|
|
|
global_system_variables.collation_connection;
|
|
|
|
thd->variables.collation_database=
|
|
|
|
global_system_variables.collation_database;
|
|
|
|
thd->variables.collation_server=
|
|
|
|
global_system_variables.collation_server;
|
|
|
|
thd->update_charset();
|
|
|
|
thd->variables.time_zone=
|
|
|
|
global_system_variables.time_zone;
|
2006-12-05 10:45:21 +01:00
|
|
|
thd->variables.lc_time_names= &my_locale_en_US;
|
2005-08-31 18:08:45 +02:00
|
|
|
thd->one_shot_set= 0;
|
|
|
|
}
|
|
|
|
|
2005-08-25 15:34:34 +02:00
|
|
|
|
2007-02-02 18:43:33 +01:00
|
|
|
static
|
|
|
|
bool sp_process_definer(THD *thd)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("sp_process_definer");
|
|
|
|
|
|
|
|
LEX *lex= thd->lex;
|
|
|
|
|
|
|
|
/*
|
|
|
|
If the definer is not specified, this means that CREATE-statement missed
|
|
|
|
DEFINER-clause. DEFINER-clause can be missed in two cases:
|
|
|
|
|
|
|
|
- The user submitted a statement w/o the clause. This is a normal
|
|
|
|
case, we should assign CURRENT_USER as definer.
|
|
|
|
|
|
|
|
- Our slave received an updated from the master, that does not
|
|
|
|
replicate definer for stored rountines. We should also assign
|
|
|
|
CURRENT_USER as definer here, but also we should mark this routine
|
|
|
|
as NON-SUID. This is essential for the sake of backward
|
|
|
|
compatibility.
|
|
|
|
|
|
|
|
The problem is the slave thread is running under "special" user (@),
|
|
|
|
that actually does not exist. In the older versions we do not fail
|
|
|
|
execution of a stored routine if its definer does not exist and
|
|
|
|
continue the execution under the authorization of the invoker
|
|
|
|
(BUG#13198). And now if we try to switch to slave-current-user (@),
|
|
|
|
we will fail.
|
|
|
|
|
|
|
|
Actually, this leads to the inconsistent state of master and
|
|
|
|
slave (different definers, different SUID behaviour), but it seems,
|
|
|
|
this is the best we can do.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!lex->definer)
|
|
|
|
{
|
|
|
|
Query_arena original_arena;
|
|
|
|
Query_arena *ps_arena= thd->activate_stmt_arena_if_needed(&original_arena);
|
|
|
|
|
|
|
|
lex->definer= create_default_definer(thd);
|
|
|
|
|
|
|
|
if (ps_arena)
|
|
|
|
thd->restore_active_arena(ps_arena, &original_arena);
|
|
|
|
|
|
|
|
/* Error has been already reported. */
|
|
|
|
if (lex->definer == NULL)
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
|
|
|
|
if (thd->slave_thread)
|
|
|
|
lex->sphead->m_chistics->suid= SP_IS_NOT_SUID;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
If the specified definer differs from the current user, we
|
|
|
|
should check that the current user has SUPER privilege (in order
|
|
|
|
to create a stored routine under another user one must have
|
|
|
|
SUPER privilege).
|
|
|
|
*/
|
|
|
|
if ((strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
|
|
|
|
my_strcasecmp(system_charset_info, lex->definer->host.str,
|
|
|
|
thd->security_ctx->priv_host)) &&
|
|
|
|
check_global_access(thd, SUPER_ACL))
|
|
|
|
{
|
|
|
|
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check that the specified definer exists. Emit a warning if not. */
|
|
|
|
|
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
|
|
|
if (!is_acl_user(lex->definer->host.str, lex->definer->user.str))
|
|
|
|
{
|
|
|
|
push_warning_printf(thd,
|
|
|
|
MYSQL_ERROR::WARN_LEVEL_NOTE,
|
|
|
|
ER_NO_SUCH_USER,
|
|
|
|
ER(ER_NO_SUCH_USER),
|
|
|
|
lex->definer->user.str,
|
|
|
|
lex->definer->host.str);
|
|
|
|
}
|
|
|
|
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
|
|
|
|
|
|
|
|
DBUG_RETURN(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-06-26 19:14:35 +02:00
|
|
|
/*
|
2006-06-30 22:14:28 +02:00
|
|
|
Execute command saved in thd and lex->sql_command
|
2006-06-26 19:14:35 +02:00
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
mysql_execute_command()
|
|
|
|
thd Thread handle
|
|
|
|
|
|
|
|
IMPLEMENTATION
|
|
|
|
|
|
|
|
Before every operation that can request a write lock for a table
|
|
|
|
wait if a global read lock exists. However do not wait if this
|
|
|
|
thread has locked tables already. No new locks can be requested
|
|
|
|
until the other locks are released. The thread that requests the
|
|
|
|
global read lock waits for write locked tables to become unlocked.
|
|
|
|
|
|
|
|
Note that wait_if_global_read_lock() sets a protection against a new
|
|
|
|
global read lock when it succeeds. This needs to be released by
|
|
|
|
start_waiting_global_read_lock() after the operation.
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
FALSE OK
|
|
|
|
TRUE Error
|
|
|
|
*/
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool
|
2002-10-02 12:33:08 +02:00
|
|
|
mysql_execute_command(THD *thd)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2006-06-26 19:14:35 +02:00
|
|
|
bool res= FALSE;
|
|
|
|
bool need_start_waiting= FALSE; // have protection against global read lock
|
2007-02-16 02:47:39 +01:00
|
|
|
int up_result= 0;
|
2006-06-26 19:14:35 +02:00
|
|
|
LEX *lex= thd->lex;
|
2004-10-29 18:26:52 +02:00
|
|
|
/* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
|
2002-10-03 15:35:08 +02:00
|
|
|
SELECT_LEX *select_lex= &lex->select_lex;
|
2004-07-16 00:15:55 +02:00
|
|
|
/* first table of first SELECT_LEX */
|
2004-10-29 18:26:52 +02:00
|
|
|
TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first;
|
2004-07-16 00:15:55 +02:00
|
|
|
/* list of all tables in query */
|
|
|
|
TABLE_LIST *all_tables;
|
|
|
|
/* most outer SELECT_LEX_UNIT of query */
|
2002-05-08 22:14:40 +02:00
|
|
|
SELECT_LEX_UNIT *unit= &lex->unit;
|
2005-02-08 20:52:50 +01:00
|
|
|
/* Saved variable value */
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_ENTER("mysql_execute_command");
|
2005-01-20 09:41:37 +01:00
|
|
|
thd->net.no_send_error= 0;
|
2006-05-02 19:56:43 +02:00
|
|
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
|
|
|
thd->work_part_info= 0;
|
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
/*
|
|
|
|
In many cases first table of main SELECT_LEX have special meaning =>
|
|
|
|
check that it is first table in global list and relink it first in
|
|
|
|
queries_tables list if it is necessary (we need such relinking only
|
|
|
|
for queries with subqueries in select list, in this case tables of
|
|
|
|
subqueries will go to global list first)
|
|
|
|
|
|
|
|
all_tables will differ from first_table only if most upper SELECT_LEX
|
|
|
|
do not contain tables.
|
|
|
|
|
|
|
|
Because of above in place where should be at least one table in most
|
|
|
|
outer SELECT_LEX we have following check:
|
|
|
|
DBUG_ASSERT(first_table == all_tables);
|
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
|
|
|
*/
|
|
|
|
lex->first_lists_tables_same();
|
2004-09-03 20:43:04 +02:00
|
|
|
/* should be assigned after making first tables same */
|
2004-07-16 00:15:55 +02:00
|
|
|
all_tables= lex->query_tables;
|
2005-07-01 06:05:42 +02:00
|
|
|
/* set context for commands which do not use setup_tables */
|
|
|
|
select_lex->
|
|
|
|
context.resolve_in_table_list_only((TABLE_LIST*)select_lex->
|
|
|
|
table_list.first);
|
2004-07-16 00:15:55 +02:00
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
/*
|
|
|
|
Reset warning count for each query that uses tables
|
|
|
|
A better approach would be to reset this for any commands
|
|
|
|
that is not a SHOW command or a select that only access local
|
|
|
|
variables, but for now this is probably good enough.
|
2005-04-22 12:53:48 +02:00
|
|
|
Don't reset warnings when executing a stored routine.
|
2002-10-02 12:33:08 +02:00
|
|
|
*/
|
2005-04-22 12:53:48 +02:00
|
|
|
if ((all_tables || &lex->select_lex != lex->all_selects_list ||
|
2005-07-18 22:55:37 +02:00
|
|
|
lex->sroutines.records) && !thd->spcont ||
|
2005-06-17 17:56:04 +02:00
|
|
|
lex->time_zone_tables_used)
|
2005-03-16 15:11:01 +01:00
|
|
|
mysql_reset_errors(thd, 0);
|
2002-10-02 12:33:08 +02:00
|
|
|
|
2003-09-29 08:47:37 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2005-10-17 10:52:34 +02:00
|
|
|
if (unlikely(thd->slave_thread))
|
2001-12-19 05:56:36 +01:00
|
|
|
{
|
2007-02-26 12:25:43 +01:00
|
|
|
if (lex->sql_command == SQLCOM_DROP_TRIGGER)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
When dropping a trigger, we need to load its table name
|
|
|
|
before checking slave filter rules.
|
|
|
|
*/
|
|
|
|
add_table_for_trigger(thd, thd->lex->spname, 1, &all_tables);
|
|
|
|
|
|
|
|
if (!all_tables)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
If table name cannot be loaded,
|
|
|
|
it means the trigger does not exists possibly because
|
|
|
|
CREATE TRIGGER was previously skipped for this trigger
|
|
|
|
according to slave filtering rules.
|
|
|
|
Returning success without producing any errors in this case.
|
|
|
|
*/
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// force searching in slave.cc:tables_ok()
|
|
|
|
all_tables->updating= 1;
|
|
|
|
}
|
|
|
|
|
2002-11-30 18:58:53 +01:00
|
|
|
/*
|
2005-10-10 15:10:14 +02:00
|
|
|
Check if statment should be skipped because of slave filtering
|
|
|
|
rules
|
2005-08-31 18:08:45 +02:00
|
|
|
|
|
|
|
Exceptions are:
|
2005-10-10 15:10:14 +02:00
|
|
|
- UPDATE MULTI: For this statement, we want to check the filtering
|
|
|
|
rules later in the code
|
2005-08-31 18:08:45 +02:00
|
|
|
- SET: we always execute it (Not that many SET commands exists in
|
2005-09-01 17:58:00 +02:00
|
|
|
the binary log anyway -- only 4.1 masters write SET statements,
|
|
|
|
in 5.0 there are no SET statements in the binary log)
|
2005-08-31 18:08:45 +02:00
|
|
|
- DROP TEMPORARY TABLE IF EXISTS: we always execute it (otherwise we
|
|
|
|
have stale files on slave caused by exclusion of one tmp table).
|
2001-12-20 12:52:04 +01:00
|
|
|
*/
|
2005-10-10 15:10:14 +02:00
|
|
|
if (!(lex->sql_command == SQLCOM_UPDATE_MULTI) &&
|
|
|
|
!(lex->sql_command == SQLCOM_SET_OPTION) &&
|
2005-08-31 18:08:45 +02:00
|
|
|
!(lex->sql_command == SQLCOM_DROP_TABLE &&
|
2005-02-14 23:47:17 +01:00
|
|
|
lex->drop_temporary && lex->drop_if_exists) &&
|
2005-02-16 21:03:58 +01:00
|
|
|
all_tables_not_ok(thd, all_tables))
|
2003-07-08 15:50:57 +02:00
|
|
|
{
|
|
|
|
/* we warn the slave SQL thread */
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
|
2006-11-01 09:30:01 +01:00
|
|
|
if (thd->one_shot_set)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
It's ok to check thd->one_shot_set here:
|
|
|
|
|
|
|
|
The charsets in a MySQL 5.0 slave can change by both a binlogged
|
|
|
|
SET ONE_SHOT statement and the event-internal charset setting,
|
|
|
|
and these two ways to change charsets do not seems to work
|
|
|
|
together.
|
|
|
|
|
|
|
|
At least there seems to be problems in the rli cache for
|
|
|
|
charsets if we are using ONE_SHOT. Note that this is normally no
|
|
|
|
problem because either the >= 5.0 slave reads a 4.1 binlog (with
|
|
|
|
ONE_SHOT) *or* or 5.0 binlog (without ONE_SHOT) but never both."
|
|
|
|
*/
|
|
|
|
reset_one_shot_variables(thd);
|
|
|
|
}
|
2003-02-12 16:17:03 +01:00
|
|
|
DBUG_RETURN(0);
|
2003-07-08 15:50:57 +02:00
|
|
|
}
|
2001-12-19 05:56:36 +01:00
|
|
|
}
|
2005-10-17 10:52:34 +02:00
|
|
|
else
|
2006-01-23 17:03:09 +01:00
|
|
|
{
|
2005-10-10 19:23:13 +02:00
|
|
|
#endif /* HAVE_REPLICATION */
|
2006-01-23 17:03:09 +01:00
|
|
|
/*
|
|
|
|
When option readonly is set deny operations which change non-temporary
|
|
|
|
tables. Except for the replication thread and the 'super' users.
|
|
|
|
*/
|
|
|
|
if (opt_readonly &&
|
|
|
|
!(thd->security_ctx->master_access & SUPER_ACL) &&
|
2006-06-20 12:20:32 +02:00
|
|
|
(sql_command_flags[lex->sql_command] & CF_CHANGES_DATA) &&
|
2006-11-20 15:35:23 +01:00
|
|
|
!((lex->sql_command == SQLCOM_CREATE_TABLE) &&
|
|
|
|
(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) &&
|
|
|
|
!((lex->sql_command == SQLCOM_DROP_TABLE) && lex->drop_temporary) &&
|
|
|
|
((lex->sql_command != SQLCOM_UPDATE_MULTI) &&
|
|
|
|
some_non_temp_table_to_be_updated(thd, all_tables)))
|
2006-01-23 17:03:09 +01:00
|
|
|
{
|
|
|
|
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
#ifdef HAVE_REPLICATION
|
|
|
|
} /* endif unlikely slave */
|
|
|
|
#endif
|
2006-06-20 12:20:32 +02:00
|
|
|
statistic_increment(thd->status_var.com_stat[lex->sql_command],
|
|
|
|
&LOCK_status);
|
2001-08-18 14:29:21 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
switch (lex->sql_command) {
|
2006-06-20 12:20:32 +02:00
|
|
|
case SQLCOM_SHOW_EVENTS:
|
|
|
|
if ((res= check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0,
|
|
|
|
is_schema_db(thd->lex->select_lex.db))))
|
|
|
|
break;
|
|
|
|
/* fall through */
|
|
|
|
case SQLCOM_SHOW_STATUS_PROC:
|
|
|
|
case SQLCOM_SHOW_STATUS_FUNC:
|
|
|
|
res= execute_sqlcom_select(thd, all_tables);
|
|
|
|
break;
|
|
|
|
case SQLCOM_SHOW_STATUS:
|
|
|
|
{
|
|
|
|
system_status_var old_status_var= thd->status_var;
|
|
|
|
thd->initial_status_var= &old_status_var;
|
|
|
|
res= execute_sqlcom_select(thd, all_tables);
|
|
|
|
/* Don't log SHOW STATUS commands to slow query log */
|
|
|
|
thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED |
|
|
|
|
SERVER_QUERY_NO_GOOD_INDEX_USED);
|
|
|
|
/*
|
|
|
|
restore status variables, as we don't want 'show status' to cause
|
|
|
|
changes
|
|
|
|
*/
|
|
|
|
pthread_mutex_lock(&LOCK_status);
|
|
|
|
add_diff_to_status(&global_status_var, &thd->status_var,
|
|
|
|
&old_status_var);
|
|
|
|
thd->status_var= old_status_var;
|
|
|
|
pthread_mutex_unlock(&LOCK_status);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_SHOW_DATABASES:
|
|
|
|
case SQLCOM_SHOW_TABLES:
|
|
|
|
case SQLCOM_SHOW_TRIGGERS:
|
|
|
|
case SQLCOM_SHOW_TABLE_STATUS:
|
|
|
|
case SQLCOM_SHOW_OPEN_TABLES:
|
|
|
|
case SQLCOM_SHOW_PLUGINS:
|
|
|
|
case SQLCOM_SHOW_FIELDS:
|
|
|
|
case SQLCOM_SHOW_KEYS:
|
|
|
|
case SQLCOM_SHOW_VARIABLES:
|
|
|
|
case SQLCOM_SHOW_CHARSETS:
|
|
|
|
case SQLCOM_SHOW_COLLATIONS:
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SELECT:
|
2006-06-20 12:20:32 +02:00
|
|
|
thd->status_var.last_query_cost= 0.0;
|
2004-07-16 00:15:55 +02:00
|
|
|
if (all_tables)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2006-06-20 12:20:32 +02:00
|
|
|
res= check_table_access(thd,
|
|
|
|
lex->exchange ? SELECT_ACL | FILE_ACL :
|
|
|
|
SELECT_ACL,
|
|
|
|
all_tables, 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
else
|
2004-07-16 00:15:55 +02:00
|
|
|
res= check_access(thd,
|
2006-06-20 12:20:32 +02:00
|
|
|
lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
|
|
|
|
any_db, 0, 0, 0, 0);
|
|
|
|
if (!res)
|
|
|
|
res= execute_sqlcom_select(thd, all_tables);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2004-04-05 17:43:37 +02:00
|
|
|
case SQLCOM_PREPARE:
|
2004-06-07 10:09:10 +02:00
|
|
|
{
|
Implement WL#2661 "Prepared Statements: Dynamic SQL in Stored Procedures".
The idea of the patch is to separate statement processing logic,
such as parsing, validation of the parsed tree, execution and cleanup,
from global query processing logic, such as logging, resetting
priorities of a thread, resetting stored procedure cache, resetting
thread count of errors and warnings.
This makes PREPARE and EXECUTE behave similarly to the rest of SQL
statements and allows their use in stored procedures.
This patch contains a change in behaviour:
until recently for each SQL prepared statement command, 2 queries
were written to the general log, e.g.
[Query] prepare stmt from @stmt_text;
[Prepare] select * from t1 <-- contents of @stmt_text
The chagne was necessary to prevent [Prepare] commands from being written
to the general log when executing a stored procedure with Dynamic SQL.
We should consider whether the old behavior is preferrable and probably
restore it.
This patch refixes Bug#7115, Bug#10975 (partially), Bug#10605 (various bugs
in Dynamic SQL reported before it was disabled).
2005-09-03 01:13:18 +02:00
|
|
|
mysql_sql_stmt_prepare(thd);
|
2004-04-05 17:43:37 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_EXECUTE:
|
|
|
|
{
|
Implement WL#2661 "Prepared Statements: Dynamic SQL in Stored Procedures".
The idea of the patch is to separate statement processing logic,
such as parsing, validation of the parsed tree, execution and cleanup,
from global query processing logic, such as logging, resetting
priorities of a thread, resetting stored procedure cache, resetting
thread count of errors and warnings.
This makes PREPARE and EXECUTE behave similarly to the rest of SQL
statements and allows their use in stored procedures.
This patch contains a change in behaviour:
until recently for each SQL prepared statement command, 2 queries
were written to the general log, e.g.
[Query] prepare stmt from @stmt_text;
[Prepare] select * from t1 <-- contents of @stmt_text
The chagne was necessary to prevent [Prepare] commands from being written
to the general log when executing a stored procedure with Dynamic SQL.
We should consider whether the old behavior is preferrable and probably
restore it.
This patch refixes Bug#7115, Bug#10975 (partially), Bug#10605 (various bugs
in Dynamic SQL reported before it was disabled).
2005-09-03 01:13:18 +02:00
|
|
|
mysql_sql_stmt_execute(thd);
|
2004-04-05 17:43:37 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_DEALLOCATE_PREPARE:
|
|
|
|
{
|
Implement WL#2661 "Prepared Statements: Dynamic SQL in Stored Procedures".
The idea of the patch is to separate statement processing logic,
such as parsing, validation of the parsed tree, execution and cleanup,
from global query processing logic, such as logging, resetting
priorities of a thread, resetting stored procedure cache, resetting
thread count of errors and warnings.
This makes PREPARE and EXECUTE behave similarly to the rest of SQL
statements and allows their use in stored procedures.
This patch contains a change in behaviour:
until recently for each SQL prepared statement command, 2 queries
were written to the general log, e.g.
[Query] prepare stmt from @stmt_text;
[Prepare] select * from t1 <-- contents of @stmt_text
The chagne was necessary to prevent [Prepare] commands from being written
to the general log when executing a stored procedure with Dynamic SQL.
We should consider whether the old behavior is preferrable and probably
restore it.
This patch refixes Bug#7115, Bug#10975 (partially), Bug#10605 (various bugs
in Dynamic SQL reported before it was disabled).
2005-09-03 01:13:18 +02:00
|
|
|
mysql_sql_stmt_close(thd);
|
2004-04-05 17:43:37 +02:00
|
|
|
break;
|
|
|
|
}
|
2001-12-17 18:59:20 +01:00
|
|
|
case SQLCOM_DO:
|
2005-03-04 14:35:28 +01:00
|
|
|
if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
|
|
|
|
open_and_lock_tables(thd, all_tables))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2003-01-03 16:08:00 +01:00
|
|
|
|
|
|
|
res= mysql_do(thd, *lex->insert_list);
|
2001-12-17 18:59:20 +01:00
|
|
|
break;
|
|
|
|
|
2002-02-14 14:04:14 +01:00
|
|
|
case SQLCOM_EMPTY_QUERY:
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2002-02-14 14:04:14 +01:00
|
|
|
break;
|
|
|
|
|
2002-10-28 14:44:19 +01:00
|
|
|
case SQLCOM_HELP:
|
|
|
|
res= mysqld_help(thd,lex->help_arg);
|
|
|
|
break;
|
|
|
|
|
2002-12-16 14:33:29 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2000-10-27 06:11:55 +02:00
|
|
|
case SQLCOM_PURGE:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd, SUPER_ACL))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error;
|
2004-07-07 10:29:39 +02:00
|
|
|
/* PURGE MASTER LOGS TO 'file' */
|
2001-08-14 19:33:49 +02:00
|
|
|
res = purge_master_logs(thd, lex->to_log);
|
|
|
|
break;
|
|
|
|
}
|
2003-02-16 17:39:12 +01:00
|
|
|
case SQLCOM_PURGE_BEFORE:
|
|
|
|
{
|
2004-11-25 16:13:06 +01:00
|
|
|
Item *it;
|
|
|
|
|
2003-02-16 17:39:12 +01:00
|
|
|
if (check_global_access(thd, SUPER_ACL))
|
|
|
|
goto error;
|
2004-07-07 10:29:39 +02:00
|
|
|
/* PURGE MASTER LOGS BEFORE 'data' */
|
2004-11-25 16:13:06 +01:00
|
|
|
it= (Item *)lex->value_list.head();
|
2005-07-01 06:05:42 +02:00
|
|
|
if ((!it->fixed && it->fix_fields(lex->thd, &it)) ||
|
2005-02-08 23:50:45 +01:00
|
|
|
it->check_cols(1))
|
2004-11-25 16:13:06 +01:00
|
|
|
{
|
|
|
|
my_error(ER_WRONG_ARGUMENTS, MYF(0), "PURGE LOGS BEFORE");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
it= new Item_func_unix_timestamp(it);
|
|
|
|
/*
|
|
|
|
it is OK only emulate fix_fieds, because we need only
|
|
|
|
value of constant
|
|
|
|
*/
|
|
|
|
it->quick_fix_field();
|
|
|
|
res = purge_master_logs_before_date(thd, (ulong)it->val_int());
|
2003-02-16 17:39:12 +01:00
|
|
|
break;
|
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
2002-06-12 23:13:12 +02:00
|
|
|
case SQLCOM_SHOW_WARNS:
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
res= mysqld_show_warnings(thd, (ulong)
|
|
|
|
((1L << (uint) MYSQL_ERROR::WARN_LEVEL_NOTE) |
|
2003-01-04 14:37:20 +01:00
|
|
|
(1L << (uint) MYSQL_ERROR::WARN_LEVEL_WARN) |
|
|
|
|
(1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR)
|
|
|
|
));
|
2002-06-12 23:13:12 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_SHOW_ERRORS:
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
res= mysqld_show_warnings(thd, (ulong)
|
|
|
|
(1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR));
|
2002-06-12 23:13:12 +02:00
|
|
|
break;
|
|
|
|
}
|
2001-07-05 01:14:31 +02:00
|
|
|
case SQLCOM_SHOW_NEW_MASTER:
|
|
|
|
{
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd, REPL_SLAVE_ACL))
|
2001-07-05 01:14:31 +02:00
|
|
|
goto error;
|
2003-08-20 01:38:31 +02:00
|
|
|
/* This query don't work now. See comment in repl_failsafe.cc */
|
2002-09-11 05:40:08 +02:00
|
|
|
#ifndef WORKING_NEW_MASTER
|
2004-10-20 03:04:37 +02:00
|
|
|
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "SHOW NEW MASTER");
|
|
|
|
goto error;
|
2002-09-11 05:40:08 +02:00
|
|
|
#else
|
2001-07-05 01:14:31 +02:00
|
|
|
res = show_new_master(thd);
|
|
|
|
break;
|
2004-10-20 03:04:37 +02:00
|
|
|
#endif
|
2001-07-05 01:14:31 +02:00
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
|
2003-09-08 12:08:53 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2001-05-31 02:50:56 +02:00
|
|
|
case SQLCOM_SHOW_SLAVE_HOSTS:
|
|
|
|
{
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd, REPL_SLAVE_ACL))
|
2001-05-31 02:50:56 +02:00
|
|
|
goto error;
|
|
|
|
res = show_slave_hosts(thd);
|
|
|
|
break;
|
|
|
|
}
|
2001-06-21 21:19:24 +02:00
|
|
|
case SQLCOM_SHOW_BINLOG_EVENTS:
|
|
|
|
{
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd, REPL_SLAVE_ACL))
|
2001-06-21 21:19:24 +02:00
|
|
|
goto error;
|
2005-01-16 13:16:23 +01:00
|
|
|
res = mysql_show_binlog_events(thd);
|
2001-06-21 21:19:24 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
|
|
|
|
2000-09-15 00:34:50 +02:00
|
|
|
case SQLCOM_BACKUP_TABLE:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2006-07-07 01:07:45 +02:00
|
|
|
if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
|
2002-06-12 14:04:18 +02:00
|
|
|
check_global_access(thd, FILE_ACL))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2005-06-16 21:05:38 +02:00
|
|
|
thd->enable_slow_log= opt_log_slow_admin_statements;
|
2004-07-16 00:15:55 +02:00
|
|
|
res = mysql_backup_table(thd, first_table);
|
2005-12-07 16:15:02 +01:00
|
|
|
select_lex->table_list.first= (byte*) first_table;
|
2005-12-03 15:02:09 +01:00
|
|
|
lex->query_tables=all_tables;
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-09-15 00:34:50 +02:00
|
|
|
case SQLCOM_RESTORE_TABLE:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2006-07-07 01:07:45 +02:00
|
|
|
if (check_table_access(thd, INSERT_ACL, all_tables, 0) ||
|
2002-06-12 14:04:18 +02:00
|
|
|
check_global_access(thd, FILE_ACL))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2005-06-16 21:05:38 +02:00
|
|
|
thd->enable_slow_log= opt_log_slow_admin_statements;
|
2004-07-16 00:15:55 +02:00
|
|
|
res = mysql_restore_table(thd, first_table);
|
2005-12-07 16:15:02 +01:00
|
|
|
select_lex->table_list.first= (byte*) first_table;
|
2005-12-03 15:02:09 +01:00
|
|
|
lex->query_tables=all_tables;
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-07-16 21:30:49 +02:00
|
|
|
case SQLCOM_ASSIGN_TO_KEYCACHE:
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2006-07-07 01:07:45 +02:00
|
|
|
if (check_access(thd, INDEX_ACL, first_table->db,
|
2005-09-13 13:07:38 +02:00
|
|
|
&first_table->grant.privilege, 0, 0,
|
|
|
|
test(first_table->schema_table)))
|
2003-08-02 12:26:02 +02:00
|
|
|
goto error;
|
2005-01-16 13:16:23 +01:00
|
|
|
res= mysql_assign_to_keycache(thd, first_table, &lex->ident);
|
2003-07-16 21:30:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-06-12 13:29:02 +02:00
|
|
|
case SQLCOM_PRELOAD_KEYS:
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2006-07-07 01:07:45 +02:00
|
|
|
if (check_access(thd, INDEX_ACL, first_table->db,
|
2005-09-13 13:07:38 +02:00
|
|
|
&first_table->grant.privilege, 0, 0,
|
|
|
|
test(first_table->schema_table)))
|
2003-11-14 13:50:19 +01:00
|
|
|
goto error;
|
2004-07-16 00:15:55 +02:00
|
|
|
res = mysql_preload_keys(thd, first_table);
|
2003-06-12 13:29:02 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-09-08 12:08:53 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_CHANGE_MASTER:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd, SUPER_ACL))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error;
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_lock(&LOCK_active_mi);
|
2002-01-20 03:16:52 +01:00
|
|
|
res = change_master(thd,active_mi);
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_unlock(&LOCK_active_mi);
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SHOW_SLAVE_STAT:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2003-05-14 00:55:23 +02:00
|
|
|
/* Accept one of two privileges */
|
|
|
|
if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error;
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_lock(&LOCK_active_mi);
|
2002-01-20 03:16:52 +01:00
|
|
|
res = show_master_info(thd,active_mi);
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_unlock(&LOCK_active_mi);
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SHOW_MASTER_STAT:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2003-05-14 00:55:23 +02:00
|
|
|
/* Accept one of two privileges */
|
|
|
|
if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error;
|
|
|
|
res = show_binlog_info(thd);
|
|
|
|
break;
|
|
|
|
}
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2001-05-29 03:18:23 +02:00
|
|
|
case SQLCOM_LOAD_MASTER_DATA: // sync with master
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd, SUPER_ACL))
|
2001-05-29 03:18:23 +02:00
|
|
|
goto error;
|
2002-08-08 02:12:02 +02:00
|
|
|
if (end_active_trans(thd))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
res = load_master_data(thd);
|
2001-05-29 03:18:23 +02:00
|
|
|
break;
|
2003-09-08 12:08:53 +02:00
|
|
|
#endif /* HAVE_REPLICATION */
|
2006-05-02 19:56:43 +02:00
|
|
|
case SQLCOM_SHOW_ENGINE_STATUS:
|
2002-07-08 18:34:49 +02:00
|
|
|
{
|
2002-08-08 02:12:02 +02:00
|
|
|
if (check_global_access(thd, SUPER_ACL))
|
2006-05-02 19:56:43 +02:00
|
|
|
goto error;
|
|
|
|
res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_STATUS);
|
2002-07-08 18:34:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2006-05-02 19:56:43 +02:00
|
|
|
case SQLCOM_SHOW_ENGINE_MUTEX:
|
2004-12-24 12:13:32 +01:00
|
|
|
{
|
|
|
|
if (check_global_access(thd, SUPER_ACL))
|
2004-12-24 13:31:21 +01:00
|
|
|
goto error;
|
2006-05-02 19:56:43 +02:00
|
|
|
res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_MUTEX);
|
2004-12-24 12:13:32 +01:00
|
|
|
break;
|
|
|
|
}
|
2003-09-08 12:08:53 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_LOAD_MASTER_TABLE:
|
2002-01-20 03:16:52 +01:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
A fix and a test case for
Bug#19022 "Memory bug when switching db during trigger execution"
Bug#17199 "Problem when view calls function from another database."
Bug#18444 "Fully qualified stored function names don't work correctly in
SELECT statements"
Documentation note: this patch introduces a change in behaviour of prepared
statements.
This patch adds a few new invariants with regard to how THD::db should
be used. These invariants should be preserved in future:
- one should never refer to THD::db by pointer and always make a deep copy
(strmake, strdup)
- one should never compare two databases by pointer, but use strncmp or
my_strncasecmp
- TABLE_LIST object table->db should be always initialized in the parser or
by creator of the object.
For prepared statements it means that if the current database is changed
after a statement is prepared, the database that was current at prepare
remains active. This also means that you can not prepare a statement that
implicitly refers to the current database if the latter is not set.
This is not documented, and therefore needs documentation. This is NOT a
change in behavior for almost all SQL statements except:
- ALTER TABLE t1 RENAME t2
- OPTIMIZE TABLE t1
- ANALYZE TABLE t1
- TRUNCATE TABLE t1 --
until this patch t1 or t2 could be evaluated at the first execution of
prepared statement.
CURRENT_DATABASE() still works OK and is evaluated at every execution
of prepared statement.
Note, that in stored routines this is not an issue as the default
database is the database of the stored procedure and "use" statement
is prohibited in stored routines.
This patch makes obsolete the use of check_db_used (it was never used in the
old code too) and all other places that check for table->db and assign it
from THD::db if it's NULL, except the parser.
How this patch was created: THD::{db,db_length} were replaced with a
LEX_STRING, THD::db. All the places that refer to THD::{db,db_length} were
manually checked and:
- if the place uses thd->db by pointer, it was fixed to make a deep copy
- if a place compared two db pointers, it was fixed to compare them by value
(via strcmp/my_strcasecmp, whatever was approproate)
Then this intermediate patch was used to write a smaller patch that does the
same thing but without a rename.
TODO in 5.1:
- remove check_db_used
- deploy THD::set_db in mysql_change_db
See also comments to individual files.
2006-06-26 22:47:52 +02:00
|
|
|
DBUG_ASSERT(first_table->db); /* Must be set in the parser */
|
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
if (check_access(thd, CREATE_ACL, first_table->db,
|
2005-09-13 13:07:38 +02:00
|
|
|
&first_table->grant.privilege, 0, 0,
|
|
|
|
test(first_table->schema_table)))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
if (grant_option)
|
|
|
|
{
|
|
|
|
/* Check that the first table has CREATE privilege */
|
2004-07-16 00:15:55 +02:00
|
|
|
if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_lock(&LOCK_active_mi);
|
2003-09-11 23:17:28 +02:00
|
|
|
/*
|
|
|
|
fetch_master_table will send the error to the client on failure.
|
|
|
|
Give error if the table already exists.
|
|
|
|
*/
|
2005-01-06 12:00:13 +01:00
|
|
|
if (!fetch_master_table(thd, first_table->db, first_table->table_name,
|
2003-09-11 23:17:28 +02:00
|
|
|
active_mi, 0, 0))
|
2002-01-20 03:16:52 +01:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2002-01-20 03:16:52 +01:00
|
|
|
}
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_unlock(&LOCK_active_mi);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2002-01-20 03:16:52 +01:00
|
|
|
}
|
2003-09-08 12:08:53 +02:00
|
|
|
#endif /* HAVE_REPLICATION */
|
2002-12-16 14:33:29 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_CREATE_TABLE:
|
2002-06-12 14:04:18 +02:00
|
|
|
{
|
2005-05-27 04:17:33 +02:00
|
|
|
/* If CREATE TABLE of non-temporary table, do implicit commit */
|
2005-06-01 15:52:32 +02:00
|
|
|
if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
|
|
|
|
{
|
|
|
|
if (end_active_trans(thd))
|
|
|
|
{
|
|
|
|
res= -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
|
|
|
bool link_to_local;
|
|
|
|
// Skip first table, which is the table we are creating
|
|
|
|
TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local);
|
|
|
|
TABLE_LIST *select_tables= lex->query_tables;
|
2003-12-19 15:25:50 +01:00
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
if ((res= create_table_precheck(thd, select_tables, create_table)))
|
2005-06-06 23:31:53 +02:00
|
|
|
goto end_with_restore_list;
|
2004-04-10 00:14:32 +02:00
|
|
|
|
2001-11-03 20:33:11 +01:00
|
|
|
#ifndef HAVE_READLINK
|
2007-01-19 00:02:58 +01:00
|
|
|
if (lex->create_info.data_file_name)
|
2006-12-07 17:01:00 +01:00
|
|
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
|
|
|
|
"DATA DIRECTORY option ignored");
|
2007-01-19 00:02:58 +01:00
|
|
|
if (lex->create_info.index_file_name)
|
2006-12-07 17:01:00 +01:00
|
|
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
|
|
|
|
"INDEX DIRECTORY option ignored");
|
2001-11-03 20:33:11 +01:00
|
|
|
lex->create_info.data_file_name=lex->create_info.index_file_name=0;
|
|
|
|
#else
|
2001-06-01 03:27:59 +02:00
|
|
|
/* Fix names if symlinked tables */
|
2001-12-05 12:03:00 +01:00
|
|
|
if (append_file_to_dir(thd, &lex->create_info.data_file_name,
|
2005-01-06 12:00:13 +01:00
|
|
|
create_table->table_name) ||
|
2004-07-16 00:15:55 +02:00
|
|
|
append_file_to_dir(thd, &lex->create_info.index_file_name,
|
2005-01-06 12:00:13 +01:00
|
|
|
create_table->table_name))
|
2005-06-06 23:31:53 +02:00
|
|
|
goto end_with_restore_list;
|
2001-11-03 20:33:11 +01:00
|
|
|
#endif
|
2003-11-18 12:47:27 +01:00
|
|
|
/*
|
2004-10-07 00:45:06 +02:00
|
|
|
If we are using SET CHARSET without DEFAULT, add an implicit
|
2003-11-18 12:47:27 +01:00
|
|
|
DEFAULT to not confuse old users. (This may change).
|
|
|
|
*/
|
|
|
|
if ((lex->create_info.used_fields &
|
|
|
|
(HA_CREATE_USED_DEFAULT_CHARSET | HA_CREATE_USED_CHARSET)) ==
|
|
|
|
HA_CREATE_USED_CHARSET)
|
|
|
|
{
|
|
|
|
lex->create_info.used_fields&= ~HA_CREATE_USED_CHARSET;
|
|
|
|
lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
|
|
|
|
lex->create_info.default_table_charset= lex->create_info.table_charset;
|
|
|
|
lex->create_info.table_charset= 0;
|
|
|
|
}
|
2005-05-31 11:08:14 +02:00
|
|
|
/*
|
|
|
|
The create-select command will open and read-lock the select table
|
|
|
|
and then create, open and write-lock the new table. If a global
|
|
|
|
read lock steps in, we get a deadlock. The write lock waits for
|
|
|
|
the global read lock, while the global read lock waits for the
|
|
|
|
select table to be closed. So we wait until the global readlock is
|
|
|
|
gone before starting both steps. Note that
|
|
|
|
wait_if_global_read_lock() sets a protection against a new global
|
|
|
|
read lock when it succeeds. This needs to be released by
|
|
|
|
start_waiting_global_read_lock(). We protect the normal CREATE
|
|
|
|
TABLE in the same way. That way we avoid that a new table is
|
|
|
|
created during a gobal read lock.
|
|
|
|
*/
|
2006-06-26 19:14:35 +02:00
|
|
|
if (!thd->locked_tables &&
|
|
|
|
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
|
2005-05-31 11:08:14 +02:00
|
|
|
{
|
2005-06-06 23:31:53 +02:00
|
|
|
res= 1;
|
|
|
|
goto end_with_restore_list;
|
2005-05-31 11:08:14 +02:00
|
|
|
}
|
2006-05-04 19:18:55 +02:00
|
|
|
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
|
|
|
{
|
|
|
|
partition_info *part_info= thd->lex->part_info;
|
|
|
|
if (part_info && !(part_info= thd->lex->part_info->get_clone()))
|
|
|
|
{
|
|
|
|
res= -1;
|
|
|
|
goto end_with_restore_list;
|
|
|
|
}
|
|
|
|
thd->work_part_info= part_info;
|
|
|
|
}
|
|
|
|
#endif
|
2001-06-07 13:10:58 +02:00
|
|
|
if (select_lex->item_list.elements) // With select
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
select_result *result;
|
2004-09-04 14:59:49 +02:00
|
|
|
|
2003-03-12 06:51:08 +01:00
|
|
|
select_lex->options|= SELECT_NO_UNLOCK;
|
2005-05-30 18:54:37 +02:00
|
|
|
unit->set_limit(select_lex);
|
2001-08-14 19:33:49 +02:00
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
if (!(res= open_and_lock_tables(thd, select_tables)))
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2004-09-08 09:18:04 +02:00
|
|
|
/*
|
|
|
|
Is table which we are changing used somewhere in other parts
|
|
|
|
of query
|
|
|
|
*/
|
2005-08-02 21:54:49 +02:00
|
|
|
if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
|
2004-09-08 09:18:04 +02:00
|
|
|
{
|
2005-08-02 21:54:49 +02:00
|
|
|
TABLE_LIST *duplicate;
|
2007-03-01 22:09:22 +01:00
|
|
|
if ((duplicate= unique_table(thd, create_table, select_tables, 0)))
|
2005-08-02 21:54:49 +02:00
|
|
|
{
|
|
|
|
update_non_unique_table_error(create_table, "CREATE", duplicate);
|
|
|
|
res= 1;
|
2006-06-26 19:14:35 +02:00
|
|
|
goto end_with_restore_list;
|
2005-08-02 21:54:49 +02:00
|
|
|
}
|
2004-09-08 09:18:04 +02:00
|
|
|
}
|
2004-09-10 01:22:44 +02:00
|
|
|
/* If we create merge table, we have to test tables in merge, too */
|
|
|
|
if (lex->create_info.used_fields & HA_CREATE_USED_UNION)
|
|
|
|
{
|
|
|
|
TABLE_LIST *tab;
|
|
|
|
for (tab= (TABLE_LIST*) lex->create_info.merge_list.first;
|
|
|
|
tab;
|
|
|
|
tab= tab->next_local)
|
|
|
|
{
|
2005-08-02 21:54:49 +02:00
|
|
|
TABLE_LIST *duplicate;
|
2007-03-01 22:09:22 +01:00
|
|
|
if ((duplicate= unique_table(thd, tab, select_tables, 0)))
|
2004-09-10 01:22:44 +02:00
|
|
|
{
|
2005-08-02 21:54:49 +02:00
|
|
|
update_non_unique_table_error(tab, "CREATE", duplicate);
|
2005-06-06 23:31:53 +02:00
|
|
|
res= 1;
|
2006-06-26 19:14:35 +02:00
|
|
|
goto end_with_restore_list;
|
2004-09-10 01:22:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-09-08 09:18:04 +02:00
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
if ((result= new select_create(create_table,
|
|
|
|
&lex->create_info,
|
|
|
|
lex->create_list,
|
|
|
|
lex->key_list,
|
|
|
|
select_lex->item_list,
|
2005-01-03 22:04:52 +01:00
|
|
|
lex->duplicates,
|
|
|
|
lex->ignore)))
|
2004-06-13 21:39:09 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
CREATE from SELECT give its SELECT_LEX for SELECT,
|
|
|
|
and item_list belong to SELECT
|
|
|
|
*/
|
2005-01-03 20:04:33 +01:00
|
|
|
res= handle_select(thd, lex, result, 0);
|
2004-09-17 02:08:23 +02:00
|
|
|
delete result;
|
2004-06-13 21:39:09 +02:00
|
|
|
}
|
2004-07-07 10:29:39 +02:00
|
|
|
/* reset for PS */
|
2004-04-07 23:16:17 +02:00
|
|
|
lex->create_list.empty();
|
|
|
|
lex->key_list.empty();
|
2001-08-14 19:33:49 +02:00
|
|
|
}
|
|
|
|
}
|
2004-07-07 10:29:39 +02:00
|
|
|
else
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
/* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
|
|
|
|
if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)
|
|
|
|
thd->options|= OPTION_KEEP_LOG;
|
2004-07-07 10:29:39 +02:00
|
|
|
/* regular create */
|
2006-05-02 19:56:43 +02:00
|
|
|
if (lex->like_name)
|
2003-12-19 15:25:50 +01:00
|
|
|
res= mysql_create_like_table(thd, create_table, &lex->create_info,
|
2006-05-02 19:56:43 +02:00
|
|
|
lex->like_name);
|
2002-12-28 10:38:29 +01:00
|
|
|
else
|
2003-08-11 15:18:34 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
res= mysql_create_table(thd, create_table->db,
|
2005-01-06 12:00:13 +01:00
|
|
|
create_table->table_name, &lex->create_info,
|
2004-07-16 00:15:55 +02:00
|
|
|
lex->create_list,
|
2006-06-21 16:57:30 +02:00
|
|
|
lex->key_list, 0, 0, 1);
|
2003-08-11 15:18:34 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!res)
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2004-04-05 23:10:43 +02:00
|
|
|
|
2004-07-07 10:29:39 +02:00
|
|
|
/* put tables back for PS rexecuting */
|
2005-06-06 23:31:53 +02:00
|
|
|
end_with_restore_list:
|
2004-07-16 00:15:55 +02:00
|
|
|
lex->link_first_table_back(create_table, link_to_local);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2002-06-12 14:04:18 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_CREATE_INDEX:
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
|
|
|
if (check_one_table_access(thd, INDEX_ACL, all_tables))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2005-06-16 21:05:38 +02:00
|
|
|
thd->enable_slow_log= opt_log_slow_admin_statements;
|
2000-12-07 13:08:48 +01:00
|
|
|
if (end_active_trans(thd))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2006-05-05 19:08:40 +02:00
|
|
|
res= mysql_create_index(thd, first_table, lex->key_list);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
|
2003-09-08 12:08:53 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SLAVE_START:
|
2002-01-20 03:16:52 +01:00
|
|
|
{
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_lock(&LOCK_active_mi);
|
2002-01-20 03:16:52 +01:00
|
|
|
start_slave(thd,active_mi,1 /* net report*/);
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_unlock(&LOCK_active_mi);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2002-01-20 03:16:52 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SLAVE_STOP:
|
2002-12-13 15:36:08 +01:00
|
|
|
/*
|
|
|
|
If the client thread has locked tables, a deadlock is possible.
|
|
|
|
Assume that
|
|
|
|
- the client thread does LOCK TABLE t READ.
|
|
|
|
- then the master updates t.
|
|
|
|
- then the SQL slave thread wants to update t,
|
2002-12-13 15:01:51 +01:00
|
|
|
so it waits for the client thread because t is locked by it.
|
2002-12-13 15:36:08 +01:00
|
|
|
- then the client thread does SLAVE STOP.
|
2002-12-13 15:01:51 +01:00
|
|
|
SLAVE STOP waits for the SQL slave thread to terminate its
|
|
|
|
update t, which waits for the client thread because t is locked by it.
|
2002-12-13 15:36:08 +01:00
|
|
|
To prevent that, refuse SLAVE STOP if the
|
|
|
|
client thread has locked tables
|
|
|
|
*/
|
2005-10-08 14:46:04 +02:00
|
|
|
if (thd->locked_tables || thd->active_transaction() || thd->global_read_lock)
|
2002-12-13 15:36:08 +01:00
|
|
|
{
|
2005-08-12 12:54:42 +02:00
|
|
|
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
|
|
|
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2002-12-13 15:36:08 +01:00
|
|
|
}
|
2002-01-20 03:16:52 +01:00
|
|
|
{
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_lock(&LOCK_active_mi);
|
2002-01-20 03:16:52 +01:00
|
|
|
stop_slave(thd,active_mi,1/* net report*/);
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_unlock(&LOCK_active_mi);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2002-01-20 03:16:52 +01:00
|
|
|
}
|
2003-09-08 12:08:53 +02:00
|
|
|
#endif /* HAVE_REPLICATION */
|
2002-12-16 14:33:29 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_ALTER_TABLE:
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-06-12 14:04:18 +02:00
|
|
|
ulong priv=0;
|
2006-05-02 19:56:43 +02:00
|
|
|
ulong priv_needed= ALTER_ACL;
|
2006-12-04 18:22:38 +01:00
|
|
|
/*
|
|
|
|
We also require DROP priv for ALTER TABLE ... DROP PARTITION, as well
|
|
|
|
as for RENAME TO, as being done by SQLCOM_RENAME_TABLE
|
|
|
|
*/
|
|
|
|
if (lex->alter_info.flags & (ALTER_DROP_PARTITION | ALTER_RENAME))
|
2006-05-02 19:56:43 +02:00
|
|
|
priv_needed|= DROP_ACL;
|
|
|
|
|
A fix and a test case for
Bug#19022 "Memory bug when switching db during trigger execution"
Bug#17199 "Problem when view calls function from another database."
Bug#18444 "Fully qualified stored function names don't work correctly in
SELECT statements"
Documentation note: this patch introduces a change in behaviour of prepared
statements.
This patch adds a few new invariants with regard to how THD::db should
be used. These invariants should be preserved in future:
- one should never refer to THD::db by pointer and always make a deep copy
(strmake, strdup)
- one should never compare two databases by pointer, but use strncmp or
my_strncasecmp
- TABLE_LIST object table->db should be always initialized in the parser or
by creator of the object.
For prepared statements it means that if the current database is changed
after a statement is prepared, the database that was current at prepare
remains active. This also means that you can not prepare a statement that
implicitly refers to the current database if the latter is not set.
This is not documented, and therefore needs documentation. This is NOT a
change in behavior for almost all SQL statements except:
- ALTER TABLE t1 RENAME t2
- OPTIMIZE TABLE t1
- ANALYZE TABLE t1
- TRUNCATE TABLE t1 --
until this patch t1 or t2 could be evaluated at the first execution of
prepared statement.
CURRENT_DATABASE() still works OK and is evaluated at every execution
of prepared statement.
Note, that in stored routines this is not an issue as the default
database is the database of the stored procedure and "use" statement
is prohibited in stored routines.
This patch makes obsolete the use of check_db_used (it was never used in the
old code too) and all other places that check for table->db and assign it
from THD::db if it's NULL, except the parser.
How this patch was created: THD::{db,db_length} were replaced with a
LEX_STRING, THD::db. All the places that refer to THD::{db,db_length} were
manually checked and:
- if the place uses thd->db by pointer, it was fixed to make a deep copy
- if a place compared two db pointers, it was fixed to compare them by value
(via strcmp/my_strcasecmp, whatever was approproate)
Then this intermediate patch was used to write a smaller patch that does the
same thing but without a rename.
TODO in 5.1:
- remove check_db_used
- deploy THD::set_db in mysql_change_db
See also comments to individual files.
2006-06-26 22:47:52 +02:00
|
|
|
/* Must be set in the parser */
|
|
|
|
DBUG_ASSERT(select_lex->db);
|
2006-05-02 19:56:43 +02:00
|
|
|
if (check_access(thd, priv_needed, first_table->db,
|
2005-09-13 13:07:38 +02:00
|
|
|
&first_table->grant.privilege, 0, 0,
|
|
|
|
test(first_table->schema_table)) ||
|
|
|
|
check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv,0,0,
|
|
|
|
is_schema_db(select_lex->db))||
|
2004-07-16 00:15:55 +02:00
|
|
|
check_merge_table_access(thd, first_table->db,
|
2000-09-14 01:39:07 +02:00
|
|
|
(TABLE_LIST *)
|
|
|
|
lex->create_info.merge_list.first))
|
|
|
|
goto error; /* purecov: inspected */
|
2000-07-31 21:29:14 +02:00
|
|
|
if (grant_option)
|
|
|
|
{
|
2006-05-02 19:56:43 +02:00
|
|
|
if (check_grant(thd, priv_needed, all_tables, 0, UINT_MAX, 0))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error;
|
2006-10-16 18:57:33 +02:00
|
|
|
if (lex->name.str && !test_all_bits(priv,INSERT_ACL | CREATE_ACL))
|
2000-07-31 21:29:14 +02:00
|
|
|
{ // Rename of table
|
|
|
|
TABLE_LIST tmp_table;
|
|
|
|
bzero((char*) &tmp_table,sizeof(tmp_table));
|
2006-10-16 18:57:33 +02:00
|
|
|
tmp_table.table_name= lex->name.str;
|
2001-06-07 13:10:58 +02:00
|
|
|
tmp_table.db=select_lex->db;
|
2000-07-31 21:29:14 +02:00
|
|
|
tmp_table.grant.privilege=priv;
|
2004-04-10 00:14:32 +02:00
|
|
|
if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, 0,
|
|
|
|
UINT_MAX, 0))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
2001-06-01 03:27:59 +02:00
|
|
|
/* Don't yet allow changing of symlinks with ALTER TABLE */
|
2006-06-27 19:22:43 +02:00
|
|
|
if (lex->create_info.data_file_name)
|
|
|
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
|
|
|
|
"DATA DIRECTORY option ignored");
|
|
|
|
if (lex->create_info.index_file_name)
|
|
|
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
|
|
|
|
"INDEX DIRECTORY option ignored");
|
2001-06-01 03:27:59 +02:00
|
|
|
lex->create_info.data_file_name=lex->create_info.index_file_name=0;
|
2000-07-31 21:29:14 +02:00
|
|
|
/* ALTER TABLE ends previous transaction */
|
2000-11-13 22:55:10 +01:00
|
|
|
if (end_active_trans(thd))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2006-05-05 19:08:40 +02:00
|
|
|
|
2006-07-04 10:02:11 +02:00
|
|
|
if (!thd->locked_tables &&
|
|
|
|
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
|
|
|
|
{
|
|
|
|
res= 1;
|
|
|
|
break;
|
2001-12-02 13:34:01 +01:00
|
|
|
}
|
2006-07-04 10:02:11 +02:00
|
|
|
|
2006-05-05 19:08:40 +02:00
|
|
|
thd->enable_slow_log= opt_log_slow_admin_statements;
|
2006-10-16 18:57:33 +02:00
|
|
|
res= mysql_alter_table(thd, select_lex->db, lex->name.str,
|
2006-05-05 19:08:40 +02:00
|
|
|
&lex->create_info,
|
|
|
|
first_table, lex->create_list,
|
|
|
|
lex->key_list,
|
|
|
|
select_lex->order_list.elements,
|
|
|
|
(ORDER *) select_lex->order_list.first,
|
2006-07-02 00:12:53 +02:00
|
|
|
lex->ignore, &lex->alter_info, 1);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-08-21 02:07:54 +02:00
|
|
|
case SQLCOM_RENAME_TABLE:
|
2000-08-21 23:18:32 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2000-08-21 23:18:32 +02:00
|
|
|
TABLE_LIST *table;
|
2004-07-16 00:15:55 +02:00
|
|
|
for (table= first_table; table; table= table->next_local->next_local)
|
2000-08-21 23:18:32 +02:00
|
|
|
{
|
2000-08-29 18:38:32 +02:00
|
|
|
if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
|
2005-09-13 13:07:38 +02:00
|
|
|
&table->grant.privilege,0,0, test(table->schema_table)) ||
|
2004-07-16 00:15:55 +02:00
|
|
|
check_access(thd, INSERT_ACL | CREATE_ACL, table->next_local->db,
|
2005-09-13 13:07:38 +02:00
|
|
|
&table->next_local->grant.privilege, 0, 0,
|
|
|
|
test(table->next_local->schema_table)))
|
2000-08-21 23:18:32 +02:00
|
|
|
goto error;
|
|
|
|
if (grant_option)
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
TABLE_LIST old_list, new_list;
|
2004-03-23 18:24:35 +01:00
|
|
|
/*
|
|
|
|
we do not need initialize old_list and new_list because we will
|
|
|
|
come table[0] and table->next[0] there
|
|
|
|
*/
|
2004-07-16 00:15:55 +02:00
|
|
|
old_list= table[0];
|
|
|
|
new_list= table->next_local[0];
|
|
|
|
if (check_grant(thd, ALTER_ACL, &old_list, 0, 1, 0) ||
|
|
|
|
(!test_all_bits(table->next_local->grant.privilege,
|
2001-08-14 19:33:49 +02:00
|
|
|
INSERT_ACL | CREATE_ACL) &&
|
2004-07-16 00:15:55 +02:00
|
|
|
check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, 1, 0)))
|
2000-08-21 23:18:32 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
2004-07-16 00:15:55 +02:00
|
|
|
query_cache_invalidate3(thd, first_table, 0);
|
2006-05-02 19:56:43 +02:00
|
|
|
if (end_active_trans(thd) || mysql_rename_tables(thd, first_table, 0))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2000-08-21 02:07:54 +02:00
|
|
|
break;
|
2000-08-21 23:18:32 +02:00
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2000-10-27 06:11:55 +02:00
|
|
|
case SQLCOM_SHOW_BINLOGS:
|
|
|
|
#ifdef DONT_ALLOW_SHOW_COMMANDS
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
|
|
|
|
MYF(0)); /* purecov: inspected */
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2000-10-27 06:11:55 +02:00
|
|
|
#else
|
|
|
|
{
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd, SUPER_ACL))
|
2000-10-27 06:11:55 +02:00
|
|
|
goto error;
|
|
|
|
res = show_binlogs(thd);
|
|
|
|
break;
|
|
|
|
}
|
2002-11-30 18:58:53 +01:00
|
|
|
#endif
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif /* EMBEDDED_LIBRARY */
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SHOW_CREATE:
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2000-08-21 02:07:54 +02:00
|
|
|
#ifdef DONT_ALLOW_SHOW_COMMANDS
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
|
|
|
|
MYF(0)); /* purecov: inspected */
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2000-08-21 02:07:54 +02:00
|
|
|
#else
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2005-04-25 02:00:35 +02:00
|
|
|
/* Ignore temporary tables if this is "SHOW CREATE VIEW" */
|
|
|
|
if (lex->only_view)
|
|
|
|
first_table->skip_temporary= 1;
|
|
|
|
|
2006-07-07 01:07:45 +02:00
|
|
|
if (check_access(thd, SELECT_ACL | EXTRA_ACL, first_table->db,
|
2005-09-13 13:07:38 +02:00
|
|
|
&first_table->grant.privilege, 0, 0,
|
|
|
|
test(first_table->schema_table)))
|
2000-08-21 02:07:54 +02:00
|
|
|
goto error;
|
2004-12-22 12:54:39 +01:00
|
|
|
if (grant_option && check_grant(thd, SELECT_ACL, all_tables, 2, UINT_MAX, 0))
|
2004-12-07 20:57:54 +01:00
|
|
|
goto error;
|
2004-12-22 12:54:39 +01:00
|
|
|
res= mysqld_show_create(thd, first_table);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-08-21 02:07:54 +02:00
|
|
|
#endif
|
2003-08-21 16:15:06 +02:00
|
|
|
case SQLCOM_CHECKSUM:
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2006-07-07 01:07:45 +02:00
|
|
|
if (check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables, 0))
|
2003-08-21 16:15:06 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2004-07-16 00:15:55 +02:00
|
|
|
res = mysql_checksum_table(thd, first_table, &lex->check_opt);
|
2003-08-21 16:15:06 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_REPAIR:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2006-07-07 01:07:45 +02:00
|
|
|
if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2005-06-16 21:05:38 +02:00
|
|
|
thd->enable_slow_log= opt_log_slow_admin_statements;
|
2004-07-16 00:15:55 +02:00
|
|
|
res= mysql_repair_table(thd, first_table, &lex->check_opt);
|
2003-05-15 18:35:39 +02:00
|
|
|
/* ! we write after unlocking the table */
|
|
|
|
if (!res && !lex->no_write_to_binlog)
|
|
|
|
{
|
2007-03-01 21:47:28 +01:00
|
|
|
/*
|
|
|
|
Presumably, REPAIR and binlog writing doesn't require synchronization
|
|
|
|
*/
|
2003-05-15 18:35:39 +02:00
|
|
|
if (mysql_bin_log.is_open())
|
|
|
|
{
|
2004-11-03 18:23:58 +01:00
|
|
|
thd->clear_error(); // No binlog error generated
|
2006-05-02 19:56:43 +02:00
|
|
|
thd->binlog_query(THD::STMT_QUERY_TYPE,
|
|
|
|
thd->query, thd->query_length, 0, FALSE);
|
2003-05-15 18:35:39 +02:00
|
|
|
}
|
|
|
|
}
|
2005-12-07 16:15:02 +01:00
|
|
|
select_lex->table_list.first= (byte*) first_table;
|
2005-12-03 15:02:09 +01:00
|
|
|
lex->query_tables=all_tables;
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_CHECK:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2006-07-07 01:07:45 +02:00
|
|
|
if (check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables, 0))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2005-06-16 21:05:38 +02:00
|
|
|
thd->enable_slow_log= opt_log_slow_admin_statements;
|
2004-07-16 00:15:55 +02:00
|
|
|
res = mysql_check_table(thd, first_table, &lex->check_opt);
|
2005-12-07 16:15:02 +01:00
|
|
|
select_lex->table_list.first= (byte*) first_table;
|
2005-12-03 15:02:09 +01:00
|
|
|
lex->query_tables=all_tables;
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_ANALYZE:
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2006-07-07 01:07:45 +02:00
|
|
|
if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2005-06-16 21:05:38 +02:00
|
|
|
thd->enable_slow_log= opt_log_slow_admin_statements;
|
2006-05-05 19:08:40 +02:00
|
|
|
res= mysql_analyze_table(thd, first_table, &lex->check_opt);
|
2003-05-15 18:35:39 +02:00
|
|
|
/* ! we write after unlocking the table */
|
|
|
|
if (!res && !lex->no_write_to_binlog)
|
|
|
|
{
|
2007-03-01 21:47:28 +01:00
|
|
|
/*
|
|
|
|
Presumably, ANALYZE and binlog writing doesn't require synchronization
|
|
|
|
*/
|
2003-05-15 18:35:39 +02:00
|
|
|
if (mysql_bin_log.is_open())
|
|
|
|
{
|
2004-11-03 18:23:58 +01:00
|
|
|
thd->clear_error(); // No binlog error generated
|
2006-05-02 19:56:43 +02:00
|
|
|
thd->binlog_query(THD::STMT_QUERY_TYPE,
|
|
|
|
thd->query, thd->query_length, 0, FALSE);
|
2003-05-15 18:35:39 +02:00
|
|
|
}
|
|
|
|
}
|
2005-12-07 16:15:02 +01:00
|
|
|
select_lex->table_list.first= (byte*) first_table;
|
2005-12-03 15:02:09 +01:00
|
|
|
lex->query_tables=all_tables;
|
2000-08-21 02:07:54 +02:00
|
|
|
break;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2000-09-12 02:02:33 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_OPTIMIZE:
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2006-07-07 01:07:45 +02:00
|
|
|
if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2005-06-16 21:05:38 +02:00
|
|
|
thd->enable_slow_log= opt_log_slow_admin_statements;
|
2004-06-10 16:41:24 +02:00
|
|
|
res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
|
2004-07-16 00:15:55 +02:00
|
|
|
mysql_recreate_table(thd, first_table, 1) :
|
|
|
|
mysql_optimize_table(thd, first_table, &lex->check_opt);
|
2003-05-15 18:35:39 +02:00
|
|
|
/* ! we write after unlocking the table */
|
|
|
|
if (!res && !lex->no_write_to_binlog)
|
|
|
|
{
|
2007-03-01 21:47:28 +01:00
|
|
|
/*
|
|
|
|
Presumably, OPTIMIZE and binlog writing doesn't require synchronization
|
|
|
|
*/
|
2003-05-15 18:35:39 +02:00
|
|
|
if (mysql_bin_log.is_open())
|
|
|
|
{
|
2004-11-03 18:23:58 +01:00
|
|
|
thd->clear_error(); // No binlog error generated
|
2006-05-02 19:56:43 +02:00
|
|
|
thd->binlog_query(THD::STMT_QUERY_TYPE,
|
|
|
|
thd->query, thd->query_length, 0, FALSE);
|
2003-05-15 18:35:39 +02:00
|
|
|
}
|
|
|
|
}
|
2005-12-07 16:15:02 +01:00
|
|
|
select_lex->table_list.first= (byte*) first_table;
|
2005-12-03 15:02:09 +01:00
|
|
|
lex->query_tables=all_tables;
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_UPDATE:
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
|
|
|
if (update_precheck(thd, all_tables))
|
2004-04-10 00:14:32 +02:00
|
|
|
break;
|
2005-06-07 12:11:36 +02:00
|
|
|
DBUG_ASSERT(select_lex->offset_limit == 0);
|
|
|
|
unit->set_limit(select_lex);
|
2007-02-16 02:47:39 +01:00
|
|
|
res= (up_result= mysql_update(thd, all_tables,
|
|
|
|
select_lex->item_list,
|
|
|
|
lex->value_list,
|
|
|
|
select_lex->where,
|
|
|
|
select_lex->order_list.elements,
|
|
|
|
(ORDER *) select_lex->order_list.first,
|
|
|
|
unit->select_limit_cnt,
|
|
|
|
lex->duplicates, lex->ignore));
|
2004-11-25 08:28:32 +01:00
|
|
|
/* mysql_update return 2 if we need to switch to multi-update */
|
2007-02-16 02:47:39 +01:00
|
|
|
if (up_result != 2)
|
2004-09-15 22:42:56 +02:00
|
|
|
break;
|
2007-02-16 02:47:39 +01:00
|
|
|
/* Fall through */
|
2002-11-29 13:17:54 +01:00
|
|
|
case SQLCOM_UPDATE_MULTI:
|
2005-10-12 10:18:46 +02:00
|
|
|
{
|
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
|
|
|
/* if we switched from normal update, rights are checked */
|
2007-02-16 02:47:39 +01:00
|
|
|
if (up_result != 2)
|
2004-09-15 22:42:56 +02:00
|
|
|
{
|
2005-10-12 10:18:46 +02:00
|
|
|
if ((res= multi_update_precheck(thd, all_tables)))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
res= 0;
|
2004-11-25 01:23:13 +01:00
|
|
|
|
2006-01-23 17:03:09 +01:00
|
|
|
res= mysql_multi_update_prepare(thd);
|
2005-10-10 15:10:14 +02:00
|
|
|
|
2005-10-10 21:42:14 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2005-10-12 10:18:46 +02:00
|
|
|
/* Check slave filtering rules */
|
2005-10-17 10:52:34 +02:00
|
|
|
if (unlikely(thd->slave_thread))
|
2005-10-12 10:18:46 +02:00
|
|
|
{
|
2005-10-17 10:52:34 +02:00
|
|
|
if (all_tables_not_ok(thd, all_tables))
|
|
|
|
{
|
2006-01-23 17:03:09 +01:00
|
|
|
if (res!= 0)
|
|
|
|
{
|
|
|
|
res= 0; /* don't care of prev failure */
|
|
|
|
thd->clear_error(); /* filters are of highest prior */
|
|
|
|
}
|
2005-10-17 10:52:34 +02:00
|
|
|
/* we warn the slave SQL thread */
|
|
|
|
my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
|
|
|
|
break;
|
|
|
|
}
|
2006-01-23 17:03:09 +01:00
|
|
|
if (res)
|
|
|
|
break;
|
2005-10-12 10:18:46 +02:00
|
|
|
}
|
2005-10-17 10:52:34 +02:00
|
|
|
else
|
|
|
|
{
|
2006-01-23 17:03:09 +01:00
|
|
|
#endif /* HAVE_REPLICATION */
|
|
|
|
if (res)
|
|
|
|
break;
|
|
|
|
if (opt_readonly &&
|
|
|
|
!(thd->security_ctx->master_access & SUPER_ACL) &&
|
|
|
|
some_non_temp_table_to_be_updated(thd, all_tables))
|
|
|
|
{
|
|
|
|
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#ifdef HAVE_REPLICATION
|
|
|
|
} /* unlikely */
|
|
|
|
#endif
|
2005-10-10 15:10:14 +02:00
|
|
|
|
2005-10-12 10:18:46 +02:00
|
|
|
res= mysql_multi_update(thd, all_tables,
|
|
|
|
&select_lex->item_list,
|
|
|
|
&lex->value_list,
|
|
|
|
select_lex->where,
|
|
|
|
select_lex->options,
|
|
|
|
lex->duplicates, lex->ignore, unit, select_lex);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2004-02-11 00:06:46 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_REPLACE:
|
2002-11-28 17:25:41 +01:00
|
|
|
case SQLCOM_INSERT:
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2004-10-29 18:26:52 +02:00
|
|
|
if ((res= insert_precheck(thd, all_tables)))
|
2004-04-10 00:14:32 +02:00
|
|
|
break;
|
2006-06-26 19:14:35 +02:00
|
|
|
|
|
|
|
if (!thd->locked_tables &&
|
|
|
|
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
|
|
|
|
{
|
|
|
|
res= 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
|
2004-12-22 12:54:39 +01:00
|
|
|
lex->update_list, lex->value_list,
|
2004-12-31 11:04:35 +01:00
|
|
|
lex->duplicates, lex->ignore);
|
2006-10-27 11:32:41 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
If we have inserted into a VIEW, and the base table has
|
|
|
|
AUTO_INCREMENT column, but this column is not accessible through
|
|
|
|
a view, then we should restore LAST_INSERT_ID to the value it
|
|
|
|
had before the statement.
|
|
|
|
*/
|
2004-07-16 00:15:55 +02:00
|
|
|
if (first_table->view && !first_table->contain_auto_increment)
|
2006-10-27 11:40:28 +02:00
|
|
|
thd->first_successful_insert_id_in_cur_stmt=
|
|
|
|
thd->first_successful_insert_id_in_prev_stmt;
|
2006-10-27 11:32:41 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2002-11-28 17:25:41 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_REPLACE_SELECT:
|
|
|
|
case SQLCOM_INSERT_SELECT:
|
|
|
|
{
|
2007-02-16 02:47:39 +01:00
|
|
|
select_result *sel_result;
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2004-12-22 12:54:39 +01:00
|
|
|
if ((res= insert_precheck(thd, all_tables)))
|
2004-04-07 23:16:17 +02:00
|
|
|
break;
|
2003-12-17 16:35:34 +01:00
|
|
|
|
2003-12-14 05:39:52 +01:00
|
|
|
/* Fix lock for first table */
|
2004-07-16 00:15:55 +02:00
|
|
|
if (first_table->lock_type == TL_WRITE_DELAYED)
|
|
|
|
first_table->lock_type= TL_WRITE;
|
2003-12-14 05:39:52 +01:00
|
|
|
|
2003-03-12 06:51:08 +01:00
|
|
|
/* Don't unlock tables until command is written to binary log */
|
|
|
|
select_lex->options|= SELECT_NO_UNLOCK;
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2005-05-30 18:54:37 +02:00
|
|
|
unit->set_limit(select_lex);
|
2006-06-26 19:14:35 +02:00
|
|
|
|
|
|
|
if (! thd->locked_tables &&
|
|
|
|
! (need_start_waiting= ! wait_if_global_read_lock(thd, 0, 1)))
|
|
|
|
{
|
|
|
|
res= 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
if (!(res= open_and_lock_tables(thd, all_tables)))
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2004-09-15 22:42:56 +02:00
|
|
|
/* Skip first table, which is the table we are inserting in */
|
2005-08-12 16:57:19 +02:00
|
|
|
TABLE_LIST *second_table= first_table->next_local;
|
|
|
|
select_lex->table_list.first= (byte*) second_table;
|
2005-08-18 02:12:42 +02:00
|
|
|
select_lex->context.table_list=
|
|
|
|
select_lex->context.first_name_resolution_table= second_table;
|
2004-09-15 22:42:56 +02:00
|
|
|
res= mysql_insert_select_prepare(thd);
|
2007-02-16 02:47:39 +01:00
|
|
|
if (!res && (sel_result= new select_insert(first_table,
|
|
|
|
first_table->table,
|
|
|
|
&lex->field_list,
|
|
|
|
&lex->update_list,
|
|
|
|
&lex->value_list,
|
|
|
|
lex->duplicates,
|
|
|
|
lex->ignore)))
|
2004-09-17 02:08:23 +02:00
|
|
|
{
|
2007-02-16 02:47:39 +01:00
|
|
|
res= handle_select(thd, lex, sel_result, OPTION_SETUP_TABLES_DONE);
|
2006-03-15 18:15:52 +01:00
|
|
|
/*
|
|
|
|
Invalidate the table in the query cache if something changed
|
|
|
|
after unlocking when changes become visible.
|
|
|
|
TODO: this is workaround. right way will be move invalidating in
|
|
|
|
the unlock procedure.
|
|
|
|
*/
|
|
|
|
if (first_table->lock_type == TL_WRITE_CONCURRENT_INSERT &&
|
|
|
|
thd->lock)
|
|
|
|
{
|
2006-11-14 17:50:44 +01:00
|
|
|
/* INSERT ... SELECT should invalidate only the very first table */
|
|
|
|
TABLE_LIST *save_table= first_table->next_local;
|
|
|
|
first_table->next_local= 0;
|
2006-03-15 18:15:52 +01:00
|
|
|
mysql_unlock_tables(thd, thd->lock);
|
|
|
|
query_cache_invalidate3(thd, first_table, 1);
|
2006-11-14 17:50:44 +01:00
|
|
|
first_table->next_local= save_table;
|
2006-03-15 18:15:52 +01:00
|
|
|
thd->lock=0;
|
|
|
|
}
|
2007-02-16 02:47:39 +01:00
|
|
|
delete sel_result;
|
2004-09-17 02:08:23 +02:00
|
|
|
}
|
2004-09-15 22:42:56 +02:00
|
|
|
/* revert changes for SP */
|
2005-05-26 21:01:55 +02:00
|
|
|
select_lex->table_list.first= (byte*) first_table;
|
2001-08-14 19:33:49 +02:00
|
|
|
}
|
2004-07-16 00:15:55 +02:00
|
|
|
|
2006-10-27 11:32:41 +02:00
|
|
|
/*
|
|
|
|
If we have inserted into a VIEW, and the base table has
|
|
|
|
AUTO_INCREMENT column, but this column is not accessible through
|
|
|
|
a view, then we should restore LAST_INSERT_ID to the value it
|
|
|
|
had before the statement.
|
|
|
|
*/
|
2004-07-16 00:15:55 +02:00
|
|
|
if (first_table->view && !first_table->contain_auto_increment)
|
2006-10-27 11:40:28 +02:00
|
|
|
thd->first_successful_insert_id_in_cur_stmt=
|
|
|
|
thd->first_successful_insert_id_in_prev_stmt;
|
2006-10-27 11:32:41 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-11-13 22:55:10 +01:00
|
|
|
case SQLCOM_TRUNCATE:
|
2005-05-27 04:17:33 +02:00
|
|
|
if (end_active_trans(thd))
|
|
|
|
{
|
|
|
|
res= -1;
|
|
|
|
break;
|
|
|
|
}
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2007-02-16 02:47:39 +01:00
|
|
|
if (check_one_table_access(thd, DROP_ACL, all_tables))
|
2003-03-07 11:55:07 +01:00
|
|
|
goto error;
|
2001-09-02 12:47:00 +02:00
|
|
|
/*
|
|
|
|
Don't allow this within a transaction because we want to use
|
|
|
|
re-generate table
|
|
|
|
*/
|
2006-04-06 12:19:01 +02:00
|
|
|
if (thd->locked_tables || thd->active_transaction())
|
2001-09-02 12:47:00 +02:00
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
|
|
|
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
2001-09-02 12:47:00 +02:00
|
|
|
goto error;
|
|
|
|
}
|
2004-07-16 00:15:55 +02:00
|
|
|
|
2004-09-06 14:14:10 +02:00
|
|
|
res= mysql_truncate(thd, first_table, 0);
|
2001-09-02 12:47:00 +02:00
|
|
|
break;
|
2000-11-29 04:09:28 +01:00
|
|
|
case SQLCOM_DELETE:
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
|
|
|
if ((res= delete_precheck(thd, all_tables)))
|
2004-04-10 00:14:32 +02:00
|
|
|
break;
|
2005-06-07 12:11:36 +02:00
|
|
|
DBUG_ASSERT(select_lex->offset_limit == 0);
|
|
|
|
unit->set_limit(select_lex);
|
2006-06-26 19:14:35 +02:00
|
|
|
|
|
|
|
if (!thd->locked_tables &&
|
|
|
|
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
|
|
|
|
{
|
|
|
|
res= 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
res = mysql_delete(thd, all_tables, select_lex->where,
|
2003-12-19 19:16:26 +01:00
|
|
|
&select_lex->order_list,
|
2005-08-30 11:39:20 +02:00
|
|
|
unit->select_limit_cnt, select_lex->options,
|
|
|
|
FALSE);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2001-12-13 21:03:09 +01:00
|
|
|
case SQLCOM_DELETE_MULTI:
|
2001-06-15 04:03:15 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2004-04-07 23:16:17 +02:00
|
|
|
TABLE_LIST *aux_tables=
|
2006-07-03 23:13:04 +02:00
|
|
|
(TABLE_LIST *)thd->lex->auxiliary_table_list.first;
|
2007-02-16 02:47:39 +01:00
|
|
|
multi_delete *del_result;
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2006-06-26 19:14:35 +02:00
|
|
|
if (!thd->locked_tables &&
|
|
|
|
!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
|
|
|
|
{
|
|
|
|
res= 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-06-08 23:07:52 +02:00
|
|
|
if ((res= multi_delete_precheck(thd, all_tables)))
|
2004-04-07 23:16:17 +02:00
|
|
|
break;
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2004-04-12 02:26:32 +02:00
|
|
|
/* condition will be TRUE on SP re-excuting */
|
2004-04-07 23:16:17 +02:00
|
|
|
if (select_lex->item_list.elements != 0)
|
|
|
|
select_lex->item_list.empty();
|
2002-12-06 20:11:27 +01:00
|
|
|
if (add_item_to_list(thd, new Item_null()))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2004-04-07 23:16:17 +02:00
|
|
|
|
2001-06-15 04:03:15 +02:00
|
|
|
thd->proc_info="init";
|
2004-07-16 00:15:55 +02:00
|
|
|
if ((res= open_and_lock_tables(thd, all_tables)))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if ((res= mysql_multi_delete_prepare(thd)))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2004-02-01 14:30:32 +01:00
|
|
|
|
2007-02-16 02:47:39 +01:00
|
|
|
if (!thd->is_fatal_error &&
|
|
|
|
(del_result= new multi_delete(aux_tables, lex->table_count)))
|
2001-06-15 04:03:15 +02:00
|
|
|
{
|
2003-01-25 01:25:52 +01:00
|
|
|
res= mysql_select(thd, &select_lex->ref_pointer_array,
|
|
|
|
select_lex->get_table_list(),
|
|
|
|
select_lex->with_wild,
|
2002-11-26 21:33:33 +01:00
|
|
|
select_lex->item_list,
|
2002-09-03 08:50:36 +02:00
|
|
|
select_lex->where,
|
2003-01-25 01:25:52 +01:00
|
|
|
0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
|
2002-09-03 08:50:36 +02:00
|
|
|
(ORDER *)NULL,
|
|
|
|
select_lex->options | thd->options |
|
2005-01-03 20:04:33 +01:00
|
|
|
SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
|
|
|
|
OPTION_SETUP_TABLES_DONE,
|
2007-02-16 02:47:39 +01:00
|
|
|
del_result, unit, select_lex);
|
|
|
|
delete del_result;
|
2001-06-15 04:03:15 +02:00
|
|
|
}
|
|
|
|
else
|
2005-02-21 13:47:57 +01:00
|
|
|
res= TRUE; // Error
|
2001-06-15 04:03:15 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_DROP_TABLE:
|
2001-08-02 05:29:50 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2003-01-04 14:37:20 +01:00
|
|
|
if (!lex->drop_temporary)
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
if (check_table_access(thd, DROP_ACL, all_tables, 0))
|
2003-01-04 14:37:20 +01:00
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
if (end_active_trans(thd))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2003-01-04 14:37:20 +01:00
|
|
|
}
|
2001-08-02 05:29:50 +02:00
|
|
|
else
|
2003-04-03 20:19:12 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
If this is a slave thread, we may sometimes execute some
|
|
|
|
DROP / * 40005 TEMPORARY * / TABLE
|
|
|
|
that come from parts of binlogs (likely if we use RESET SLAVE or CHANGE
|
|
|
|
MASTER TO), while the temporary table has already been dropped.
|
2004-02-11 00:06:46 +01:00
|
|
|
To not generate such irrelevant "table does not exist errors",
|
|
|
|
we silently add IF EXISTS if TEMPORARY was used.
|
2003-04-03 20:19:12 +02:00
|
|
|
*/
|
|
|
|
if (thd->slave_thread)
|
|
|
|
lex->drop_if_exists= 1;
|
2005-06-01 15:52:32 +02:00
|
|
|
|
2005-06-07 11:47:59 +02:00
|
|
|
/* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
thd->options|= OPTION_KEEP_LOG;
|
2003-04-03 20:19:12 +02:00
|
|
|
}
|
2006-10-03 19:38:25 +02:00
|
|
|
/* DDL and binlog write order protected by LOCK_open */
|
2004-07-16 00:15:55 +02:00
|
|
|
res= mysql_rm_table(thd, first_table, lex->drop_if_exists,
|
|
|
|
lex->drop_temporary);
|
2001-08-02 05:29:50 +02:00
|
|
|
}
|
|
|
|
break;
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_DROP_INDEX:
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
|
|
|
if (check_one_table_access(thd, INDEX_ACL, all_tables))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2000-12-07 13:08:48 +01:00
|
|
|
if (end_active_trans(thd))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2006-05-05 19:08:40 +02:00
|
|
|
res= mysql_drop_index(thd, first_table, &lex->alter_info);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
case SQLCOM_SHOW_PROCESSLIST:
|
2005-09-15 21:29:07 +02:00
|
|
|
if (!thd->security_ctx->priv_user[0] &&
|
|
|
|
check_global_access(thd,PROCESS_ACL))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2003-09-26 12:33:13 +02:00
|
|
|
mysqld_list_processes(thd,
|
2005-09-15 21:29:07 +02:00
|
|
|
(thd->security_ctx->master_access & PROCESS_ACL ?
|
|
|
|
NullS :
|
|
|
|
thd->security_ctx->priv_user),
|
|
|
|
lex->verbose);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2003-12-17 23:52:03 +01:00
|
|
|
case SQLCOM_SHOW_STORAGE_ENGINES:
|
|
|
|
res= mysqld_show_storage_engines(thd);
|
2002-06-12 23:13:12 +02:00
|
|
|
break;
|
2006-05-02 19:56:43 +02:00
|
|
|
case SQLCOM_SHOW_AUTHORS:
|
|
|
|
res= mysqld_show_authors(thd);
|
|
|
|
break;
|
2006-05-03 02:53:26 +02:00
|
|
|
case SQLCOM_SHOW_CONTRIBUTORS:
|
|
|
|
res= mysqld_show_contributors(thd);
|
|
|
|
break;
|
2002-06-12 23:13:12 +02:00
|
|
|
case SQLCOM_SHOW_PRIVILEGES:
|
|
|
|
res= mysqld_show_privileges(thd);
|
|
|
|
break;
|
|
|
|
case SQLCOM_SHOW_COLUMN_TYPES:
|
|
|
|
res= mysqld_show_column_types(thd);
|
|
|
|
break;
|
2006-05-02 19:56:43 +02:00
|
|
|
case SQLCOM_SHOW_ENGINE_LOGS:
|
2000-12-15 12:18:52 +01:00
|
|
|
#ifdef DONT_ALLOW_SHOW_COMMANDS
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
|
|
|
|
MYF(0)); /* purecov: inspected */
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2000-12-15 12:18:52 +01:00
|
|
|
#else
|
|
|
|
{
|
2005-09-13 13:07:38 +02:00
|
|
|
if (grant_option && check_access(thd, FILE_ACL, any_db,0,0,0,0))
|
2000-12-15 12:18:52 +01:00
|
|
|
goto error;
|
2006-05-02 19:56:43 +02:00
|
|
|
res= ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_LOGS);
|
2000-12-15 12:18:52 +01:00
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
#endif
|
|
|
|
case SQLCOM_CHANGE_DB:
|
2005-08-12 02:04:16 +02:00
|
|
|
if (!mysql_change_db(thd,select_lex->db,FALSE))
|
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2003-09-06 15:50:30 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_LOAD:
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
uint privilege= (lex->duplicates == DUP_REPLACE ?
|
2005-03-16 02:32:47 +01:00
|
|
|
INSERT_ACL | DELETE_ACL : INSERT_ACL) |
|
|
|
|
(lex->local_file ? 0 : FILE_ACL);
|
2002-02-13 20:53:26 +01:00
|
|
|
|
2005-03-16 02:32:47 +01:00
|
|
|
if (lex->local_file)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-02-13 20:53:26 +01:00
|
|
|
if (!(thd->client_capabilities & CLIENT_LOCAL_FILES) ||
|
2005-09-01 11:46:43 +02:00
|
|
|
!opt_local_infile)
|
2002-02-13 20:53:26 +01:00
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), MYF(0));
|
2002-02-13 20:53:26 +01:00
|
|
|
goto error;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2005-03-16 02:32:47 +01:00
|
|
|
|
|
|
|
if (check_one_table_access(thd, privilege, all_tables))
|
|
|
|
goto error;
|
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
res= mysql_load(thd, lex->exchange, first_table, lex->field_list,
|
2005-03-16 02:32:47 +01:00
|
|
|
lex->update_list, lex->value_list, lex->duplicates,
|
2005-03-16 10:13:35 +01:00
|
|
|
lex->ignore, (bool) lex->local_file);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-09-06 15:50:30 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SET_OPTION:
|
2004-06-03 23:17:18 +02:00
|
|
|
{
|
|
|
|
List<set_var_base> *lex_var_list= &lex->var_list;
|
2005-03-04 14:35:28 +01:00
|
|
|
if ((check_table_access(thd, SELECT_ACL, all_tables, 0) ||
|
2004-10-20 03:04:37 +02:00
|
|
|
open_and_lock_tables(thd, all_tables)))
|
|
|
|
goto error;
|
2004-06-03 23:17:18 +02:00
|
|
|
if (lex->one_shot_set && not_all_support_one_shot(lex_var_list))
|
|
|
|
{
|
2004-10-20 03:04:37 +02:00
|
|
|
my_error(ER_RESERVED_SYNTAX, MYF(0), "SET ONE_SHOT");
|
|
|
|
goto error;
|
2004-06-03 23:17:18 +02:00
|
|
|
}
|
|
|
|
if (!(res= sql_set_variables(thd, lex_var_list)))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
If the previous command was a SET ONE_SHOT, we don't want to forget
|
|
|
|
about the ONE_SHOT property of that SET. So we use a |= instead of = .
|
|
|
|
*/
|
|
|
|
thd->one_shot_set|= lex->one_shot_set;
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2004-06-03 23:17:18 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2004-06-03 23:17:18 +02:00
|
|
|
}
|
2003-01-04 15:33:42 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_UNLOCK_TABLES:
|
2004-11-10 17:56:45 +01:00
|
|
|
/*
|
|
|
|
It is critical for mysqldump --single-transaction --master-data that
|
|
|
|
UNLOCK TABLES does not implicitely commit a connection which has only
|
|
|
|
done FLUSH TABLES WITH READ LOCK + BEGIN. If this assumption becomes
|
|
|
|
false, mysqldump will not work.
|
|
|
|
*/
|
2002-11-16 19:19:10 +01:00
|
|
|
unlock_locked_tables(thd);
|
2001-09-08 00:02:41 +02:00
|
|
|
if (thd->options & OPTION_TABLE_LOCK)
|
|
|
|
{
|
2001-04-19 19:41:19 +02:00
|
|
|
end_active_trans(thd);
|
2001-09-08 00:02:41 +02:00
|
|
|
thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
if (thd->global_read_lock)
|
2001-08-14 19:33:49 +02:00
|
|
|
unlock_global_read_lock(thd);
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
case SQLCOM_LOCK_TABLES:
|
2002-11-16 19:19:10 +01:00
|
|
|
unlock_locked_tables(thd);
|
2006-07-07 01:07:45 +02:00
|
|
|
if (end_active_trans(thd))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error;
|
2004-07-16 00:15:55 +02:00
|
|
|
if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0))
|
2001-06-03 16:07:26 +02:00
|
|
|
goto error;
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->in_lock_tables=1;
|
2001-09-08 00:02:41 +02:00
|
|
|
thd->options|= OPTION_TABLE_LOCK;
|
2004-07-16 00:15:55 +02:00
|
|
|
|
2005-03-04 14:35:28 +01:00
|
|
|
if (!(res= simple_open_n_lock_tables(thd, all_tables)))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-03-04 17:32:55 +01:00
|
|
|
#ifdef HAVE_QUERY_CACHE
|
|
|
|
if (thd->variables.query_cache_wlock_invalidate)
|
2004-07-16 00:15:55 +02:00
|
|
|
query_cache.invalidate_locked_for_write(first_table);
|
2004-03-04 17:32:55 +01:00
|
|
|
#endif /*HAVE_QUERY_CACHE*/
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->locked_tables=thd->lock;
|
|
|
|
thd->lock=0;
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2001-09-08 00:02:41 +02:00
|
|
|
else
|
|
|
|
thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->in_lock_tables=0;
|
|
|
|
break;
|
|
|
|
case SQLCOM_CREATE_DB:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2005-05-27 04:17:33 +02:00
|
|
|
if (end_active_trans(thd))
|
|
|
|
{
|
|
|
|
res= -1;
|
|
|
|
break;
|
|
|
|
}
|
2003-12-30 12:14:21 +01:00
|
|
|
char *alias;
|
2006-10-16 18:57:33 +02:00
|
|
|
if (!(alias=thd->strmake(lex->name.str, lex->name.length)) ||
|
|
|
|
check_db_name(&lex->name))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2006-10-16 18:57:33 +02:00
|
|
|
my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-01-25 14:07:51 +01:00
|
|
|
/*
|
|
|
|
If in a slave thread :
|
|
|
|
CREATE DATABASE DB was certainly not preceded by USE DB.
|
2004-12-27 12:08:22 +01:00
|
|
|
For that reason, db_ok() in sql/slave.cc did not check the
|
2003-01-25 14:07:51 +01:00
|
|
|
do_db/ignore_db. And as this query involves no tables, tables_ok()
|
|
|
|
above was not called. So we have to check rules again here.
|
|
|
|
*/
|
2003-02-07 14:47:24 +01:00
|
|
|
#ifdef HAVE_REPLICATION
|
2006-05-02 19:56:43 +02:00
|
|
|
if (thd->slave_thread &&
|
2006-10-16 18:57:33 +02:00
|
|
|
(!rpl_filter->db_ok(lex->name.str) ||
|
|
|
|
!rpl_filter->db_ok_with_wild_table(lex->name.str)))
|
2003-07-08 15:50:57 +02:00
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
|
2003-01-25 14:07:51 +01:00
|
|
|
break;
|
2003-07-08 15:50:57 +02:00
|
|
|
}
|
2003-02-07 14:47:24 +01:00
|
|
|
#endif
|
2006-10-16 18:57:33 +02:00
|
|
|
if (check_access(thd,CREATE_ACL,lex->name.str, 0, 1, 0,
|
|
|
|
is_schema_db(lex->name.str)))
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
2006-10-16 18:57:33 +02:00
|
|
|
res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias :
|
|
|
|
lex->name.str), &lex->create_info, 0);
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_DROP_DB:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2005-05-27 04:17:33 +02:00
|
|
|
if (end_active_trans(thd))
|
|
|
|
{
|
|
|
|
res= -1;
|
|
|
|
break;
|
|
|
|
}
|
2006-10-16 18:57:33 +02:00
|
|
|
if (check_db_name(&lex->name))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2006-10-16 18:57:33 +02:00
|
|
|
my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-01-25 14:07:51 +01:00
|
|
|
/*
|
|
|
|
If in a slave thread :
|
|
|
|
DROP DATABASE DB may not be preceded by USE DB.
|
|
|
|
For that reason, maybe db_ok() in sql/slave.cc did not check the
|
|
|
|
do_db/ignore_db. And as this query involves no tables, tables_ok()
|
|
|
|
above was not called. So we have to check rules again here.
|
|
|
|
*/
|
2003-02-07 14:47:24 +01:00
|
|
|
#ifdef HAVE_REPLICATION
|
2003-01-25 14:07:51 +01:00
|
|
|
if (thd->slave_thread &&
|
2006-10-16 18:57:33 +02:00
|
|
|
(!rpl_filter->db_ok(lex->name.str) ||
|
|
|
|
!rpl_filter->db_ok_with_wild_table(lex->name.str)))
|
2003-07-08 15:50:57 +02:00
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
|
2003-01-25 14:07:51 +01:00
|
|
|
break;
|
2003-07-08 15:50:57 +02:00
|
|
|
}
|
2003-02-07 14:47:24 +01:00
|
|
|
#endif
|
2006-10-16 18:57:33 +02:00
|
|
|
if (check_access(thd,DROP_ACL,lex->name.str,0,1,0,
|
|
|
|
is_schema_db(lex->name.str)))
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
2001-09-02 12:47:00 +02:00
|
|
|
if (thd->locked_tables || thd->active_transaction())
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
|
|
|
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
2001-09-02 12:47:00 +02:00
|
|
|
goto error;
|
|
|
|
}
|
2006-10-16 18:57:33 +02:00
|
|
|
res= mysql_rm_db(thd, lex->name.str, lex->drop_if_exists, 0);
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2006-05-02 19:56:43 +02:00
|
|
|
case SQLCOM_RENAME_DB:
|
|
|
|
{
|
|
|
|
LEX_STRING *olddb, *newdb;
|
|
|
|
List_iterator <LEX_STRING> db_list(lex->db_list);
|
|
|
|
olddb= db_list++;
|
|
|
|
newdb= db_list++;
|
|
|
|
if (end_active_trans(thd))
|
|
|
|
{
|
|
|
|
res= 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#ifdef HAVE_REPLICATION
|
|
|
|
if (thd->slave_thread &&
|
|
|
|
(!rpl_filter->db_ok(olddb->str) ||
|
|
|
|
!rpl_filter->db_ok(newdb->str) ||
|
|
|
|
!rpl_filter->db_ok_with_wild_table(olddb->str) ||
|
|
|
|
!rpl_filter->db_ok_with_wild_table(newdb->str)))
|
|
|
|
{
|
|
|
|
res= 1;
|
|
|
|
my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
2006-10-16 18:57:33 +02:00
|
|
|
if (check_db_name(newdb))
|
|
|
|
{
|
|
|
|
my_error(ER_WRONG_DB_NAME, MYF(0), newdb->str);
|
|
|
|
break;
|
|
|
|
}
|
2006-05-02 19:56:43 +02:00
|
|
|
if (check_access(thd,ALTER_ACL,olddb->str,0,1,0,is_schema_db(olddb->str)) ||
|
|
|
|
check_access(thd,DROP_ACL,olddb->str,0,1,0,is_schema_db(olddb->str)) ||
|
|
|
|
check_access(thd,CREATE_ACL,newdb->str,0,1,0,is_schema_db(newdb->str)))
|
|
|
|
{
|
|
|
|
res= 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (thd->locked_tables || thd->active_transaction())
|
|
|
|
{
|
|
|
|
res= 1;
|
|
|
|
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
|
|
|
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
res= mysql_rename_db(thd, olddb, newdb);
|
|
|
|
if (!res)
|
|
|
|
send_ok(thd);
|
|
|
|
break;
|
|
|
|
}
|
2002-06-27 11:41:02 +02:00
|
|
|
case SQLCOM_ALTER_DB:
|
|
|
|
{
|
2006-10-16 18:57:33 +02:00
|
|
|
LEX_STRING *db= &lex->name;
|
|
|
|
if (check_db_name(db))
|
2002-06-27 11:41:02 +02:00
|
|
|
{
|
2006-10-16 18:57:33 +02:00
|
|
|
my_error(ER_WRONG_DB_NAME, MYF(0), db->str);
|
2002-06-27 11:41:02 +02:00
|
|
|
break;
|
|
|
|
}
|
2004-02-28 22:22:29 +01:00
|
|
|
/*
|
|
|
|
If in a slave thread :
|
|
|
|
ALTER DATABASE DB may not be preceded by USE DB.
|
2005-01-24 15:48:25 +01:00
|
|
|
For that reason, maybe db_ok() in sql/slave.cc did not check the
|
2004-02-28 22:22:29 +01:00
|
|
|
do_db/ignore_db. And as this query involves no tables, tables_ok()
|
|
|
|
above was not called. So we have to check rules again here.
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_REPLICATION
|
2005-01-24 15:48:25 +01:00
|
|
|
if (thd->slave_thread &&
|
2006-10-16 18:57:33 +02:00
|
|
|
(!rpl_filter->db_ok(db->str) ||
|
|
|
|
!rpl_filter->db_ok_with_wild_table(db->str)))
|
2004-02-28 22:22:29 +01:00
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
|
2004-02-28 22:22:29 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
2006-10-16 18:57:33 +02:00
|
|
|
if (check_access(thd, ALTER_ACL, db->str, 0, 1, 0, is_schema_db(db->str)))
|
2002-06-27 11:41:02 +02:00
|
|
|
break;
|
|
|
|
if (thd->locked_tables || thd->active_transaction())
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
|
|
|
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
2002-06-27 11:41:02 +02:00
|
|
|
goto error;
|
|
|
|
}
|
2006-10-16 18:57:33 +02:00
|
|
|
res= mysql_alter_db(thd, db->str, &lex->create_info);
|
2002-06-27 11:41:02 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-07-02 11:31:54 +02:00
|
|
|
case SQLCOM_SHOW_CREATE_DB:
|
|
|
|
{
|
2006-10-16 18:57:33 +02:00
|
|
|
if (check_db_name(&lex->name))
|
2002-07-02 11:31:54 +02:00
|
|
|
{
|
2006-10-16 18:57:33 +02:00
|
|
|
my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
|
2002-07-02 11:31:54 +02:00
|
|
|
break;
|
|
|
|
}
|
2006-10-16 18:57:33 +02:00
|
|
|
res= mysqld_show_create_db(thd, lex->name.str, &lex->create_info);
|
2002-07-02 11:31:54 +02:00
|
|
|
break;
|
|
|
|
}
|
2006-05-02 19:56:43 +02:00
|
|
|
case SQLCOM_CREATE_EVENT:
|
|
|
|
case SQLCOM_ALTER_EVENT:
|
2006-11-28 19:35:20 +01:00
|
|
|
do
|
2006-05-02 19:56:43 +02:00
|
|
|
{
|
2006-06-29 00:42:25 +02:00
|
|
|
DBUG_ASSERT(lex->event_parse_data);
|
2006-11-02 13:51:43 +01:00
|
|
|
if (lex->table_or_sp_used())
|
|
|
|
{
|
|
|
|
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored "
|
|
|
|
"function calls as part of this statement");
|
|
|
|
break;
|
|
|
|
}
|
2007-02-02 18:43:33 +01:00
|
|
|
|
|
|
|
res= sp_process_definer(thd);
|
|
|
|
if (res)
|
|
|
|
break;
|
|
|
|
|
2006-06-29 00:42:25 +02:00
|
|
|
switch (lex->sql_command) {
|
|
|
|
case SQLCOM_CREATE_EVENT:
|
2006-07-05 17:12:50 +02:00
|
|
|
res= Events::get_instance()->
|
|
|
|
create_event(thd, lex->event_parse_data,
|
2006-08-28 10:27:42 +02:00
|
|
|
lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS);
|
2006-06-29 00:42:25 +02:00
|
|
|
break;
|
|
|
|
case SQLCOM_ALTER_EVENT:
|
2006-08-28 10:27:42 +02:00
|
|
|
res= Events::get_instance()->update_event(thd, lex->event_parse_data,
|
|
|
|
lex->spname);
|
2006-06-29 00:42:25 +02:00
|
|
|
break;
|
2006-08-28 10:27:42 +02:00
|
|
|
default:
|
|
|
|
DBUG_ASSERT(0);
|
2006-06-29 00:42:25 +02:00
|
|
|
}
|
2006-08-28 10:27:42 +02:00
|
|
|
DBUG_PRINT("info",("DDL error code=%d", res));
|
2006-06-29 00:42:25 +02:00
|
|
|
if (!res)
|
2006-08-28 10:27:42 +02:00
|
|
|
send_ok(thd);
|
2006-05-02 19:56:43 +02:00
|
|
|
|
2006-11-28 19:35:20 +01:00
|
|
|
} while (0);
|
|
|
|
/* Don't do it, if we are inside a SP */
|
|
|
|
if (!thd->spcont)
|
|
|
|
{
|
|
|
|
delete lex->sphead;
|
|
|
|
lex->sphead= NULL;
|
2006-05-02 19:56:43 +02:00
|
|
|
}
|
2006-11-28 19:35:20 +01:00
|
|
|
/* lex->unit.cleanup() is called outside, no need to call it here */
|
|
|
|
break;
|
2006-06-27 11:51:11 +02:00
|
|
|
case SQLCOM_DROP_EVENT:
|
2006-05-02 19:56:43 +02:00
|
|
|
case SQLCOM_SHOW_CREATE_EVENT:
|
|
|
|
{
|
|
|
|
DBUG_ASSERT(lex->spname);
|
|
|
|
if (! lex->spname->m_db.str)
|
|
|
|
{
|
|
|
|
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
|
2006-07-05 17:12:50 +02:00
|
|
|
goto error;
|
2006-05-02 19:56:43 +02:00
|
|
|
}
|
|
|
|
if (check_access(thd, EVENT_ACL, lex->spname->m_db.str, 0, 0, 0,
|
|
|
|
is_schema_db(lex->spname->m_db.str)))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (lex->spname->m_name.length > NAME_LEN)
|
|
|
|
{
|
|
|
|
my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
|
2006-06-27 11:51:11 +02:00
|
|
|
/* this jumps to the end of the function and skips own messaging */
|
2006-05-02 19:56:43 +02:00
|
|
|
goto error;
|
|
|
|
}
|
2006-06-27 11:51:11 +02:00
|
|
|
|
|
|
|
if (lex->sql_command == SQLCOM_SHOW_CREATE_EVENT)
|
2006-07-11 18:28:15 +02:00
|
|
|
res= Events::get_instance()->show_create_event(thd, lex->spname->m_db,
|
|
|
|
lex->spname->m_name);
|
2006-06-27 11:51:11 +02:00
|
|
|
else
|
|
|
|
{
|
2006-07-10 13:44:43 +02:00
|
|
|
if (!(res= Events::get_instance()->drop_event(thd,
|
|
|
|
lex->spname->m_db,
|
|
|
|
lex->spname->m_name,
|
2007-01-29 18:46:29 +01:00
|
|
|
lex->drop_if_exists)))
|
2006-08-28 10:27:42 +02:00
|
|
|
send_ok(thd);
|
2006-06-27 11:51:11 +02:00
|
|
|
}
|
2006-05-22 20:46:13 +02:00
|
|
|
break;
|
|
|
|
}
|
2004-07-07 10:29:39 +02:00
|
|
|
case SQLCOM_CREATE_FUNCTION: // UDF function
|
2003-12-21 11:48:03 +01:00
|
|
|
{
|
2005-09-13 13:07:38 +02:00
|
|
|
if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0))
|
2003-12-21 11:48:03 +01:00
|
|
|
break;
|
2000-07-31 21:29:14 +02:00
|
|
|
#ifdef HAVE_DLOPEN
|
2005-11-23 00:11:19 +01:00
|
|
|
if (sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
|
|
|
|
&thd->sp_func_cache, FALSE))
|
2003-12-21 11:48:03 +01:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_UDF_EXISTS, MYF(0), lex->spname->m_name.str);
|
2003-12-21 11:48:03 +01:00
|
|
|
goto error;
|
|
|
|
}
|
2004-11-23 19:19:09 +01:00
|
|
|
if (!(res = mysql_create_function(thd, &lex->udf)))
|
2003-12-21 11:48:03 +01:00
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
#else
|
2005-08-10 13:02:36 +02:00
|
|
|
my_error(ER_CANT_OPEN_LIBRARY, MYF(0), lex->udf.dl, 0, "feature disabled");
|
2004-10-20 03:04:37 +02:00
|
|
|
res= TRUE;
|
2000-07-31 21:29:14 +02:00
|
|
|
#endif
|
|
|
|
break;
|
2003-12-21 11:48:03 +01:00
|
|
|
}
|
2003-09-26 12:33:13 +02:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2004-11-25 21:55:49 +01:00
|
|
|
case SQLCOM_CREATE_USER:
|
|
|
|
{
|
2005-09-13 13:07:38 +02:00
|
|
|
if (check_access(thd, INSERT_ACL, "mysql", 0, 1, 1, 0) &&
|
2005-03-22 15:54:18 +01:00
|
|
|
check_global_access(thd,CREATE_USER_ACL))
|
2004-11-25 21:55:49 +01:00
|
|
|
break;
|
2005-10-13 11:12:17 +02:00
|
|
|
if (end_active_trans(thd))
|
|
|
|
goto error;
|
2006-10-03 19:38:25 +02:00
|
|
|
/* Conditionally writes to binlog */
|
2004-11-25 21:55:49 +01:00
|
|
|
if (!(res= mysql_create_user(thd, lex->users_list)))
|
|
|
|
send_ok(thd);
|
|
|
|
break;
|
|
|
|
}
|
2003-06-06 14:43:23 +02:00
|
|
|
case SQLCOM_DROP_USER:
|
|
|
|
{
|
2005-09-13 13:07:38 +02:00
|
|
|
if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 1, 0) &&
|
2005-03-22 15:54:18 +01:00
|
|
|
check_global_access(thd,CREATE_USER_ACL))
|
2003-06-06 14:43:23 +02:00
|
|
|
break;
|
2005-10-13 11:12:17 +02:00
|
|
|
if (end_active_trans(thd))
|
|
|
|
goto error;
|
2006-10-03 19:38:25 +02:00
|
|
|
/* Conditionally writes to binlog */
|
2003-06-06 14:43:23 +02:00
|
|
|
if (!(res= mysql_drop_user(thd, lex->users_list)))
|
2004-11-25 21:55:49 +01:00
|
|
|
send_ok(thd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_RENAME_USER:
|
|
|
|
{
|
2005-09-13 13:07:38 +02:00
|
|
|
if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) &&
|
2005-03-22 15:54:18 +01:00
|
|
|
check_global_access(thd,CREATE_USER_ACL))
|
2004-11-25 21:55:49 +01:00
|
|
|
break;
|
2005-10-13 11:12:17 +02:00
|
|
|
if (end_active_trans(thd))
|
|
|
|
goto error;
|
2006-10-03 19:38:25 +02:00
|
|
|
/* Conditionally writes to binlog */
|
2004-11-25 21:55:49 +01:00
|
|
|
if (!(res= mysql_rename_user(thd, lex->users_list)))
|
2003-06-06 14:43:23 +02:00
|
|
|
send_ok(thd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_REVOKE_ALL:
|
|
|
|
{
|
2005-09-13 13:07:38 +02:00
|
|
|
if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) &&
|
2005-03-22 15:54:18 +01:00
|
|
|
check_global_access(thd,CREATE_USER_ACL))
|
2003-06-06 14:43:23 +02:00
|
|
|
break;
|
2006-10-03 19:38:25 +02:00
|
|
|
/* Conditionally writes to binlog */
|
2003-06-06 14:43:23 +02:00
|
|
|
if (!(res = mysql_revoke_all(thd, lex->users_list)))
|
|
|
|
send_ok(thd);
|
|
|
|
break;
|
|
|
|
}
|
2001-08-14 19:33:49 +02:00
|
|
|
case SQLCOM_REVOKE:
|
|
|
|
case SQLCOM_GRANT:
|
|
|
|
{
|
|
|
|
if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
|
2005-01-24 19:41:42 +01:00
|
|
|
first_table ? first_table->db : select_lex->db,
|
2004-07-16 00:15:55 +02:00
|
|
|
first_table ? &first_table->grant.privilege : 0,
|
2005-09-13 13:07:38 +02:00
|
|
|
first_table ? 0 : 1, 0,
|
|
|
|
first_table ? (bool) first_table->schema_table :
|
|
|
|
select_lex->db ? is_schema_db(select_lex->db) : 0))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error;
|
|
|
|
|
2005-09-15 21:29:07 +02:00
|
|
|
if (thd->security_ctx->user) // If not replication
|
2005-03-17 07:16:56 +01:00
|
|
|
{
|
2006-06-29 12:50:44 +02:00
|
|
|
LEX_USER *user, *tmp_user;
|
2005-03-18 12:32:28 +01:00
|
|
|
|
2005-03-17 07:16:56 +01:00
|
|
|
List_iterator <LEX_USER> user_list(lex->users_list);
|
2006-06-29 12:50:44 +02:00
|
|
|
while ((tmp_user= user_list++))
|
2005-03-17 07:16:56 +01:00
|
|
|
{
|
2006-06-29 12:50:44 +02:00
|
|
|
if (!(user= get_current_user(thd, tmp_user)))
|
|
|
|
goto error;
|
2005-03-22 15:54:18 +01:00
|
|
|
if (specialflag & SPECIAL_NO_RESOLVE &&
|
|
|
|
hostname_requires_resolving(user->host.str))
|
|
|
|
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
|
|
ER_WARN_HOSTNAME_WONT_WORK,
|
|
|
|
ER(ER_WARN_HOSTNAME_WONT_WORK),
|
|
|
|
user->host.str);
|
|
|
|
// Are we trying to change a password of another user
|
|
|
|
DBUG_ASSERT(user->host.str != 0);
|
2005-09-15 21:29:07 +02:00
|
|
|
if (strcmp(thd->security_ctx->user, user->user.str) ||
|
2005-03-22 15:54:18 +01:00
|
|
|
my_strcasecmp(system_charset_info,
|
2005-09-15 21:29:07 +02:00
|
|
|
user->host.str, thd->security_ctx->host_or_ip))
|
2005-03-22 15:54:18 +01:00
|
|
|
{
|
|
|
|
// TODO: use check_change_password()
|
2006-05-06 09:25:59 +02:00
|
|
|
if (is_acl_user(user->host.str, user->user.str) &&
|
|
|
|
user->password.str &&
|
2005-09-13 13:07:38 +02:00
|
|
|
check_access(thd, UPDATE_ACL,"mysql",0,1,1,0))
|
2005-03-22 15:54:18 +01:00
|
|
|
{
|
|
|
|
my_message(ER_PASSWORD_NOT_ALLOWED,
|
|
|
|
ER(ER_PASSWORD_NOT_ALLOWED), MYF(0));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
2003-11-20 09:55:48 +01:00
|
|
|
}
|
|
|
|
}
|
2004-07-16 00:15:55 +02:00
|
|
|
if (first_table)
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2005-05-17 20:54:20 +02:00
|
|
|
if (lex->type == TYPE_ENUM_PROCEDURE ||
|
|
|
|
lex->type == TYPE_ENUM_FUNCTION)
|
2004-12-23 11:46:24 +01:00
|
|
|
{
|
|
|
|
uint grants= lex->all_privileges
|
|
|
|
? (PROC_ACLS & ~GRANT_ACL) | (lex->grant & GRANT_ACL)
|
|
|
|
: lex->grant;
|
|
|
|
if (grant_option &&
|
2005-05-17 20:54:20 +02:00
|
|
|
check_grant_routine(thd, grants | GRANT_ACL, all_tables,
|
|
|
|
lex->type == TYPE_ENUM_PROCEDURE, 0))
|
2004-12-23 11:46:24 +01:00
|
|
|
goto error;
|
2006-10-03 19:38:25 +02:00
|
|
|
/* Conditionally writes to binlog */
|
2005-05-17 20:54:20 +02:00
|
|
|
res= mysql_routine_grant(thd, all_tables,
|
|
|
|
lex->type == TYPE_ENUM_PROCEDURE,
|
|
|
|
lex->users_list, grants,
|
|
|
|
lex->sql_command == SQLCOM_REVOKE, 0);
|
2004-12-23 11:46:24 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (grant_option && check_grant(thd,
|
|
|
|
(lex->grant | lex->grant_tot_col |
|
|
|
|
GRANT_ACL),
|
|
|
|
all_tables, 0, UINT_MAX, 0))
|
|
|
|
goto error;
|
2006-10-03 19:38:25 +02:00
|
|
|
/* Conditionally writes to binlog */
|
2004-12-23 11:46:24 +01:00
|
|
|
res= mysql_table_grant(thd, all_tables, lex->users_list,
|
|
|
|
lex->columns, lex->grant,
|
|
|
|
lex->sql_command == SQLCOM_REVOKE);
|
|
|
|
}
|
2001-08-14 19:33:49 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-05-17 20:54:20 +02:00
|
|
|
if (lex->columns.elements || lex->type)
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
|
|
|
|
MYF(0));
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2001-08-14 19:33:49 +02:00
|
|
|
}
|
|
|
|
else
|
2006-10-03 19:38:25 +02:00
|
|
|
/* Conditionally writes to binlog */
|
2001-08-14 19:33:49 +02:00
|
|
|
res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
|
|
|
|
lex->sql_command == SQLCOM_REVOKE);
|
|
|
|
if (!res)
|
|
|
|
{
|
2004-12-29 18:30:37 +01:00
|
|
|
if (lex->sql_command == SQLCOM_GRANT)
|
2002-02-01 19:53:24 +01:00
|
|
|
{
|
2002-02-13 21:37:19 +01:00
|
|
|
List_iterator <LEX_USER> str_list(lex->users_list);
|
2006-06-29 12:50:44 +02:00
|
|
|
LEX_USER *user, *tmp_user;
|
|
|
|
while ((tmp_user=str_list++))
|
|
|
|
{
|
|
|
|
if (!(user= get_current_user(thd, tmp_user)))
|
|
|
|
goto error;
|
2007-03-01 21:47:28 +01:00
|
|
|
reset_mqh(user, 0);
|
2006-06-29 12:50:44 +02:00
|
|
|
}
|
2002-02-01 19:53:24 +01:00
|
|
|
}
|
2001-08-14 19:33:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2003-09-29 08:47:37 +02:00
|
|
|
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
|
2000-10-14 10:16:17 +02:00
|
|
|
case SQLCOM_RESET:
|
2005-02-26 11:19:02 +01:00
|
|
|
/*
|
|
|
|
RESET commands are never written to the binary log, so we have to
|
|
|
|
initialize this variable because RESET shares the same code as FLUSH
|
2003-05-15 18:35:39 +02:00
|
|
|
*/
|
|
|
|
lex->no_write_to_binlog= 1;
|
|
|
|
case SQLCOM_FLUSH:
|
|
|
|
{
|
2005-08-11 14:58:15 +02:00
|
|
|
bool write_to_binlog;
|
2006-07-07 01:07:45 +02:00
|
|
|
if (check_global_access(thd,RELOAD_ACL))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error;
|
2006-12-01 09:49:19 +01:00
|
|
|
|
2003-05-15 18:35:39 +02:00
|
|
|
/*
|
|
|
|
reload_acl_and_cache() will tell us if we are allowed to write to the
|
|
|
|
binlog or not.
|
|
|
|
*/
|
2004-10-20 03:04:37 +02:00
|
|
|
if (!reload_acl_and_cache(thd, lex->type, first_table, &write_to_binlog))
|
2003-05-15 18:35:39 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
We WANT to write and we CAN write.
|
|
|
|
! we write after unlocking the table.
|
|
|
|
*/
|
2007-03-01 21:47:28 +01:00
|
|
|
/*
|
|
|
|
Presumably, RESET and binlog writing doesn't require synchronization
|
|
|
|
*/
|
2003-05-15 18:35:39 +02:00
|
|
|
if (!lex->no_write_to_binlog && write_to_binlog)
|
|
|
|
{
|
|
|
|
if (mysql_bin_log.is_open())
|
|
|
|
{
|
2006-05-02 19:56:43 +02:00
|
|
|
thd->binlog_query(THD::STMT_QUERY_TYPE,
|
|
|
|
thd->query, thd->query_length, 0, FALSE);
|
2003-05-15 18:35:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
send_ok(thd);
|
2006-12-01 09:49:19 +01:00
|
|
|
}
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2003-05-15 18:35:39 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_KILL:
|
2004-11-25 16:13:06 +01:00
|
|
|
{
|
|
|
|
Item *it= (Item *)lex->value_list.head();
|
|
|
|
|
2006-11-02 13:51:43 +01:00
|
|
|
if (lex->table_or_sp_used())
|
|
|
|
{
|
|
|
|
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored "
|
|
|
|
"function calls as part of this statement");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-07-01 06:05:42 +02:00
|
|
|
if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1))
|
2004-11-25 16:13:06 +01:00
|
|
|
{
|
|
|
|
my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
|
|
|
|
MYF(0));
|
|
|
|
goto error;
|
|
|
|
}
|
2006-05-22 20:46:13 +02:00
|
|
|
sql_kill(thd, (ulong)it->val_int(), lex->type & ONLY_KILL_QUERY);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2004-11-25 16:13:06 +01:00
|
|
|
}
|
2003-09-26 12:33:13 +02:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SHOW_GRANTS:
|
2006-06-29 12:50:44 +02:00
|
|
|
{
|
|
|
|
LEX_USER *grant_user= get_current_user(thd, lex->grant_user);
|
|
|
|
if (!grant_user)
|
|
|
|
goto error;
|
2005-09-15 21:29:07 +02:00
|
|
|
if ((thd->security_ctx->priv_user &&
|
2006-06-29 12:50:44 +02:00
|
|
|
!strcmp(thd->security_ctx->priv_user, grant_user->user.str)) ||
|
2005-09-13 13:07:38 +02:00
|
|
|
!check_access(thd, SELECT_ACL, "mysql",0,1,0,0))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2006-06-29 12:50:44 +02:00
|
|
|
res = mysql_show_grants(thd, grant_user);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
break;
|
2006-06-29 12:50:44 +02:00
|
|
|
}
|
2003-09-26 12:33:13 +02:00
|
|
|
#endif
|
2001-04-07 00:18:33 +02:00
|
|
|
case SQLCOM_HA_OPEN:
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2006-07-07 01:07:45 +02:00
|
|
|
if (check_table_access(thd, SELECT_ACL, all_tables, 0))
|
2001-04-07 00:18:33 +02:00
|
|
|
goto error;
|
2005-03-16 15:11:01 +01:00
|
|
|
res= mysql_ha_open(thd, first_table, 0);
|
2001-04-07 00:18:33 +02:00
|
|
|
break;
|
|
|
|
case SQLCOM_HA_CLOSE:
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
|
|
|
res= mysql_ha_close(thd, first_table);
|
2001-04-07 00:18:33 +02:00
|
|
|
break;
|
|
|
|
case SQLCOM_HA_READ:
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2003-07-03 10:55:36 +02:00
|
|
|
/*
|
|
|
|
There is no need to check for table permissions here, because
|
|
|
|
if a user has no permissions to read a table, he won't be
|
|
|
|
able to open it (with SQLCOM_HA_OPEN) in the first place.
|
|
|
|
*/
|
2005-06-07 12:11:36 +02:00
|
|
|
unit->set_limit(select_lex);
|
2005-01-16 13:16:23 +01:00
|
|
|
res= mysql_ha_read(thd, first_table, lex->ha_read_mode, lex->ident.str,
|
2004-07-16 00:15:55 +02:00
|
|
|
lex->insert_list, lex->ha_rkey_mode, select_lex->where,
|
2005-06-07 12:11:36 +02:00
|
|
|
unit->select_limit_cnt, unit->offset_limit_cnt);
|
2001-04-07 00:18:33 +02:00
|
|
|
break;
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_BEGIN:
|
2005-10-05 19:58:16 +02:00
|
|
|
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;
|
|
|
|
}
|
2005-02-01 20:48:05 +01:00
|
|
|
if (begin_trans(thd))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2005-02-14 21:50:09 +01:00
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
case SQLCOM_COMMIT:
|
2005-02-17 13:52:16 +01:00
|
|
|
if (end_trans(thd, lex->tx_release ? COMMIT_RELEASE :
|
2005-02-16 17:34:02 +01:00
|
|
|
lex->tx_chain ? COMMIT_AND_CHAIN : COMMIT))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2005-02-17 13:52:16 +01:00
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
case SQLCOM_ROLLBACK:
|
2005-02-17 13:52:16 +01:00
|
|
|
if (end_trans(thd, lex->tx_release ? ROLLBACK_RELEASE :
|
2005-02-16 17:34:02 +01:00
|
|
|
lex->tx_chain ? ROLLBACK_AND_CHAIN : ROLLBACK))
|
2005-02-01 20:48:05 +01:00
|
|
|
goto error;
|
2005-02-17 13:52:16 +01:00
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2005-02-14 21:50:09 +01:00
|
|
|
case SQLCOM_RELEASE_SAVEPOINT:
|
2005-02-16 17:34:02 +01:00
|
|
|
{
|
2005-03-11 21:10:41 +01:00
|
|
|
SAVEPOINT *sv;
|
|
|
|
for (sv=thd->transaction.savepoints; sv; sv=sv->prev)
|
2005-02-14 21:50:09 +01:00
|
|
|
{
|
|
|
|
if (my_strnncoll(system_charset_info,
|
|
|
|
(uchar *)lex->ident.str, lex->ident.length,
|
2005-03-11 21:10:41 +01:00
|
|
|
(uchar *)sv->name, sv->length) == 0)
|
2005-02-14 21:50:09 +01:00
|
|
|
break;
|
|
|
|
}
|
2005-03-11 21:10:41 +01:00
|
|
|
if (sv)
|
2005-02-14 21:50:09 +01:00
|
|
|
{
|
2005-03-11 21:10:41 +01:00
|
|
|
if (ha_release_savepoint(thd, sv))
|
2005-02-14 21:50:09 +01:00
|
|
|
res= TRUE; // cannot happen
|
2005-02-16 17:34:02 +01:00
|
|
|
else
|
|
|
|
send_ok(thd);
|
2005-03-11 21:10:41 +01:00
|
|
|
thd->transaction.savepoints=sv->prev;
|
2005-02-14 21:50:09 +01:00
|
|
|
}
|
2005-01-16 13:16:23 +01:00
|
|
|
else
|
2005-02-14 21:50:09 +01:00
|
|
|
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2005-02-14 21:50:09 +01:00
|
|
|
}
|
2003-06-15 00:04:28 +02:00
|
|
|
case SQLCOM_ROLLBACK_TO_SAVEPOINT:
|
2005-02-16 17:34:02 +01:00
|
|
|
{
|
2005-03-11 21:10:41 +01:00
|
|
|
SAVEPOINT *sv;
|
|
|
|
for (sv=thd->transaction.savepoints; sv; sv=sv->prev)
|
2005-01-16 13:16:23 +01:00
|
|
|
{
|
|
|
|
if (my_strnncoll(system_charset_info,
|
|
|
|
(uchar *)lex->ident.str, lex->ident.length,
|
2005-03-11 21:10:41 +01:00
|
|
|
(uchar *)sv->name, sv->length) == 0)
|
2005-01-16 13:16:23 +01:00
|
|
|
break;
|
|
|
|
}
|
2005-03-11 21:10:41 +01:00
|
|
|
if (sv)
|
2005-01-16 13:16:23 +01:00
|
|
|
{
|
2005-03-11 21:10:41 +01:00
|
|
|
if (ha_rollback_to_savepoint(thd, sv))
|
2005-01-16 13:16:23 +01:00
|
|
|
res= TRUE; // cannot happen
|
|
|
|
else
|
2005-02-14 21:50:09 +01:00
|
|
|
{
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
if ((thd->options &
|
|
|
|
(OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG)) &&
|
2005-02-14 21:50:09 +01:00
|
|
|
!thd->slave_thread)
|
|
|
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
|
|
ER_WARNING_NOT_COMPLETE_ROLLBACK,
|
|
|
|
ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
|
|
|
|
send_ok(thd);
|
|
|
|
}
|
2005-03-11 21:10:41 +01:00
|
|
|
thd->transaction.savepoints=sv;
|
2003-06-15 00:04:28 +02:00
|
|
|
}
|
|
|
|
else
|
2005-01-16 13:16:23 +01:00
|
|
|
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str);
|
2003-06-15 00:04:28 +02:00
|
|
|
break;
|
2005-01-16 13:16:23 +01:00
|
|
|
}
|
2003-06-06 03:18:58 +02:00
|
|
|
case SQLCOM_SAVEPOINT:
|
2005-11-19 13:09:23 +01:00
|
|
|
if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) ||
|
|
|
|
thd->in_sub_stmt) || !opt_using_transactions)
|
2003-08-18 23:08:08 +02:00
|
|
|
send_ok(thd);
|
2003-06-15 00:04:28 +02:00
|
|
|
else
|
2005-01-16 13:16:23 +01:00
|
|
|
{
|
|
|
|
SAVEPOINT **sv, *newsv;
|
|
|
|
for (sv=&thd->transaction.savepoints; *sv; sv=&(*sv)->prev)
|
|
|
|
{
|
|
|
|
if (my_strnncoll(system_charset_info,
|
|
|
|
(uchar *)lex->ident.str, lex->ident.length,
|
|
|
|
(uchar *)(*sv)->name, (*sv)->length) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (*sv) /* old savepoint of the same name exists */
|
|
|
|
{
|
|
|
|
newsv=*sv;
|
|
|
|
ha_release_savepoint(thd, *sv); // it cannot fail
|
|
|
|
*sv=(*sv)->prev;
|
|
|
|
}
|
|
|
|
else if ((newsv=(SAVEPOINT *) alloc_root(&thd->transaction.mem_root,
|
|
|
|
savepoint_alloc_size)) == 0)
|
|
|
|
{
|
|
|
|
my_error(ER_OUT_OF_RESOURCES, MYF(0));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
newsv->name=strmake_root(&thd->transaction.mem_root,
|
|
|
|
lex->ident.str, lex->ident.length);
|
|
|
|
newsv->length=lex->ident.length;
|
|
|
|
/*
|
|
|
|
if we'll get an error here, don't add new savepoint to the list.
|
|
|
|
we'll lose a little bit of memory in transaction mem_root, but it'll
|
|
|
|
be free'd when transaction ends anyway
|
|
|
|
*/
|
|
|
|
if (ha_savepoint(thd, newsv))
|
|
|
|
res= TRUE;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
newsv->prev=thd->transaction.savepoints;
|
|
|
|
thd->transaction.savepoints=newsv;
|
|
|
|
send_ok(thd);
|
|
|
|
}
|
|
|
|
}
|
2005-02-01 20:48:05 +01:00
|
|
|
break;
|
2003-03-05 19:45:17 +01:00
|
|
|
case SQLCOM_CREATE_PROCEDURE:
|
|
|
|
case SQLCOM_CREATE_SPFUNCTION:
|
2003-12-21 11:48:03 +01:00
|
|
|
{
|
2004-10-07 15:32:36 +02:00
|
|
|
uint namelen;
|
A fix and a test case for
Bug#19022 "Memory bug when switching db during trigger execution"
Bug#17199 "Problem when view calls function from another database."
Bug#18444 "Fully qualified stored function names don't work correctly in
SELECT statements"
Documentation note: this patch introduces a change in behaviour of prepared
statements.
This patch adds a few new invariants with regard to how THD::db should
be used. These invariants should be preserved in future:
- one should never refer to THD::db by pointer and always make a deep copy
(strmake, strdup)
- one should never compare two databases by pointer, but use strncmp or
my_strncasecmp
- TABLE_LIST object table->db should be always initialized in the parser or
by creator of the object.
For prepared statements it means that if the current database is changed
after a statement is prepared, the database that was current at prepare
remains active. This also means that you can not prepare a statement that
implicitly refers to the current database if the latter is not set.
This is not documented, and therefore needs documentation. This is NOT a
change in behavior for almost all SQL statements except:
- ALTER TABLE t1 RENAME t2
- OPTIMIZE TABLE t1
- ANALYZE TABLE t1
- TRUNCATE TABLE t1 --
until this patch t1 or t2 could be evaluated at the first execution of
prepared statement.
CURRENT_DATABASE() still works OK and is evaluated at every execution
of prepared statement.
Note, that in stored routines this is not an issue as the default
database is the database of the stored procedure and "use" statement
is prohibited in stored routines.
This patch makes obsolete the use of check_db_used (it was never used in the
old code too) and all other places that check for table->db and assign it
from THD::db if it's NULL, except the parser.
How this patch was created: THD::{db,db_length} were replaced with a
LEX_STRING, THD::db. All the places that refer to THD::{db,db_length} were
manually checked and:
- if the place uses thd->db by pointer, it was fixed to make a deep copy
- if a place compared two db pointers, it was fixed to compare them by value
(via strcmp/my_strcasecmp, whatever was approproate)
Then this intermediate patch was used to write a smaller patch that does the
same thing but without a rename.
TODO in 5.1:
- remove check_db_used
- deploy THD::set_db in mysql_change_db
See also comments to individual files.
2006-06-26 22:47:52 +02:00
|
|
|
char *name;
|
2007-02-16 02:47:39 +01:00
|
|
|
int sp_result= SP_INTERNAL_ERROR;
|
2004-10-07 15:32:36 +02:00
|
|
|
|
2005-02-25 15:53:22 +01:00
|
|
|
DBUG_ASSERT(lex->sphead != 0);
|
A fix and a test case for
Bug#19022 "Memory bug when switching db during trigger execution"
Bug#17199 "Problem when view calls function from another database."
Bug#18444 "Fully qualified stored function names don't work correctly in
SELECT statements"
Documentation note: this patch introduces a change in behaviour of prepared
statements.
This patch adds a few new invariants with regard to how THD::db should
be used. These invariants should be preserved in future:
- one should never refer to THD::db by pointer and always make a deep copy
(strmake, strdup)
- one should never compare two databases by pointer, but use strncmp or
my_strncasecmp
- TABLE_LIST object table->db should be always initialized in the parser or
by creator of the object.
For prepared statements it means that if the current database is changed
after a statement is prepared, the database that was current at prepare
remains active. This also means that you can not prepare a statement that
implicitly refers to the current database if the latter is not set.
This is not documented, and therefore needs documentation. This is NOT a
change in behavior for almost all SQL statements except:
- ALTER TABLE t1 RENAME t2
- OPTIMIZE TABLE t1
- ANALYZE TABLE t1
- TRUNCATE TABLE t1 --
until this patch t1 or t2 could be evaluated at the first execution of
prepared statement.
CURRENT_DATABASE() still works OK and is evaluated at every execution
of prepared statement.
Note, that in stored routines this is not an issue as the default
database is the database of the stored procedure and "use" statement
is prohibited in stored routines.
This patch makes obsolete the use of check_db_used (it was never used in the
old code too) and all other places that check for table->db and assign it
from THD::db if it's NULL, except the parser.
How this patch was created: THD::{db,db_length} were replaced with a
LEX_STRING, THD::db. All the places that refer to THD::{db,db_length} were
manually checked and:
- if the place uses thd->db by pointer, it was fixed to make a deep copy
- if a place compared two db pointers, it was fixed to compare them by value
(via strcmp/my_strcasecmp, whatever was approproate)
Then this intermediate patch was used to write a smaller patch that does the
same thing but without a rename.
TODO in 5.1:
- remove check_db_used
- deploy THD::set_db in mysql_change_db
See also comments to individual files.
2006-06-26 22:47:52 +02:00
|
|
|
DBUG_ASSERT(lex->sphead->m_db.str); /* Must be initialized in the parser */
|
2006-11-28 16:03:53 +01:00
|
|
|
/*
|
|
|
|
Verify that the database name is allowed, optionally
|
|
|
|
lowercase it.
|
|
|
|
*/
|
2006-11-30 09:13:09 +01:00
|
|
|
if (check_db_name(&lex->sphead->m_db))
|
2004-10-07 15:32:36 +02:00
|
|
|
{
|
2006-11-28 16:03:53 +01:00
|
|
|
my_error(ER_WRONG_DB_NAME, MYF(0), lex->sphead->m_db.str);
|
2006-12-01 12:50:57 +01:00
|
|
|
goto create_sp_error;
|
2004-10-07 15:32:36 +02:00
|
|
|
}
|
|
|
|
|
2006-11-28 16:03:53 +01:00
|
|
|
/*
|
2006-12-01 12:50:57 +01:00
|
|
|
Check that a database directory with this name
|
|
|
|
exists. Design note: This won't work on virtual databases
|
|
|
|
like information_schema.
|
2006-11-28 16:03:53 +01:00
|
|
|
*/
|
|
|
|
if (check_db_dir_existence(lex->sphead->m_db.str))
|
2004-12-23 11:46:24 +01:00
|
|
|
{
|
2006-11-28 16:03:53 +01:00
|
|
|
my_error(ER_BAD_DB_ERROR, MYF(0), lex->sphead->m_db.str);
|
2006-12-01 12:50:57 +01:00
|
|
|
goto create_sp_error;
|
2004-12-23 11:46:24 +01:00
|
|
|
}
|
2004-10-07 15:32:36 +02:00
|
|
|
|
2005-09-13 13:07:38 +02:00
|
|
|
if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str, 0, 0, 0,
|
|
|
|
is_schema_db(lex->sphead->m_db.str)))
|
2006-12-01 12:50:57 +01:00
|
|
|
goto create_sp_error;
|
2004-10-07 15:32:36 +02:00
|
|
|
|
2006-12-01 12:50:57 +01:00
|
|
|
if (end_active_trans(thd))
|
|
|
|
goto create_sp_error;
|
2004-10-07 15:32:36 +02:00
|
|
|
|
|
|
|
name= lex->sphead->name(&namelen);
|
2003-02-26 19:22:29 +01:00
|
|
|
#ifdef HAVE_DLOPEN
|
2003-12-21 11:48:03 +01:00
|
|
|
if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
|
|
|
|
{
|
|
|
|
udf_func *udf = find_udf(name, namelen);
|
2003-04-03 20:00:52 +02:00
|
|
|
|
2003-12-21 11:48:03 +01:00
|
|
|
if (udf)
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
{
|
2006-12-01 12:50:57 +01:00
|
|
|
my_error(ER_UDF_EXISTS, MYF(0), name);
|
|
|
|
goto create_sp_error;
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
}
|
2003-12-21 11:48:03 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-02-02 18:43:33 +01:00
|
|
|
if (sp_process_definer(thd))
|
|
|
|
goto create_sp_error;
|
2006-03-02 13:18:49 +01:00
|
|
|
|
2007-02-16 02:47:39 +01:00
|
|
|
res= (sp_result= lex->sphead->create(thd));
|
|
|
|
switch (sp_result) {
|
2006-12-01 12:50:57 +01:00
|
|
|
case SP_OK:
|
2004-12-23 12:42:57 +01:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2004-12-23 11:46:24 +01:00
|
|
|
/* only add privileges if really neccessary */
|
2005-05-26 16:44:46 +02:00
|
|
|
if (sp_automatic_privileges && !opt_noacl &&
|
2005-05-17 20:54:20 +02:00
|
|
|
check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS,
|
2006-12-01 12:50:57 +01:00
|
|
|
lex->sphead->m_db.str, name,
|
2005-05-17 20:54:20 +02:00
|
|
|
lex->sql_command == SQLCOM_CREATE_PROCEDURE, 1))
|
2004-12-23 11:46:24 +01:00
|
|
|
{
|
A fix and a test case for
Bug#19022 "Memory bug when switching db during trigger execution"
Bug#17199 "Problem when view calls function from another database."
Bug#18444 "Fully qualified stored function names don't work correctly in
SELECT statements"
Documentation note: this patch introduces a change in behaviour of prepared
statements.
This patch adds a few new invariants with regard to how THD::db should
be used. These invariants should be preserved in future:
- one should never refer to THD::db by pointer and always make a deep copy
(strmake, strdup)
- one should never compare two databases by pointer, but use strncmp or
my_strncasecmp
- TABLE_LIST object table->db should be always initialized in the parser or
by creator of the object.
For prepared statements it means that if the current database is changed
after a statement is prepared, the database that was current at prepare
remains active. This also means that you can not prepare a statement that
implicitly refers to the current database if the latter is not set.
This is not documented, and therefore needs documentation. This is NOT a
change in behavior for almost all SQL statements except:
- ALTER TABLE t1 RENAME t2
- OPTIMIZE TABLE t1
- ANALYZE TABLE t1
- TRUNCATE TABLE t1 --
until this patch t1 or t2 could be evaluated at the first execution of
prepared statement.
CURRENT_DATABASE() still works OK and is evaluated at every execution
of prepared statement.
Note, that in stored routines this is not an issue as the default
database is the database of the stored procedure and "use" statement
is prohibited in stored routines.
This patch makes obsolete the use of check_db_used (it was never used in the
old code too) and all other places that check for table->db and assign it
from THD::db if it's NULL, except the parser.
How this patch was created: THD::{db,db_length} were replaced with a
LEX_STRING, THD::db. All the places that refer to THD::{db,db_length} were
manually checked and:
- if the place uses thd->db by pointer, it was fixed to make a deep copy
- if a place compared two db pointers, it was fixed to compare them by value
(via strcmp/my_strcasecmp, whatever was approproate)
Then this intermediate patch was used to write a smaller patch that does the
same thing but without a rename.
TODO in 5.1:
- remove check_db_used
- deploy THD::set_db in mysql_change_db
See also comments to individual files.
2006-06-26 22:47:52 +02:00
|
|
|
if (sp_grant_privileges(thd, lex->sphead->m_db.str, name,
|
2005-05-17 20:54:20 +02:00
|
|
|
lex->sql_command == SQLCOM_CREATE_PROCEDURE))
|
2006-12-01 12:50:57 +01:00
|
|
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
|
|
ER_PROC_AUTO_GRANT_FAIL,
|
|
|
|
ER(ER_PROC_AUTO_GRANT_FAIL));
|
2006-05-02 19:56:43 +02:00
|
|
|
close_thread_tables(thd);
|
2004-12-23 11:46:24 +01:00
|
|
|
}
|
2004-12-23 12:42:57 +01:00
|
|
|
#endif
|
2003-12-21 11:48:03 +01:00
|
|
|
break;
|
2006-12-01 12:50:57 +01:00
|
|
|
case SP_WRITE_ROW_FAILED:
|
|
|
|
my_error(ER_SP_ALREADY_EXISTS, MYF(0), SP_TYPE_STRING(lex), name);
|
|
|
|
break;
|
|
|
|
case SP_BAD_IDENTIFIER:
|
|
|
|
my_error(ER_TOO_LONG_IDENT, MYF(0), name);
|
|
|
|
break;
|
|
|
|
case SP_BODY_TOO_LONG:
|
|
|
|
my_error(ER_TOO_LONG_BODY, MYF(0), name);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(lex), name);
|
|
|
|
break;
|
|
|
|
} /* end switch */
|
|
|
|
|
|
|
|
/*
|
|
|
|
Capture all errors within this CASE and
|
|
|
|
clean up the environment.
|
|
|
|
*/
|
|
|
|
create_sp_error:
|
2007-02-16 02:47:39 +01:00
|
|
|
if (sp_result != SP_OK )
|
2006-12-01 12:50:57 +01:00
|
|
|
goto error;
|
|
|
|
send_ok(thd);
|
|
|
|
break; /* break super switch */
|
|
|
|
} /* end case group bracket */
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
case SQLCOM_CALL:
|
|
|
|
{
|
|
|
|
sp_head *sp;
|
|
|
|
|
2005-03-04 14:35:28 +01:00
|
|
|
/*
|
|
|
|
This will cache all SP and SF and open and lock all tables
|
|
|
|
required for execution.
|
|
|
|
*/
|
|
|
|
if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
|
|
|
|
open_and_lock_tables(thd, all_tables))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
/*
|
2005-07-30 10:19:57 +02:00
|
|
|
By this moment all needed SPs should be in cache so no need to look
|
|
|
|
into DB.
|
2005-03-04 14:35:28 +01:00
|
|
|
*/
|
2005-11-23 00:11:19 +01:00
|
|
|
if (!(sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
|
|
|
|
&thd->sp_proc_cache, TRUE)))
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
|
2004-10-20 03:04:37 +02:00
|
|
|
lex->spname->m_qname.str);
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-05-14 16:00:57 +02:00
|
|
|
ha_rows select_limit;
|
2004-12-30 23:44:00 +01:00
|
|
|
/* bits that should be cleared in thd->server_status */
|
|
|
|
uint bits_to_be_cleared= 0;
|
Implement WL#2661 "Prepared Statements: Dynamic SQL in Stored Procedures".
The idea of the patch is to separate statement processing logic,
such as parsing, validation of the parsed tree, execution and cleanup,
from global query processing logic, such as logging, resetting
priorities of a thread, resetting stored procedure cache, resetting
thread count of errors and warnings.
This makes PREPARE and EXECUTE behave similarly to the rest of SQL
statements and allows their use in stored procedures.
This patch contains a change in behaviour:
until recently for each SQL prepared statement command, 2 queries
were written to the general log, e.g.
[Query] prepare stmt from @stmt_text;
[Prepare] select * from t1 <-- contents of @stmt_text
The chagne was necessary to prevent [Prepare] commands from being written
to the general log when executing a stored procedure with Dynamic SQL.
We should consider whether the old behavior is preferrable and probably
restore it.
This patch refixes Bug#7115, Bug#10975 (partially), Bug#10605 (various bugs
in Dynamic SQL reported before it was disabled).
2005-09-03 01:13:18 +02:00
|
|
|
/*
|
|
|
|
Check that the stored procedure doesn't contain Dynamic SQL
|
|
|
|
and doesn't return result sets: such stored procedures can't
|
|
|
|
be called from a function or trigger.
|
|
|
|
*/
|
|
|
|
if (thd->in_sub_stmt)
|
|
|
|
{
|
|
|
|
const char *where= (thd->in_sub_stmt & SUB_STMT_TRIGGER ?
|
|
|
|
"trigger" : "function");
|
|
|
|
if (sp->is_not_allowed_in_function(where))
|
|
|
|
goto error;
|
|
|
|
}
|
2003-04-23 09:22:54 +02:00
|
|
|
|
2003-01-15 15:39:36 +01:00
|
|
|
my_bool nsok= thd->net.no_send_ok;
|
|
|
|
thd->net.no_send_ok= TRUE;
|
Implement WL#2661 "Prepared Statements: Dynamic SQL in Stored Procedures".
The idea of the patch is to separate statement processing logic,
such as parsing, validation of the parsed tree, execution and cleanup,
from global query processing logic, such as logging, resetting
priorities of a thread, resetting stored procedure cache, resetting
thread count of errors and warnings.
This makes PREPARE and EXECUTE behave similarly to the rest of SQL
statements and allows their use in stored procedures.
This patch contains a change in behaviour:
until recently for each SQL prepared statement command, 2 queries
were written to the general log, e.g.
[Query] prepare stmt from @stmt_text;
[Prepare] select * from t1 <-- contents of @stmt_text
The chagne was necessary to prevent [Prepare] commands from being written
to the general log when executing a stored procedure with Dynamic SQL.
We should consider whether the old behavior is preferrable and probably
restore it.
This patch refixes Bug#7115, Bug#10975 (partially), Bug#10605 (various bugs
in Dynamic SQL reported before it was disabled).
2005-09-03 01:13:18 +02:00
|
|
|
if (sp->m_flags & sp_head::MULTI_RESULTS)
|
2003-04-23 09:22:54 +02:00
|
|
|
{
|
2003-08-27 17:04:33 +02:00
|
|
|
if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS))
|
2003-04-23 09:22:54 +02:00
|
|
|
{
|
Implement WL#2661 "Prepared Statements: Dynamic SQL in Stored Procedures".
The idea of the patch is to separate statement processing logic,
such as parsing, validation of the parsed tree, execution and cleanup,
from global query processing logic, such as logging, resetting
priorities of a thread, resetting stored procedure cache, resetting
thread count of errors and warnings.
This makes PREPARE and EXECUTE behave similarly to the rest of SQL
statements and allows their use in stored procedures.
This patch contains a change in behaviour:
until recently for each SQL prepared statement command, 2 queries
were written to the general log, e.g.
[Query] prepare stmt from @stmt_text;
[Prepare] select * from t1 <-- contents of @stmt_text
The chagne was necessary to prevent [Prepare] commands from being written
to the general log when executing a stored procedure with Dynamic SQL.
We should consider whether the old behavior is preferrable and probably
restore it.
This patch refixes Bug#7115, Bug#10975 (partially), Bug#10605 (various bugs
in Dynamic SQL reported before it was disabled).
2005-09-03 01:13:18 +02:00
|
|
|
/*
|
|
|
|
The client does not support multiple result sets being sent
|
|
|
|
back
|
|
|
|
*/
|
2005-04-27 16:35:49 +02:00
|
|
|
my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str);
|
2003-04-23 09:22:54 +02:00
|
|
|
thd->net.no_send_ok= nsok;
|
|
|
|
goto error;
|
|
|
|
}
|
2004-12-30 23:44:00 +01:00
|
|
|
/*
|
|
|
|
If SERVER_MORE_RESULTS_EXISTS is not set,
|
|
|
|
then remember that it should be cleared
|
|
|
|
*/
|
|
|
|
bits_to_be_cleared= (~thd->server_status &
|
|
|
|
SERVER_MORE_RESULTS_EXISTS);
|
|
|
|
thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
|
2003-04-23 09:22:54 +02:00
|
|
|
}
|
|
|
|
|
2003-12-16 14:15:27 +01:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2005-09-15 21:29:07 +02:00
|
|
|
if (check_routine_access(thd, EXECUTE_ACL,
|
2006-07-13 15:12:31 +02:00
|
|
|
sp->m_db.str, sp->m_name.str, TRUE, FALSE))
|
2004-12-23 11:46:24 +01:00
|
|
|
{
|
|
|
|
thd->net.no_send_ok= nsok;
|
|
|
|
goto error;
|
|
|
|
}
|
2003-12-16 14:15:27 +01:00
|
|
|
#endif
|
2004-05-14 16:00:57 +02:00
|
|
|
select_limit= thd->variables.select_limit;
|
|
|
|
thd->variables.select_limit= HA_POS_ERROR;
|
2003-12-13 16:40:52 +01:00
|
|
|
|
2005-08-25 15:34:34 +02:00
|
|
|
/*
|
Implement WL#2661 "Prepared Statements: Dynamic SQL in Stored Procedures".
The idea of the patch is to separate statement processing logic,
such as parsing, validation of the parsed tree, execution and cleanup,
from global query processing logic, such as logging, resetting
priorities of a thread, resetting stored procedure cache, resetting
thread count of errors and warnings.
This makes PREPARE and EXECUTE behave similarly to the rest of SQL
statements and allows their use in stored procedures.
This patch contains a change in behaviour:
until recently for each SQL prepared statement command, 2 queries
were written to the general log, e.g.
[Query] prepare stmt from @stmt_text;
[Prepare] select * from t1 <-- contents of @stmt_text
The chagne was necessary to prevent [Prepare] commands from being written
to the general log when executing a stored procedure with Dynamic SQL.
We should consider whether the old behavior is preferrable and probably
restore it.
This patch refixes Bug#7115, Bug#10975 (partially), Bug#10605 (various bugs
in Dynamic SQL reported before it was disabled).
2005-09-03 01:13:18 +02:00
|
|
|
We never write CALL statements into binlog:
|
2005-08-25 15:34:34 +02:00
|
|
|
- If the mode is non-prelocked, each statement will be logged
|
|
|
|
separately.
|
|
|
|
- If the mode is prelocked, the invoking statement will care
|
|
|
|
about writing into binlog.
|
|
|
|
So just execute the statement.
|
2005-05-05 14:20:53 +02:00
|
|
|
*/
|
2005-08-25 15:34:34 +02:00
|
|
|
res= sp->execute_procedure(thd, &lex->value_list);
|
2005-02-26 11:19:02 +01:00
|
|
|
/*
|
|
|
|
If warnings have been cleared, we have to clear total_warn_count
|
|
|
|
too, otherwise the clients get confused.
|
2005-02-24 13:56:09 +01:00
|
|
|
*/
|
|
|
|
if (thd->warn_list.is_empty())
|
|
|
|
thd->total_warn_count= 0;
|
|
|
|
|
2004-05-14 16:00:57 +02:00
|
|
|
thd->variables.select_limit= select_limit;
|
2003-12-13 16:40:52 +01:00
|
|
|
|
2003-01-15 15:39:36 +01:00
|
|
|
thd->net.no_send_ok= nsok;
|
2004-12-30 23:44:00 +01:00
|
|
|
thd->server_status&= ~bits_to_be_cleared;
|
2003-01-15 15:39:36 +01:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
if (!res)
|
2004-12-30 23:44:00 +01:00
|
|
|
send_ok(thd, (ulong) (thd->row_count_func < 0 ? 0 :
|
|
|
|
thd->row_count_func));
|
2003-01-15 15:39:36 +01:00
|
|
|
else
|
2006-11-01 18:41:09 +01:00
|
|
|
{
|
|
|
|
DBUG_ASSERT(thd->net.report_error == 1 || thd->killed);
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error; // Substatement should already have sent error
|
2006-11-01 18:41:09 +01:00
|
|
|
}
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
}
|
2003-04-23 09:22:54 +02:00
|
|
|
break;
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
}
|
|
|
|
case SQLCOM_ALTER_PROCEDURE:
|
2003-02-21 17:37:05 +01:00
|
|
|
case SQLCOM_ALTER_FUNCTION:
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
{
|
2007-02-16 02:47:39 +01:00
|
|
|
int sp_result;
|
2004-10-22 20:29:06 +02:00
|
|
|
sp_head *sp;
|
|
|
|
st_sp_chistics chistics;
|
|
|
|
|
|
|
|
memcpy(&chistics, &lex->sp_chistics, sizeof(chistics));
|
2003-11-17 18:21:36 +01:00
|
|
|
if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
|
2005-11-23 00:11:19 +01:00
|
|
|
sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
|
|
|
|
&thd->sp_proc_cache, FALSE);
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
else
|
2005-11-23 00:11:19 +01:00
|
|
|
sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
|
|
|
|
&thd->sp_func_cache, FALSE);
|
2005-03-16 15:11:01 +01:00
|
|
|
mysql_reset_errors(thd, 0);
|
2004-10-22 20:29:06 +02:00
|
|
|
if (! sp)
|
2005-04-12 14:52:54 +02:00
|
|
|
{
|
|
|
|
if (lex->spname->m_db.str)
|
2007-02-16 02:47:39 +01:00
|
|
|
sp_result= SP_KEY_NOT_FOUND;
|
2005-04-12 14:52:54 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
2004-10-22 20:29:06 +02:00
|
|
|
else
|
|
|
|
{
|
2005-05-17 20:54:20 +02:00
|
|
|
if (check_routine_access(thd, ALTER_PROC_ACL, sp->m_db.str,
|
|
|
|
sp->m_name.str,
|
|
|
|
lex->sql_command == SQLCOM_ALTER_PROCEDURE, 0))
|
2004-12-23 11:46:24 +01:00
|
|
|
goto error;
|
2005-09-14 20:42:39 +02:00
|
|
|
|
|
|
|
if (end_active_trans(thd))
|
|
|
|
goto error;
|
2004-10-22 20:29:06 +02:00
|
|
|
memcpy(&lex->sp_chistics, &chistics, sizeof(lex->sp_chistics));
|
2005-11-10 17:50:51 +01:00
|
|
|
if ((sp->m_type == TYPE_ENUM_FUNCTION) &&
|
|
|
|
!trust_function_creators && mysql_bin_log.is_open() &&
|
2005-05-05 14:20:53 +02:00
|
|
|
!sp->m_chistics->detistic &&
|
|
|
|
(chistics.daccess == SP_CONTAINS_SQL ||
|
|
|
|
chistics.daccess == SP_MODIFIES_SQL_DATA))
|
|
|
|
{
|
|
|
|
my_message(ER_BINLOG_UNSAFE_ROUTINE,
|
|
|
|
ER(ER_BINLOG_UNSAFE_ROUTINE), MYF(0));
|
2007-02-16 02:47:39 +01:00
|
|
|
sp_result= SP_INTERNAL_ERROR;
|
2005-05-05 14:20:53 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-11-10 17:50:51 +01:00
|
|
|
/*
|
|
|
|
Note that if you implement the capability of ALTER FUNCTION to
|
|
|
|
alter the body of the function, this command should be made to
|
|
|
|
follow the restrictions that log-bin-trust-function-creators=0
|
|
|
|
already puts on CREATE FUNCTION.
|
|
|
|
*/
|
2007-02-16 02:47:39 +01:00
|
|
|
/* Conditionally writes to binlog */
|
2005-05-05 14:20:53 +02:00
|
|
|
if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
|
2007-02-16 02:47:39 +01:00
|
|
|
sp_result= sp_update_procedure(thd, lex->spname,
|
|
|
|
&lex->sp_chistics);
|
2005-05-05 14:20:53 +02:00
|
|
|
else
|
2007-02-16 02:47:39 +01:00
|
|
|
sp_result= sp_update_function(thd, lex->spname, &lex->sp_chistics);
|
2005-05-05 14:20:53 +02:00
|
|
|
}
|
2004-10-22 20:29:06 +02:00
|
|
|
}
|
2007-02-16 02:47:39 +01:00
|
|
|
switch (sp_result)
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
{
|
2003-11-17 18:21:36 +01:00
|
|
|
case SP_OK:
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
send_ok(thd);
|
2003-11-17 18:21:36 +01:00
|
|
|
break;
|
|
|
|
case SP_KEY_NOT_FOUND:
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
|
|
|
|
SP_COM_STRING(lex), lex->spname->m_qname.str);
|
2003-11-17 18:21:36 +01:00
|
|
|
goto error;
|
|
|
|
default:
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SP_CANT_ALTER, MYF(0),
|
|
|
|
SP_COM_STRING(lex), lex->spname->m_qname.str);
|
2003-11-17 18:21:36 +01:00
|
|
|
goto error;
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
}
|
2003-04-23 09:22:54 +02:00
|
|
|
break;
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
}
|
|
|
|
case SQLCOM_DROP_PROCEDURE:
|
2003-02-21 17:37:05 +01:00
|
|
|
case SQLCOM_DROP_FUNCTION:
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
{
|
2007-02-16 02:47:39 +01:00
|
|
|
int sp_result;
|
2006-01-26 13:29:46 +01:00
|
|
|
int type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ?
|
|
|
|
TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
|
2004-10-22 20:29:06 +02:00
|
|
|
|
2007-02-16 02:47:39 +01:00
|
|
|
sp_result= sp_routine_exists_in_table(thd, type, lex->spname);
|
2005-03-16 15:11:01 +01:00
|
|
|
mysql_reset_errors(thd, 0);
|
2007-02-16 02:47:39 +01:00
|
|
|
if (sp_result == SP_OK)
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
{
|
2006-01-26 13:29:46 +01:00
|
|
|
char *db= lex->spname->m_db.str;
|
|
|
|
char *name= lex->spname->m_name.str;
|
|
|
|
|
2005-05-17 20:54:20 +02:00
|
|
|
if (check_routine_access(thd, ALTER_PROC_ACL, db, name,
|
|
|
|
lex->sql_command == SQLCOM_DROP_PROCEDURE, 0))
|
2004-10-28 10:02:48 +02:00
|
|
|
goto error;
|
2005-09-14 20:42:39 +02:00
|
|
|
|
|
|
|
if (end_active_trans(thd))
|
|
|
|
goto error;
|
2004-12-23 12:42:57 +01:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2005-05-26 16:44:46 +02:00
|
|
|
if (sp_automatic_privileges && !opt_noacl &&
|
2005-05-17 20:54:20 +02:00
|
|
|
sp_revoke_privileges(thd, db, name,
|
|
|
|
lex->sql_command == SQLCOM_DROP_PROCEDURE))
|
2004-12-23 11:46:24 +01:00
|
|
|
{
|
|
|
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
|
|
ER_PROC_AUTO_REVOKE_FAIL,
|
|
|
|
ER(ER_PROC_AUTO_REVOKE_FAIL));
|
|
|
|
}
|
2004-12-23 12:42:57 +01:00
|
|
|
#endif
|
2007-02-16 02:47:39 +01:00
|
|
|
/* Conditionally writes to binlog */
|
2004-10-22 20:29:06 +02:00
|
|
|
if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
|
2007-02-16 02:47:39 +01:00
|
|
|
sp_result= sp_drop_procedure(thd, lex->spname);
|
2004-10-22 20:29:06 +02:00
|
|
|
else
|
2007-02-16 02:47:39 +01:00
|
|
|
sp_result= sp_drop_function(thd, lex->spname);
|
2004-11-23 19:19:09 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-10-22 20:29:06 +02:00
|
|
|
#ifdef HAVE_DLOPEN
|
2004-11-23 19:19:09 +01:00
|
|
|
if (lex->sql_command == SQLCOM_DROP_FUNCTION)
|
|
|
|
{
|
|
|
|
udf_func *udf = find_udf(lex->spname->m_name.str,
|
|
|
|
lex->spname->m_name.length);
|
|
|
|
if (udf)
|
|
|
|
{
|
2005-09-13 13:07:38 +02:00
|
|
|
if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 0, 0))
|
2004-11-23 19:19:09 +01:00
|
|
|
goto error;
|
2006-10-03 19:38:25 +02:00
|
|
|
|
|
|
|
/* Does NOT write to binlog */
|
2004-11-23 19:19:09 +01:00
|
|
|
if (!(res = mysql_drop_function(thd, &lex->spname->m_name)))
|
2003-02-21 17:37:05 +01:00
|
|
|
{
|
2004-11-23 19:19:09 +01:00
|
|
|
send_ok(thd);
|
|
|
|
break;
|
2003-02-21 17:37:05 +01:00
|
|
|
}
|
|
|
|
}
|
2004-10-22 20:29:06 +02:00
|
|
|
}
|
2004-11-23 19:19:09 +01:00
|
|
|
#endif
|
2005-04-12 14:52:54 +02:00
|
|
|
if (lex->spname->m_db.str)
|
2007-02-16 02:47:39 +01:00
|
|
|
sp_result= SP_KEY_NOT_FOUND;
|
2005-04-12 14:52:54 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
|
|
|
|
goto error;
|
|
|
|
}
|
2003-02-21 17:37:05 +01:00
|
|
|
}
|
2007-02-16 02:47:39 +01:00
|
|
|
res= sp_result;
|
|
|
|
switch (sp_result) {
|
2003-02-21 17:37:05 +01:00
|
|
|
case SP_OK:
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
send_ok(thd);
|
2003-02-21 17:37:05 +01:00
|
|
|
break;
|
|
|
|
case SP_KEY_NOT_FOUND:
|
2003-03-26 12:29:58 +01:00
|
|
|
if (lex->drop_if_exists)
|
|
|
|
{
|
2004-09-09 05:59:26 +02:00
|
|
|
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
2003-03-26 12:29:58 +01:00
|
|
|
ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
|
2004-02-17 17:36:53 +01:00
|
|
|
SP_COM_STRING(lex), lex->spname->m_name.str);
|
2004-10-20 03:04:37 +02:00
|
|
|
res= FALSE;
|
2003-03-26 12:29:58 +01:00
|
|
|
send_ok(thd);
|
|
|
|
break;
|
|
|
|
}
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
|
|
|
|
SP_COM_STRING(lex), lex->spname->m_qname.str);
|
2003-02-21 17:37:05 +01:00
|
|
|
goto error;
|
|
|
|
default:
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SP_DROP_FAILED, MYF(0),
|
|
|
|
SP_COM_STRING(lex), lex->spname->m_qname.str);
|
2003-02-21 17:37:05 +01:00
|
|
|
goto error;
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
}
|
2003-04-23 09:22:54 +02:00
|
|
|
break;
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
}
|
2003-11-17 18:21:36 +01:00
|
|
|
case SQLCOM_SHOW_CREATE_PROC:
|
|
|
|
{
|
2004-02-17 17:36:53 +01:00
|
|
|
if (lex->spname->m_name.length > NAME_LEN)
|
2003-11-17 18:21:36 +01:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
|
2003-11-17 18:21:36 +01:00
|
|
|
goto error;
|
|
|
|
}
|
2004-10-20 03:04:37 +02:00
|
|
|
if (sp_show_create_procedure(thd, lex->spname) != SP_OK)
|
2003-11-20 15:07:22 +01:00
|
|
|
{ /* We don't distinguish between errors for now */
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
|
|
|
|
SP_COM_STRING(lex), lex->spname->m_name.str);
|
2003-11-17 18:21:36 +01:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_SHOW_CREATE_FUNC:
|
|
|
|
{
|
2004-02-17 17:36:53 +01:00
|
|
|
if (lex->spname->m_name.length > NAME_LEN)
|
2003-11-17 18:21:36 +01:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
|
2003-11-17 18:21:36 +01:00
|
|
|
goto error;
|
|
|
|
}
|
2004-10-20 03:04:37 +02:00
|
|
|
if (sp_show_create_function(thd, lex->spname) != SP_OK)
|
2003-11-20 15:07:22 +01:00
|
|
|
{ /* We don't distinguish between errors for now */
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
|
|
|
|
SP_COM_STRING(lex), lex->spname->m_name.str);
|
2003-11-17 18:21:36 +01:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2006-06-20 12:20:32 +02:00
|
|
|
#ifdef NOT_USED
|
2003-11-17 18:21:36 +01:00
|
|
|
case SQLCOM_SHOW_STATUS_PROC:
|
|
|
|
{
|
2003-11-20 15:07:22 +01:00
|
|
|
res= sp_show_status_procedure(thd, (lex->wild ?
|
2003-11-17 18:21:36 +01:00
|
|
|
lex->wild->ptr() : NullS));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_SHOW_STATUS_FUNC:
|
|
|
|
{
|
2003-11-20 15:07:22 +01:00
|
|
|
res= sp_show_status_function(thd, (lex->wild ?
|
2003-11-17 18:21:36 +01:00
|
|
|
lex->wild->ptr() : NullS));
|
|
|
|
break;
|
|
|
|
}
|
2006-06-20 12:20:32 +02:00
|
|
|
#endif
|
2005-11-17 11:11:48 +01:00
|
|
|
#ifndef DBUG_OFF
|
|
|
|
case SQLCOM_SHOW_PROC_CODE:
|
|
|
|
case SQLCOM_SHOW_FUNC_CODE:
|
|
|
|
{
|
|
|
|
sp_head *sp;
|
|
|
|
|
|
|
|
if (lex->spname->m_name.length > NAME_LEN)
|
|
|
|
{
|
|
|
|
my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (lex->sql_command == SQLCOM_SHOW_PROC_CODE)
|
2005-11-23 01:49:44 +01:00
|
|
|
sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
|
|
|
|
&thd->sp_proc_cache, FALSE);
|
2005-11-17 11:11:48 +01:00
|
|
|
else
|
2005-11-23 01:49:44 +01:00
|
|
|
sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
|
|
|
|
&thd->sp_func_cache, FALSE);
|
2006-01-26 17:26:25 +01:00
|
|
|
if (!sp || sp->show_routine_code(thd))
|
2005-11-18 16:30:27 +01:00
|
|
|
{
|
|
|
|
/* We don't distinguish between errors for now */
|
2005-11-17 11:11:48 +01:00
|
|
|
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
|
|
|
|
SP_COM_STRING(lex), lex->spname->m_name.str);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif // ifndef DBUG_OFF
|
2004-07-16 00:15:55 +02:00
|
|
|
case SQLCOM_CREATE_VIEW:
|
|
|
|
{
|
2005-10-13 11:12:17 +02:00
|
|
|
if (end_active_trans(thd))
|
|
|
|
goto error;
|
|
|
|
|
2006-10-03 19:38:25 +02:00
|
|
|
res= mysql_create_view(thd, first_table, thd->lex->create_view_mode);
|
2004-07-16 00:15:55 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_DROP_VIEW:
|
|
|
|
{
|
2004-10-20 03:04:37 +02:00
|
|
|
if (check_table_access(thd, DROP_ACL, all_tables, 0) ||
|
|
|
|
end_active_trans(thd))
|
|
|
|
goto error;
|
2006-10-03 19:38:25 +02:00
|
|
|
/* Conditionally writes to binlog. */
|
|
|
|
res= mysql_drop_view(thd, first_table, thd->lex->drop_mode);
|
2004-07-16 00:15:55 +02:00
|
|
|
break;
|
|
|
|
}
|
2004-09-07 14:29:46 +02:00
|
|
|
case SQLCOM_CREATE_TRIGGER:
|
|
|
|
{
|
2005-10-13 11:12:17 +02:00
|
|
|
if (end_active_trans(thd))
|
|
|
|
goto error;
|
|
|
|
|
2006-10-03 19:38:25 +02:00
|
|
|
/* Conditionally writes to binlog. */
|
2004-11-24 10:24:02 +01:00
|
|
|
res= mysql_create_or_drop_trigger(thd, all_tables, 1);
|
|
|
|
|
2004-09-07 14:29:46 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_DROP_TRIGGER:
|
|
|
|
{
|
2005-10-13 11:12:17 +02:00
|
|
|
if (end_active_trans(thd))
|
|
|
|
goto error;
|
|
|
|
|
2006-10-03 19:38:25 +02:00
|
|
|
/* Conditionally writes to binlog. */
|
2004-09-07 14:29:46 +02:00
|
|
|
res= mysql_create_or_drop_trigger(thd, all_tables, 0);
|
|
|
|
break;
|
|
|
|
}
|
2005-01-16 13:16:23 +01:00
|
|
|
case SQLCOM_XA_START:
|
2005-08-12 21:15:01 +02:00
|
|
|
if (thd->transaction.xid_state.xa_state == XA_IDLE &&
|
|
|
|
thd->lex->xa_opt == XA_RESUME)
|
2005-01-16 13:16:23 +01:00
|
|
|
{
|
2005-08-12 21:15:01 +02:00
|
|
|
if (! thd->transaction.xid_state.xid.eq(thd->lex->xid))
|
2005-01-16 13:16:23 +01:00
|
|
|
{
|
|
|
|
my_error(ER_XAER_NOTA, MYF(0));
|
|
|
|
break;
|
|
|
|
}
|
2005-08-12 21:15:01 +02:00
|
|
|
thd->transaction.xid_state.xa_state=XA_ACTIVE;
|
2005-01-16 13:16:23 +01:00
|
|
|
send_ok(thd);
|
|
|
|
break;
|
|
|
|
}
|
2005-04-04 00:50:05 +02:00
|
|
|
if (thd->lex->xa_opt != XA_NONE)
|
2005-01-16 13:16:23 +01:00
|
|
|
{ // JOIN is not supported yet. TODO
|
|
|
|
my_error(ER_XAER_INVAL, MYF(0));
|
|
|
|
break;
|
|
|
|
}
|
2005-08-12 21:15:01 +02:00
|
|
|
if (thd->transaction.xid_state.xa_state != XA_NOTR)
|
2005-01-16 13:16:23 +01:00
|
|
|
{
|
2005-01-27 22:38:56 +01:00
|
|
|
my_error(ER_XAER_RMFAIL, MYF(0),
|
2005-08-12 21:15:01 +02:00
|
|
|
xa_state_names[thd->transaction.xid_state.xa_state]);
|
2005-01-16 13:16:23 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (thd->active_transaction() || thd->locked_tables)
|
|
|
|
{
|
|
|
|
my_error(ER_XAER_OUTSIDE, MYF(0));
|
|
|
|
break;
|
|
|
|
}
|
2005-08-12 21:15:01 +02:00
|
|
|
if (xid_cache_search(thd->lex->xid))
|
|
|
|
{
|
|
|
|
my_error(ER_XAER_DUPID, MYF(0));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
DBUG_ASSERT(thd->transaction.xid_state.xid.is_null());
|
|
|
|
thd->transaction.xid_state.xa_state=XA_ACTIVE;
|
|
|
|
thd->transaction.xid_state.xid.set(thd->lex->xid);
|
|
|
|
xid_cache_insert(&thd->transaction.xid_state);
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
thd->options= ((thd->options & ~(OPTION_STATUS_NO_TRANS_UPDATE |
|
|
|
|
OPTION_KEEP_LOG)) |
|
2005-01-16 13:16:23 +01:00
|
|
|
OPTION_BEGIN);
|
|
|
|
thd->server_status|= SERVER_STATUS_IN_TRANS;
|
|
|
|
send_ok(thd);
|
|
|
|
break;
|
|
|
|
case SQLCOM_XA_END:
|
|
|
|
/* fake it */
|
|
|
|
if (thd->lex->xa_opt != XA_NONE)
|
|
|
|
{ // SUSPEND and FOR MIGRATE are not supported yet. TODO
|
|
|
|
my_error(ER_XAER_INVAL, MYF(0));
|
|
|
|
break;
|
|
|
|
}
|
2005-08-12 21:15:01 +02:00
|
|
|
if (thd->transaction.xid_state.xa_state != XA_ACTIVE)
|
2005-01-16 13:16:23 +01:00
|
|
|
{
|
2005-01-27 22:38:56 +01:00
|
|
|
my_error(ER_XAER_RMFAIL, MYF(0),
|
2005-08-12 21:15:01 +02:00
|
|
|
xa_state_names[thd->transaction.xid_state.xa_state]);
|
2005-01-16 13:16:23 +01:00
|
|
|
break;
|
|
|
|
}
|
2005-08-12 21:15:01 +02:00
|
|
|
if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
|
2005-01-16 13:16:23 +01:00
|
|
|
{
|
|
|
|
my_error(ER_XAER_NOTA, MYF(0));
|
|
|
|
break;
|
|
|
|
}
|
2005-08-12 21:15:01 +02:00
|
|
|
thd->transaction.xid_state.xa_state=XA_IDLE;
|
2005-01-16 13:16:23 +01:00
|
|
|
send_ok(thd);
|
|
|
|
break;
|
|
|
|
case SQLCOM_XA_PREPARE:
|
2005-08-12 21:15:01 +02:00
|
|
|
if (thd->transaction.xid_state.xa_state != XA_IDLE)
|
2005-01-16 13:16:23 +01:00
|
|
|
{
|
2005-01-27 22:38:56 +01:00
|
|
|
my_error(ER_XAER_RMFAIL, MYF(0),
|
2005-08-12 21:15:01 +02:00
|
|
|
xa_state_names[thd->transaction.xid_state.xa_state]);
|
2005-01-16 13:16:23 +01:00
|
|
|
break;
|
|
|
|
}
|
2005-08-12 21:15:01 +02:00
|
|
|
if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
|
2005-01-16 13:16:23 +01:00
|
|
|
{
|
|
|
|
my_error(ER_XAER_NOTA, MYF(0));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ha_prepare(thd))
|
|
|
|
{
|
|
|
|
my_error(ER_XA_RBROLLBACK, MYF(0));
|
2005-08-12 21:15:01 +02:00
|
|
|
xid_cache_delete(&thd->transaction.xid_state);
|
|
|
|
thd->transaction.xid_state.xa_state=XA_NOTR;
|
2005-01-16 13:16:23 +01:00
|
|
|
break;
|
|
|
|
}
|
2005-08-12 21:15:01 +02:00
|
|
|
thd->transaction.xid_state.xa_state=XA_PREPARED;
|
2005-01-16 13:16:23 +01:00
|
|
|
send_ok(thd);
|
|
|
|
break;
|
|
|
|
case SQLCOM_XA_COMMIT:
|
2005-08-12 21:15:01 +02:00
|
|
|
if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
|
2005-01-16 13:16:23 +01:00
|
|
|
{
|
2005-08-12 21:15:01 +02:00
|
|
|
XID_STATE *xs=xid_cache_search(thd->lex->xid);
|
|
|
|
if (!xs || xs->in_thd)
|
2005-01-16 13:16:23 +01:00
|
|
|
my_error(ER_XAER_NOTA, MYF(0));
|
2005-03-16 08:42:06 +01:00
|
|
|
else
|
2005-08-12 21:15:01 +02:00
|
|
|
{
|
|
|
|
ha_commit_or_rollback_by_xid(thd->lex->xid, 1);
|
|
|
|
xid_cache_delete(xs);
|
2005-03-16 08:42:06 +01:00
|
|
|
send_ok(thd);
|
2005-08-12 21:15:01 +02:00
|
|
|
}
|
2005-01-16 13:16:23 +01:00
|
|
|
break;
|
|
|
|
}
|
2005-08-12 21:15:01 +02:00
|
|
|
if (thd->transaction.xid_state.xa_state == XA_IDLE &&
|
2005-06-05 16:01:20 +02:00
|
|
|
thd->lex->xa_opt == XA_ONE_PHASE)
|
2005-01-16 13:16:23 +01:00
|
|
|
{
|
2005-01-27 22:38:56 +01:00
|
|
|
int r;
|
|
|
|
if ((r= ha_commit(thd)))
|
|
|
|
my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
|
2005-01-16 13:16:23 +01:00
|
|
|
else
|
|
|
|
send_ok(thd);
|
|
|
|
}
|
2005-08-12 21:15:01 +02:00
|
|
|
else if (thd->transaction.xid_state.xa_state == XA_PREPARED &&
|
2005-06-05 16:01:20 +02:00
|
|
|
thd->lex->xa_opt == XA_NONE)
|
2005-01-16 13:16:23 +01:00
|
|
|
{
|
2005-04-12 17:15:54 +02:00
|
|
|
if (wait_if_global_read_lock(thd, 0, 0))
|
|
|
|
{
|
|
|
|
ha_rollback(thd);
|
2005-01-16 13:16:23 +01:00
|
|
|
my_error(ER_XAER_RMERR, MYF(0));
|
2005-04-12 17:15:54 +02:00
|
|
|
}
|
2005-01-16 13:16:23 +01:00
|
|
|
else
|
2005-04-12 17:15:54 +02:00
|
|
|
{
|
|
|
|
if (ha_commit_one_phase(thd, 1))
|
|
|
|
my_error(ER_XAER_RMERR, MYF(0));
|
|
|
|
else
|
|
|
|
send_ok(thd);
|
|
|
|
start_waiting_global_read_lock(thd);
|
|
|
|
}
|
2005-01-16 13:16:23 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-01-27 22:38:56 +01:00
|
|
|
my_error(ER_XAER_RMFAIL, MYF(0),
|
2005-08-12 21:15:01 +02:00
|
|
|
xa_state_names[thd->transaction.xid_state.xa_state]);
|
2005-01-16 13:16:23 +01:00
|
|
|
break;
|
|
|
|
}
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
thd->options&= ~(OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE |
|
|
|
|
OPTION_KEEP_LOG);
|
2005-01-16 13:16:23 +01:00
|
|
|
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
2005-08-12 21:15:01 +02:00
|
|
|
xid_cache_delete(&thd->transaction.xid_state);
|
|
|
|
thd->transaction.xid_state.xa_state=XA_NOTR;
|
2005-01-16 13:16:23 +01:00
|
|
|
break;
|
|
|
|
case SQLCOM_XA_ROLLBACK:
|
2005-08-12 21:15:01 +02:00
|
|
|
if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
|
2005-01-16 13:16:23 +01:00
|
|
|
{
|
2005-08-12 21:15:01 +02:00
|
|
|
XID_STATE *xs=xid_cache_search(thd->lex->xid);
|
|
|
|
if (!xs || xs->in_thd)
|
2005-01-16 13:16:23 +01:00
|
|
|
my_error(ER_XAER_NOTA, MYF(0));
|
2005-03-16 08:42:06 +01:00
|
|
|
else
|
2005-08-12 21:15:01 +02:00
|
|
|
{
|
|
|
|
ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
|
|
|
|
xid_cache_delete(xs);
|
2005-03-16 08:42:06 +01:00
|
|
|
send_ok(thd);
|
2005-08-12 21:15:01 +02:00
|
|
|
}
|
2005-01-16 13:16:23 +01:00
|
|
|
break;
|
|
|
|
}
|
2005-08-12 21:15:01 +02:00
|
|
|
if (thd->transaction.xid_state.xa_state != XA_IDLE &&
|
|
|
|
thd->transaction.xid_state.xa_state != XA_PREPARED)
|
2005-01-16 13:16:23 +01:00
|
|
|
{
|
2005-01-27 22:38:56 +01:00
|
|
|
my_error(ER_XAER_RMFAIL, MYF(0),
|
2005-08-12 21:15:01 +02:00
|
|
|
xa_state_names[thd->transaction.xid_state.xa_state]);
|
2005-01-16 13:16:23 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ha_rollback(thd))
|
|
|
|
my_error(ER_XAER_RMERR, MYF(0));
|
|
|
|
else
|
|
|
|
send_ok(thd);
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
thd->options&= ~(OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE |
|
|
|
|
OPTION_KEEP_LOG);
|
2005-01-16 13:16:23 +01:00
|
|
|
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
2005-08-12 21:15:01 +02:00
|
|
|
xid_cache_delete(&thd->transaction.xid_state);
|
|
|
|
thd->transaction.xid_state.xa_state=XA_NOTR;
|
2005-01-16 13:16:23 +01:00
|
|
|
break;
|
|
|
|
case SQLCOM_XA_RECOVER:
|
2005-02-21 13:47:57 +01:00
|
|
|
res= mysql_xa_recover(thd);
|
2005-01-16 13:16:23 +01:00
|
|
|
break;
|
2006-05-02 19:56:43 +02:00
|
|
|
case SQLCOM_ALTER_TABLESPACE:
|
|
|
|
if (check_access(thd, ALTER_ACL, thd->db, 0, 1, 0, thd->db ? is_schema_db(thd->db) : 0))
|
|
|
|
break;
|
|
|
|
if (!(res= mysql_alter_tablespace(thd, lex->alter_tablespace_info)))
|
|
|
|
send_ok(thd);
|
|
|
|
break;
|
|
|
|
case SQLCOM_INSTALL_PLUGIN:
|
|
|
|
if (! (res= mysql_install_plugin(thd, &thd->lex->comment,
|
|
|
|
&thd->lex->ident)))
|
|
|
|
send_ok(thd);
|
|
|
|
break;
|
|
|
|
case SQLCOM_UNINSTALL_PLUGIN:
|
|
|
|
if (! (res= mysql_uninstall_plugin(thd, &thd->lex->comment)))
|
|
|
|
send_ok(thd);
|
|
|
|
break;
|
|
|
|
case SQLCOM_BINLOG_BASE64_EVENT:
|
|
|
|
{
|
|
|
|
#ifndef EMBEDDED_LIBRARY
|
|
|
|
mysql_client_binlog_statement(thd);
|
|
|
|
#else /* EMBEDDED_LIBRARY */
|
|
|
|
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "embedded");
|
|
|
|
#endif /* EMBEDDED_LIBRARY */
|
|
|
|
break;
|
|
|
|
}
|
2006-12-02 01:47:45 +01:00
|
|
|
case SQLCOM_CREATE_SERVER:
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
LEX *lex= thd->lex;
|
|
|
|
DBUG_PRINT("info", ("case SQLCOM_CREATE_SERVER"));
|
|
|
|
if ((error= create_server(thd, &lex->server_options)))
|
|
|
|
{
|
2007-01-03 06:16:24 +01:00
|
|
|
DBUG_PRINT("info", ("problem creating server <%s>",
|
2006-12-02 01:47:45 +01:00
|
|
|
lex->server_options.server_name));
|
|
|
|
my_error(error, MYF(0), lex->server_options.server_name);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
send_ok(thd, 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_ALTER_SERVER:
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
LEX *lex= thd->lex;
|
|
|
|
DBUG_PRINT("info", ("case SQLCOM_ALTER_SERVER"));
|
|
|
|
if ((error= alter_server(thd, &lex->server_options)))
|
|
|
|
{
|
2007-01-03 06:16:24 +01:00
|
|
|
DBUG_PRINT("info", ("problem altering server <%s>",
|
2006-12-02 01:47:45 +01:00
|
|
|
lex->server_options.server_name));
|
|
|
|
my_error(error, MYF(0), lex->server_options.server_name);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
send_ok(thd, 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_DROP_SERVER:
|
|
|
|
{
|
|
|
|
int err_code;
|
|
|
|
LEX *lex= thd->lex;
|
|
|
|
DBUG_PRINT("info", ("case SQLCOM_DROP_SERVER"));
|
|
|
|
if ((err_code= drop_server(thd, &lex->server_options)))
|
|
|
|
{
|
|
|
|
if (! lex->drop_if_exists && err_code == ER_FOREIGN_SERVER_EXISTS)
|
|
|
|
{
|
|
|
|
DBUG_PRINT("info", ("problem dropping server %s",
|
|
|
|
lex->server_options.server_name));
|
|
|
|
my_error(err_code, MYF(0), lex->server_options.server_name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
send_ok(thd, 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
send_ok(thd, 1);
|
|
|
|
break;
|
|
|
|
}
|
2005-01-16 13:16:23 +01:00
|
|
|
default:
|
2006-02-24 17:34:15 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2005-01-16 13:16:23 +01:00
|
|
|
DBUG_ASSERT(0); /* Impossible */
|
2006-02-24 17:34:15 +01:00
|
|
|
#endif
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2006-05-02 19:56:43 +02:00
|
|
|
|
2004-07-07 10:29:39 +02:00
|
|
|
thd->proc_info="query end";
|
2005-08-31 18:08:45 +02:00
|
|
|
|
|
|
|
/*
|
2006-05-02 19:56:43 +02:00
|
|
|
Binlog-related cleanup:
|
2005-08-31 18:08:45 +02:00
|
|
|
Reset system variables temporarily modified by SET ONE SHOT.
|
|
|
|
|
|
|
|
Exception: If this is a SET, do nothing. This is to allow
|
|
|
|
mysqlbinlog to print many SET commands (in this case we want the
|
|
|
|
charset temp setting to live until the real query). This is also
|
|
|
|
needed so that SET CHARACTER_SET_CLIENT... does not cancel itself
|
|
|
|
immediately.
|
|
|
|
*/
|
|
|
|
if (thd->one_shot_set && lex->sql_command != SQLCOM_SET_OPTION)
|
|
|
|
reset_one_shot_variables(thd);
|
|
|
|
|
2004-05-04 17:18:47 +02:00
|
|
|
/*
|
2005-11-23 23:57:26 +01:00
|
|
|
The return value for ROW_COUNT() is "implementation dependent" if the
|
|
|
|
statement is not DELETE, INSERT or UPDATE, but -1 is what JDBC and ODBC
|
2006-06-20 12:20:32 +02:00
|
|
|
wants. We also keep the last value in case of SQLCOM_CALL or
|
|
|
|
SQLCOM_EXECUTE.
|
|
|
|
*/
|
|
|
|
if (!(sql_command_flags[lex->sql_command] & CF_HAS_ROW_COUNT))
|
2004-05-04 17:18:47 +02:00
|
|
|
thd->row_count_func= -1;
|
2006-06-26 19:14:35 +02:00
|
|
|
|
2006-07-04 10:02:11 +02:00
|
|
|
goto finish;
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
error:
|
2006-06-26 19:14:35 +02:00
|
|
|
res= TRUE;
|
|
|
|
|
2006-07-04 10:02:11 +02:00
|
|
|
finish:
|
2006-06-26 19:14:35 +02:00
|
|
|
if (need_start_waiting)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Release the protection against the global read lock and wake
|
|
|
|
everyone, who might want to set a global read lock.
|
|
|
|
*/
|
|
|
|
start_waiting_global_read_lock(thd);
|
|
|
|
}
|
|
|
|
DBUG_RETURN(res || thd->net.report_error);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-06-20 12:20:32 +02:00
|
|
|
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
|
|
|
|
{
|
|
|
|
LEX *lex= thd->lex;
|
|
|
|
select_result *result=lex->result;
|
|
|
|
bool res;
|
|
|
|
/* assign global limit variable if limit is not given */
|
|
|
|
{
|
|
|
|
SELECT_LEX *param= lex->unit.global_parameters;
|
|
|
|
if (!param->explicit_limit)
|
|
|
|
param->select_limit=
|
|
|
|
new Item_int((ulonglong) thd->variables.select_limit);
|
|
|
|
}
|
|
|
|
if (!(res= open_and_lock_tables(thd, all_tables)))
|
|
|
|
{
|
|
|
|
if (lex->describe)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
We always use select_send for EXPLAIN, even if it's an EXPLAIN
|
|
|
|
for SELECT ... INTO OUTFILE: a user application should be able
|
|
|
|
to prepend EXPLAIN to any query and receive output for it,
|
|
|
|
even if the query itself redirects the output.
|
|
|
|
*/
|
|
|
|
if (!(result= new select_send()))
|
2006-06-28 13:03:08 +02:00
|
|
|
return 1; /* purecov: inspected */
|
2006-06-20 12:20:32 +02:00
|
|
|
thd->send_explain_fields(result);
|
|
|
|
res= mysql_explain_union(thd, &thd->lex->unit, result);
|
|
|
|
if (lex->describe & DESCRIBE_EXTENDED)
|
|
|
|
{
|
|
|
|
char buff[1024];
|
|
|
|
String str(buff,(uint32) sizeof(buff), system_charset_info);
|
|
|
|
str.length(0);
|
|
|
|
thd->lex->unit.print(&str);
|
|
|
|
str.append('\0');
|
|
|
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
|
|
|
ER_YES, str.ptr());
|
|
|
|
}
|
|
|
|
result->send_eof();
|
|
|
|
delete result;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!result && !(result= new select_send()))
|
2006-06-28 13:03:08 +02:00
|
|
|
return 1; /* purecov: inspected */
|
2006-06-20 12:20:32 +02:00
|
|
|
query_cache_store_query(thd, all_tables);
|
|
|
|
res= handle_select(thd, lex, result, 0);
|
|
|
|
if (result != lex->result)
|
|
|
|
delete result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-05-28 01:06:56 +02:00
|
|
|
/*
|
2006-06-21 11:12:46 +02:00
|
|
|
Check grants for commands which work only with one table.
|
2003-05-28 01:06:56 +02:00
|
|
|
|
2003-07-01 21:40:59 +02:00
|
|
|
SYNOPSIS
|
2006-06-21 11:12:46 +02:00
|
|
|
check_single_table_access()
|
2004-02-11 00:06:46 +01:00
|
|
|
thd Thread handler
|
2004-10-07 00:45:06 +02:00
|
|
|
privilege requested privilege
|
2004-07-16 00:15:55 +02:00
|
|
|
all_tables global table list of query
|
2003-05-28 01:06:56 +02:00
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 - OK
|
2004-02-11 00:06:46 +01:00
|
|
|
1 - access denied, error is sent to client
|
2003-05-28 01:06:56 +02:00
|
|
|
*/
|
|
|
|
|
2006-06-21 11:12:46 +02:00
|
|
|
bool check_single_table_access(THD *thd, ulong privilege,
|
|
|
|
TABLE_LIST *all_tables)
|
2003-05-28 01:06:56 +02:00
|
|
|
{
|
2006-05-26 10:47:53 +02:00
|
|
|
Security_context * backup_ctx= thd->security_ctx;
|
|
|
|
|
|
|
|
/* we need to switch to the saved context (if any) */
|
|
|
|
if (all_tables->security_ctx)
|
|
|
|
thd->security_ctx= all_tables->security_ctx;
|
|
|
|
|
2006-06-12 17:15:08 +02:00
|
|
|
const char *db_name;
|
|
|
|
if ((all_tables->view || all_tables->field_translation) &&
|
|
|
|
!all_tables->schema_table)
|
|
|
|
db_name= all_tables->view_db.str;
|
|
|
|
else
|
|
|
|
db_name= all_tables->db;
|
|
|
|
|
|
|
|
if (check_access(thd, privilege, db_name,
|
2005-09-13 13:07:38 +02:00
|
|
|
&all_tables->grant.privilege, 0, 0,
|
|
|
|
test(all_tables->schema_table)))
|
2006-05-26 10:47:53 +02:00
|
|
|
goto deny;
|
2003-05-28 01:06:56 +02:00
|
|
|
|
2004-04-12 02:26:32 +02:00
|
|
|
/* Show only 1 table for check_grant */
|
2004-07-16 00:15:55 +02:00
|
|
|
if (grant_option && check_grant(thd, privilege, all_tables, 0, 1, 0))
|
2006-05-26 10:47:53 +02:00
|
|
|
goto deny;
|
|
|
|
|
|
|
|
thd->security_ctx= backup_ctx;
|
2006-06-21 11:12:46 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
deny:
|
|
|
|
thd->security_ctx= backup_ctx;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Check grants for commands which work only with one table and all other
|
|
|
|
tables belonging to subselects or implicitly opened tables.
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
check_one_table_access()
|
|
|
|
thd Thread handler
|
|
|
|
privilege requested privilege
|
|
|
|
all_tables global table list of query
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 - OK
|
|
|
|
1 - access denied, error is sent to client
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
|
|
|
|
{
|
|
|
|
if (check_single_table_access (thd,privilege,all_tables))
|
|
|
|
return 1;
|
2003-05-28 01:06:56 +02:00
|
|
|
|
2004-12-09 11:31:46 +01:00
|
|
|
/* Check rights on tables of subselects and implictly opened tables */
|
2006-08-24 01:05:13 +02:00
|
|
|
TABLE_LIST *subselects_tables, *view= all_tables->view ? all_tables : 0;
|
2004-07-16 00:15:55 +02:00
|
|
|
if ((subselects_tables= all_tables->next_global))
|
2003-12-19 15:25:50 +01:00
|
|
|
{
|
2006-08-24 01:05:13 +02:00
|
|
|
/*
|
|
|
|
Access rights asked for the first table of a view should be the same
|
|
|
|
as for the view
|
|
|
|
*/
|
|
|
|
if (view && subselects_tables->belong_to_view == view)
|
|
|
|
{
|
|
|
|
if (check_single_table_access (thd, privilege, subselects_tables))
|
|
|
|
return 1;
|
|
|
|
subselects_tables= subselects_tables->next_global;
|
|
|
|
}
|
|
|
|
if (subselects_tables &&
|
|
|
|
(check_table_access(thd, SELECT_ACL, subselects_tables, 0)))
|
2006-06-21 11:12:46 +02:00
|
|
|
return 1;
|
2003-12-19 15:25:50 +01:00
|
|
|
}
|
|
|
|
return 0;
|
2003-05-28 01:06:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/****************************************************************************
|
2002-06-12 14:04:18 +02:00
|
|
|
Get the user (global) and database privileges for all used tables
|
2003-04-28 09:32:56 +02:00
|
|
|
|
|
|
|
NOTES
|
|
|
|
The idea of EXTRA_ACL is that one will be granted access to the table if
|
|
|
|
one has the asked privilege on any column combination of the table; For
|
|
|
|
example to be able to check a table one needs to have SELECT privilege on
|
|
|
|
any column of the table.
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 ok
|
|
|
|
1 If we can't get the privileges and we don't use table/column grants.
|
|
|
|
|
|
|
|
save_priv In this we store global and db level grants for the table
|
|
|
|
Note that we don't store db level grants if the global grants
|
2003-05-07 22:15:46 +02:00
|
|
|
is enough to satisfy the request and the global grants contains
|
|
|
|
a SELECT grant.
|
2000-07-31 21:29:14 +02:00
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
bool
|
2002-06-12 14:04:18 +02:00
|
|
|
check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
|
2005-09-13 13:07:38 +02:00
|
|
|
bool dont_check_global_grants, bool no_errors, bool schema_db)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2005-09-20 20:20:38 +02:00
|
|
|
Security_context *sctx= thd->security_ctx;
|
2004-04-05 12:56:05 +02:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
|
|
|
ulong db_access;
|
2005-03-30 10:43:24 +02:00
|
|
|
bool db_is_pattern= test(want_access & GRANT_ACL);
|
2004-04-05 12:56:05 +02:00
|
|
|
#endif
|
|
|
|
ulong dummy;
|
2004-09-03 20:43:04 +02:00
|
|
|
DBUG_ENTER("check_access");
|
|
|
|
DBUG_PRINT("enter",("db: %s want_access: %lu master_access: %lu",
|
2005-09-15 21:29:07 +02:00
|
|
|
db ? db : "", want_access, sctx->master_access));
|
2000-07-31 21:29:14 +02:00
|
|
|
if (save_priv)
|
|
|
|
*save_priv=0;
|
|
|
|
else
|
|
|
|
save_priv= &dummy;
|
|
|
|
|
2001-09-18 05:05:55 +02:00
|
|
|
if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-09-03 20:43:04 +02:00
|
|
|
DBUG_PRINT("error",("No database"));
|
2001-12-22 14:13:31 +01:00
|
|
|
if (!no_errors)
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR),
|
|
|
|
MYF(0)); /* purecov: tested */
|
2002-06-12 14:04:18 +02:00
|
|
|
DBUG_RETURN(TRUE); /* purecov: tested */
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2005-09-13 13:07:38 +02:00
|
|
|
if (schema_db)
|
|
|
|
{
|
|
|
|
if (want_access & ~(SELECT_ACL | EXTRA_ACL))
|
|
|
|
{
|
|
|
|
if (!no_errors)
|
2005-10-08 02:37:23 +02:00
|
|
|
{
|
|
|
|
const char *db_name= db ? db : thd->db;
|
2005-09-13 13:07:38 +02:00
|
|
|
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
|
2005-10-08 02:37:23 +02:00
|
|
|
sctx->priv_user, sctx->priv_host, db_name);
|
|
|
|
}
|
2005-09-13 13:07:38 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*save_priv= SELECT_ACL;
|
|
|
|
DBUG_RETURN(FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-12-19 15:25:50 +01:00
|
|
|
#ifdef NO_EMBEDDED_ACCESS_CHECKS
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
#else
|
2005-09-15 21:29:07 +02:00
|
|
|
if ((sctx->master_access & want_access) == want_access)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2003-05-07 22:59:24 +02:00
|
|
|
/*
|
|
|
|
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
|
|
|
|
*/
|
2005-09-15 21:29:07 +02:00
|
|
|
db_access= sctx->db_access;
|
|
|
|
if (!(sctx->master_access & SELECT_ACL) &&
|
2005-03-30 10:43:24 +02:00
|
|
|
(db && (!thd->db || db_is_pattern || strcmp(db,thd->db))))
|
2005-09-15 21:29:07 +02:00
|
|
|
db_access=acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
|
|
|
|
db_is_pattern);
|
|
|
|
*save_priv=sctx->master_access | db_access;
|
2002-06-12 14:04:18 +02:00
|
|
|
DBUG_RETURN(FALSE);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2005-09-15 21:29:07 +02:00
|
|
|
if (((want_access & ~sctx->master_access) & ~(DB_ACLS | EXTRA_ACL)) ||
|
2001-01-14 11:25:30 +01:00
|
|
|
! db && dont_check_global_grants)
|
2000-07-31 21:29:14 +02:00
|
|
|
{ // We can never grant this
|
2004-09-03 20:43:04 +02:00
|
|
|
DBUG_PRINT("error",("No possible access"));
|
2001-12-22 14:13:31 +01:00
|
|
|
if (!no_errors)
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
|
2005-09-15 21:29:07 +02:00
|
|
|
sctx->priv_user,
|
|
|
|
sctx->priv_host,
|
2004-11-13 18:35:51 +01:00
|
|
|
(thd->password ?
|
|
|
|
ER(ER_YES) :
|
|
|
|
ER(ER_NO))); /* purecov: tested */
|
2002-06-12 14:04:18 +02:00
|
|
|
DBUG_RETURN(TRUE); /* purecov: tested */
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (db == any_db)
|
2002-06-12 14:04:18 +02:00
|
|
|
DBUG_RETURN(FALSE); // Allow select on anything
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2005-03-30 10:43:24 +02:00
|
|
|
if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))
|
2005-09-15 21:29:07 +02:00
|
|
|
db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
|
|
|
|
db_is_pattern);
|
2000-07-31 21:29:14 +02:00
|
|
|
else
|
2005-09-15 21:29:07 +02:00
|
|
|
db_access= sctx->db_access;
|
2004-10-26 18:30:01 +02:00
|
|
|
DBUG_PRINT("info",("db_access: %lu", db_access));
|
2004-07-07 10:29:39 +02:00
|
|
|
/* Remove SHOW attribute and access rights we already have */
|
2005-09-15 21:29:07 +02:00
|
|
|
want_access &= ~(sctx->master_access | EXTRA_ACL);
|
2004-09-03 20:43:04 +02:00
|
|
|
DBUG_PRINT("info",("db_access: %lu want_access: %lu",
|
|
|
|
db_access, want_access));
|
2005-09-15 21:29:07 +02:00
|
|
|
db_access= ((*save_priv=(db_access | sctx->master_access)) & want_access);
|
2001-01-14 11:25:30 +01:00
|
|
|
|
|
|
|
/* grant_option is set if there exists a single table or column grant */
|
2000-07-31 21:29:14 +02:00
|
|
|
if (db_access == want_access ||
|
2004-09-03 20:43:04 +02:00
|
|
|
(grant_option && !dont_check_global_grants &&
|
2004-12-23 11:46:24 +01:00
|
|
|
!(want_access & ~(db_access | TABLE_ACLS | PROC_ACLS))))
|
2002-06-12 14:04:18 +02:00
|
|
|
DBUG_RETURN(FALSE); /* Ok */
|
2004-09-03 20:43:04 +02:00
|
|
|
|
|
|
|
DBUG_PRINT("error",("Access denied"));
|
2001-12-22 14:13:31 +01:00
|
|
|
if (!no_errors)
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
|
2005-09-15 21:29:07 +02:00
|
|
|
sctx->priv_user, sctx->priv_host,
|
2004-11-13 18:35:51 +01:00
|
|
|
(db ? db : (thd->db ?
|
|
|
|
thd->db :
|
|
|
|
"unknown"))); /* purecov: tested */
|
2002-06-12 14:04:18 +02:00
|
|
|
DBUG_RETURN(TRUE); /* purecov: tested */
|
2003-12-19 15:25:50 +01:00
|
|
|
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-05-14 00:55:23 +02:00
|
|
|
/*
|
|
|
|
check for global access and give descriptive error message if it fails
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
check_global_access()
|
|
|
|
thd Thread handler
|
|
|
|
want_access Use should have any of these global rights
|
|
|
|
|
|
|
|
WARNING
|
2004-10-07 00:45:06 +02:00
|
|
|
One gets access right if one has ANY of the rights in want_access
|
2003-05-14 00:55:23 +02:00
|
|
|
This is useful as one in most cases only need one global right,
|
|
|
|
but in some case we want to check if the user has SUPER or
|
|
|
|
REPL_CLIENT_ACL rights.
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 ok
|
|
|
|
1 Access denied. In this case an error is sent to the client
|
|
|
|
*/
|
2002-06-12 14:04:18 +02:00
|
|
|
|
|
|
|
bool check_global_access(THD *thd, ulong want_access)
|
2001-03-21 00:02:22 +01:00
|
|
|
{
|
2003-12-19 15:25:50 +01:00
|
|
|
#ifdef NO_EMBEDDED_ACCESS_CHECKS
|
|
|
|
return 0;
|
|
|
|
#else
|
2002-06-12 14:04:18 +02:00
|
|
|
char command[128];
|
2005-09-15 21:29:07 +02:00
|
|
|
if ((thd->security_ctx->master_access & want_access))
|
2002-06-12 14:04:18 +02:00
|
|
|
return 0;
|
|
|
|
get_privilege_desc(command, sizeof(command), want_access);
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
|
2002-06-12 14:04:18 +02:00
|
|
|
return 1;
|
2003-12-19 15:25:50 +01:00
|
|
|
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
|
2001-03-21 00:02:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/*
|
2006-06-20 23:50:20 +02:00
|
|
|
Check the privilege for all used tables.
|
|
|
|
|
|
|
|
SYNOPSYS
|
|
|
|
check_table_access()
|
|
|
|
thd Thread context
|
|
|
|
want_access Privileges requested
|
|
|
|
tables List of tables to be checked
|
|
|
|
no_errors FALSE/TRUE - report/don't report error to
|
|
|
|
the client (using my_error() call).
|
|
|
|
|
|
|
|
NOTES
|
|
|
|
Table privileges are cached in the table list for GRANT checking.
|
|
|
|
This functions assumes that table list used and
|
|
|
|
thd->lex->query_tables_own_last value correspond to each other
|
|
|
|
(the latter should be either 0 or point to next_global member
|
|
|
|
of one of elements of this table list).
|
|
|
|
|
|
|
|
RETURN VALUE
|
|
|
|
FALSE - OK
|
|
|
|
TRUE - Access denied
|
2000-07-31 21:29:14 +02:00
|
|
|
*/
|
|
|
|
|
2001-04-11 13:04:03 +02:00
|
|
|
bool
|
2002-06-12 14:04:18 +02:00
|
|
|
check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
|
2001-12-22 14:13:31 +01:00
|
|
|
bool no_errors)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-06-12 14:04:18 +02:00
|
|
|
uint found=0;
|
|
|
|
ulong found_access=0;
|
2007-03-10 14:44:25 +01:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2005-10-27 23:18:23 +02:00
|
|
|
TABLE_LIST *org_tables= tables;
|
2007-02-16 02:47:39 +01:00
|
|
|
#endif
|
2005-10-27 23:18:23 +02:00
|
|
|
TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
|
2006-05-26 10:47:53 +02:00
|
|
|
Security_context *sctx= thd->security_ctx, *backup_ctx= thd->security_ctx;
|
2005-10-27 23:18:23 +02:00
|
|
|
/*
|
2005-10-27 23:56:44 +02:00
|
|
|
The check that first_not_own_table is not reached is for the case when
|
|
|
|
the given table list refers to the list for prelocking (contains tables
|
|
|
|
of other queries). For simple queries first_not_own_table is 0.
|
2005-10-27 23:18:23 +02:00
|
|
|
*/
|
2006-01-05 23:47:49 +01:00
|
|
|
for (; tables != first_not_own_table; tables= tables->next_global)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2006-05-26 10:47:53 +02:00
|
|
|
if (tables->security_ctx)
|
|
|
|
sctx= tables->security_ctx;
|
|
|
|
else
|
|
|
|
sctx= backup_ctx;
|
|
|
|
|
2005-09-13 13:07:38 +02:00
|
|
|
if (tables->schema_table &&
|
2005-10-07 11:16:44 +02:00
|
|
|
(want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL)))
|
2005-09-13 13:07:38 +02:00
|
|
|
{
|
|
|
|
if (!no_errors)
|
|
|
|
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
|
2006-05-26 10:47:53 +02:00
|
|
|
sctx->priv_user, sctx->priv_host,
|
2005-09-13 13:07:38 +02:00
|
|
|
information_schema_name.str);
|
|
|
|
return TRUE;
|
|
|
|
}
|
2005-10-27 23:18:23 +02:00
|
|
|
/*
|
|
|
|
Register access for view underlying table.
|
|
|
|
Remove SHOW_VIEW_ACL, because it will be checked during making view
|
|
|
|
*/
|
|
|
|
tables->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
|
2006-05-26 10:47:53 +02:00
|
|
|
if (tables->derived || tables->schema_table ||
|
2005-01-06 12:00:13 +01:00
|
|
|
(tables->table && (int)tables->table->s->tmp_table) ||
|
2004-10-21 20:18:00 +02:00
|
|
|
my_tz_check_n_skip_implicit_tables(&tables,
|
|
|
|
thd->lex->time_zone_tables_used))
|
2002-11-11 13:04:50 +01:00
|
|
|
continue;
|
2006-05-26 10:47:53 +02:00
|
|
|
thd->security_ctx= sctx;
|
|
|
|
if ((sctx->master_access & want_access) ==
|
2005-09-15 21:29:07 +02:00
|
|
|
(want_access & ~EXTRA_ACL) &&
|
2000-08-29 11:31:01 +02:00
|
|
|
thd->db)
|
2000-07-31 21:29:14 +02:00
|
|
|
tables->grant.privilege= want_access;
|
A fix and a test case for
Bug#19022 "Memory bug when switching db during trigger execution"
Bug#17199 "Problem when view calls function from another database."
Bug#18444 "Fully qualified stored function names don't work correctly in
SELECT statements"
Documentation note: this patch introduces a change in behaviour of prepared
statements.
This patch adds a few new invariants with regard to how THD::db should
be used. These invariants should be preserved in future:
- one should never refer to THD::db by pointer and always make a deep copy
(strmake, strdup)
- one should never compare two databases by pointer, but use strncmp or
my_strncasecmp
- TABLE_LIST object table->db should be always initialized in the parser or
by creator of the object.
For prepared statements it means that if the current database is changed
after a statement is prepared, the database that was current at prepare
remains active. This also means that you can not prepare a statement that
implicitly refers to the current database if the latter is not set.
This is not documented, and therefore needs documentation. This is NOT a
change in behavior for almost all SQL statements except:
- ALTER TABLE t1 RENAME t2
- OPTIMIZE TABLE t1
- ANALYZE TABLE t1
- TRUNCATE TABLE t1 --
until this patch t1 or t2 could be evaluated at the first execution of
prepared statement.
CURRENT_DATABASE() still works OK and is evaluated at every execution
of prepared statement.
Note, that in stored routines this is not an issue as the default
database is the database of the stored procedure and "use" statement
is prohibited in stored routines.
This patch makes obsolete the use of check_db_used (it was never used in the
old code too) and all other places that check for table->db and assign it
from THD::db if it's NULL, except the parser.
How this patch was created: THD::{db,db_length} were replaced with a
LEX_STRING, THD::db. All the places that refer to THD::{db,db_length} were
manually checked and:
- if the place uses thd->db by pointer, it was fixed to make a deep copy
- if a place compared two db pointers, it was fixed to compare them by value
(via strcmp/my_strcasecmp, whatever was approproate)
Then this intermediate patch was used to write a smaller patch that does the
same thing but without a rename.
TODO in 5.1:
- remove check_db_used
- deploy THD::set_db in mysql_change_db
See also comments to individual files.
2006-06-26 22:47:52 +02:00
|
|
|
else if (tables->db && thd->db && strcmp(tables->db, thd->db) == 0)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
if (found && !grant_option) // db already checked
|
|
|
|
tables->grant.privilege=found_access;
|
|
|
|
else
|
|
|
|
{
|
2001-12-22 14:13:31 +01:00
|
|
|
if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
|
2005-09-13 13:07:38 +02:00
|
|
|
0, no_errors, test(tables->schema_table)))
|
2006-05-26 10:47:53 +02:00
|
|
|
goto deny; // Access denied
|
2000-07-31 21:29:14 +02:00
|
|
|
found_access=tables->grant.privilege;
|
2000-08-21 02:07:54 +02:00
|
|
|
found=1;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
}
|
2002-11-12 13:40:32 +01:00
|
|
|
else if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
|
2005-09-13 13:07:38 +02:00
|
|
|
0, no_errors, test(tables->schema_table)))
|
2006-05-26 10:47:53 +02:00
|
|
|
goto deny;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2006-05-26 10:47:53 +02:00
|
|
|
thd->security_ctx= backup_ctx;
|
2000-07-31 21:29:14 +02:00
|
|
|
if (grant_option)
|
2001-04-11 13:04:03 +02:00
|
|
|
return check_grant(thd,want_access & ~EXTRA_ACL,org_tables,
|
2004-04-10 00:14:32 +02:00
|
|
|
test(want_access & EXTRA_ACL), UINT_MAX, no_errors);
|
2000-07-31 21:29:14 +02:00
|
|
|
return FALSE;
|
2006-05-26 10:47:53 +02:00
|
|
|
deny:
|
|
|
|
thd->security_ctx= backup_ctx;
|
|
|
|
return TRUE;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2004-09-03 20:43:04 +02:00
|
|
|
|
2004-12-23 11:46:24 +01:00
|
|
|
bool
|
2005-05-17 20:54:20 +02:00
|
|
|
check_routine_access(THD *thd, ulong want_access,char *db, char *name,
|
|
|
|
bool is_proc, bool no_errors)
|
2004-12-23 11:46:24 +01:00
|
|
|
{
|
|
|
|
TABLE_LIST tables[1];
|
|
|
|
|
|
|
|
bzero((char *)tables, sizeof(TABLE_LIST));
|
|
|
|
tables->db= db;
|
2005-01-06 12:00:13 +01:00
|
|
|
tables->table_name= tables->alias= name;
|
2004-12-23 11:46:24 +01:00
|
|
|
|
2005-10-11 23:58:22 +02:00
|
|
|
/*
|
|
|
|
The following test is just a shortcut for check_access() (to avoid
|
|
|
|
calculating db_access) under the assumption that it's common to
|
|
|
|
give persons global right to execute all stored SP (but not
|
|
|
|
necessary to create them).
|
|
|
|
*/
|
|
|
|
if ((thd->security_ctx->master_access & want_access) == want_access)
|
2004-12-23 11:46:24 +01:00
|
|
|
tables->grant.privilege= want_access;
|
|
|
|
else if (check_access(thd,want_access,db,&tables->grant.privilege,
|
2005-10-11 23:58:22 +02:00
|
|
|
0, no_errors, 0))
|
2004-12-23 11:46:24 +01:00
|
|
|
return TRUE;
|
|
|
|
|
2004-12-23 12:42:57 +01:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2004-12-23 11:46:24 +01:00
|
|
|
if (grant_option)
|
2005-05-17 20:54:20 +02:00
|
|
|
return check_grant_routine(thd, want_access, tables, is_proc, no_errors);
|
2004-12-23 12:42:57 +01:00
|
|
|
#endif
|
2004-12-23 11:46:24 +01:00
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2005-03-05 12:35:32 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Check if the routine has any of the routine privileges
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
check_some_routine_access()
|
|
|
|
thd Thread handler
|
|
|
|
db Database name
|
|
|
|
name Routine name
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 ok
|
|
|
|
1 error
|
|
|
|
*/
|
|
|
|
|
2005-05-17 20:54:20 +02:00
|
|
|
bool check_some_routine_access(THD *thd, const char *db, const char *name,
|
|
|
|
bool is_proc)
|
2005-03-05 12:35:32 +01:00
|
|
|
{
|
|
|
|
ulong save_priv;
|
2005-09-15 21:29:07 +02:00
|
|
|
if (thd->security_ctx->master_access & SHOW_PROC_ACLS)
|
2005-03-05 12:35:32 +01:00
|
|
|
return FALSE;
|
2005-09-13 13:07:38 +02:00
|
|
|
/*
|
|
|
|
There are no routines in information_schema db. So we can safely
|
|
|
|
pass zero to last paramter of check_access function
|
|
|
|
*/
|
|
|
|
if (!check_access(thd, SHOW_PROC_ACLS, db, &save_priv, 0, 1, 0) ||
|
2005-03-05 12:35:32 +01:00
|
|
|
(save_priv & SHOW_PROC_ACLS))
|
|
|
|
return FALSE;
|
2005-05-17 20:54:20 +02:00
|
|
|
return check_routine_level_acl(thd, db, name, is_proc);
|
2005-03-05 12:35:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-09-03 20:43:04 +02:00
|
|
|
/*
|
|
|
|
Check if the given table has any of the asked privileges
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
check_some_access()
|
|
|
|
thd Thread handler
|
|
|
|
want_access Bitmap of possible privileges to check for
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 ok
|
|
|
|
1 error
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
|
|
|
|
{
|
|
|
|
ulong access;
|
|
|
|
DBUG_ENTER("check_some_access");
|
|
|
|
|
|
|
|
/* This loop will work as long as we have less than 32 privileges */
|
|
|
|
for (access= 1; access < want_access ; access<<= 1)
|
|
|
|
{
|
|
|
|
if (access & want_access)
|
|
|
|
{
|
|
|
|
if (!check_access(thd, access, table->db,
|
2005-09-13 13:07:38 +02:00
|
|
|
&table->grant.privilege, 0, 1,
|
|
|
|
test(table->schema_table)) &&
|
2004-09-03 20:43:04 +02:00
|
|
|
!grant_option || !check_grant(thd, access, table, 0, 1, 1))
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DBUG_PRINT("exit",("no matching access rights"));
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-04-07 23:16:17 +02:00
|
|
|
bool check_merge_table_access(THD *thd, char *db,
|
|
|
|
TABLE_LIST *table_list)
|
2000-09-14 01:39:07 +02:00
|
|
|
{
|
|
|
|
int error=0;
|
|
|
|
if (table_list)
|
|
|
|
{
|
2001-08-29 16:33:41 +02:00
|
|
|
/* Check that all tables use the current database */
|
2000-09-14 01:39:07 +02:00
|
|
|
TABLE_LIST *tmp;
|
2004-07-16 00:15:55 +02:00
|
|
|
for (tmp= table_list; tmp; tmp= tmp->next_local)
|
2001-08-29 16:33:41 +02:00
|
|
|
{
|
|
|
|
if (!tmp->db || !tmp->db[0])
|
|
|
|
tmp->db=db;
|
|
|
|
}
|
2000-09-14 01:39:07 +02:00
|
|
|
error=check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
|
2003-09-26 12:33:13 +02:00
|
|
|
table_list,0);
|
2000-09-14 01:39:07 +02:00
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2003-09-26 12:33:13 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/****************************************************************************
|
|
|
|
Check stack size; Send error if there isn't enough stack to continue
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#if STACK_DIRECTION < 0
|
|
|
|
#define used_stack(A,B) (long) (A - B)
|
|
|
|
#else
|
|
|
|
#define used_stack(A,B) (long) (B - A)
|
|
|
|
#endif
|
|
|
|
|
2004-04-07 03:33:58 +02:00
|
|
|
#ifndef DBUG_OFF
|
|
|
|
long max_stack_used;
|
|
|
|
#endif
|
|
|
|
|
2003-04-08 07:35:13 +02:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2005-05-31 12:06:15 +02:00
|
|
|
/*
|
|
|
|
Note: The 'buf' parameter is necessary, even if it is unused here.
|
|
|
|
- fix_fields functions has a "dummy" buffer large enough for the
|
|
|
|
corresponding exec. (Thus we only have to check in fix_fields.)
|
|
|
|
- Passing to check_stack_overrun() prevents the compiler from removing it.
|
|
|
|
*/
|
|
|
|
bool check_stack_overrun(THD *thd, long margin,
|
|
|
|
char *buf __attribute__((unused)))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
long stack_used;
|
2005-11-23 19:18:10 +01:00
|
|
|
DBUG_ASSERT(thd == current_thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
|
2005-06-07 04:43:59 +02:00
|
|
|
(long) (thread_stack - margin))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2005-06-22 20:02:44 +02:00
|
|
|
sprintf(errbuff[0],ER(ER_STACK_OVERRUN_NEED_MORE),
|
|
|
|
stack_used,thread_stack,margin);
|
|
|
|
my_message(ER_STACK_OVERRUN_NEED_MORE,errbuff[0],MYF(0));
|
2003-01-30 21:15:44 +01:00
|
|
|
thd->fatal_error();
|
2000-07-31 21:29:14 +02:00
|
|
|
return 1;
|
|
|
|
}
|
2004-04-07 03:33:58 +02:00
|
|
|
#ifndef DBUG_OFF
|
|
|
|
max_stack_used= max(max_stack_used, stack_used);
|
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2003-04-08 07:35:13 +02:00
|
|
|
#endif /* EMBEDDED_LIBRARY */
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
#define MY_YACC_INIT 1000 // Start with big alloc
|
|
|
|
#define MY_YACC_MAX 32000 // Because of 'short'
|
|
|
|
|
2004-06-24 01:57:57 +02:00
|
|
|
bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2006-06-30 22:14:28 +02:00
|
|
|
LEX *lex= current_thd->lex;
|
2004-06-24 01:57:57 +02:00
|
|
|
ulong old_info=0;
|
2000-07-31 21:29:14 +02:00
|
|
|
if ((uint) *yystacksize >= MY_YACC_MAX)
|
|
|
|
return 1;
|
|
|
|
if (!lex->yacc_yyvs)
|
|
|
|
old_info= *yystacksize;
|
|
|
|
*yystacksize= set_zone((*yystacksize)*2,MY_YACC_INIT,MY_YACC_MAX);
|
|
|
|
if (!(lex->yacc_yyvs= (char*)
|
|
|
|
my_realloc((gptr) lex->yacc_yyvs,
|
|
|
|
*yystacksize*sizeof(**yyvs),
|
|
|
|
MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))) ||
|
|
|
|
!(lex->yacc_yyss= (char*)
|
|
|
|
my_realloc((gptr) lex->yacc_yyss,
|
|
|
|
*yystacksize*sizeof(**yyss),
|
|
|
|
MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))))
|
|
|
|
return 1;
|
|
|
|
if (old_info)
|
|
|
|
{ // Copy old info from stack
|
|
|
|
memcpy(lex->yacc_yyss, (gptr) *yyss, old_info*sizeof(**yyss));
|
|
|
|
memcpy(lex->yacc_yyvs, (gptr) *yyvs, old_info*sizeof(**yyvs));
|
|
|
|
}
|
|
|
|
*yyss=(short*) lex->yacc_yyss;
|
|
|
|
*yyvs=(YYSTYPE*) lex->yacc_yyvs;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
2003-02-12 20:55:37 +01:00
|
|
|
Initialize global thd variables needed for query
|
2000-07-31 21:29:14 +02:00
|
|
|
****************************************************************************/
|
|
|
|
|
2002-05-27 19:52:54 +02:00
|
|
|
void
|
2004-07-21 21:44:12 +02:00
|
|
|
mysql_init_query(THD *thd, uchar *buf, uint length)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("mysql_init_query");
|
2004-07-21 21:44:12 +02:00
|
|
|
lex_start(thd, buf, length);
|
2004-10-14 00:53:59 +02:00
|
|
|
mysql_reset_thd_for_next_command(thd);
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Reset THD part responsible for command processing state.
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
This needs to be called before execution of every statement
|
|
|
|
(prepared or conventional).
|
WL#3146 "less locking in auto_increment":
this is a cleanup patch for our current auto_increment handling:
new names for auto_increment variables in THD, new methods to manipulate them
(see sql_class.h), some move into handler::, causing less backup/restore
work when executing substatements.
This makes the logic hopefully clearer, less work is is needed in
mysql_insert().
By cleaning up, using different variables for different purposes (instead
of one for 3 things...), we fix those bugs, which someone may want to fix
in 5.0 too:
BUG#20339 "stored procedure using LAST_INSERT_ID() does not replicate
statement-based"
BUG#20341 "stored function inserting into one auto_increment puts bad
data in slave"
BUG#19243 "wrong LAST_INSERT_ID() after ON DUPLICATE KEY UPDATE"
(now if a row is updated, LAST_INSERT_ID() will return its id)
and re-fixes:
BUG#6880 "LAST_INSERT_ID() value changes during multi-row INSERT"
(already fixed differently by Ramil in 4.1)
Test of documented behaviour of mysql_insert_id() (there was no test).
The behaviour changes introduced are:
- LAST_INSERT_ID() now returns "the first autogenerated auto_increment value
successfully inserted", instead of "the first autogenerated auto_increment
value if any row was successfully inserted", see auto_increment.test.
Same for mysql_insert_id(), see mysql_client_test.c.
- LAST_INSERT_ID() returns the id of the updated row if ON DUPLICATE KEY
UPDATE, see auto_increment.test. Same for mysql_insert_id(), see
mysql_client_test.c.
- LAST_INSERT_ID() does not change if no autogenerated value was successfully
inserted (it used to then be 0), see auto_increment.test.
- if in INSERT SELECT no autogenerated value was successfully inserted,
mysql_insert_id() now returns the id of the last inserted row (it already
did this for INSERT VALUES), see mysql_client_test.c.
- if INSERT SELECT uses LAST_INSERT_ID(X), mysql_insert_id() now returns X
(it already did this for INSERT VALUES), see mysql_client_test.c.
- NDB now behaves like other engines wrt SET INSERT_ID: with INSERT IGNORE,
the id passed in SET INSERT_ID is re-used until a row succeeds; SET INSERT_ID
influences not only the first row now.
Additionally, when unlocking a table we check that the thread is not keeping
a next_insert_id (as the table is unlocked that id is potentially out-of-date);
forgetting about this next_insert_id is done in a new
handler::ha_release_auto_increment().
Finally we prepare for engines capable of reserving finite-length intervals
of auto_increment values: we store such intervals in THD. The next step
(to be done by the replication team in 5.1) is to read those intervals from
THD and actually store them in the statement-based binary log. NDB
will be a good engine to test that.
2006-07-09 17:52:19 +02:00
|
|
|
It is not called by substatements of routines.
|
2004-10-14 00:53:59 +02:00
|
|
|
|
|
|
|
TODO
|
|
|
|
Make it a method of THD and align its name with the rest of
|
|
|
|
reset/end/start/init methods.
|
|
|
|
Call it after we use THD for queries, not before.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void mysql_reset_thd_for_next_command(THD *thd)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("mysql_reset_thd_for_next_command");
|
WL#3146 "less locking in auto_increment":
this is a cleanup patch for our current auto_increment handling:
new names for auto_increment variables in THD, new methods to manipulate them
(see sql_class.h), some move into handler::, causing less backup/restore
work when executing substatements.
This makes the logic hopefully clearer, less work is is needed in
mysql_insert().
By cleaning up, using different variables for different purposes (instead
of one for 3 things...), we fix those bugs, which someone may want to fix
in 5.0 too:
BUG#20339 "stored procedure using LAST_INSERT_ID() does not replicate
statement-based"
BUG#20341 "stored function inserting into one auto_increment puts bad
data in slave"
BUG#19243 "wrong LAST_INSERT_ID() after ON DUPLICATE KEY UPDATE"
(now if a row is updated, LAST_INSERT_ID() will return its id)
and re-fixes:
BUG#6880 "LAST_INSERT_ID() value changes during multi-row INSERT"
(already fixed differently by Ramil in 4.1)
Test of documented behaviour of mysql_insert_id() (there was no test).
The behaviour changes introduced are:
- LAST_INSERT_ID() now returns "the first autogenerated auto_increment value
successfully inserted", instead of "the first autogenerated auto_increment
value if any row was successfully inserted", see auto_increment.test.
Same for mysql_insert_id(), see mysql_client_test.c.
- LAST_INSERT_ID() returns the id of the updated row if ON DUPLICATE KEY
UPDATE, see auto_increment.test. Same for mysql_insert_id(), see
mysql_client_test.c.
- LAST_INSERT_ID() does not change if no autogenerated value was successfully
inserted (it used to then be 0), see auto_increment.test.
- if in INSERT SELECT no autogenerated value was successfully inserted,
mysql_insert_id() now returns the id of the last inserted row (it already
did this for INSERT VALUES), see mysql_client_test.c.
- if INSERT SELECT uses LAST_INSERT_ID(X), mysql_insert_id() now returns X
(it already did this for INSERT VALUES), see mysql_client_test.c.
- NDB now behaves like other engines wrt SET INSERT_ID: with INSERT IGNORE,
the id passed in SET INSERT_ID is re-used until a row succeeds; SET INSERT_ID
influences not only the first row now.
Additionally, when unlocking a table we check that the thread is not keeping
a next_insert_id (as the table is unlocked that id is potentially out-of-date);
forgetting about this next_insert_id is done in a new
handler::ha_release_auto_increment().
Finally we prepare for engines capable of reserving finite-length intervals
of auto_increment values: we store such intervals in THD. The next step
(to be done by the replication team in 5.1) is to read those intervals from
THD and actually store them in the statement-based binary log. NDB
will be a good engine to test that.
2006-07-09 17:52:19 +02:00
|
|
|
DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */
|
2004-10-14 01:37:20 +02:00
|
|
|
thd->free_list= 0;
|
2004-10-14 00:53:59 +02:00
|
|
|
thd->select_number= 1;
|
2006-09-12 15:42:13 +02:00
|
|
|
/*
|
|
|
|
Those two lines below are theoretically unneeded as
|
|
|
|
THD::cleanup_after_query() should take care of this already.
|
|
|
|
*/
|
WL#3146 "less locking in auto_increment":
this is a cleanup patch for our current auto_increment handling:
new names for auto_increment variables in THD, new methods to manipulate them
(see sql_class.h), some move into handler::, causing less backup/restore
work when executing substatements.
This makes the logic hopefully clearer, less work is is needed in
mysql_insert().
By cleaning up, using different variables for different purposes (instead
of one for 3 things...), we fix those bugs, which someone may want to fix
in 5.0 too:
BUG#20339 "stored procedure using LAST_INSERT_ID() does not replicate
statement-based"
BUG#20341 "stored function inserting into one auto_increment puts bad
data in slave"
BUG#19243 "wrong LAST_INSERT_ID() after ON DUPLICATE KEY UPDATE"
(now if a row is updated, LAST_INSERT_ID() will return its id)
and re-fixes:
BUG#6880 "LAST_INSERT_ID() value changes during multi-row INSERT"
(already fixed differently by Ramil in 4.1)
Test of documented behaviour of mysql_insert_id() (there was no test).
The behaviour changes introduced are:
- LAST_INSERT_ID() now returns "the first autogenerated auto_increment value
successfully inserted", instead of "the first autogenerated auto_increment
value if any row was successfully inserted", see auto_increment.test.
Same for mysql_insert_id(), see mysql_client_test.c.
- LAST_INSERT_ID() returns the id of the updated row if ON DUPLICATE KEY
UPDATE, see auto_increment.test. Same for mysql_insert_id(), see
mysql_client_test.c.
- LAST_INSERT_ID() does not change if no autogenerated value was successfully
inserted (it used to then be 0), see auto_increment.test.
- if in INSERT SELECT no autogenerated value was successfully inserted,
mysql_insert_id() now returns the id of the last inserted row (it already
did this for INSERT VALUES), see mysql_client_test.c.
- if INSERT SELECT uses LAST_INSERT_ID(X), mysql_insert_id() now returns X
(it already did this for INSERT VALUES), see mysql_client_test.c.
- NDB now behaves like other engines wrt SET INSERT_ID: with INSERT IGNORE,
the id passed in SET INSERT_ID is re-used until a row succeeds; SET INSERT_ID
influences not only the first row now.
Additionally, when unlocking a table we check that the thread is not keeping
a next_insert_id (as the table is unlocked that id is potentially out-of-date);
forgetting about this next_insert_id is done in a new
handler::ha_release_auto_increment().
Finally we prepare for engines capable of reserving finite-length intervals
of auto_increment values: we store such intervals in THD. The next step
(to be done by the replication team in 5.1) is to read those intervals from
THD and actually store them in the statement-based binary log. NDB
will be a good engine to test that.
2006-07-09 17:52:19 +02:00
|
|
|
thd->auto_inc_intervals_in_cur_stmt_for_binlog.empty();
|
2006-09-12 15:42:13 +02:00
|
|
|
thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
|
|
|
|
|
|
|
|
thd->query_start_used= 0;
|
2005-08-15 17:15:12 +02:00
|
|
|
thd->is_fatal_error= thd->time_zone_used= 0;
|
2003-12-17 16:35:34 +01:00
|
|
|
thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS |
|
2004-10-29 18:26:52 +02:00
|
|
|
SERVER_QUERY_NO_INDEX_USED |
|
|
|
|
SERVER_QUERY_NO_GOOD_INDEX_USED);
|
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes
Changes that requires code changes in other code of other storage engines.
(Note that all changes are very straightforward and one should find all issues
by compiling a --debug build and fixing all compiler errors and all
asserts in field.cc while running the test suite),
- New optional handler function introduced: reset()
This is called after every DML statement to make it easy for a handler to
statement specific cleanups.
(The only case it's not called is if force the file to be closed)
- handler::extra(HA_EXTRA_RESET) is removed. Code that was there before
should be moved to handler::reset()
- table->read_set contains a bitmap over all columns that are needed
in the query. read_row() and similar functions only needs to read these
columns
- table->write_set contains a bitmap over all columns that will be updated
in the query. write_row() and update_row() only needs to update these
columns.
The above bitmaps should now be up to date in all context
(including ALTER TABLE, filesort()).
The handler is informed of any changes to the bitmap after
fix_fields() by calling the virtual function
handler::column_bitmaps_signal(). If the handler does caching of
these bitmaps (instead of using table->read_set, table->write_set),
it should redo the caching in this code. as the signal() may be sent
several times, it's probably best to set a variable in the signal
and redo the caching on read_row() / write_row() if the variable was
set.
- Removed the read_set and write_set bitmap objects from the handler class
- Removed all column bit handling functions from the handler class.
(Now one instead uses the normal bitmap functions in my_bitmap.c instead
of handler dedicated bitmap functions)
- field->query_id is removed. One should instead instead check
table->read_set and table->write_set if a field is used in the query.
- handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and
handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now
instead use table->read_set to check for which columns to retrieve.
- If a handler needs to call Field->val() or Field->store() on columns
that are not used in the query, one should install a temporary
all-columns-used map while doing so. For this, we provide the following
functions:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
field->val();
dbug_tmp_restore_column_map(table->read_set, old_map);
and similar for the write map:
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
field->val();
dbug_tmp_restore_column_map(table->write_set, old_map);
If this is not done, you will sooner or later hit a DBUG_ASSERT
in the field store() / val() functions.
(For not DBUG binaries, the dbug_tmp_restore_column_map() and
dbug_tmp_restore_column_map() are inline dummy functions and should
be optimized away be the compiler).
- If one needs to temporary set the column map for all binaries (and not
just to avoid the DBUG_ASSERT() in the Field::store() / Field::val()
methods) one should use the functions tmp_use_all_columns() and
tmp_restore_column_map() instead of the above dbug_ variants.
- All 'status' fields in the handler base class (like records,
data_file_length etc) are now stored in a 'stats' struct. This makes
it easier to know what status variables are provided by the base
handler. This requires some trivial variable names in the extra()
function.
- New virtual function handler::records(). This is called to optimize
COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true.
(stats.records is not supposed to be an exact value. It's only has to
be 'reasonable enough' for the optimizer to be able to choose a good
optimization path).
- Non virtual handler::init() function added for caching of virtual
constants from engine.
- Removed has_transactions() virtual method. Now one should instead return
HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support
transactions.
- The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument
that is to be used with 'new handler_name()' to allocate the handler
in the right area. The xxxx_create_handler() function is also
responsible for any initialization of the object before returning.
For example, one should change:
static handler *myisam_create_handler(TABLE_SHARE *table)
{
return new ha_myisam(table);
}
->
static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root)
{
return new (mem_root) ha_myisam(table);
}
- New optional virtual function: use_hidden_primary_key().
This is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
but we don't have a primary key. This allows the handler to take precisions
in remembering any hidden primary key to able to update/delete any
found row. The default handler marks all columns to be read.
- handler::table_flags() now returns a ulonglong (to allow for more flags).
- New/changed table_flags()
- HA_HAS_RECORDS Set if ::records() is supported
- HA_NO_TRANSACTIONS Set if engine doesn't support transactions
- HA_PRIMARY_KEY_REQUIRED_FOR_DELETE
Set if we should mark all primary key columns for
read when reading rows as part of a DELETE
statement. If there is no primary key,
all columns are marked for read.
- HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some
cases (based on table->read_set)
- HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS
Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION.
- HA_DUPP_POS Renamed to HA_DUPLICATE_POS
- HA_REQUIRES_KEY_COLUMNS_FOR_DELETE
Set this if we should mark ALL key columns for
read when when reading rows as part of a DELETE
statement. In case of an update we will mark
all keys for read for which key part changed
value.
- HA_STATS_RECORDS_IS_EXACT
Set this if stats.records is exact.
(This saves us some extra records() calls
when optimizing COUNT(*))
- Removed table_flags()
- HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if
handler::records() gives an exact count() and
HA_STATS_RECORDS_IS_EXACT if stats.records is exact.
- HA_READ_RND_SAME Removed (no one supported this one)
- Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk()
- Renamed handler::dupp_pos to handler::dup_pos
- Removed not used variable handler::sortkey
Upper level handler changes:
- ha_reset() now does some overall checks and calls ::reset()
- ha_table_flags() added. This is a cached version of table_flags(). The
cache is updated on engine creation time and updated on open.
MySQL level changes (not obvious from the above):
- DBUG_ASSERT() added to check that column usage matches what is set
in the column usage bit maps. (This found a LOT of bugs in current
column marking code).
- In 5.1 before, all used columns was marked in read_set and only updated
columns was marked in write_set. Now we only mark columns for which we
need a value in read_set.
- Column bitmaps are created in open_binary_frm() and open_table_from_share().
(Before this was in table.cc)
- handler::table_flags() calls are replaced with handler::ha_table_flags()
- For calling field->val() you must have the corresponding bit set in
table->read_set. For calling field->store() you must have the
corresponding bit set in table->write_set. (There are asserts in
all store()/val() functions to catch wrong usage)
- thd->set_query_id is renamed to thd->mark_used_columns and instead
of setting this to an integer value, this has now the values:
MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE
Changed also all variables named 'set_query_id' to mark_used_columns.
- In filesort() we now inform the handler of exactly which columns are needed
doing the sort and choosing the rows.
- The TABLE_SHARE object has a 'all_set' column bitmap one can use
when one needs a column bitmap with all columns set.
(This is used for table->use_all_columns() and other places)
- The TABLE object has 3 column bitmaps:
- def_read_set Default bitmap for columns to be read
- def_write_set Default bitmap for columns to be written
- tmp_set Can be used as a temporary bitmap when needed.
The table object has also two pointer to bitmaps read_set and write_set
that the handler should use to find out which columns are used in which way.
- count() optimization now calls handler::records() instead of using
handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true).
- Added extra argument to Item::walk() to indicate if we should also
traverse sub queries.
- Added TABLE parameter to cp_buffer_from_ref()
- Don't close tables created with CREATE ... SELECT but keep them in
the table cache. (Faster usage of newly created tables).
New interfaces:
- table->clear_column_bitmaps() to initialize the bitmaps for tables
at start of new statements.
- table->column_bitmaps_set() to set up new column bitmaps and signal
the handler about this.
- table->column_bitmaps_set_no_signal() for some few cases where we need
to setup new column bitmaps but don't signal the handler (as the handler
has already been signaled about these before). Used for the momement
only in opt_range.cc when doing ROR scans.
- table->use_all_columns() to install a bitmap where all columns are marked
as use in the read and the write set.
- table->default_column_bitmaps() to install the normal read and write
column bitmaps, but not signaling the handler about this.
This is mainly used when creating TABLE instances.
- table->mark_columns_needed_for_delete(),
table->mark_columns_needed_for_delete() and
table->mark_columns_needed_for_insert() to allow us to put additional
columns in column usage maps if handler so requires.
(The handler indicates what it neads in handler->table_flags())
- table->prepare_for_position() to allow us to tell handler that it
needs to read primary key parts to be able to store them in
future table->position() calls.
(This replaces the table->file->ha_retrieve_all_pk function)
- table->mark_auto_increment_column() to tell handler are going to update
columns part of any auto_increment key.
- table->mark_columns_used_by_index() to mark all columns that is part of
an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow
it to quickly know that it only needs to read colums that are part
of the key. (The handler can also use the column map for detecting this,
but simpler/faster handler can just monitor the extra() call).
- table->mark_columns_used_by_index_no_reset() to in addition to other columns,
also mark all columns that is used by the given key.
- table->restore_column_maps_after_mark_index() to restore to default
column maps after a call to table->mark_columns_used_by_index().
- New item function register_field_in_read_map(), for marking used columns
in table->read_map. Used by filesort() to mark all used columns
- Maintain in TABLE->merge_keys set of all keys that are used in query.
(Simplices some optimization loops)
- Maintain Field->part_of_key_not_clustered which is like Field->part_of_key
but the field in the clustered key is not assumed to be part of all index.
(used in opt_range.cc for faster loops)
- dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map()
tmp_use_all_columns() and tmp_restore_column_map() functions to temporally
mark all columns as usable. The 'dbug_' version is primarily intended
inside a handler when it wants to just call Field:store() & Field::val()
functions, but don't need the column maps set for any other usage.
(ie:: bitmap_is_set() is never called)
- We can't use compare_records() to skip updates for handlers that returns
a partial column set and the read_set doesn't cover all columns in the
write set. The reason for this is that if we have a column marked only for
write we can't in the MySQL level know if the value changed or not.
The reason this worked before was that MySQL marked all to be written
columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden
bug'.
- open_table_from_share() does not anymore setup temporary MEM_ROOT
object as a thread specific variable for the handler. Instead we
send the to-be-used MEMROOT to get_new_handler().
(Simpler, faster code)
Bugs fixed:
- Column marking was not done correctly in a lot of cases.
(ALTER TABLE, when using triggers, auto_increment fields etc)
(Could potentially result in wrong values inserted in table handlers
relying on that the old column maps or field->set_query_id was correct)
Especially when it comes to triggers, there may be cases where the
old code would cause lost/wrong values for NDB and/or InnoDB tables.
- Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags:
OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG.
This allowed me to remove some wrong warnings about:
"Some non-transactional changed tables couldn't be rolled back"
- Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset
(thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose
some warnings about
"Some non-transactional changed tables couldn't be rolled back")
- Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table()
which could cause delete_table to report random failures.
- Fixed core dumps for some tests when running with --debug
- Added missing FN_LIBCHAR in mysql_rm_tmp_tables()
(This has probably caused us to not properly remove temporary files after
crash)
- slow_logs was not properly initialized, which could maybe cause
extra/lost entries in slow log.
- If we get an duplicate row on insert, change column map to read and
write all columns while retrying the operation. This is required by
the definition of REPLACE and also ensures that fields that are only
part of UPDATE are properly handled. This fixed a bug in NDB and
REPLACE where REPLACE wrongly copied some column values from the replaced
row.
- For table handler that doesn't support NULL in keys, we would give an error
when creating a primary key with NULL fields, even after the fields has been
automaticly converted to NOT NULL.
- Creating a primary key on a SPATIAL key, would fail if field was not
declared as NOT NULL.
Cleanups:
- Removed not used condition argument to setup_tables
- Removed not needed item function reset_query_id_processor().
- Field->add_index is removed. Now this is instead maintained in
(field->flags & FIELD_IN_ADD_INDEX)
- Field->fieldnr is removed (use field->field_index instead)
- New argument to filesort() to indicate that it should return a set of
row pointers (not used columns). This allowed me to remove some references
to sql_command in filesort and should also enable us to return column
results in some cases where we couldn't before.
- Changed column bitmap handling in opt_range.cc to be aligned with TABLE
bitmap, which allowed me to use bitmap functions instead of looping over
all fields to create some needed bitmaps. (Faster and smaller code)
- Broke up found too long lines
- Moved some variable declaration at start of function for better code
readability.
- Removed some not used arguments from functions.
(setup_fields(), mysql_prepare_insert_check_table())
- setup_fields() now takes an enum instead of an int for marking columns
usage.
- For internal temporary tables, use handler::write_row(),
handler::delete_row() and handler::update_row() instead of
handler::ha_xxxx() for faster execution.
- Changed some constants to enum's and define's.
- Using separate column read and write sets allows for easier checking
of timestamp field was set by statement.
- Remove calls to free_io_cache() as this is now done automaticly in ha_reset()
- Don't build table->normalized_path as this is now identical to table->path
(after bar's fixes to convert filenames)
- Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to
do comparision with the 'convert-dbug-for-diff' tool.
Things left to do in 5.1:
- We wrongly log failed CREATE TABLE ... SELECT in some cases when using
row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result)
Mats has promised to look into this.
- Test that my fix for CREATE TABLE ... SELECT is indeed correct.
(I added several test cases for this, but in this case it's better that
someone else also tests this throughly).
Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
|
|
|
/*
|
|
|
|
If in autocommit mode and not in a transaction, reset
|
|
|
|
OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG to not get warnings
|
|
|
|
in ha_rollback_trans() about some tables couldn't be rolled back.
|
|
|
|
*/
|
|
|
|
if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
|
|
|
|
thd->options&= ~(OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG);
|
|
|
|
|
2005-09-20 20:20:38 +02:00
|
|
|
DBUG_ASSERT(thd->security_ctx== &thd->main_security_ctx);
|
2003-02-10 16:59:16 +01:00
|
|
|
thd->tmp_table_used= 0;
|
2005-08-15 17:15:12 +02:00
|
|
|
if (!thd->in_sub_stmt)
|
|
|
|
{
|
2005-08-15 19:08:58 +02:00
|
|
|
if (opt_bin_log)
|
2005-09-07 17:39:47 +02:00
|
|
|
{
|
2005-08-15 19:08:58 +02:00
|
|
|
reset_dynamic(&thd->user_var_events);
|
2005-09-07 17:39:47 +02:00
|
|
|
thd->user_var_events_alloc= thd->mem_root;
|
|
|
|
}
|
2005-08-15 19:08:58 +02:00
|
|
|
thd->clear_error();
|
2005-08-15 17:15:12 +02:00
|
|
|
thd->total_warn_count=0; // Warnings for this query
|
|
|
|
thd->rand_used= 0;
|
|
|
|
thd->sent_row_count= thd->examined_row_count= 0;
|
|
|
|
}
|
2006-07-10 18:41:03 +02:00
|
|
|
/*
|
|
|
|
Because we come here only for start of top-statements, binlog format is
|
|
|
|
constant inside a complex statement (using stored functions) etc.
|
|
|
|
*/
|
* Mixed replication mode * :
1) Fix for BUG#19630 "stored function inserting into two auto_increment breaks
statement-based binlog":
a stored function inserting into two such tables may fail to replicate
(inserting wrong data in the slave's copy of the second table) if the slave's
second table had an internal auto_increment counter different from master's.
Because the auto_increment value autogenerated by master for the 2nd table
does not go into binlog, only the first does, so the slave lacks information.
To fix this, if running in mixed binlogging mode, if the stored function or
trigger plans to update two different tables both having auto_increment
columns, we switch to row-based for the whole function.
We don't have a simple solution for statement-based binlogging mode, there
the bug remains and will be documented as a known problem.
Re-enabling rpl_switch_stm_row_mixed.
2) Fix for BUG#20630 "Mixed binlogging mode does not work with stored
functions, triggers, views", which was a documented limitation (in mixed
mode, we didn't detect that a stored function's execution needed row-based
binlogging (due to some UUID() call for example); same for
triggers, same for views (a view created from a SELECT UUID(), and doing
INSERT INTO sometable SELECT theview; would not replicate row-based).
This is implemented by, after parsing a routine's body, remembering in sp_head
that this routine needs row-based binlogging. Then when this routine is used,
the caller is marked to require row-based binlogging too.
Same for views: when we parse a view and detect that its SELECT needs
row-based binary logging, we mark the calling LEX as such.
3) Fix for BUG#20499 "mixed mode with temporary table breaks binlog":
a temporary table containing e.g. UUID has its changes not binlogged,
so any query updating a permanent table with data from the temporary table
will run wrongly on slave. Solution: in mixed mode we don't switch back
from row-based to statement-based when there exists temporary tables.
4) Attempt to test mysqlbinlog on a binlog generated by mysqlbinlog;
impossible due to BUG#11312 and BUG#20329, but test is in place for when
they are fixed.
2006-07-09 17:00:47 +02:00
|
|
|
thd->reset_current_stmt_binlog_row_based();
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
|
2000-08-15 19:09:37 +02:00
|
|
|
void
|
|
|
|
mysql_init_select(LEX *lex)
|
|
|
|
{
|
2003-07-03 01:30:52 +02:00
|
|
|
SELECT_LEX *select_lex= lex->current_select;
|
2002-05-06 23:04:16 +02:00
|
|
|
select_lex->init_select();
|
2004-11-13 11:56:39 +01:00
|
|
|
lex->wild= 0;
|
2002-12-05 23:40:28 +01:00
|
|
|
if (select_lex == &lex->select_lex)
|
|
|
|
{
|
2004-08-24 18:17:11 +02:00
|
|
|
DBUG_ASSERT(lex->result == 0);
|
2002-12-05 23:40:28 +01:00
|
|
|
lex->exchange= 0;
|
|
|
|
}
|
2000-08-15 19:09:37 +02:00
|
|
|
}
|
|
|
|
|
2002-06-16 16:06:12 +02:00
|
|
|
|
2001-12-13 14:53:18 +01:00
|
|
|
bool
|
2002-05-06 23:04:16 +02:00
|
|
|
mysql_new_select(LEX *lex, bool move_down)
|
2001-06-13 12:36:53 +02:00
|
|
|
{
|
2004-11-08 00:13:54 +01:00
|
|
|
SELECT_LEX *select_lex;
|
2005-06-07 22:34:53 +02:00
|
|
|
THD *thd= lex->thd;
|
2005-02-05 15:05:46 +01:00
|
|
|
DBUG_ENTER("mysql_new_select");
|
|
|
|
|
2005-06-07 22:34:53 +02:00
|
|
|
if (!(select_lex= new (thd->mem_root) SELECT_LEX()))
|
2005-02-05 15:05:46 +01:00
|
|
|
DBUG_RETURN(1);
|
2005-06-07 22:34:53 +02:00
|
|
|
select_lex->select_number= ++thd->select_number;
|
2005-08-12 16:57:19 +02:00
|
|
|
select_lex->parent_lex= lex; /* Used in init_query. */
|
2002-05-06 23:04:16 +02:00
|
|
|
select_lex->init_query();
|
|
|
|
select_lex->init_select();
|
2005-10-15 23:32:37 +02:00
|
|
|
lex->nest_level++;
|
|
|
|
select_lex->nest_level= lex->nest_level;
|
2005-07-13 15:38:55 +02:00
|
|
|
/*
|
|
|
|
Don't evaluate this subquery during statement prepare even if
|
|
|
|
it's a constant one. The flag is switched off in the end of
|
|
|
|
mysql_stmt_prepare.
|
|
|
|
*/
|
2005-09-02 15:21:19 +02:00
|
|
|
if (thd->stmt_arena->is_stmt_prepare())
|
2005-06-07 22:34:53 +02:00
|
|
|
select_lex->uncacheable|= UNCACHEABLE_PREPARE;
|
2002-05-06 23:04:16 +02:00
|
|
|
if (move_down)
|
|
|
|
{
|
2005-02-05 15:05:46 +01:00
|
|
|
SELECT_LEX_UNIT *unit;
|
2004-11-30 20:41:12 +01:00
|
|
|
lex->subqueries= TRUE;
|
2002-05-06 23:04:16 +02:00
|
|
|
/* first select_lex of subselect or derived table */
|
2005-06-07 22:34:53 +02:00
|
|
|
if (!(unit= new (thd->mem_root) SELECT_LEX_UNIT()))
|
2005-02-05 15:05:46 +01:00
|
|
|
DBUG_RETURN(1);
|
2004-11-08 00:13:54 +01:00
|
|
|
|
2002-05-06 23:04:16 +02:00
|
|
|
unit->init_query();
|
|
|
|
unit->init_select();
|
2005-06-07 22:34:53 +02:00
|
|
|
unit->thd= thd;
|
2003-07-03 01:30:52 +02:00
|
|
|
unit->include_down(lex->current_select);
|
2002-12-15 21:01:09 +01:00
|
|
|
unit->link_next= 0;
|
|
|
|
unit->link_prev= 0;
|
2003-03-11 00:06:28 +01:00
|
|
|
unit->return_to= lex->current_select;
|
2002-05-06 23:04:16 +02:00
|
|
|
select_lex->include_down(unit);
|
2005-07-01 06:05:42 +02:00
|
|
|
/*
|
|
|
|
By default we assume that it is usual subselect and we have outer name
|
|
|
|
resolution context, if no we will assign it to 0 later
|
|
|
|
*/
|
|
|
|
select_lex->context.outer_context= &select_lex->outer_select()->context;
|
2002-05-06 23:04:16 +02:00
|
|
|
}
|
|
|
|
else
|
2003-07-03 01:30:52 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
if (lex->current_select->order_list.first && !lex->current_select->braces)
|
|
|
|
{
|
2004-10-20 03:04:37 +02:00
|
|
|
my_error(ER_WRONG_USAGE, MYF(0), "UNION", "ORDER BY");
|
2005-02-05 15:05:46 +01:00
|
|
|
DBUG_RETURN(1);
|
2004-07-16 00:15:55 +02:00
|
|
|
}
|
2002-10-30 12:18:52 +01:00
|
|
|
select_lex->include_neighbour(lex->current_select);
|
2006-04-21 09:36:20 +02:00
|
|
|
SELECT_LEX_UNIT *unit= select_lex->master_unit();
|
|
|
|
if (!unit->fake_select_lex && unit->add_fake_select_lex(lex->thd))
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
select_lex->context.outer_context=
|
|
|
|
unit->first_select()->context.outer_context;
|
2003-07-03 01:30:52 +02:00
|
|
|
}
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2002-05-27 19:52:54 +02:00
|
|
|
select_lex->master_unit()->global_parameters= select_lex;
|
2002-11-27 00:12:16 +01:00
|
|
|
select_lex->include_global((st_select_lex_node**)&lex->all_selects_list);
|
2002-10-30 12:18:52 +01:00
|
|
|
lex->current_select= select_lex;
|
2005-07-01 06:05:42 +02:00
|
|
|
/*
|
|
|
|
in subquery is SELECT query and we allow resolution of names in SELECT
|
|
|
|
list
|
|
|
|
*/
|
|
|
|
select_lex->context.resolve_in_select_list= TRUE;
|
2005-02-05 15:05:46 +01:00
|
|
|
DBUG_RETURN(0);
|
2001-06-13 12:36:53 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
/*
|
|
|
|
Create a select to return the same output as 'SELECT @@var_name'.
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
create_select_for_variable()
|
|
|
|
var_name Variable name
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
Used for SHOW COUNT(*) [ WARNINGS | ERROR]
|
|
|
|
|
|
|
|
This will crash with a core dump if the variable doesn't exists
|
|
|
|
*/
|
|
|
|
|
|
|
|
void create_select_for_variable(const char *var_name)
|
|
|
|
{
|
2003-07-06 18:09:57 +02:00
|
|
|
THD *thd;
|
2002-10-02 12:33:08 +02:00
|
|
|
LEX *lex;
|
2003-07-06 18:09:57 +02:00
|
|
|
LEX_STRING tmp, null_lex_string;
|
2005-07-25 20:25:28 +02:00
|
|
|
Item *var;
|
|
|
|
char buff[MAX_SYS_VAR_LENGTH*2+4+8], *end;
|
2002-10-02 12:33:08 +02:00
|
|
|
DBUG_ENTER("create_select_for_variable");
|
2003-07-06 18:09:57 +02:00
|
|
|
|
|
|
|
thd= current_thd;
|
2003-08-26 17:41:40 +02:00
|
|
|
lex= thd->lex;
|
2002-10-02 12:33:08 +02:00
|
|
|
mysql_init_select(lex);
|
|
|
|
lex->sql_command= SQLCOM_SELECT;
|
|
|
|
tmp.str= (char*) var_name;
|
|
|
|
tmp.length=strlen(var_name);
|
2003-07-06 18:09:57 +02:00
|
|
|
bzero((char*) &null_lex_string.str, sizeof(null_lex_string));
|
2005-07-25 20:25:28 +02:00
|
|
|
/*
|
|
|
|
We set the name of Item to @@session.var_name because that then is used
|
|
|
|
as the column name in the output.
|
|
|
|
*/
|
2005-08-08 23:13:49 +02:00
|
|
|
if ((var= get_system_var(thd, OPT_SESSION, tmp, null_lex_string)))
|
|
|
|
{
|
|
|
|
end= strxmov(buff, "@@session.", var_name, NullS);
|
|
|
|
var->set_name(buff, end-buff, system_charset_info);
|
|
|
|
add_item_to_list(thd, var);
|
|
|
|
}
|
2002-10-02 12:33:08 +02:00
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
2002-06-16 16:06:12 +02:00
|
|
|
|
2002-01-02 23:46:43 +01:00
|
|
|
void mysql_init_multi_delete(LEX *lex)
|
|
|
|
{
|
2002-05-06 23:04:16 +02:00
|
|
|
lex->sql_command= SQLCOM_DELETE_MULTI;
|
2002-01-02 23:46:43 +01:00
|
|
|
mysql_init_select(lex);
|
2005-06-07 12:11:36 +02:00
|
|
|
lex->select_lex.select_limit= 0;
|
|
|
|
lex->unit.select_limit_cnt= HA_POS_ERROR;
|
2006-07-03 23:13:04 +02:00
|
|
|
lex->select_lex.table_list.save_and_clear(&lex->auxiliary_table_list);
|
2005-05-24 12:44:34 +02:00
|
|
|
lex->lock_option= using_update_log ? TL_READ_NO_INSERT : TL_READ;
|
2004-07-16 00:15:55 +02:00
|
|
|
lex->query_tables= 0;
|
|
|
|
lex->query_tables_last= &lex->query_tables;
|
2002-01-02 23:46:43 +01:00
|
|
|
}
|
2001-12-13 14:53:18 +01:00
|
|
|
|
2004-03-11 17:38:19 +01:00
|
|
|
/*
|
|
|
|
When you modify mysql_parse(), you may need to mofify
|
|
|
|
mysql_test_parse_for_slave() in this same file.
|
|
|
|
*/
|
2004-04-12 02:26:32 +02:00
|
|
|
|
2004-03-11 17:38:19 +01:00
|
|
|
void mysql_parse(THD *thd, char *inBuf, uint length)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("mysql_parse");
|
2006-08-18 01:08:51 +02:00
|
|
|
|
|
|
|
DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on(););
|
|
|
|
|
2004-07-21 21:44:12 +02:00
|
|
|
mysql_init_query(thd, (uchar*) inBuf, length);
|
2002-03-22 21:55:08 +01:00
|
|
|
if (query_cache_send_result_to_client(thd, inBuf, length) <= 0)
|
2001-12-02 13:34:01 +01:00
|
|
|
{
|
2004-07-21 21:44:12 +02:00
|
|
|
LEX *lex= thd->lex;
|
2005-08-25 15:34:34 +02:00
|
|
|
|
2005-08-10 23:17:02 +02:00
|
|
|
sp_cache_flush_obsolete(&thd->sp_proc_cache);
|
|
|
|
sp_cache_flush_obsolete(&thd->sp_func_cache);
|
2005-08-25 15:34:34 +02:00
|
|
|
|
2006-03-10 01:44:08 +01:00
|
|
|
if (!MYSQLparse((void *)thd) && ! thd->is_fatal_error)
|
2001-12-02 13:34:01 +01:00
|
|
|
{
|
2003-09-26 12:33:13 +02:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2002-06-16 16:06:12 +02:00
|
|
|
if (mqh_used && thd->user_connect &&
|
2002-11-09 14:40:46 +01:00
|
|
|
check_mqh(thd, lex->sql_command))
|
2002-05-15 12:50:38 +02:00
|
|
|
{
|
|
|
|
thd->net.error = 0;
|
|
|
|
}
|
|
|
|
else
|
2003-09-26 12:33:13 +02:00
|
|
|
#endif
|
2002-05-15 12:50:38 +02:00
|
|
|
{
|
A fix for Bug#26750 "valgrind leak in sp_head" (and post-review
fixes).
The legend: on a replication slave, in case a trigger creation
was filtered out because of application of replicate-do-table/
replicate-ignore-table rule, the parsed definition of a trigger was not
cleaned up properly. LEX::sphead member was left around and leaked
memory. Until the actual implementation of support of
replicate-ignore-table rules for triggers by the patch for Bug 24478 it
was never the case that "case SQLCOM_CREATE_TRIGGER"
was not executed once a trigger was parsed,
so the deletion of lex->sphead there worked and the memory did not leak.
The fix:
The real cause of the bug is that there is no 1 or 2 places where
we can clean up the main LEX after parse. And the reason we
can not have just one or two places where we clean up the LEX is
asymmetric behaviour of MYSQLparse in case of success or error.
One of the root causes of this behaviour is the code in Item::Item()
constructor. There, a newly created item adds itself to THD::free_list
- a single-linked list of Items used in a statement. Yuck. This code
is unaware that we may have more than one statement active at a time,
and always assumes that the free_list of the current statement is
located in THD::free_list. One day we need to be able to explicitly
allocate an item in a given Query_arena.
Thus, when parsing a definition of a stored procedure, like
CREATE PROCEDURE p1() BEGIN SELECT a FROM t1; SELECT b FROM t1; END;
we actually need to reset THD::mem_root, THD::free_list and THD::lex
to parse the nested procedure statement (SELECT *).
The actual reset and restore is implemented in semantic actions
attached to sp_proc_stmt grammar rule.
The problem is that in case of a parsing error inside a nested statement
Bison generated parser would abort immediately, without executing the
restore part of the semantic action. This would leave THD in an
in-the-middle-of-parsing state.
This is why we couldn't have had a single place where we clean up the LEX
after MYSQLparse - in case of an error we needed to do a clean up
immediately, in case of success a clean up could have been delayed.
This left the door open for a memory leak.
One of the following possibilities were considered when working on a fix:
- patch the replication logic to do the clean up. Rejected
as breaks module borders, replication code should not need to know the
gory details of clean up procedure after CREATE TRIGGER.
- wrap MYSQLparse with a function that would do a clean up.
Rejected as ideally we should fix the problem when it happens, not
adjust for it outside of the problematic code.
- make sure MYSQLparse cleans up after itself by invoking the clean up
functionality in the appropriate places before return. Implemented in
this patch.
- use %destructor rule for sp_proc_stmt to restore THD - cleaner
than the prevoius approach, but rejected
because needs a careful analysis of the side effects, and this patch is
for 5.0, and long term we need to use the next alternative anyway
- make sure that sp_proc_stmt doesn't juggle with THD - this is a
large work that will affect many modules.
Cleanup: move main_lex and main_mem_root from Statement to its
only two descendants Prepared_statement and THD. This ensures that
when a Statement instance was created for purposes of statement backup,
we do not involve LEX constructor/destructor, which is fairly expensive.
In order to track that the transformation produces equivalent
functionality please check the respective constructors and destructors
of Statement, Prepared_statement and THD - these members were
used only there.
This cleanup is unrelated to the patch.
2007-03-07 10:24:46 +01:00
|
|
|
if (! thd->net.report_error)
|
2002-11-28 18:29:26 +01:00
|
|
|
{
|
2005-02-14 23:47:17 +01:00
|
|
|
/*
|
|
|
|
Binlog logs a string starting from thd->query and having length
|
|
|
|
thd->query_length; so we set thd->query_length correctly (to not
|
|
|
|
log several statements in one event, when we executed only first).
|
|
|
|
We set it to not see the ';' (otherwise it would get into binlog
|
|
|
|
and Query_log_event::print() would give ';;' output).
|
|
|
|
This also helps display only the current query in SHOW
|
|
|
|
PROCESSLIST.
|
|
|
|
Note that we don't need LOCK_thread_count to modify query_length.
|
|
|
|
*/
|
2005-02-18 14:42:14 +01:00
|
|
|
if (lex->found_semicolon &&
|
|
|
|
(thd->query_length= (ulong)(lex->found_semicolon - thd->query)))
|
2005-02-14 23:47:17 +01:00
|
|
|
thd->query_length--;
|
|
|
|
/* Actually execute the query */
|
2002-11-28 18:29:26 +01:00
|
|
|
mysql_execute_command(thd);
|
2003-12-01 14:19:10 +01:00
|
|
|
query_cache_end_of_result(thd);
|
2002-11-28 18:29:26 +01:00
|
|
|
}
|
2002-05-15 12:50:38 +02:00
|
|
|
}
|
2001-12-02 13:34:01 +01:00
|
|
|
}
|
|
|
|
else
|
2002-07-24 18:55:08 +02:00
|
|
|
{
|
2006-05-03 16:02:43 +02:00
|
|
|
DBUG_ASSERT(thd->net.report_error);
|
2002-07-24 18:55:08 +02:00
|
|
|
DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
|
2003-01-30 21:15:44 +01:00
|
|
|
thd->is_fatal_error));
|
2006-10-19 12:43:52 +02:00
|
|
|
|
|
|
|
query_cache_abort(&thd->net);
|
2002-07-24 18:55:08 +02:00
|
|
|
}
|
A fix for Bug#26750 "valgrind leak in sp_head" (and post-review
fixes).
The legend: on a replication slave, in case a trigger creation
was filtered out because of application of replicate-do-table/
replicate-ignore-table rule, the parsed definition of a trigger was not
cleaned up properly. LEX::sphead member was left around and leaked
memory. Until the actual implementation of support of
replicate-ignore-table rules for triggers by the patch for Bug 24478 it
was never the case that "case SQLCOM_CREATE_TRIGGER"
was not executed once a trigger was parsed,
so the deletion of lex->sphead there worked and the memory did not leak.
The fix:
The real cause of the bug is that there is no 1 or 2 places where
we can clean up the main LEX after parse. And the reason we
can not have just one or two places where we clean up the LEX is
asymmetric behaviour of MYSQLparse in case of success or error.
One of the root causes of this behaviour is the code in Item::Item()
constructor. There, a newly created item adds itself to THD::free_list
- a single-linked list of Items used in a statement. Yuck. This code
is unaware that we may have more than one statement active at a time,
and always assumes that the free_list of the current statement is
located in THD::free_list. One day we need to be able to explicitly
allocate an item in a given Query_arena.
Thus, when parsing a definition of a stored procedure, like
CREATE PROCEDURE p1() BEGIN SELECT a FROM t1; SELECT b FROM t1; END;
we actually need to reset THD::mem_root, THD::free_list and THD::lex
to parse the nested procedure statement (SELECT *).
The actual reset and restore is implemented in semantic actions
attached to sp_proc_stmt grammar rule.
The problem is that in case of a parsing error inside a nested statement
Bison generated parser would abort immediately, without executing the
restore part of the semantic action. This would leave THD in an
in-the-middle-of-parsing state.
This is why we couldn't have had a single place where we clean up the LEX
after MYSQLparse - in case of an error we needed to do a clean up
immediately, in case of success a clean up could have been delayed.
This left the door open for a memory leak.
One of the following possibilities were considered when working on a fix:
- patch the replication logic to do the clean up. Rejected
as breaks module borders, replication code should not need to know the
gory details of clean up procedure after CREATE TRIGGER.
- wrap MYSQLparse with a function that would do a clean up.
Rejected as ideally we should fix the problem when it happens, not
adjust for it outside of the problematic code.
- make sure MYSQLparse cleans up after itself by invoking the clean up
functionality in the appropriate places before return. Implemented in
this patch.
- use %destructor rule for sp_proc_stmt to restore THD - cleaner
than the prevoius approach, but rejected
because needs a careful analysis of the side effects, and this patch is
for 5.0, and long term we need to use the next alternative anyway
- make sure that sp_proc_stmt doesn't juggle with THD - this is a
large work that will affect many modules.
Cleanup: move main_lex and main_mem_root from Statement to its
only two descendants Prepared_statement and THD. This ensures that
when a Statement instance was created for purposes of statement backup,
we do not involve LEX constructor/destructor, which is fairly expensive.
In order to track that the transformation produces equivalent
functionality please check the respective constructors and destructors
of Statement, Prepared_statement and THD - these members were
used only there.
This cleanup is unrelated to the patch.
2007-03-07 10:24:46 +01:00
|
|
|
if (thd->lex->sphead)
|
|
|
|
{
|
|
|
|
delete thd->lex->sphead;
|
|
|
|
thd->lex->sphead= 0;
|
|
|
|
}
|
|
|
|
lex->unit.cleanup();
|
2001-12-02 13:34:01 +01:00
|
|
|
thd->proc_info="freeing items";
|
2004-08-24 18:17:11 +02:00
|
|
|
thd->end_statement();
|
2004-09-15 21:10:31 +02:00
|
|
|
thd->cleanup_after_query();
|
2004-10-08 00:21:19 +02:00
|
|
|
DBUG_ASSERT(thd->change_list.is_empty());
|
2001-12-02 13:34:01 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-17 23:09:13 +01:00
|
|
|
#ifdef HAVE_REPLICATION
|
2004-03-11 17:38:19 +01:00
|
|
|
/*
|
|
|
|
Usable by the replication SQL thread only: just parse a query to know if it
|
|
|
|
can be ignored because of replicate-*-table rules.
|
|
|
|
|
|
|
|
RETURN VALUES
|
|
|
|
0 cannot be ignored
|
|
|
|
1 can be ignored
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
|
|
|
|
{
|
2004-07-21 21:44:12 +02:00
|
|
|
LEX *lex= thd->lex;
|
2004-03-11 17:38:19 +01:00
|
|
|
bool error= 0;
|
2004-07-15 03:19:07 +02:00
|
|
|
DBUG_ENTER("mysql_test_parse_for_slave");
|
2004-03-11 17:38:19 +01:00
|
|
|
|
2004-07-21 21:44:12 +02:00
|
|
|
mysql_init_query(thd, (uchar*) inBuf, length);
|
2006-03-10 01:44:08 +01:00
|
|
|
if (!MYSQLparse((void*) thd) && ! thd->is_fatal_error &&
|
2004-03-11 17:38:19 +01:00
|
|
|
all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
|
2004-07-15 03:19:07 +02:00
|
|
|
error= 1; /* Ignore question */
|
2004-08-24 18:17:11 +02:00
|
|
|
thd->end_statement();
|
2004-09-15 21:10:31 +02:00
|
|
|
thd->cleanup_after_query();
|
2004-07-15 03:19:07 +02:00
|
|
|
DBUG_RETURN(error);
|
2004-03-11 17:38:19 +01:00
|
|
|
}
|
2004-03-17 23:09:13 +01:00
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2004-09-15 21:10:31 +02:00
|
|
|
|
2004-10-29 18:26:52 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/*****************************************************************************
|
|
|
|
** Store field definition for create
|
|
|
|
** Return 0 if ok
|
|
|
|
******************************************************************************/
|
|
|
|
|
2002-12-06 20:11:27 +01:00
|
|
|
bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
|
2000-07-31 21:29:14 +02:00
|
|
|
char *length, char *decimals,
|
2002-06-02 20:22:20 +02:00
|
|
|
uint type_modifier,
|
2004-04-02 08:12:53 +02:00
|
|
|
Item *default_value, Item *on_update_value,
|
|
|
|
LEX_STRING *comment,
|
2004-12-02 09:48:43 +01:00
|
|
|
char *change,
|
|
|
|
List<String> *interval_list, CHARSET_INFO *cs,
|
2003-03-27 10:09:09 +01:00
|
|
|
uint uint_geom_type)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
register create_field *new_field;
|
2003-05-05 20:54:37 +02:00
|
|
|
LEX *lex= thd->lex;
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_ENTER("add_field_to_list");
|
|
|
|
|
|
|
|
if (strlen(field_name) > NAME_LEN)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_TOO_LONG_IDENT, MYF(0), field_name); /* purecov: inspected */
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(1); /* purecov: inspected */
|
|
|
|
}
|
|
|
|
if (type_modifier & PRI_KEY_FLAG)
|
|
|
|
{
|
|
|
|
lex->col_list.push_back(new key_part_spec(field_name,0));
|
2006-05-03 14:59:17 +02:00
|
|
|
lex->key_list.push_back(new Key(Key::PRIMARY, NullS,
|
|
|
|
&default_key_create_info,
|
2004-05-11 23:29:52 +02:00
|
|
|
0, lex->col_list));
|
2000-07-31 21:29:14 +02:00
|
|
|
lex->col_list.empty();
|
|
|
|
}
|
|
|
|
if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
|
|
|
|
{
|
|
|
|
lex->col_list.push_back(new key_part_spec(field_name,0));
|
2006-05-03 14:59:17 +02:00
|
|
|
lex->key_list.push_back(new Key(Key::UNIQUE, NullS,
|
|
|
|
&default_key_create_info, 0,
|
2000-07-31 21:29:14 +02:00
|
|
|
lex->col_list));
|
|
|
|
lex->col_list.empty();
|
|
|
|
}
|
|
|
|
|
2003-03-22 18:55:09 +01:00
|
|
|
if (default_value)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-04-02 08:12:53 +02:00
|
|
|
/*
|
2004-03-20 12:36:26 +01:00
|
|
|
Default value should be literal => basic constants =>
|
|
|
|
no need fix_fields()
|
2004-04-02 08:12:53 +02:00
|
|
|
|
|
|
|
We allow only one function as part of default value -
|
|
|
|
NOW() as default for TIMESTAMP type.
|
2004-01-30 13:13:19 +01:00
|
|
|
*/
|
2004-04-02 08:12:53 +02:00
|
|
|
if (default_value->type() == Item::FUNC_ITEM &&
|
|
|
|
!(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC &&
|
2006-12-02 02:26:52 +01:00
|
|
|
type == MYSQL_TYPE_TIMESTAMP))
|
2003-12-09 09:36:57 +01:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
|
2003-12-09 09:36:57 +01:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
else if (default_value->type() == Item::NULL_ITEM)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-10-04 15:13:31 +02:00
|
|
|
default_value= 0;
|
2003-03-22 18:55:09 +01:00
|
|
|
if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) ==
|
|
|
|
NOT_NULL_FLAG)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
|
2003-03-22 18:55:09 +01:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (type_modifier & AUTO_INCREMENT_FLAG)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
}
|
2004-04-02 08:12:53 +02:00
|
|
|
|
2006-12-02 02:26:52 +01:00
|
|
|
if (on_update_value && type != MYSQL_TYPE_TIMESTAMP)
|
2004-04-02 08:12:53 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name);
|
2004-04-02 08:12:53 +02:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
2005-03-04 22:14:35 +01:00
|
|
|
|
2006-12-02 02:26:52 +01:00
|
|
|
if (type == MYSQL_TYPE_TIMESTAMP && length)
|
2005-06-20 12:09:00 +02:00
|
|
|
{
|
|
|
|
/* Display widths are no longer supported for TIMSTAMP as of MySQL 4.1.
|
|
|
|
In other words, for declarations such as TIMESTAMP(2), TIMESTAMP(4),
|
|
|
|
and so on, the display width is ignored.
|
|
|
|
*/
|
2005-06-27 14:26:07 +02:00
|
|
|
char buf[32];
|
2005-09-23 15:47:08 +02:00
|
|
|
my_snprintf(buf, sizeof(buf), "TIMESTAMP(%s)", length);
|
2006-05-02 19:56:43 +02:00
|
|
|
WARN_DEPRECATED(thd, "5.2", buf, "'TIMESTAMP'");
|
2005-06-20 12:09:00 +02:00
|
|
|
}
|
|
|
|
|
2005-12-07 15:01:17 +01:00
|
|
|
if (!(new_field= new create_field()) ||
|
|
|
|
new_field->init(thd, field_name, type, length, decimals, type_modifier,
|
|
|
|
default_value, on_update_value, comment, change,
|
|
|
|
interval_list, cs, uint_geom_type))
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(1);
|
2005-03-04 22:14:35 +01:00
|
|
|
|
|
|
|
lex->create_list.push_back(new_field);
|
|
|
|
lex->last_field=new_field;
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
2004-12-18 04:19:21 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/* Store position for column in ALTER TABLE .. ADD column */
|
|
|
|
|
|
|
|
void store_position_for_column(const char *name)
|
|
|
|
{
|
2006-06-30 22:14:28 +02:00
|
|
|
current_thd->lex->last_field->after=my_const_cast(char*) (name);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2002-11-29 15:40:18 +01:00
|
|
|
add_proc_to_list(THD* thd, Item *item)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
ORDER *order;
|
|
|
|
Item **item_ptr;
|
|
|
|
|
2002-12-06 20:11:27 +01:00
|
|
|
if (!(order = (ORDER *) thd->alloc(sizeof(ORDER)+sizeof(Item*))))
|
2000-07-31 21:29:14 +02:00
|
|
|
return 1;
|
|
|
|
item_ptr = (Item**) (order+1);
|
|
|
|
*item_ptr= item;
|
|
|
|
order->item=item_ptr;
|
|
|
|
order->free_me=0;
|
2003-05-05 20:54:37 +02:00
|
|
|
thd->lex->proc_list.link_in_list((byte*) order,(byte**) &order->next);
|
2000-07-31 21:29:14 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
** save order by and tables in own lists
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
2002-12-06 20:11:27 +01:00
|
|
|
bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
ORDER *order;
|
|
|
|
DBUG_ENTER("add_to_list");
|
2004-04-03 10:13:51 +02:00
|
|
|
if (!(order = (ORDER *) thd->alloc(sizeof(ORDER))))
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(1);
|
2004-04-03 10:13:51 +02:00
|
|
|
order->item_ptr= item;
|
|
|
|
order->item= &order->item_ptr;
|
2000-07-31 21:29:14 +02:00
|
|
|
order->asc = asc;
|
|
|
|
order->free_me=0;
|
|
|
|
order->used=0;
|
2004-08-31 10:58:45 +02:00
|
|
|
order->counter_used= 0;
|
2002-11-29 15:40:18 +01:00
|
|
|
list.link_in_list((byte*) order,(byte**) &order->next);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-01-09 21:42:31 +01:00
|
|
|
/*
|
|
|
|
Add a table to list of used tables
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
add_table_to_list()
|
|
|
|
table Table to add
|
|
|
|
alias alias for table (or null if no alias)
|
|
|
|
table_options A set of the following bits:
|
|
|
|
TL_OPTION_UPDATING Table will be updated
|
|
|
|
TL_OPTION_FORCE_INDEX Force usage of index
|
2006-09-04 17:40:30 +02:00
|
|
|
TL_OPTION_ALIAS an alias in multi table DELETE
|
2003-01-09 21:42:31 +01:00
|
|
|
lock_type How table should be locked
|
|
|
|
use_index List of indexed used in USE INDEX
|
|
|
|
ignore_index List of indexed used in IGNORE INDEX
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 Error
|
|
|
|
# Pointer to TABLE_LIST element added to the total table list
|
|
|
|
*/
|
|
|
|
|
2002-12-06 20:11:27 +01:00
|
|
|
TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
|
|
|
|
Table_ident *table,
|
2002-10-30 12:18:52 +01:00
|
|
|
LEX_STRING *alias,
|
2003-01-09 21:42:31 +01:00
|
|
|
ulong table_options,
|
|
|
|
thr_lock_type lock_type,
|
2007-03-05 18:08:41 +01:00
|
|
|
List<index_hint> *index_hints_arg,
|
2003-07-16 21:30:49 +02:00
|
|
|
LEX_STRING *option)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
register TABLE_LIST *ptr;
|
2005-08-12 16:57:19 +02:00
|
|
|
TABLE_LIST *previous_table_ref; /* The table preceding the current one. */
|
2000-07-31 21:29:14 +02:00
|
|
|
char *alias_str;
|
2004-09-03 20:43:04 +02:00
|
|
|
LEX *lex= thd->lex;
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_ENTER("add_table_to_list");
|
2005-08-18 02:12:42 +02:00
|
|
|
LINT_INIT(previous_table_ref);
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
if (!table)
|
|
|
|
DBUG_RETURN(0); // End of memory
|
|
|
|
alias_str= alias ? alias->str : table->table.str;
|
2006-09-04 17:45:48 +02:00
|
|
|
if (!test(table_options & TL_OPTION_ALIAS) &&
|
|
|
|
check_table_name(table->table.str, table->table.length))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_WRONG_TABLE_NAME, MYF(0), table->table.str);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
2006-08-09 14:37:26 +02:00
|
|
|
|
|
|
|
if (table->is_derived_table() == FALSE && table->db.str &&
|
2006-10-16 18:57:33 +02:00
|
|
|
check_db_name(&table->db))
|
2006-05-02 19:56:43 +02:00
|
|
|
{
|
|
|
|
my_error(ER_WRONG_DB_NAME, MYF(0), table->db.str);
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
if (!alias) /* Alias is case sensitive */
|
2002-11-28 18:19:21 +01:00
|
|
|
{
|
|
|
|
if (table->sel)
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_DERIVED_MUST_HAVE_ALIAS,
|
|
|
|
ER(ER_DERIVED_MUST_HAVE_ALIAS), MYF(0));
|
2002-11-28 18:19:21 +01:00
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
2001-08-14 19:33:49 +02:00
|
|
|
if (!(alias_str=thd->memdup(alias_str,table->table.length+1)))
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(0);
|
2002-11-28 18:19:21 +01:00
|
|
|
}
|
2000-08-21 02:07:54 +02:00
|
|
|
if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(0); /* purecov: inspected */
|
2002-11-30 18:58:53 +01:00
|
|
|
if (table->db.str)
|
2002-03-15 22:57:31 +01:00
|
|
|
{
|
|
|
|
ptr->db= table->db.str;
|
|
|
|
ptr->db_length= table->db.length;
|
|
|
|
}
|
A fix and a test case for
Bug#19022 "Memory bug when switching db during trigger execution"
Bug#17199 "Problem when view calls function from another database."
Bug#18444 "Fully qualified stored function names don't work correctly in
SELECT statements"
Documentation note: this patch introduces a change in behaviour of prepared
statements.
This patch adds a few new invariants with regard to how THD::db should
be used. These invariants should be preserved in future:
- one should never refer to THD::db by pointer and always make a deep copy
(strmake, strdup)
- one should never compare two databases by pointer, but use strncmp or
my_strncasecmp
- TABLE_LIST object table->db should be always initialized in the parser or
by creator of the object.
For prepared statements it means that if the current database is changed
after a statement is prepared, the database that was current at prepare
remains active. This also means that you can not prepare a statement that
implicitly refers to the current database if the latter is not set.
This is not documented, and therefore needs documentation. This is NOT a
change in behavior for almost all SQL statements except:
- ALTER TABLE t1 RENAME t2
- OPTIMIZE TABLE t1
- ANALYZE TABLE t1
- TRUNCATE TABLE t1 --
until this patch t1 or t2 could be evaluated at the first execution of
prepared statement.
CURRENT_DATABASE() still works OK and is evaluated at every execution
of prepared statement.
Note, that in stored routines this is not an issue as the default
database is the database of the stored procedure and "use" statement
is prohibited in stored routines.
This patch makes obsolete the use of check_db_used (it was never used in the
old code too) and all other places that check for table->db and assign it
from THD::db if it's NULL, except the parser.
How this patch was created: THD::{db,db_length} were replaced with a
LEX_STRING, THD::db. All the places that refer to THD::{db,db_length} were
manually checked and:
- if the place uses thd->db by pointer, it was fixed to make a deep copy
- if a place compared two db pointers, it was fixed to compare them by value
(via strcmp/my_strcasecmp, whatever was approproate)
Then this intermediate patch was used to write a smaller patch that does the
same thing but without a rename.
TODO in 5.1:
- remove check_db_used
- deploy THD::set_db in mysql_change_db
See also comments to individual files.
2006-06-26 22:47:52 +02:00
|
|
|
else if (thd->copy_db_to(&ptr->db, &ptr->db_length))
|
|
|
|
DBUG_RETURN(0);
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2002-09-20 13:05:18 +02:00
|
|
|
ptr->alias= alias_str;
|
2003-02-27 11:45:00 +01:00
|
|
|
if (lower_case_table_names && table->table.length)
|
2006-10-30 11:40:15 +01:00
|
|
|
table->table.length= my_casedn_str(files_charset_info, table->table.str);
|
2005-01-06 12:00:13 +01:00
|
|
|
ptr->table_name=table->table.str;
|
|
|
|
ptr->table_name_length=table->table.length;
|
2003-02-12 20:55:37 +01:00
|
|
|
ptr->lock_type= lock_type;
|
2003-01-09 21:42:31 +01:00
|
|
|
ptr->updating= test(table_options & TL_OPTION_UPDATING);
|
|
|
|
ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX);
|
2003-06-12 13:29:02 +02:00
|
|
|
ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
|
2003-02-12 20:55:37 +01:00
|
|
|
ptr->derived= table->sel;
|
2005-10-21 10:14:54 +02:00
|
|
|
if (!ptr->derived && !my_strcasecmp(system_charset_info, ptr->db,
|
|
|
|
information_schema_name.str))
|
2004-11-13 11:56:39 +01:00
|
|
|
{
|
2005-01-06 12:00:13 +01:00
|
|
|
ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, ptr->table_name);
|
2004-12-18 11:49:13 +01:00
|
|
|
if (!schema_table ||
|
|
|
|
(schema_table->hidden &&
|
2006-06-20 12:20:32 +02:00
|
|
|
(sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0))
|
2004-11-13 11:56:39 +01:00
|
|
|
{
|
2004-11-13 22:26:15 +01:00
|
|
|
my_error(ER_UNKNOWN_TABLE, MYF(0),
|
2005-01-06 12:00:13 +01:00
|
|
|
ptr->table_name, information_schema_name.str);
|
2004-11-13 11:56:39 +01:00
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
2005-01-27 11:16:51 +01:00
|
|
|
ptr->schema_table_name= ptr->table_name;
|
2004-11-13 11:56:39 +01:00
|
|
|
ptr->schema_table= schema_table;
|
|
|
|
}
|
2004-09-03 20:43:04 +02:00
|
|
|
ptr->select_lex= lex->current_select;
|
2004-04-07 12:25:24 +02:00
|
|
|
ptr->cacheable_table= 1;
|
2007-03-05 18:08:41 +01:00
|
|
|
ptr->index_hints= index_hints_arg;
|
2003-07-16 21:30:49 +02:00
|
|
|
ptr->option= option ? option->str : 0;
|
2000-07-31 21:29:14 +02:00
|
|
|
/* check that used name is unique */
|
2003-01-09 01:19:14 +01:00
|
|
|
if (lock_type != TL_IGNORE)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2005-09-12 16:01:17 +02:00
|
|
|
TABLE_LIST *first_table= (TABLE_LIST*) table_list.first;
|
|
|
|
if (lex->sql_command == SQLCOM_CREATE_VIEW)
|
|
|
|
first_table= first_table ? first_table->next_local : NULL;
|
|
|
|
for (TABLE_LIST *tables= first_table ;
|
2001-12-13 01:31:19 +01:00
|
|
|
tables ;
|
2004-07-16 00:15:55 +02:00
|
|
|
tables=tables->next_local)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-03-22 14:43:13 +01:00
|
|
|
if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) &&
|
|
|
|
!strcmp(ptr->db, tables->db))
|
2000-08-21 02:07:54 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_NONUNIQ_TABLE, MYF(0), alias_str); /* purecov: tested */
|
2000-08-21 02:07:54 +02:00
|
|
|
DBUG_RETURN(0); /* purecov: tested */
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
}
|
2005-08-12 16:57:19 +02:00
|
|
|
/* Store the table reference preceding the current one. */
|
|
|
|
if (table_list.elements > 0)
|
|
|
|
{
|
2005-08-18 02:12:42 +02:00
|
|
|
/*
|
|
|
|
table_list.next points to the last inserted TABLE_LIST->next_local'
|
|
|
|
element
|
2006-02-25 16:46:30 +01:00
|
|
|
We don't use the offsetof() macro here to avoid warnings from gcc
|
2005-08-18 02:12:42 +02:00
|
|
|
*/
|
2006-02-25 16:46:30 +01:00
|
|
|
previous_table_ref= (TABLE_LIST*) ((char*) table_list.next -
|
|
|
|
((char*) &(ptr->next_local) -
|
|
|
|
(char*) ptr));
|
2005-08-18 02:12:42 +02:00
|
|
|
/*
|
|
|
|
Set next_name_resolution_table of the previous table reference to point
|
|
|
|
to the current table reference. In effect the list
|
|
|
|
TABLE_LIST::next_name_resolution_table coincides with
|
|
|
|
TABLE_LIST::next_local. Later this may be changed in
|
|
|
|
store_top_level_join_columns() for NATURAL/USING joins.
|
|
|
|
*/
|
|
|
|
previous_table_ref->next_name_resolution_table= ptr;
|
2005-08-12 16:57:19 +02:00
|
|
|
}
|
2005-08-18 02:12:42 +02:00
|
|
|
|
2005-08-12 16:57:19 +02:00
|
|
|
/*
|
|
|
|
Link the current table reference in a local list (list for current select).
|
|
|
|
Notice that as a side effect here we set the next_local field of the
|
|
|
|
previous table reference to 'ptr'. Here we also add one element to the
|
|
|
|
list 'table_list'.
|
|
|
|
*/
|
2004-07-16 00:15:55 +02:00
|
|
|
table_list.link_in_list((byte*) ptr, (byte**) &ptr->next_local);
|
2005-08-12 16:57:19 +02:00
|
|
|
ptr->next_name_resolution_table= NULL;
|
2004-09-03 20:43:04 +02:00
|
|
|
/* Link table in global list (all used tables) */
|
2004-09-11 22:52:55 +02:00
|
|
|
lex->add_to_query_tables(ptr);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(ptr);
|
|
|
|
}
|
|
|
|
|
2001-08-02 05:29:50 +02:00
|
|
|
|
2004-06-11 07:27:21 +02:00
|
|
|
/*
|
|
|
|
Initialize a new table list for a nested join
|
|
|
|
|
|
|
|
SYNOPSIS
|
2005-01-25 15:25:46 +01:00
|
|
|
init_nested_join()
|
2004-06-11 07:27:21 +02:00
|
|
|
thd current thread
|
2004-09-11 22:52:55 +02:00
|
|
|
|
2004-06-11 07:27:21 +02:00
|
|
|
DESCRIPTION
|
|
|
|
The function initializes a structure of the TABLE_LIST type
|
|
|
|
for a nested join. It sets up its nested join list as empty.
|
|
|
|
The created structure is added to the front of the current
|
|
|
|
join list in the st_select_lex object. Then the function
|
|
|
|
changes the current nest level for joins to refer to the newly
|
|
|
|
created empty list after having saved the info on the old level
|
|
|
|
in the initialized structure.
|
|
|
|
|
|
|
|
RETURN VALUE
|
|
|
|
0, if success
|
|
|
|
1, otherwise
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool st_select_lex::init_nested_join(THD *thd)
|
|
|
|
{
|
|
|
|
TABLE_LIST *ptr;
|
|
|
|
NESTED_JOIN *nested_join;
|
|
|
|
DBUG_ENTER("init_nested_join");
|
2004-09-11 22:52:55 +02:00
|
|
|
|
2005-08-18 02:12:42 +02:00
|
|
|
if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
|
|
|
|
sizeof(NESTED_JOIN))))
|
2004-06-11 07:27:21 +02:00
|
|
|
DBUG_RETURN(1);
|
2005-08-18 02:12:42 +02:00
|
|
|
nested_join= ptr->nested_join=
|
|
|
|
((NESTED_JOIN*) ((byte*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
|
|
|
|
|
2004-06-11 07:27:21 +02:00
|
|
|
join_list->push_front(ptr);
|
|
|
|
ptr->embedding= embedding;
|
|
|
|
ptr->join_list= join_list;
|
2007-03-01 21:47:28 +01:00
|
|
|
ptr->alias= (char*) "(nested_join)";
|
2004-06-11 07:27:21 +02:00
|
|
|
embedding= ptr;
|
|
|
|
join_list= &nested_join->join_list;
|
|
|
|
join_list->empty();
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
End a nested join table list
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
end_nested_join()
|
|
|
|
thd current thread
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
The function returns to the previous join nest level.
|
|
|
|
If the current level contains only one member, the function
|
2004-09-11 22:52:55 +02:00
|
|
|
moves it one level up, eliminating the nest.
|
2004-06-11 07:27:21 +02:00
|
|
|
|
|
|
|
RETURN VALUE
|
|
|
|
Pointer to TABLE_LIST element added to the total table list, if success
|
|
|
|
0, otherwise
|
|
|
|
*/
|
|
|
|
|
|
|
|
TABLE_LIST *st_select_lex::end_nested_join(THD *thd)
|
|
|
|
{
|
|
|
|
TABLE_LIST *ptr;
|
2005-03-17 00:22:12 +01:00
|
|
|
NESTED_JOIN *nested_join;
|
2004-06-11 07:27:21 +02:00
|
|
|
DBUG_ENTER("end_nested_join");
|
2005-03-17 00:22:12 +01:00
|
|
|
|
2005-03-16 01:13:23 +01:00
|
|
|
DBUG_ASSERT(embedding);
|
2004-06-11 07:27:21 +02:00
|
|
|
ptr= embedding;
|
|
|
|
join_list= ptr->join_list;
|
|
|
|
embedding= ptr->embedding;
|
2005-03-17 00:22:12 +01:00
|
|
|
nested_join= ptr->nested_join;
|
2004-06-11 07:27:21 +02:00
|
|
|
if (nested_join->join_list.elements == 1)
|
|
|
|
{
|
|
|
|
TABLE_LIST *embedded= nested_join->join_list.head();
|
|
|
|
join_list->pop();
|
|
|
|
embedded->join_list= join_list;
|
|
|
|
embedded->embedding= embedding;
|
|
|
|
join_list->push_front(embedded);
|
|
|
|
ptr= embedded;
|
|
|
|
}
|
2005-03-17 00:22:12 +01:00
|
|
|
else if (nested_join->join_list.elements == 0)
|
2005-03-16 01:13:23 +01:00
|
|
|
{
|
|
|
|
join_list->pop();
|
2005-03-17 00:22:12 +01:00
|
|
|
ptr= 0; // return value
|
2005-03-16 01:13:23 +01:00
|
|
|
}
|
2004-06-11 07:27:21 +02:00
|
|
|
DBUG_RETURN(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Nest last join operation
|
|
|
|
|
|
|
|
SYNOPSIS
|
2004-09-11 22:52:55 +02:00
|
|
|
nest_last_join()
|
2004-06-11 07:27:21 +02:00
|
|
|
thd current thread
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
The function nest last join operation as if it was enclosed in braces.
|
|
|
|
|
|
|
|
RETURN VALUE
|
2005-08-18 02:12:42 +02:00
|
|
|
0 Error
|
|
|
|
# Pointer to TABLE_LIST element created for the new nested join
|
|
|
|
|
2004-06-11 07:27:21 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
|
|
|
|
{
|
|
|
|
TABLE_LIST *ptr;
|
|
|
|
NESTED_JOIN *nested_join;
|
2005-08-18 02:12:42 +02:00
|
|
|
List<TABLE_LIST> *embedded_list;
|
2004-06-11 07:27:21 +02:00
|
|
|
DBUG_ENTER("nest_last_join");
|
2004-09-11 22:52:55 +02:00
|
|
|
|
2005-08-18 02:12:42 +02:00
|
|
|
if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
|
|
|
|
sizeof(NESTED_JOIN))))
|
2004-06-11 07:27:21 +02:00
|
|
|
DBUG_RETURN(0);
|
2005-08-18 02:12:42 +02:00
|
|
|
nested_join= ptr->nested_join=
|
|
|
|
((NESTED_JOIN*) ((byte*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
|
|
|
|
|
2004-06-11 07:27:21 +02:00
|
|
|
ptr->embedding= embedding;
|
|
|
|
ptr->join_list= join_list;
|
2007-03-01 21:47:28 +01:00
|
|
|
ptr->alias= (char*) "(nest_last_join)";
|
2005-08-18 02:12:42 +02:00
|
|
|
embedded_list= &nested_join->join_list;
|
2004-06-11 07:27:21 +02:00
|
|
|
embedded_list->empty();
|
2005-08-18 02:12:42 +02:00
|
|
|
|
|
|
|
for (uint i=0; i < 2; i++)
|
2004-06-11 07:27:21 +02:00
|
|
|
{
|
|
|
|
TABLE_LIST *table= join_list->pop();
|
|
|
|
table->join_list= embedded_list;
|
|
|
|
table->embedding= ptr;
|
|
|
|
embedded_list->push_back(table);
|
2005-08-12 16:57:19 +02:00
|
|
|
if (table->natural_join)
|
|
|
|
{
|
|
|
|
ptr->is_natural_join= TRUE;
|
|
|
|
/*
|
|
|
|
If this is a JOIN ... USING, move the list of joined fields to the
|
|
|
|
table reference that describes the join.
|
|
|
|
*/
|
2007-02-16 02:47:39 +01:00
|
|
|
if (prev_join_using)
|
|
|
|
ptr->join_using_fields= prev_join_using;
|
2005-08-12 16:57:19 +02:00
|
|
|
}
|
2004-06-11 07:27:21 +02:00
|
|
|
}
|
|
|
|
join_list->push_front(ptr);
|
|
|
|
nested_join->used_tables= nested_join->not_null_tables= (table_map) 0;
|
|
|
|
DBUG_RETURN(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Add a table to the current join list
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
add_joined_table()
|
|
|
|
table the table to add
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
The function puts a table in front of the current join list
|
|
|
|
of st_select_lex object.
|
|
|
|
Thus, joined tables are put into this list in the reverse order
|
|
|
|
(the most outer join operation follows first).
|
|
|
|
|
|
|
|
RETURN VALUE
|
|
|
|
None
|
|
|
|
*/
|
|
|
|
|
|
|
|
void st_select_lex::add_joined_table(TABLE_LIST *table)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("add_joined_table");
|
|
|
|
join_list->push_front(table);
|
|
|
|
table->join_list= join_list;
|
|
|
|
table->embedding= embedding;
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Convert a right join into equivalent left join
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
convert_right_join()
|
|
|
|
thd current thread
|
2004-09-11 22:52:55 +02:00
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
The function takes the current join list t[0],t[1] ... and
|
2004-06-11 07:27:21 +02:00
|
|
|
effectively converts it into the list t[1],t[0] ...
|
|
|
|
Although the outer_join flag for the new nested table contains
|
|
|
|
JOIN_TYPE_RIGHT, it will be handled as the inner table of a left join
|
|
|
|
operation.
|
|
|
|
|
|
|
|
EXAMPLES
|
|
|
|
SELECT * FROM t1 RIGHT JOIN t2 ON on_expr =>
|
|
|
|
SELECT * FROM t2 LEFT JOIN t1 ON on_expr
|
|
|
|
|
|
|
|
SELECT * FROM t1,t2 RIGHT JOIN t3 ON on_expr =>
|
|
|
|
SELECT * FROM t1,t3 LEFT JOIN t2 ON on_expr
|
|
|
|
|
|
|
|
SELECT * FROM t1,t2 RIGHT JOIN (t3,t4) ON on_expr =>
|
|
|
|
SELECT * FROM t1,(t3,t4) LEFT JOIN t2 ON on_expr
|
|
|
|
|
|
|
|
SELECT * FROM t1 LEFT JOIN t2 ON on_expr1 RIGHT JOIN t3 ON on_expr2 =>
|
|
|
|
SELECT * FROM t3 LEFT JOIN (t1 LEFT JOIN t2 ON on_expr2) ON on_expr1
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
Pointer to the table representing the inner table, if success
|
|
|
|
0, otherwise
|
|
|
|
*/
|
|
|
|
|
2004-09-11 22:52:55 +02:00
|
|
|
TABLE_LIST *st_select_lex::convert_right_join()
|
2004-06-11 07:27:21 +02:00
|
|
|
{
|
|
|
|
TABLE_LIST *tab2= join_list->pop();
|
2004-09-11 22:52:55 +02:00
|
|
|
TABLE_LIST *tab1= join_list->pop();
|
2004-06-11 07:27:21 +02:00
|
|
|
DBUG_ENTER("convert_right_join");
|
|
|
|
|
|
|
|
join_list->push_front(tab2);
|
|
|
|
join_list->push_front(tab1);
|
|
|
|
tab1->outer_join|= JOIN_TYPE_RIGHT;
|
|
|
|
|
|
|
|
DBUG_RETURN(tab1);
|
|
|
|
}
|
|
|
|
|
2002-11-16 19:19:10 +01:00
|
|
|
/*
|
|
|
|
Set lock for all tables in current select level
|
|
|
|
|
|
|
|
SYNOPSIS:
|
|
|
|
set_lock_for_tables()
|
|
|
|
lock_type Lock to set for tables
|
|
|
|
|
|
|
|
NOTE:
|
|
|
|
If lock is a write lock, then tables->updating is set 1
|
|
|
|
This is to get tables_ok to know that the table is updated by the
|
|
|
|
query
|
|
|
|
*/
|
|
|
|
|
2002-11-21 21:25:53 +01:00
|
|
|
void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
|
2002-11-16 19:19:10 +01:00
|
|
|
{
|
|
|
|
bool for_update= lock_type >= TL_READ_NO_INSERT;
|
|
|
|
DBUG_ENTER("set_lock_for_tables");
|
|
|
|
DBUG_PRINT("enter", ("lock_type: %d for_update: %d", lock_type,
|
|
|
|
for_update));
|
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first;
|
|
|
|
tables;
|
|
|
|
tables= tables->next_local)
|
2002-11-16 19:19:10 +01:00
|
|
|
{
|
|
|
|
tables->lock_type= lock_type;
|
|
|
|
tables->updating= for_update;
|
|
|
|
}
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
2001-08-02 05:29:50 +02:00
|
|
|
|
2006-04-21 07:15:38 +02:00
|
|
|
/*
|
|
|
|
Create a fake SELECT_LEX for a unit
|
|
|
|
|
|
|
|
SYNOPSIS:
|
|
|
|
add_fake_select_lex()
|
|
|
|
thd thread handle
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
The method create a fake SELECT_LEX object for a unit.
|
|
|
|
This object is created for any union construct containing a union
|
|
|
|
operation and also for any single select union construct of the form
|
|
|
|
(SELECT ... ORDER BY order_list [LIMIT n]) ORDER BY ...
|
|
|
|
or of the form
|
|
|
|
(SELECT ... ORDER BY LIMIT n) ORDER BY ...
|
|
|
|
|
|
|
|
NOTES
|
|
|
|
The object is used to retrieve rows from the temporary table
|
|
|
|
where the result on the union is obtained.
|
|
|
|
|
|
|
|
RETURN VALUES
|
|
|
|
1 on failure to create the object
|
|
|
|
0 on success
|
|
|
|
*/
|
|
|
|
|
2007-02-16 02:47:39 +01:00
|
|
|
bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg)
|
2006-04-21 07:15:38 +02:00
|
|
|
{
|
|
|
|
SELECT_LEX *first_sl= first_select();
|
|
|
|
DBUG_ENTER("add_fake_select_lex");
|
|
|
|
DBUG_ASSERT(!fake_select_lex);
|
2006-04-21 17:19:38 +02:00
|
|
|
|
2007-02-16 02:47:39 +01:00
|
|
|
if (!(fake_select_lex= new (thd_arg->mem_root) SELECT_LEX()))
|
2006-04-21 07:15:38 +02:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
fake_select_lex->include_standalone(this,
|
|
|
|
(SELECT_LEX_NODE**)&fake_select_lex);
|
|
|
|
fake_select_lex->select_number= INT_MAX;
|
2007-02-16 02:47:39 +01:00
|
|
|
fake_select_lex->parent_lex= thd_arg->lex; /* Used in init_query. */
|
2006-04-21 07:15:38 +02:00
|
|
|
fake_select_lex->make_empty_select();
|
|
|
|
fake_select_lex->linkage= GLOBAL_OPTIONS_TYPE;
|
2006-04-21 09:36:20 +02:00
|
|
|
fake_select_lex->select_limit= 0;
|
|
|
|
|
2006-04-21 17:19:38 +02:00
|
|
|
fake_select_lex->context.outer_context=first_sl->context.outer_context;
|
2006-04-21 09:36:20 +02:00
|
|
|
/* allow item list resolving in fake select for ORDER BY */
|
|
|
|
fake_select_lex->context.resolve_in_select_list= TRUE;
|
|
|
|
fake_select_lex->context.select_lex= fake_select_lex;
|
2006-04-21 07:15:38 +02:00
|
|
|
|
|
|
|
if (!first_sl->next_select())
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
This works only for
|
|
|
|
(SELECT ... ORDER BY list [LIMIT n]) ORDER BY order_list [LIMIT m],
|
|
|
|
(SELECT ... LIMIT n) ORDER BY order_list [LIMIT m]
|
|
|
|
just before the parser starts processing order_list
|
|
|
|
*/
|
|
|
|
global_parameters= fake_select_lex;
|
|
|
|
fake_select_lex->no_table_names_allowed= 1;
|
2007-02-16 02:47:39 +01:00
|
|
|
thd_arg->lex->current_select= fake_select_lex;
|
2006-04-21 07:15:38 +02:00
|
|
|
}
|
2007-02-16 02:47:39 +01:00
|
|
|
thd_arg->lex->pop_context();
|
2006-04-21 07:15:38 +02:00
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
2006-04-21 09:36:20 +02:00
|
|
|
|
2005-08-12 16:57:19 +02:00
|
|
|
/*
|
2005-11-28 20:57:50 +01:00
|
|
|
Push a new name resolution context for a JOIN ... ON clause to the
|
|
|
|
context stack of a query block.
|
2005-08-12 16:57:19 +02:00
|
|
|
|
|
|
|
SYNOPSIS
|
2005-11-28 20:57:50 +01:00
|
|
|
push_new_name_resolution_context()
|
2005-08-12 16:57:19 +02:00
|
|
|
thd pointer to current thread
|
2005-09-10 14:01:54 +02:00
|
|
|
left_op left operand of the JOIN
|
2005-08-12 16:57:19 +02:00
|
|
|
right_op rigth operand of the JOIN
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
Create a new name resolution context for a JOIN ... ON clause,
|
2005-11-28 20:57:50 +01:00
|
|
|
set the first and last leaves of the list of table references
|
|
|
|
to be used for name resolution, and push the newly created
|
|
|
|
context to the stack of contexts of the query.
|
2005-08-12 16:57:19 +02:00
|
|
|
|
|
|
|
RETURN
|
2005-11-28 20:57:50 +01:00
|
|
|
FALSE if all is OK
|
|
|
|
TRUE if a memory allocation error occured
|
2005-08-12 16:57:19 +02:00
|
|
|
*/
|
|
|
|
|
2005-11-28 20:57:50 +01:00
|
|
|
bool
|
|
|
|
push_new_name_resolution_context(THD *thd,
|
|
|
|
TABLE_LIST *left_op, TABLE_LIST *right_op)
|
2005-08-12 16:57:19 +02:00
|
|
|
{
|
|
|
|
Name_resolution_context *on_context;
|
2005-08-22 00:13:37 +02:00
|
|
|
if (!(on_context= new (thd->mem_root) Name_resolution_context))
|
2005-11-28 20:57:50 +01:00
|
|
|
return TRUE;
|
2005-08-12 16:57:19 +02:00
|
|
|
on_context->init();
|
|
|
|
on_context->first_name_resolution_table=
|
|
|
|
left_op->first_leaf_for_name_resolution();
|
|
|
|
on_context->last_name_resolution_table=
|
|
|
|
right_op->last_leaf_for_name_resolution();
|
2005-11-28 20:57:50 +01:00
|
|
|
return thd->lex->push_context(on_context);
|
2005-08-12 16:57:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Add an ON condition to the second operand of a JOIN ... ON.
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
add_join_on
|
|
|
|
b the second operand of a JOIN ... ON
|
|
|
|
expr the condition to be added to the ON clause
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
Add an ON condition to the right operand of a JOIN ... ON clause.
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
FALSE if there was some error
|
|
|
|
TRUE if all is OK
|
|
|
|
*/
|
|
|
|
|
|
|
|
void add_join_on(TABLE_LIST *b, Item *expr)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-11-11 14:57:35 +01:00
|
|
|
if (expr)
|
2000-09-25 23:33:25 +02:00
|
|
|
{
|
2002-11-11 14:57:35 +01:00
|
|
|
if (!b->on_expr)
|
2005-08-12 16:57:19 +02:00
|
|
|
b->on_expr= expr;
|
2002-11-11 14:57:35 +01:00
|
|
|
else
|
|
|
|
{
|
2005-08-12 16:57:19 +02:00
|
|
|
/*
|
|
|
|
If called from the parser, this happens if you have both a
|
|
|
|
right and left join. If called later, it happens if we add more
|
|
|
|
than one condition to the ON clause.
|
|
|
|
*/
|
|
|
|
b->on_expr= new Item_cond_and(b->on_expr,expr);
|
2002-11-11 14:57:35 +01:00
|
|
|
}
|
|
|
|
b->on_expr->top_level_item();
|
2000-09-25 23:33:25 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-04-26 19:43:28 +02:00
|
|
|
/*
|
2005-08-12 16:57:19 +02:00
|
|
|
Mark that there is a NATURAL JOIN or JOIN ... USING between two
|
|
|
|
tables.
|
2003-04-26 19:43:28 +02:00
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
add_join_natural()
|
2005-08-12 16:57:19 +02:00
|
|
|
a Left join argument
|
|
|
|
b Right join argument
|
|
|
|
using_fields Field names from USING clause
|
2007-02-16 02:47:39 +01:00
|
|
|
lex The current st_select_lex
|
2005-08-12 16:57:19 +02:00
|
|
|
|
2003-04-26 19:43:28 +02:00
|
|
|
IMPLEMENTATION
|
2005-08-12 16:57:19 +02:00
|
|
|
This function marks that table b should be joined with a either via
|
|
|
|
a NATURAL JOIN or via JOIN ... USING. Both join types are special
|
|
|
|
cases of each other, so we treat them together. The function
|
|
|
|
setup_conds() creates a list of equal condition between all fields
|
|
|
|
of the same name for NATURAL JOIN or the fields in 'using_fields'
|
|
|
|
for JOIN ... USING. The list of equality conditions is stored
|
|
|
|
either in b->on_expr, or in JOIN::conds, depending on whether there
|
|
|
|
was an outer join.
|
|
|
|
|
|
|
|
EXAMPLE
|
2003-04-26 19:43:28 +02:00
|
|
|
SELECT * FROM t1 NATURAL LEFT JOIN t2
|
|
|
|
<=>
|
|
|
|
SELECT * FROM t1 LEFT JOIN t2 ON (t1.i=t2.i and t1.j=t2.j ... )
|
2005-08-12 16:57:19 +02:00
|
|
|
|
|
|
|
SELECT * FROM t1 NATURAL JOIN t2 WHERE <some_cond>
|
|
|
|
<=>
|
|
|
|
SELECT * FROM t1, t2 WHERE (t1.i=t2.i and t1.j=t2.j and <some_cond>)
|
|
|
|
|
|
|
|
SELECT * FROM t1 JOIN t2 USING(j) WHERE <some_cond>
|
|
|
|
<=>
|
|
|
|
SELECT * FROM t1, t2 WHERE (t1.j=t2.j and <some_cond>)
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
None
|
2003-04-26 19:43:28 +02:00
|
|
|
*/
|
|
|
|
|
2007-02-16 02:47:39 +01:00
|
|
|
void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
|
|
|
|
SELECT_LEX *lex)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2005-08-12 16:57:19 +02:00
|
|
|
b->natural_join= a;
|
2007-02-16 02:47:39 +01:00
|
|
|
lex->prev_join_using= using_fields;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2005-08-12 16:57:19 +02:00
|
|
|
|
2002-12-13 11:05:24 +01:00
|
|
|
/*
|
2003-05-15 18:35:39 +02:00
|
|
|
Reload/resets privileges and the different caches.
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
reload_acl_and_cache()
|
2005-08-24 21:44:42 +02:00
|
|
|
thd Thread handler (can be NULL!)
|
2003-05-15 18:35:39 +02:00
|
|
|
options What should be reset/reloaded (tables, privileges,
|
|
|
|
slave...)
|
|
|
|
tables Tables to flush (if any)
|
|
|
|
write_to_binlog Depending on 'options', it may be very bad to write the
|
|
|
|
query to the binlog (e.g. FLUSH SLAVE); this is a
|
2005-08-11 14:58:15 +02:00
|
|
|
pointer where reload_acl_and_cache() will put 0 if
|
|
|
|
it thinks we really should not write to the binlog.
|
|
|
|
Otherwise it will put 1.
|
2003-05-15 18:35:39 +02:00
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 ok
|
2005-08-11 14:58:15 +02:00
|
|
|
!=0 error. thd->killed or thd->net.report_error is set
|
2002-12-13 11:05:24 +01:00
|
|
|
*/
|
|
|
|
|
2003-05-15 18:35:39 +02:00
|
|
|
bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
|
|
|
|
bool *write_to_binlog)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
bool result=0;
|
|
|
|
select_errors=0; /* Write if more errors */
|
2003-05-15 18:35:39 +02:00
|
|
|
bool tmp_write_to_binlog= 1;
|
2005-08-10 08:31:32 +02:00
|
|
|
|
2006-08-25 15:51:29 +02:00
|
|
|
DBUG_ASSERT(!thd || !thd->in_sub_stmt);
|
2005-08-10 08:31:32 +02:00
|
|
|
|
2003-09-29 08:47:37 +02:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2000-07-31 21:29:14 +02:00
|
|
|
if (options & REFRESH_GRANT)
|
|
|
|
{
|
2005-09-01 14:52:59 +02:00
|
|
|
THD *tmp_thd= 0;
|
|
|
|
/*
|
|
|
|
If reload_acl_and_cache() is called from SIGHUP handler we have to
|
|
|
|
allocate temporary THD for execution of acl_reload()/grant_reload().
|
|
|
|
*/
|
|
|
|
if (!thd && (thd= (tmp_thd= new THD)))
|
2005-11-23 19:18:10 +01:00
|
|
|
{
|
|
|
|
thd->thread_stack= (char*) &tmp_thd;
|
2005-09-01 14:52:59 +02:00
|
|
|
thd->store_globals();
|
2005-11-23 19:18:10 +01:00
|
|
|
}
|
2005-09-01 14:52:59 +02:00
|
|
|
if (thd)
|
|
|
|
{
|
|
|
|
(void)acl_reload(thd);
|
|
|
|
(void)grant_reload(thd);
|
|
|
|
}
|
|
|
|
if (tmp_thd)
|
|
|
|
{
|
|
|
|
delete tmp_thd;
|
|
|
|
/* Remember that we don't have a THD */
|
|
|
|
my_pthread_setspecific_ptr(THR_THD, 0);
|
|
|
|
thd= 0;
|
|
|
|
}
|
2004-12-29 20:35:16 +01:00
|
|
|
reset_mqh((LEX_USER *)NULL, TRUE);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2003-09-29 08:47:37 +02:00
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
if (options & REFRESH_LOG)
|
|
|
|
{
|
2003-07-06 17:59:54 +02:00
|
|
|
/*
|
2003-08-11 21:44:43 +02:00
|
|
|
Flush the normal query log, the update log, the binary log,
|
2006-05-02 19:56:43 +02:00
|
|
|
the slow query log, the relay log (if it exists) and the log
|
|
|
|
tables.
|
2003-07-06 17:59:54 +02:00
|
|
|
*/
|
2003-08-11 21:44:43 +02:00
|
|
|
|
2005-02-22 00:15:31 +01:00
|
|
|
/*
|
2005-07-03 13:17:52 +02:00
|
|
|
Writing this command to the binlog may result in infinite loops
|
|
|
|
when doing mysqlbinlog|mysql, and anyway it does not really make
|
|
|
|
sense to log it automatically (would cause more trouble to users
|
|
|
|
than it would help them)
|
2003-05-15 18:35:39 +02:00
|
|
|
*/
|
|
|
|
tmp_write_to_binlog= 0;
|
2006-12-01 09:49:19 +01:00
|
|
|
if( mysql_bin_log.is_open() )
|
|
|
|
{
|
|
|
|
mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE);
|
|
|
|
}
|
2003-03-13 11:12:28 +01:00
|
|
|
#ifdef HAVE_REPLICATION
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_lock(&LOCK_active_mi);
|
2003-07-06 17:59:54 +02:00
|
|
|
rotate_relay_log(active_mi);
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_unlock(&LOCK_active_mi);
|
2003-03-13 11:12:28 +01:00
|
|
|
#endif
|
2006-05-02 19:56:43 +02:00
|
|
|
|
|
|
|
/* flush slow and general logs */
|
|
|
|
logger.flush_logs(thd);
|
|
|
|
|
|
|
|
if (ha_flush_logs(NULL))
|
2000-07-31 21:29:14 +02:00
|
|
|
result=1;
|
2003-01-27 18:37:25 +01:00
|
|
|
if (flush_error_log())
|
|
|
|
result=1;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2002-03-22 21:55:08 +01:00
|
|
|
#ifdef HAVE_QUERY_CACHE
|
2001-12-02 13:34:01 +01:00
|
|
|
if (options & REFRESH_QUERY_CACHE_FREE)
|
|
|
|
{
|
2001-12-06 00:05:30 +01:00
|
|
|
query_cache.pack(); // FLUSH QUERY CACHE
|
2005-02-22 00:15:31 +01:00
|
|
|
options &= ~REFRESH_QUERY_CACHE; // Don't flush cache, just free memory
|
2001-12-02 13:34:01 +01:00
|
|
|
}
|
|
|
|
if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE))
|
|
|
|
{
|
2001-12-06 00:05:30 +01:00
|
|
|
query_cache.flush(); // RESET QUERY CACHE
|
2001-12-02 13:34:01 +01:00
|
|
|
}
|
2002-03-22 21:55:08 +01:00
|
|
|
#endif /*HAVE_QUERY_CACHE*/
|
2003-05-15 18:35:39 +02:00
|
|
|
/*
|
|
|
|
Note that if REFRESH_READ_LOCK bit is set then REFRESH_TABLES is set too
|
|
|
|
(see sql_yacc.yy)
|
|
|
|
*/
|
|
|
|
if (options & (REFRESH_TABLES | REFRESH_READ_LOCK))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2001-08-14 19:33:49 +02:00
|
|
|
if ((options & REFRESH_READ_LOCK) && thd)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2005-08-05 15:37:24 +02:00
|
|
|
/*
|
|
|
|
We must not try to aspire a global read lock if we have a write
|
|
|
|
locked table. This would lead to a deadlock when trying to
|
|
|
|
reopen (and re-lock) the table after the flush.
|
|
|
|
*/
|
|
|
|
if (thd->locked_tables)
|
|
|
|
{
|
|
|
|
THR_LOCK_DATA **lock_p= thd->locked_tables->locks;
|
|
|
|
THR_LOCK_DATA **end_p= lock_p + thd->locked_tables->lock_count;
|
|
|
|
|
|
|
|
for (; lock_p < end_p; lock_p++)
|
2005-08-08 23:13:49 +02:00
|
|
|
{
|
2005-08-05 15:37:24 +02:00
|
|
|
if ((*lock_p)->type == TL_WRITE)
|
|
|
|
{
|
|
|
|
my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
|
|
|
|
return 1;
|
|
|
|
}
|
2005-08-08 23:13:49 +02:00
|
|
|
}
|
2005-08-05 15:37:24 +02:00
|
|
|
}
|
2003-10-08 20:50:05 +02:00
|
|
|
/*
|
|
|
|
Writing to the binlog could cause deadlocks, as we don't log
|
|
|
|
UNLOCK TABLES
|
|
|
|
*/
|
2003-05-15 18:35:39 +02:00
|
|
|
tmp_write_to_binlog= 0;
|
2001-08-14 19:33:49 +02:00
|
|
|
if (lock_global_read_lock(thd))
|
2005-08-11 14:58:15 +02:00
|
|
|
return 1; // Killed
|
2004-08-20 16:35:23 +02:00
|
|
|
result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1,
|
|
|
|
tables);
|
2005-08-11 14:58:15 +02:00
|
|
|
if (make_global_read_lock_block_commit(thd)) // Killed
|
2004-12-02 23:02:38 +01:00
|
|
|
{
|
|
|
|
/* Don't leave things in a half-locked state */
|
|
|
|
unlock_global_read_lock(thd);
|
|
|
|
return 1;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2004-08-20 16:35:23 +02:00
|
|
|
else
|
|
|
|
result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables);
|
2004-07-09 09:55:16 +02:00
|
|
|
my_dbopt_cleanup();
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
if (options & REFRESH_HOSTS)
|
|
|
|
hostname_cache_refresh();
|
2004-12-06 16:15:54 +01:00
|
|
|
if (thd && (options & REFRESH_STATUS))
|
2006-01-10 17:56:23 +01:00
|
|
|
refresh_status(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
if (options & REFRESH_THREADS)
|
|
|
|
flush_thread_cache();
|
2003-09-08 12:08:53 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2000-07-31 21:29:14 +02:00
|
|
|
if (options & REFRESH_MASTER)
|
2003-05-15 18:35:39 +02:00
|
|
|
{
|
2006-01-03 17:54:54 +01:00
|
|
|
DBUG_ASSERT(thd);
|
2003-05-15 18:35:39 +02:00
|
|
|
tmp_write_to_binlog= 0;
|
2002-01-20 03:16:52 +01:00
|
|
|
if (reset_master(thd))
|
2005-08-11 14:58:15 +02:00
|
|
|
{
|
2002-01-20 03:16:52 +01:00
|
|
|
result=1;
|
2005-08-11 14:58:15 +02:00
|
|
|
thd->fatal_error(); // Ensure client get error
|
|
|
|
}
|
2003-05-15 18:35:39 +02:00
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
2001-12-13 14:53:18 +01:00
|
|
|
#ifdef OPENSSL
|
2002-01-20 06:46:25 +01:00
|
|
|
if (options & REFRESH_DES_KEY_FILE)
|
|
|
|
{
|
|
|
|
if (des_key_file)
|
|
|
|
result=load_des_key_file(des_key_file);
|
|
|
|
}
|
|
|
|
#endif
|
2003-09-08 12:08:53 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2002-05-15 12:50:38 +02:00
|
|
|
if (options & REFRESH_SLAVE)
|
|
|
|
{
|
2003-05-15 18:35:39 +02:00
|
|
|
tmp_write_to_binlog= 0;
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_lock(&LOCK_active_mi);
|
2002-08-08 02:12:02 +02:00
|
|
|
if (reset_slave(thd, active_mi))
|
2002-05-15 12:50:38 +02:00
|
|
|
result=1;
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_unlock(&LOCK_active_mi);
|
2002-05-15 12:50:38 +02:00
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
2002-05-15 12:50:38 +02:00
|
|
|
if (options & REFRESH_USER_RESOURCES)
|
2007-03-01 21:47:28 +01:00
|
|
|
reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */
|
2005-08-11 14:58:15 +02:00
|
|
|
*write_to_binlog= tmp_write_to_binlog;
|
2002-05-15 12:50:38 +02:00
|
|
|
return result;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2006-05-22 20:46:13 +02:00
|
|
|
|
2002-08-22 15:50:58 +02:00
|
|
|
/*
|
2006-05-22 20:46:13 +02:00
|
|
|
kills a thread
|
2002-08-22 15:50:58 +02:00
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
kill_one_thread()
|
|
|
|
thd Thread class
|
|
|
|
id Thread id
|
2006-05-22 20:46:13 +02:00
|
|
|
only_kill_query Should it kill the query or the connection
|
2002-08-22 15:50:58 +02:00
|
|
|
|
|
|
|
NOTES
|
|
|
|
This is written such that we have a short lock on LOCK_thread_count
|
|
|
|
*/
|
|
|
|
|
2006-05-22 20:46:13 +02:00
|
|
|
uint kill_one_thread(THD *thd, ulong id, bool only_kill_query)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
THD *tmp;
|
|
|
|
uint error=ER_NO_SUCH_THREAD;
|
2006-05-22 20:46:13 +02:00
|
|
|
DBUG_ENTER("kill_one_thread");
|
|
|
|
DBUG_PRINT("enter", ("id=%lu only_kill=%d", id, only_kill_query));
|
2002-08-22 15:50:58 +02:00
|
|
|
VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
|
|
|
|
I_List_iterator<THD> it(threads);
|
2000-07-31 21:29:14 +02:00
|
|
|
while ((tmp=it++))
|
|
|
|
{
|
2006-05-02 19:56:43 +02:00
|
|
|
if (tmp->command == COM_DAEMON)
|
|
|
|
continue;
|
2000-07-31 21:29:14 +02:00
|
|
|
if (tmp->thread_id == id)
|
|
|
|
{
|
2002-08-22 15:50:58 +02:00
|
|
|
pthread_mutex_lock(&tmp->LOCK_delete); // Lock from delete
|
|
|
|
break;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
2002-08-22 15:50:58 +02:00
|
|
|
if (tmp)
|
|
|
|
{
|
2005-09-15 21:29:07 +02:00
|
|
|
if ((thd->security_ctx->master_access & SUPER_ACL) ||
|
|
|
|
!strcmp(thd->security_ctx->user, tmp->security_ctx->user))
|
2002-08-22 15:50:58 +02:00
|
|
|
{
|
2003-03-31 10:39:46 +02:00
|
|
|
tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION);
|
2002-08-22 15:50:58 +02:00
|
|
|
error=0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
error=ER_KILL_DENIED_ERROR;
|
|
|
|
pthread_mutex_unlock(&tmp->LOCK_delete);
|
|
|
|
}
|
2006-05-22 20:46:13 +02:00
|
|
|
DBUG_PRINT("exit", ("%d", error));
|
|
|
|
DBUG_RETURN(error);
|
|
|
|
}
|
|
|
|
|
2002-08-22 15:50:58 +02:00
|
|
|
|
2006-05-22 20:46:13 +02:00
|
|
|
/*
|
|
|
|
kills a thread and sends response
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
sql_kill()
|
|
|
|
thd Thread class
|
|
|
|
id Thread id
|
|
|
|
only_kill_query Should it kill the query or the connection
|
|
|
|
*/
|
|
|
|
|
|
|
|
void sql_kill(THD *thd, ulong id, bool only_kill_query)
|
|
|
|
{
|
|
|
|
uint error;
|
|
|
|
if (!(error= kill_one_thread(thd, id, only_kill_query)))
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
else
|
2004-10-20 03:04:37 +02:00
|
|
|
my_error(error, MYF(0), id);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2005-01-19 11:55:54 +01:00
|
|
|
|
2001-06-01 03:27:59 +02:00
|
|
|
/* If pointer is not a null pointer, append filename to it */
|
|
|
|
|
2006-05-02 19:56:43 +02:00
|
|
|
bool append_file_to_dir(THD *thd, const char **filename_ptr,
|
|
|
|
const char *table_name)
|
2001-06-01 03:27:59 +02:00
|
|
|
{
|
2001-10-08 03:58:07 +02:00
|
|
|
char buff[FN_REFLEN],*ptr, *end;
|
2001-06-01 03:27:59 +02:00
|
|
|
if (!*filename_ptr)
|
|
|
|
return 0; // nothing to do
|
|
|
|
|
|
|
|
/* Check that the filename is not too long and it's a hard path */
|
|
|
|
if (strlen(*filename_ptr)+strlen(table_name) >= FN_REFLEN-1 ||
|
|
|
|
!test_if_hard_path(*filename_ptr))
|
|
|
|
{
|
2003-11-18 16:28:00 +01:00
|
|
|
my_error(ER_WRONG_TABLE_NAME, MYF(0), *filename_ptr);
|
2001-06-01 03:27:59 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
/* Fix is using unix filename format on dos */
|
|
|
|
strmov(buff,*filename_ptr);
|
2001-10-08 03:58:07 +02:00
|
|
|
end=convert_dirname(buff, *filename_ptr, NullS);
|
2001-12-05 12:03:00 +01:00
|
|
|
if (!(ptr=thd->alloc((uint) (end-buff)+(uint) strlen(table_name)+1)))
|
2001-06-01 03:27:59 +02:00
|
|
|
return 1; // End of memory
|
|
|
|
*filename_ptr=ptr;
|
2001-06-05 02:38:10 +02:00
|
|
|
strxmov(ptr,buff,table_name,NullS);
|
2001-06-01 03:27:59 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2002-07-24 18:55:08 +02:00
|
|
|
|
2003-06-27 15:29:10 +02:00
|
|
|
|
2002-07-24 18:55:08 +02:00
|
|
|
/*
|
|
|
|
Check if the select is a simple select (not an union)
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
check_simple_select()
|
|
|
|
|
|
|
|
RETURN VALUES
|
|
|
|
0 ok
|
|
|
|
1 error ; In this case the error messege is sent to the client
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool check_simple_select()
|
|
|
|
{
|
|
|
|
THD *thd= current_thd;
|
2004-11-11 20:37:48 +01:00
|
|
|
LEX *lex= thd->lex;
|
|
|
|
if (lex->current_select != &lex->select_lex)
|
2002-07-24 18:55:08 +02:00
|
|
|
{
|
|
|
|
char command[80];
|
2004-11-11 20:37:48 +01:00
|
|
|
strmake(command, lex->yylval->symbol.str,
|
|
|
|
min(lex->yylval->symbol.length, sizeof(command)-1));
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_CANT_USE_OPTION_HERE, MYF(0), command);
|
2002-07-24 18:55:08 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2002-11-07 22:45:19 +01:00
|
|
|
|
2003-10-08 16:40:54 +02:00
|
|
|
|
2003-11-03 11:28:36 +01:00
|
|
|
Comp_creator *comp_eq_creator(bool invert)
|
2002-11-07 22:45:19 +01:00
|
|
|
{
|
2003-11-03 11:28:36 +01:00
|
|
|
return invert?(Comp_creator *)&ne_creator:(Comp_creator *)&eq_creator;
|
2002-11-07 22:45:19 +01:00
|
|
|
}
|
|
|
|
|
2003-10-08 16:40:54 +02:00
|
|
|
|
2003-11-03 11:28:36 +01:00
|
|
|
Comp_creator *comp_ge_creator(bool invert)
|
2002-11-07 22:45:19 +01:00
|
|
|
{
|
2003-11-03 11:28:36 +01:00
|
|
|
return invert?(Comp_creator *)<_creator:(Comp_creator *)&ge_creator;
|
2002-11-07 22:45:19 +01:00
|
|
|
}
|
|
|
|
|
2003-10-08 16:40:54 +02:00
|
|
|
|
2003-11-03 11:28:36 +01:00
|
|
|
Comp_creator *comp_gt_creator(bool invert)
|
2002-11-07 22:45:19 +01:00
|
|
|
{
|
2003-11-03 11:28:36 +01:00
|
|
|
return invert?(Comp_creator *)&le_creator:(Comp_creator *)>_creator;
|
2002-11-07 22:45:19 +01:00
|
|
|
}
|
|
|
|
|
2003-10-08 16:40:54 +02:00
|
|
|
|
2003-11-03 11:28:36 +01:00
|
|
|
Comp_creator *comp_le_creator(bool invert)
|
2002-11-07 22:45:19 +01:00
|
|
|
{
|
2003-11-03 11:28:36 +01:00
|
|
|
return invert?(Comp_creator *)>_creator:(Comp_creator *)&le_creator;
|
2002-11-07 22:45:19 +01:00
|
|
|
}
|
|
|
|
|
2003-10-08 16:40:54 +02:00
|
|
|
|
2003-11-03 11:28:36 +01:00
|
|
|
Comp_creator *comp_lt_creator(bool invert)
|
2002-11-07 22:45:19 +01:00
|
|
|
{
|
2003-11-03 11:28:36 +01:00
|
|
|
return invert?(Comp_creator *)&ge_creator:(Comp_creator *)<_creator;
|
2002-11-07 22:45:19 +01:00
|
|
|
}
|
|
|
|
|
2003-10-08 16:40:54 +02:00
|
|
|
|
2003-11-03 11:28:36 +01:00
|
|
|
Comp_creator *comp_ne_creator(bool invert)
|
2002-11-07 22:45:19 +01:00
|
|
|
{
|
2003-11-03 11:28:36 +01:00
|
|
|
return invert?(Comp_creator *)&eq_creator:(Comp_creator *)&ne_creator;
|
2002-11-07 22:45:19 +01:00
|
|
|
}
|
2003-10-08 16:40:54 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Construct ALL/ANY/SOME subquery Item
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
all_any_subquery_creator()
|
|
|
|
left_expr - pointer to left expression
|
|
|
|
cmp - compare function creator
|
|
|
|
all - true if we create ALL subquery
|
|
|
|
select_lex - pointer on parsed subquery structure
|
|
|
|
|
|
|
|
RETURN VALUE
|
|
|
|
constructed Item (or 0 if out of memory)
|
|
|
|
*/
|
|
|
|
Item * all_any_subquery_creator(Item *left_expr,
|
|
|
|
chooser_compare_func_creator cmp,
|
|
|
|
bool all,
|
|
|
|
SELECT_LEX *select_lex)
|
|
|
|
{
|
2003-10-25 12:00:53 +02:00
|
|
|
if ((cmp == &comp_eq_creator) && !all) // = ANY <=> IN
|
2003-10-08 16:40:54 +02:00
|
|
|
return new Item_in_subselect(left_expr, select_lex);
|
2003-10-25 12:00:53 +02:00
|
|
|
|
|
|
|
if ((cmp == &comp_ne_creator) && all) // <> ALL <=> NOT IN
|
2003-10-08 16:40:54 +02:00
|
|
|
return new Item_func_not(new Item_in_subselect(left_expr, select_lex));
|
|
|
|
|
|
|
|
Item_allany_subselect *it=
|
2006-07-21 01:04:04 +02:00
|
|
|
new Item_allany_subselect(left_expr, cmp, select_lex, all);
|
2003-10-08 16:40:54 +02:00
|
|
|
if (all)
|
2004-11-18 17:10:07 +01:00
|
|
|
return it->upper_item= new Item_func_not_all(it); /* ALL */
|
2003-10-08 16:40:54 +02:00
|
|
|
|
2004-11-18 17:10:07 +01:00
|
|
|
return it->upper_item= new Item_func_nop_all(it); /* ANY/SOME */
|
2003-10-08 16:40:54 +02:00
|
|
|
}
|
2004-04-07 23:16:17 +02:00
|
|
|
|
|
|
|
|
2004-04-08 16:56:45 +02:00
|
|
|
/*
|
|
|
|
CREATE INDEX and DROP INDEX are implemented by calling ALTER TABLE with
|
|
|
|
the proper arguments. This isn't very fast but it should work for most
|
|
|
|
cases.
|
|
|
|
|
|
|
|
In the future ALTER TABLE will notice that only added indexes
|
|
|
|
and create these one by one for the existing table without having to do
|
|
|
|
a full rebuild.
|
|
|
|
|
|
|
|
One should normally create all indexes with CREATE TABLE or ALTER TABLE.
|
|
|
|
*/
|
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
|
2004-04-08 16:56:45 +02:00
|
|
|
{
|
|
|
|
List<create_field> fields;
|
2004-05-21 16:57:03 +02:00
|
|
|
ALTER_INFO alter_info;
|
|
|
|
alter_info.flags= ALTER_ADD_INDEX;
|
2004-04-08 16:56:45 +02:00
|
|
|
HA_CREATE_INFO create_info;
|
|
|
|
DBUG_ENTER("mysql_create_index");
|
|
|
|
bzero((char*) &create_info,sizeof(create_info));
|
2006-05-28 14:51:01 +02:00
|
|
|
create_info.db_type= 0;
|
2004-04-08 16:56:45 +02:00
|
|
|
create_info.default_table_charset= thd->variables.collation_database;
|
2006-12-07 15:32:40 +01:00
|
|
|
create_info.row_type= ROW_TYPE_NOT_USED;
|
2005-01-06 12:00:13 +01:00
|
|
|
DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->table_name,
|
2004-04-08 16:56:45 +02:00
|
|
|
&create_info, table_list,
|
2004-05-21 16:57:03 +02:00
|
|
|
fields, keys, 0, (ORDER*)0,
|
2006-07-01 23:51:10 +02:00
|
|
|
0, &alter_info, 1));
|
2004-04-08 16:56:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info)
|
2004-04-08 16:56:45 +02:00
|
|
|
{
|
|
|
|
List<create_field> fields;
|
|
|
|
List<Key> keys;
|
|
|
|
HA_CREATE_INFO create_info;
|
|
|
|
DBUG_ENTER("mysql_drop_index");
|
|
|
|
bzero((char*) &create_info,sizeof(create_info));
|
2006-05-28 14:51:01 +02:00
|
|
|
create_info.db_type= 0;
|
2004-04-08 16:56:45 +02:00
|
|
|
create_info.default_table_charset= thd->variables.collation_database;
|
2006-12-07 15:32:40 +01:00
|
|
|
create_info.row_type= ROW_TYPE_NOT_USED;
|
2004-05-21 16:57:03 +02:00
|
|
|
alter_info->clear();
|
|
|
|
alter_info->flags= ALTER_DROP_INDEX;
|
2005-01-06 12:00:13 +01:00
|
|
|
DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->table_name,
|
2004-04-08 16:56:45 +02:00
|
|
|
&create_info, table_list,
|
2004-05-21 16:57:03 +02:00
|
|
|
fields, keys, 0, (ORDER*)0,
|
2006-07-01 23:51:10 +02:00
|
|
|
0, alter_info, 1));
|
2004-04-08 16:56:45 +02:00
|
|
|
}
|
2004-04-08 22:50:10 +02:00
|
|
|
|
|
|
|
|
2004-04-07 23:16:17 +02:00
|
|
|
/*
|
|
|
|
Multi update query pre-check
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
multi_update_precheck()
|
2004-04-12 02:26:32 +02:00
|
|
|
thd Thread handler
|
2004-07-16 00:15:55 +02:00
|
|
|
tables Global/local table list (have to be the same)
|
2004-04-07 23:16:17 +02:00
|
|
|
|
2004-04-10 00:14:32 +02:00
|
|
|
RETURN VALUE
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE OK
|
|
|
|
TRUE Error
|
2004-04-07 23:16:17 +02:00
|
|
|
*/
|
2004-04-12 02:26:32 +02:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
|
2004-04-07 23:16:17 +02:00
|
|
|
{
|
|
|
|
const char *msg= 0;
|
|
|
|
TABLE_LIST *table;
|
|
|
|
LEX *lex= thd->lex;
|
|
|
|
SELECT_LEX *select_lex= &lex->select_lex;
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ENTER("multi_update_precheck");
|
2004-04-07 23:16:17 +02:00
|
|
|
|
|
|
|
if (select_lex->item_list.elements != lex->value_list.elements)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
Ensure that we have UPDATE or SELECT privilege for each table
|
|
|
|
The exact privilege is checked in mysql_multi_update()
|
|
|
|
*/
|
2004-07-16 00:15:55 +02:00
|
|
|
for (table= tables; table; table= table->next_local)
|
2004-04-07 23:16:17 +02:00
|
|
|
{
|
2004-10-26 18:30:01 +02:00
|
|
|
if (table->derived)
|
|
|
|
table->grant.privilege= SELECT_ACL;
|
|
|
|
else if ((check_access(thd, UPDATE_ACL, table->db,
|
2005-09-13 13:07:38 +02:00
|
|
|
&table->grant.privilege, 0, 1,
|
|
|
|
test(table->schema_table)) ||
|
2004-10-26 18:30:01 +02:00
|
|
|
grant_option &&
|
|
|
|
check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) &&
|
2004-10-29 18:26:52 +02:00
|
|
|
(check_access(thd, SELECT_ACL, table->db,
|
2005-09-13 13:07:38 +02:00
|
|
|
&table->grant.privilege, 0, 0,
|
|
|
|
test(table->schema_table)) ||
|
2004-10-29 18:26:52 +02:00
|
|
|
grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0)))
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-10 00:14:32 +02:00
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
table->table_in_first_from_clause= 1;
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
2004-04-10 00:14:32 +02:00
|
|
|
/*
|
|
|
|
Is there tables of subqueries?
|
|
|
|
*/
|
2005-06-17 17:56:04 +02:00
|
|
|
if (&lex->select_lex != lex->all_selects_list || lex->time_zone_tables_used)
|
2004-04-07 23:16:17 +02:00
|
|
|
{
|
2004-10-26 18:30:01 +02:00
|
|
|
DBUG_PRINT("info",("Checking sub query list"));
|
2004-07-16 00:15:55 +02:00
|
|
|
for (table= tables; table; table= table->next_global)
|
2004-04-07 23:16:17 +02:00
|
|
|
{
|
2004-12-30 23:44:00 +01:00
|
|
|
if (!my_tz_check_n_skip_implicit_tables(&table,
|
|
|
|
lex->time_zone_tables_used) &&
|
|
|
|
!table->table_in_first_from_clause)
|
2004-04-07 23:16:17 +02:00
|
|
|
{
|
|
|
|
if (check_access(thd, SELECT_ACL, table->db,
|
2005-09-13 13:07:38 +02:00
|
|
|
&table->grant.privilege, 0, 0,
|
|
|
|
test(table->schema_table)) ||
|
2004-04-10 00:14:32 +02:00
|
|
|
grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (select_lex->order_list.elements)
|
|
|
|
msg= "ORDER BY";
|
2005-06-07 12:11:36 +02:00
|
|
|
else if (select_lex->select_limit)
|
2004-04-07 23:16:17 +02:00
|
|
|
msg= "LIMIT";
|
|
|
|
if (msg)
|
|
|
|
{
|
|
|
|
my_error(ER_WRONG_USAGE, MYF(0), "UPDATE", msg);
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(FALSE);
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Multi delete query pre-check
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
multi_delete_precheck()
|
2004-04-12 02:26:32 +02:00
|
|
|
thd Thread handler
|
2004-07-16 00:15:55 +02:00
|
|
|
tables Global/local table list
|
2004-04-07 23:16:17 +02:00
|
|
|
|
2004-04-10 00:14:32 +02:00
|
|
|
RETURN VALUE
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE OK
|
|
|
|
TRUE error
|
2004-04-07 23:16:17 +02:00
|
|
|
*/
|
2004-10-20 03:04:37 +02:00
|
|
|
|
2005-06-08 23:07:52 +02:00
|
|
|
bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
|
2004-04-07 23:16:17 +02:00
|
|
|
{
|
|
|
|
SELECT_LEX *select_lex= &thd->lex->select_lex;
|
|
|
|
TABLE_LIST *aux_tables=
|
2006-07-03 23:13:04 +02:00
|
|
|
(TABLE_LIST *)thd->lex->auxiliary_table_list.first;
|
2006-06-20 23:50:20 +02:00
|
|
|
TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ENTER("multi_delete_precheck");
|
2004-04-10 00:14:32 +02:00
|
|
|
|
2004-04-07 23:16:17 +02:00
|
|
|
/* sql_yacc guarantees that tables and aux_tables are not zero */
|
|
|
|
DBUG_ASSERT(aux_tables != 0);
|
2006-07-07 01:07:45 +02:00
|
|
|
if (check_table_access(thd, SELECT_ACL, tables, 0))
|
2006-06-20 23:50:20 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
|
|
|
|
/*
|
|
|
|
Since aux_tables list is not part of LEX::query_tables list we
|
|
|
|
have to juggle with LEX::query_tables_own_last value to be able
|
|
|
|
call check_table_access() safely.
|
|
|
|
*/
|
|
|
|
thd->lex->query_tables_own_last= 0;
|
|
|
|
if (check_table_access(thd, DELETE_ACL, aux_tables, 0))
|
|
|
|
{
|
|
|
|
thd->lex->query_tables_own_last= save_query_tables_own_last;
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2006-06-20 23:50:20 +02:00
|
|
|
}
|
|
|
|
thd->lex->query_tables_own_last= save_query_tables_own_last;
|
|
|
|
|
2004-04-07 23:16:17 +02:00
|
|
|
if ((thd->options & OPTION_SAFE_UPDATES) && !select_lex->where)
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
|
|
|
|
ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
2005-06-08 23:07:52 +02:00
|
|
|
DBUG_RETURN(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Link tables in auxilary table list of multi-delete with corresponding
|
|
|
|
elements in main table list, and set proper locks for them.
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
multi_delete_set_locks_and_link_aux_tables()
|
|
|
|
lex - pointer to LEX representing multi-delete
|
|
|
|
|
|
|
|
RETURN VALUE
|
|
|
|
FALSE - success
|
|
|
|
TRUE - error
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool multi_delete_set_locks_and_link_aux_tables(LEX *lex)
|
|
|
|
{
|
|
|
|
TABLE_LIST *tables= (TABLE_LIST*)lex->select_lex.table_list.first;
|
|
|
|
TABLE_LIST *target_tbl;
|
|
|
|
DBUG_ENTER("multi_delete_set_locks_and_link_aux_tables");
|
|
|
|
|
|
|
|
lex->table_count= 0;
|
|
|
|
|
2006-07-03 23:13:04 +02:00
|
|
|
for (target_tbl= (TABLE_LIST *)lex->auxiliary_table_list.first;
|
2005-06-08 23:07:52 +02:00
|
|
|
target_tbl; target_tbl= target_tbl->next_local)
|
2004-04-07 23:16:17 +02:00
|
|
|
{
|
2005-06-08 23:07:52 +02:00
|
|
|
lex->table_count++;
|
2004-04-07 23:16:17 +02:00
|
|
|
/* All tables in aux_tables must be found in FROM PART */
|
|
|
|
TABLE_LIST *walk;
|
2004-07-16 00:15:55 +02:00
|
|
|
for (walk= tables; walk; walk= walk->next_local)
|
2004-04-07 23:16:17 +02:00
|
|
|
{
|
2004-04-10 00:14:32 +02:00
|
|
|
if (!my_strcasecmp(table_alias_charset,
|
|
|
|
target_tbl->alias, walk->alias) &&
|
|
|
|
!strcmp(walk->db, target_tbl->db))
|
2004-04-07 23:16:17 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!walk)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_UNKNOWN_TABLE, MYF(0),
|
2005-01-06 12:00:13 +01:00
|
|
|
target_tbl->table_name, "MULTI DELETE");
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
2005-09-21 17:38:26 +02:00
|
|
|
if (!walk->derived)
|
|
|
|
{
|
|
|
|
target_tbl->table_name= walk->table_name;
|
|
|
|
target_tbl->table_name_length= walk->table_name_length;
|
|
|
|
}
|
2005-09-21 12:37:51 +02:00
|
|
|
walk->updating= target_tbl->updating;
|
2004-04-10 00:14:32 +02:00
|
|
|
walk->lock_type= target_tbl->lock_type;
|
2004-07-16 00:15:55 +02:00
|
|
|
target_tbl->correspondent_table= walk; // Remember corresponding table
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(FALSE);
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-04-10 00:14:32 +02:00
|
|
|
/*
|
|
|
|
simple UPDATE query pre-check
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
update_precheck()
|
2004-04-12 02:26:32 +02:00
|
|
|
thd Thread handler
|
|
|
|
tables Global table list
|
2004-04-10 00:14:32 +02:00
|
|
|
|
|
|
|
RETURN VALUE
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE OK
|
|
|
|
TRUE Error
|
2004-04-10 00:14:32 +02:00
|
|
|
*/
|
2004-04-12 02:26:32 +02:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool update_precheck(THD *thd, TABLE_LIST *tables)
|
2004-04-10 00:14:32 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("update_precheck");
|
|
|
|
if (thd->lex->select_lex.item_list.elements != thd->lex->value_list.elements)
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-10 00:14:32 +02:00
|
|
|
}
|
2006-07-07 01:07:45 +02:00
|
|
|
DBUG_RETURN(check_one_table_access(thd, UPDATE_ACL, tables));
|
2004-04-10 00:14:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
simple DELETE query pre-check
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
delete_precheck()
|
2004-04-12 02:26:32 +02:00
|
|
|
thd Thread handler
|
|
|
|
tables Global table list
|
2004-04-10 00:14:32 +02:00
|
|
|
|
|
|
|
RETURN VALUE
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE OK
|
|
|
|
TRUE error
|
2004-04-10 00:14:32 +02:00
|
|
|
*/
|
2004-04-12 02:26:32 +02:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool delete_precheck(THD *thd, TABLE_LIST *tables)
|
2004-04-10 00:14:32 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("delete_precheck");
|
|
|
|
if (check_one_table_access(thd, DELETE_ACL, tables))
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-12 02:26:32 +02:00
|
|
|
/* Set privilege for the WHERE clause */
|
2004-04-10 00:14:32 +02:00
|
|
|
tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(FALSE);
|
2004-04-10 00:14:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
simple INSERT query pre-check
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
insert_precheck()
|
2004-04-12 02:26:32 +02:00
|
|
|
thd Thread handler
|
|
|
|
tables Global table list
|
2004-04-10 00:14:32 +02:00
|
|
|
|
|
|
|
RETURN VALUE
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE OK
|
|
|
|
TRUE error
|
2004-04-10 00:14:32 +02:00
|
|
|
*/
|
2004-04-12 02:26:32 +02:00
|
|
|
|
2004-11-12 14:36:31 +01:00
|
|
|
bool insert_precheck(THD *thd, TABLE_LIST *tables)
|
2004-04-10 00:14:32 +02:00
|
|
|
{
|
|
|
|
LEX *lex= thd->lex;
|
|
|
|
DBUG_ENTER("insert_precheck");
|
|
|
|
|
2004-12-13 13:26:28 +01:00
|
|
|
/*
|
|
|
|
Check that we have modify privileges for the first table and
|
|
|
|
select privileges for the rest
|
|
|
|
*/
|
2004-11-03 11:39:38 +01:00
|
|
|
ulong privilege= (INSERT_ACL |
|
|
|
|
(lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) |
|
|
|
|
(lex->value_list.elements ? UPDATE_ACL : 0));
|
2004-04-10 00:14:32 +02:00
|
|
|
|
|
|
|
if (check_one_table_access(thd, privilege, tables))
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-10 00:14:32 +02:00
|
|
|
|
2004-12-13 13:26:28 +01:00
|
|
|
if (lex->update_list.elements != lex->value_list.elements)
|
2004-04-10 00:14:32 +02:00
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-10 00:14:32 +02:00
|
|
|
}
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(FALSE);
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
2004-04-10 00:14:32 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
CREATE TABLE query pre-check
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
create_table_precheck()
|
2004-04-12 02:26:32 +02:00
|
|
|
thd Thread handler
|
|
|
|
tables Global table list
|
|
|
|
create_table Table which will be created
|
2004-04-10 00:14:32 +02:00
|
|
|
|
|
|
|
RETURN VALUE
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE OK
|
|
|
|
TRUE Error
|
2004-04-10 00:14:32 +02:00
|
|
|
*/
|
2004-04-12 02:26:32 +02:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool create_table_precheck(THD *thd, TABLE_LIST *tables,
|
|
|
|
TABLE_LIST *create_table)
|
2004-04-10 00:14:32 +02:00
|
|
|
{
|
|
|
|
LEX *lex= thd->lex;
|
2004-10-22 17:44:51 +02:00
|
|
|
SELECT_LEX *select_lex= &lex->select_lex;
|
|
|
|
ulong want_priv;
|
2004-11-12 14:36:31 +01:00
|
|
|
bool error= TRUE; // Error message is given
|
2004-04-10 00:14:32 +02:00
|
|
|
DBUG_ENTER("create_table_precheck");
|
2004-10-22 17:44:51 +02:00
|
|
|
|
|
|
|
want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
|
|
|
|
CREATE_TMP_ACL : CREATE_ACL);
|
2004-04-10 00:14:32 +02:00
|
|
|
lex->create_info.alias= create_table->alias;
|
|
|
|
if (check_access(thd, want_priv, create_table->db,
|
2005-09-13 13:07:38 +02:00
|
|
|
&create_table->grant.privilege, 0, 0,
|
|
|
|
test(create_table->schema_table)) ||
|
2004-04-10 00:14:32 +02:00
|
|
|
check_merge_table_access(thd, create_table->db,
|
|
|
|
(TABLE_LIST *)
|
|
|
|
lex->create_info.merge_list.first))
|
2004-10-22 17:44:51 +02:00
|
|
|
goto err;
|
|
|
|
if (grant_option && want_priv != CREATE_TMP_ACL &&
|
2006-05-10 12:12:32 +02:00
|
|
|
check_grant(thd, want_priv, create_table, 0, 1, 0))
|
2004-10-22 17:44:51 +02:00
|
|
|
goto err;
|
|
|
|
|
|
|
|
if (select_lex->item_list.elements)
|
|
|
|
{
|
|
|
|
/* Check permissions for used tables in CREATE TABLE ... SELECT */
|
|
|
|
|
2005-06-01 13:22:17 +02:00
|
|
|
#ifdef NOT_NECESSARY_TO_CHECK_CREATE_TABLE_EXIST_WHEN_PREPARING_STATEMENT
|
|
|
|
/* This code throws an ill error for CREATE TABLE t1 SELECT * FROM t1 */
|
2004-10-22 17:44:51 +02:00
|
|
|
/*
|
2006-10-16 18:57:33 +02:00
|
|
|
Only do the check for PS, because we on execute we have to check that
|
2004-10-29 18:26:52 +02:00
|
|
|
against the opened tables to ensure we don't use a table that is part
|
|
|
|
of the view (which can only be done after the table has been opened).
|
2004-10-22 17:44:51 +02:00
|
|
|
*/
|
2005-09-02 15:21:19 +02:00
|
|
|
if (thd->stmt_arena->is_stmt_prepare_or_first_sp_execute())
|
2004-10-22 17:44:51 +02:00
|
|
|
{
|
2004-10-29 18:26:52 +02:00
|
|
|
/*
|
|
|
|
For temporary tables we don't have to check if the created table exists
|
|
|
|
*/
|
|
|
|
if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
|
2004-11-03 11:39:38 +01:00
|
|
|
find_table_in_global_list(tables, create_table->db,
|
2005-01-06 12:00:13 +01:00
|
|
|
create_table->table_name))
|
2004-10-29 18:26:52 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
error= FALSE;
|
2004-10-29 18:26:52 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
2005-06-01 13:22:17 +02:00
|
|
|
#endif
|
2004-10-22 17:44:51 +02:00
|
|
|
if (tables && check_table_access(thd, SELECT_ACL, tables,0))
|
|
|
|
goto err;
|
|
|
|
}
|
2004-11-12 14:36:31 +01:00
|
|
|
error= FALSE;
|
2004-10-22 17:44:51 +02:00
|
|
|
|
|
|
|
err:
|
|
|
|
DBUG_RETURN(error);
|
2004-04-10 00:14:32 +02:00
|
|
|
}
|
2004-08-31 20:10:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
negate given expression
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
negate_expression()
|
2004-10-07 00:45:06 +02:00
|
|
|
thd thread handler
|
2004-08-31 20:10:57 +02:00
|
|
|
expr expression for negation
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
negated expression
|
|
|
|
*/
|
|
|
|
|
|
|
|
Item *negate_expression(THD *thd, Item *expr)
|
|
|
|
{
|
|
|
|
Item *negated;
|
|
|
|
if (expr->type() == Item::FUNC_ITEM &&
|
|
|
|
((Item_func *) expr)->functype() == Item_func::NOT_FUNC)
|
|
|
|
{
|
|
|
|
/* it is NOT(NOT( ... )) */
|
|
|
|
Item *arg= ((Item_func *) expr)->arguments()[0];
|
|
|
|
enum_parsing_place place= thd->lex->current_select->parsing_place;
|
|
|
|
if (arg->is_bool_func() || place == IN_WHERE || place == IN_HAVING)
|
|
|
|
return arg;
|
|
|
|
/*
|
|
|
|
if it is not boolean function then we have to emulate value of
|
|
|
|
not(not(a)), it will be a != 0
|
|
|
|
*/
|
|
|
|
return new Item_func_ne(arg, new Item_int((char*) "0", 0, 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((negated= expr->neg_transformer(thd)) != 0)
|
|
|
|
return negated;
|
|
|
|
return new Item_func_not(expr);
|
|
|
|
}
|
2005-09-14 09:53:09 +02:00
|
|
|
|
2005-11-10 20:25:03 +01:00
|
|
|
/*
|
|
|
|
Set the specified definer to the default value, which is the current user in
|
2006-03-02 12:17:13 +01:00
|
|
|
the thread.
|
2005-11-10 20:25:03 +01:00
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
get_default_definer()
|
|
|
|
thd [in] thread handler
|
|
|
|
definer [out] definer
|
|
|
|
*/
|
|
|
|
|
2006-03-02 12:17:13 +01:00
|
|
|
void get_default_definer(THD *thd, LEX_USER *definer)
|
2005-11-10 20:25:03 +01:00
|
|
|
{
|
|
|
|
const Security_context *sctx= thd->security_ctx;
|
|
|
|
|
|
|
|
definer->user.str= (char *) sctx->priv_user;
|
|
|
|
definer->user.length= strlen(definer->user.str);
|
|
|
|
|
|
|
|
definer->host.str= (char *) sctx->priv_host;
|
|
|
|
definer->host.length= strlen(definer->host.str);
|
|
|
|
}
|
|
|
|
|
2005-09-14 09:53:09 +02:00
|
|
|
|
2006-03-01 12:13:07 +01:00
|
|
|
/*
|
2006-03-02 12:17:13 +01:00
|
|
|
Create default definer for the specified THD.
|
2006-03-01 12:13:07 +01:00
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
create_default_definer()
|
|
|
|
thd [in] thread handler
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
On success, return a valid pointer to the created and initialized
|
|
|
|
LEX_USER, which contains definer information.
|
|
|
|
On error, return 0.
|
|
|
|
*/
|
|
|
|
|
|
|
|
LEX_USER *create_default_definer(THD *thd)
|
|
|
|
{
|
|
|
|
LEX_USER *definer;
|
|
|
|
|
|
|
|
if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
|
|
|
|
return 0;
|
|
|
|
|
2006-03-02 12:17:13 +01:00
|
|
|
get_default_definer(thd, definer);
|
2006-03-01 12:13:07 +01:00
|
|
|
|
|
|
|
return definer;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-09-14 09:53:09 +02:00
|
|
|
/*
|
2006-03-10 12:40:15 +01:00
|
|
|
Create definer with the given user and host names.
|
2005-09-14 09:53:09 +02:00
|
|
|
|
|
|
|
SYNOPSIS
|
2005-11-10 20:25:03 +01:00
|
|
|
create_definer()
|
|
|
|
thd [in] thread handler
|
|
|
|
user_name [in] user name
|
|
|
|
host_name [in] host name
|
2005-09-14 09:53:09 +02:00
|
|
|
|
|
|
|
RETURN
|
2005-11-10 20:25:03 +01:00
|
|
|
On success, return a valid pointer to the created and initialized
|
2006-03-01 12:13:07 +01:00
|
|
|
LEX_USER, which contains definer information.
|
2005-11-10 20:25:03 +01:00
|
|
|
On error, return 0.
|
2005-09-14 09:53:09 +02:00
|
|
|
*/
|
|
|
|
|
2005-11-10 20:25:03 +01:00
|
|
|
LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
|
2005-09-14 09:53:09 +02:00
|
|
|
{
|
2005-11-10 20:25:03 +01:00
|
|
|
LEX_USER *definer;
|
|
|
|
|
|
|
|
/* Create and initialize. */
|
|
|
|
|
2006-01-05 23:47:49 +01:00
|
|
|
if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
|
2005-11-10 20:25:03 +01:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
definer->user= *user_name;
|
|
|
|
definer->host= *host_name;
|
|
|
|
|
|
|
|
return definer;
|
2005-09-14 09:53:09 +02:00
|
|
|
}
|
2006-06-29 12:50:44 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Retuns information about user or current user.
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
get_current_user()
|
|
|
|
thd [in] thread handler
|
|
|
|
user [in] user
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
On success, return a valid pointer to initialized
|
|
|
|
LEX_USER, which contains user information.
|
|
|
|
On error, return 0.
|
|
|
|
*/
|
|
|
|
|
|
|
|
LEX_USER *get_current_user(THD *thd, LEX_USER *user)
|
|
|
|
{
|
|
|
|
if (!user->user.str) // current_user
|
2006-08-23 19:31:00 +02:00
|
|
|
return create_default_definer(thd);
|
|
|
|
|
2006-06-29 12:50:44 +02:00
|
|
|
return user;
|
|
|
|
}
|
2006-08-23 19:31:00 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Check that length of a string does not exceed some limit.
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
check_string_length()
|
|
|
|
str string to be checked
|
|
|
|
err_msg error message to be displayed if the string is too long
|
|
|
|
max_length max length
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
FALSE the passed string is not longer than max_length
|
|
|
|
TRUE the passed string is longer than max_length
|
|
|
|
*/
|
|
|
|
|
2006-09-27 17:11:11 +02:00
|
|
|
bool check_string_length(LEX_STRING *str, const char *err_msg,
|
|
|
|
uint max_length)
|
2006-08-23 19:31:00 +02:00
|
|
|
{
|
2006-09-27 17:11:11 +02:00
|
|
|
if (str->length <= max_length)
|
|
|
|
return FALSE;
|
2006-08-23 19:31:00 +02:00
|
|
|
|
|
|
|
my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_length);
|
2006-09-27 17:11:11 +02:00
|
|
|
|
2006-08-23 19:31:00 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|