summaryrefslogtreecommitdiff
path: root/sql/sql_table.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_table.cc')
-rw-r--r--sql/sql_table.cc670
1 files changed, 466 insertions, 204 deletions
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 758757ea7dd..c9194bcb276 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2000, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2016, Oracle and/or its affiliates.
Copyright (c) 2010, 2016, MariaDB
This program is free software; you can redistribute it and/or modify
@@ -25,7 +25,7 @@
#include "sql_table.h"
#include "sql_parse.h" // test_if_data_home_dir
#include "sql_cache.h" // query_cache_*
-#include "sql_base.h" // open_table_uncached, lock_table_names
+#include "sql_base.h" // lock_table_names
#include "lock.h" // mysql_unlock_tables
#include "strfunc.h" // find_type2, find_set
#include "sql_truncate.h" // regenerate_locked_table
@@ -37,8 +37,7 @@
#include "sql_time.h" // make_truncated_value_warning
#include "records.h" // init_read_record, end_read_record
#include "filesort.h" // filesort_free_buffers
-#include "sql_select.h" // setup_order,
- // make_unireg_sortorder
+#include "sql_select.h" // setup_order
#include "sql_handler.h" // mysql_ha_rm_tables
#include "discover.h" // readfrm
#include "my_pthread.h" // pthread_mutex_t
@@ -65,6 +64,9 @@ const char *primary_key_name="PRIMARY";
static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end);
static char *make_unique_key_name(THD *thd, const char *field_name, KEY *start,
KEY *end);
+static void make_unique_constraint_name(THD *thd, LEX_STRING *name,
+ List<Virtual_column_info> *vcol,
+ uint *nr);
static int copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
List<Create_field> &create, bool ignore,
uint order_num, ORDER *order,
@@ -1642,7 +1644,7 @@ void execute_ddl_log_recovery()
/*
To be able to run this from boot, we allocate a temporary THD
*/
- if (!(thd=new THD))
+ if (!(thd=new THD(0)))
DBUG_VOID_RETURN;
thd->thread_stack= (char*) &thd;
thd->store_globals();
@@ -2030,7 +2032,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
LEX_STRING db_name= { table->db, table->db_length };
LEX_STRING table_name= { table->table_name, table->table_name_length };
if (table->open_type == OT_BASE_ONLY ||
- !find_temporary_table(thd, table))
+ !thd->find_temporary_table(table))
(void) delete_statistics_for_table(thd, &db_name, &table_name);
}
}
@@ -2283,23 +2285,17 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
*/
DBUG_ASSERT(!(thd->locked_tables_mode &&
table->open_type != OT_BASE_ONLY &&
- find_temporary_table(thd, table) &&
+ thd->find_temporary_table(table) &&
table->mdl_request.ticket != NULL));
- /*
- drop_temporary_table may return one of the following error codes:
- . 0 - a temporary table was successfully dropped.
- . 1 - a temporary table was not found.
- . -1 - a temporary table is used by an outer statement.
- */
if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table))
error= 1;
else
{
table_creation_was_logged= table->table->s->table_creation_was_logged;
- if ((error= drop_temporary_table(thd, table->table, &is_trans)) == -1)
+ if (thd->drop_temporary_table(table->table, &is_trans, true))
{
- DBUG_ASSERT(thd->in_sub_stmt);
+ error= 1;
goto err;
}
table->table= 0;
@@ -2882,7 +2878,8 @@ int prepare_create_field(Column_definition *sql_field,
uint *blob_columns,
longlong table_flags)
{
- unsigned int dup_val_count;
+ uint dup_val_count;
+ uint decimals= sql_field->decimals;
DBUG_ENTER("prepare_create_field");
/*
@@ -3000,8 +2997,18 @@ int prepare_create_field(Column_definition *sql_field,
FIELDFLAG_DECIMAL) |
(sql_field->flags & ZEROFILL_FLAG ?
FIELDFLAG_ZEROFILL : 0) |
- (sql_field->decimals << FIELDFLAG_DEC_SHIFT));
+ (decimals << FIELDFLAG_DEC_SHIFT));
break;
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ /*
+ User specified FLOAT() or DOUBLE() without precision. Change to
+ FLOATING_POINT_DECIMALS to keep things compatible with earlier MariaDB
+ versions.
+ */
+ if (decimals >= FLOATING_POINT_DECIMALS)
+ decimals= FLOATING_POINT_DECIMALS;
+ /* fall-trough */
case MYSQL_TYPE_TIMESTAMP:
case MYSQL_TYPE_TIMESTAMP2:
/* fall-through */
@@ -3012,7 +3019,7 @@ int prepare_create_field(Column_definition *sql_field,
(sql_field->flags & ZEROFILL_FLAG ?
FIELDFLAG_ZEROFILL : 0) |
f_settype((uint) sql_field->sql_type) |
- (sql_field->decimals << FIELDFLAG_DEC_SHIFT));
+ (decimals << FIELDFLAG_DEC_SHIFT));
break;
}
if (!(sql_field->flags & NOT_NULL_FLAG) ||
@@ -3065,19 +3072,20 @@ CHARSET_INFO* get_sql_field_charset(Create_field *sql_field,
@param column_definitions The list of column definitions, in the physical
order in which they appear in the table.
- */
+*/
+
void promote_first_timestamp_column(List<Create_field> *column_definitions)
{
- List_iterator<Create_field> it(*column_definitions);
+ List_iterator_fast<Create_field> it(*column_definitions);
Create_field *column_definition;
while ((column_definition= it++) != NULL)
{
- if (is_timestamp_type(column_definition->sql_type) || // TIMESTAMP
+ if (is_timestamp_type(column_definition->sql_type) || // TIMESTAMP
column_definition->unireg_check == Field::TIMESTAMP_OLD_FIELD) // Legacy
{
if ((column_definition->flags & NOT_NULL_FLAG) != 0 && // NOT NULL,
- column_definition->def == NULL && // no constant default,
+ column_definition->default_value == NULL && // no constant default,
column_definition->unireg_check == Field::NONE && // no function default
column_definition->vcol_info == NULL)
{
@@ -3116,8 +3124,8 @@ static void check_duplicate_key(THD *thd,
if (!key->key_create_info.check_for_duplicate_indexes || key->generated)
return;
- List_iterator<Key> key_list_iterator(*key_list);
- List_iterator<Key_part_spec> key_column_iterator(key->columns);
+ List_iterator_fast<Key> key_list_iterator(*key_list);
+ List_iterator_fast<Key_part_spec> key_column_iterator(key->columns);
Key *k;
while ((k= key_list_iterator++))
@@ -3141,7 +3149,7 @@ static void check_duplicate_key(THD *thd,
Check that the keys have identical columns in the same order.
*/
- List_iterator<Key_part_spec> k_column_iterator(k->columns);
+ List_iterator_fast<Key_part_spec> k_column_iterator(k->columns);
bool all_columns_are_identical= true;
@@ -3219,7 +3227,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
KEY_PART_INFO *key_part_info;
int field_no,dup_no;
int select_field_pos,auto_increment=0;
- List_iterator<Create_field> it(alter_info->create_list);
+ List_iterator_fast<Create_field> it(alter_info->create_list);
List_iterator<Create_field> it2(alter_info->create_list);
uint total_uneven_bit_length= 0;
int select_field_count= C_CREATE_SELECT(create_table_mode);
@@ -3247,37 +3255,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
!(sql_field->charset= find_bin_collation(sql_field->charset)))
DBUG_RETURN(TRUE);
- /*
- Convert the default value from client character
- set into the column character set if necessary.
- */
- if (sql_field->def &&
- save_cs != sql_field->def->collation.collation &&
- (sql_field->sql_type == MYSQL_TYPE_VAR_STRING ||
- sql_field->sql_type == MYSQL_TYPE_STRING ||
- sql_field->sql_type == MYSQL_TYPE_SET ||
- sql_field->sql_type == MYSQL_TYPE_ENUM))
- {
- /*
- Starting from 5.1 we work here with a copy of Create_field
- created by the caller, not with the instance that was
- originally created during parsing. It's OK to create
- a temporary item and initialize with it a member of the
- copy -- this item will be thrown away along with the copy
- at the end of execution, and thus not introduce a dangling
- pointer in the parsed tree of a prepared statement or a
- stored procedure statement.
- */
- sql_field->def= sql_field->def->safe_charset_converter(thd, save_cs);
-
- if (sql_field->def == NULL)
- {
- /* Could not convert */
- my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
- DBUG_RETURN(TRUE);
- }
- }
-
if (sql_field->sql_type == MYSQL_TYPE_SET ||
sql_field->sql_type == MYSQL_TYPE_ENUM)
{
@@ -3343,36 +3320,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (sql_field->sql_type == MYSQL_TYPE_SET)
{
uint32 field_length;
- if (sql_field->def != NULL)
- {
- char *not_used;
- uint not_used2;
- bool not_found= 0;
- String str, *def= sql_field->def->val_str(&str);
- if (def == NULL) /* SQL "NULL" maps to NULL */
- {
- if ((sql_field->flags & NOT_NULL_FLAG) != 0)
- {
- my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
- DBUG_RETURN(TRUE);
- }
-
- /* else, NULL is an allowed value */
- (void) find_set(interval, NULL, 0,
- cs, &not_used, &not_used2, &not_found);
- }
- else /* not NULL */
- {
- (void) find_set(interval, def->ptr(), def->length(),
- cs, &not_used, &not_used2, &not_found);
- }
-
- if (not_found)
- {
- my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
- DBUG_RETURN(TRUE);
- }
- }
calculate_interval_lengths(cs, interval, &dummy, &field_length);
sql_field->length= field_length + (interval->count - 1);
}
@@ -3380,29 +3327,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
{
uint32 field_length;
DBUG_ASSERT(sql_field->sql_type == MYSQL_TYPE_ENUM);
- if (sql_field->def != NULL)
- {
- String str, *def= sql_field->def->val_str(&str);
- if (def == NULL) /* SQL "NULL" maps to NULL */
- {
- if ((sql_field->flags & NOT_NULL_FLAG) != 0)
- {
- my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
- DBUG_RETURN(TRUE);
- }
-
- /* else, the defaults yield the correct length for NULLs. */
- }
- else /* not NULL */
- {
- def->length(cs->cset->lengthsp(cs, def->ptr(), def->length()));
- if (find_type2(interval, def->ptr(), def->length(), cs) == 0) /* not found */
- {
- my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
- DBUG_RETURN(TRUE);
- }
- }
- }
calculate_interval_lengths(cs, interval, &field_length, &dummy);
sql_field->length= field_length;
}
@@ -3422,6 +3346,112 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (prepare_blob_field(thd, sql_field))
DBUG_RETURN(TRUE);
+ if (sql_field->default_value)
+ {
+ Virtual_column_info *def= sql_field->default_value;
+
+ if (!sql_field->has_default_expression())
+ def->expr_str= null_lex_str;
+
+ if (!def->expr_item->basic_const_item() && !def->flags)
+ {
+ Item *expr= def->expr_item;
+ int err= !expr->fixed && // may be already fixed if ALTER TABLE
+ expr->fix_fields(thd, &expr);
+ if (!err)
+ {
+ if (expr->result_type() == REAL_RESULT)
+ { // don't convert floats to string and back, it can be lossy
+ double res= expr->val_real();
+ if (expr->null_value)
+ expr= new (thd->mem_root) Item_null(thd);
+ else
+ expr= new (thd->mem_root) Item_float(thd, res, expr->decimals);
+ }
+ else
+ {
+ StringBuffer<MAX_FIELD_WIDTH> buf;
+ String *res= expr->val_str(&buf);
+ if (expr->null_value)
+ expr= new (thd->mem_root) Item_null(thd);
+ else
+ {
+ char *str= (char*) thd->strmake(res->ptr(), res->length());
+ expr= new (thd->mem_root) Item_string(thd, str, res->length(), res->charset());
+ }
+ }
+ thd->change_item_tree(&def->expr_item, expr);
+ }
+ }
+ }
+
+ /*
+ Convert the default value from client character
+ set into the column character set if necessary.
+ We can only do this for constants as we have not yet run fix_fields.
+ */
+ if (sql_field->default_value &&
+ sql_field->default_value->expr_item->basic_const_item() &&
+ save_cs != sql_field->default_value->expr_item->collation.collation &&
+ (sql_field->sql_type == MYSQL_TYPE_VAR_STRING ||
+ sql_field->sql_type == MYSQL_TYPE_STRING ||
+ sql_field->sql_type == MYSQL_TYPE_SET ||
+ sql_field->sql_type == MYSQL_TYPE_TINY_BLOB ||
+ sql_field->sql_type == MYSQL_TYPE_MEDIUM_BLOB ||
+ sql_field->sql_type == MYSQL_TYPE_LONG_BLOB ||
+ sql_field->sql_type == MYSQL_TYPE_BLOB ||
+ sql_field->sql_type == MYSQL_TYPE_ENUM))
+ {
+ Item *item;
+ if (!(item= sql_field->default_value->expr_item->
+ safe_charset_converter(thd, save_cs)))
+ {
+ /* Could not convert */
+ my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
+ DBUG_RETURN(TRUE);
+ }
+ /* Fix for prepare statement */
+ thd->change_item_tree(&sql_field->default_value->expr_item, item);
+ }
+
+ if (sql_field->default_value &&
+ sql_field->default_value->expr_item->basic_const_item() &&
+ (sql_field->sql_type == MYSQL_TYPE_SET ||
+ sql_field->sql_type == MYSQL_TYPE_ENUM))
+ {
+ StringBuffer<MAX_FIELD_WIDTH> str;
+ String *def= sql_field->default_value->expr_item->val_str(&str);
+ bool not_found;
+ if (def == NULL) /* SQL "NULL" maps to NULL */
+ {
+ not_found= sql_field->flags & NOT_NULL_FLAG;
+ }
+ else
+ {
+ not_found= false;
+ if (sql_field->sql_type == MYSQL_TYPE_SET)
+ {
+ char *not_used;
+ uint not_used2;
+ find_set(sql_field->interval, def->ptr(), def->length(),
+ sql_field->charset, &not_used, &not_used2, &not_found);
+ }
+ else /* MYSQL_TYPE_ENUM */
+ {
+ def->length(sql_field->charset->cset->lengthsp(sql_field->charset,
+ def->ptr(), def->length()));
+ not_found= !find_type2(sql_field->interval, def->ptr(),
+ def->length(), sql_field->charset);
+ }
+ }
+
+ if (not_found)
+ {
+ my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
+ DBUG_RETURN(TRUE);
+ }
+ }
+
if (!(sql_field->flags & NOT_NULL_FLAG))
null_fields++;
@@ -3459,7 +3489,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
file->ha_table_flags() & HA_CAN_BIT_FIELD)
total_uneven_bit_length-= sql_field->length & 7;
- sql_field->def= dup_field->def;
+ sql_field->default_value= dup_field->default_value;
sql_field->sql_type= dup_field->sql_type;
/*
@@ -3573,7 +3603,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
therefore mark it as unsafe.
*/
if (select_field_count > 0 && auto_increment)
- thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_CREATE_SELECT_AUTOINC);
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_CREATE_SELECT_AUTOINC);
/* Create keys */
@@ -4124,7 +4154,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
it is NOT NULL, not an AUTO_INCREMENT field, not a TIMESTAMP and not
updated trough a NOW() function.
*/
- if (!sql_field->def &&
+ if (!sql_field->default_value &&
!sql_field->has_default_function() &&
(sql_field->flags & NOT_NULL_FLAG) &&
!is_timestamp_type(sql_field->sql_type))
@@ -4134,7 +4164,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
if (thd->variables.sql_mode & MODE_NO_ZERO_DATE &&
- !sql_field->def &&
+ !sql_field->default_value &&
is_timestamp_type(sql_field->sql_type) &&
(sql_field->flags & NOT_NULL_FLAG) &&
(type == Field::NONE || type == Field::TIMESTAMP_UN_FIELD))
@@ -4158,6 +4188,31 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
}
+ /* Check table level constraints */
+ create_info->check_constraint_list= &alter_info->check_constraint_list;
+ {
+ uint nr= 1;
+ List_iterator_fast<Virtual_column_info> c_it(alter_info->check_constraint_list);
+ Virtual_column_info *check;
+ while ((check= c_it++))
+ {
+ if (!check->name.length)
+ make_unique_constraint_name(thd, &check->name,
+ &alter_info->check_constraint_list,
+ &nr);
+
+ if (check_string_char_length(&check->name, 0, NAME_CHAR_LEN,
+ system_charset_info, 1))
+ {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), key->name.str);
+ DBUG_RETURN(TRUE);
+ }
+ if (check_expression(check, "CONSTRAINT CHECK",
+ check->name.str ? check->name.str : "", 0))
+ DBUG_RETURN(TRUE);
+ }
+ }
+
/* Give warnings for not supported table options */
#if defined(WITH_ARIA_STORAGE_ENGINE)
extern handlerton *maria_hton;
@@ -4226,7 +4281,7 @@ bool validate_comment_length(THD *thd, LEX_STRING *comment, size_t max_len,
create_info Table create information
DESCRIPTION
- If the table character set was not given explicitely,
+ If the table character set was not given explicitly,
let's fetch the database default character set and
apply it to the table.
*/
@@ -4273,7 +4328,7 @@ static bool prepare_blob_field(THD *thd, Column_definition *sql_field)
/* Convert long VARCHAR columns to TEXT or BLOB */
char warn_buff[MYSQL_ERRMSG_SIZE];
- if (sql_field->def || thd->is_strict_mode())
+ if (thd->is_strict_mode())
{
my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name,
static_cast<ulong>(MAX_FIELD_VARCHARLENGTH /
@@ -4352,7 +4407,7 @@ void sp_prepare_create_field(THD *thd, Column_definition *sql_field)
FIELDFLAG_TREAT_BIT_AS_CHAR;
}
sql_field->create_length_to_internal_length();
- DBUG_ASSERT(sql_field->def == 0);
+ DBUG_ASSERT(sql_field->default_value == 0);
/* Can't go wrong as sql_field->def is not defined */
(void) prepare_blob_field(thd, sql_field);
}
@@ -4635,7 +4690,8 @@ err:
which was created.
@param[out] key_count Number of keys in table which was created.
- If one creates a temporary table, this is automatically opened
+ If one creates a temporary table, its is automatically opened and its
+ TABLE_SHARE is added to THD::all_temp_tables list.
Note that this function assumes that caller already have taken
exclusive metadata lock on table being created or used some other
@@ -4695,20 +4751,22 @@ int create_table_impl(THD *thd,
/* Check if table exists */
if (create_info->tmp_table())
{
- TABLE *tmp_table;
- if (find_and_use_temporary_table(thd, db, table_name, &tmp_table))
- goto err;
+ /*
+ If a table exists, it must have been pre-opened. Try looking for one
+ in-use in THD::all_temp_tables list of TABLE_SHAREs.
+ */
+ TABLE *tmp_table= thd->find_temporary_table(db, table_name);
+
if (tmp_table)
{
bool table_creation_was_logged= tmp_table->s->table_creation_was_logged;
if (options.or_replace())
{
- bool tmp;
/*
We are using CREATE OR REPLACE on an existing temporary table
Remove the old table so that we can re-create it.
*/
- if (drop_temporary_table(thd, tmp_table, &tmp))
+ if (thd->drop_temporary_table(tmp_table, NULL, true))
goto err;
}
else if (options.if_not_exists())
@@ -4847,17 +4905,12 @@ int create_table_impl(THD *thd,
create_info->table= 0;
if (!frm_only && create_info->tmp_table())
{
- /*
- Open a table (skipping table cache) and add it into
- THD::temporary_tables list.
- */
-
- TABLE *table= open_table_uncached(thd, create_info->db_type, frm, path,
- db, table_name, true, true);
+ TABLE *table= thd->create_and_open_tmp_table(create_info->db_type, frm,
+ path, db, table_name, true);
if (!table)
{
- (void) rm_temporary_table(create_info->db_type, path);
+ (void) thd->rm_temporary_table(create_info->db_type, path);
goto err;
}
@@ -4889,7 +4942,7 @@ int create_table_impl(THD *thd,
open_table_from_share(thd, &share, "", 0, (uint) READ_ALL,
0, &table, true));
if (!result)
- (void) closefrm(&table, 0);
+ (void) closefrm(&table);
free_table_share(&share);
@@ -5119,6 +5172,38 @@ make_unique_key_name(THD *thd, const char *field_name,KEY *start,KEY *end)
return (char*) "not_specified"; // Should never happen
}
+/**
+ Make an unique name for constraints without a name
+*/
+
+static void make_unique_constraint_name(THD *thd, LEX_STRING *name,
+ List<Virtual_column_info> *vcol,
+ uint *nr)
+{
+ char buff[MAX_FIELD_NAME], *end;
+ List_iterator_fast<Virtual_column_info> it(*vcol);
+
+ end=strmov(buff, "CONSTRAINT_");
+ for (;;)
+ {
+ Virtual_column_info *check;
+ char *real_end= int10_to_str((*nr)++, end, 10);
+ it.rewind();
+ while ((check= it++))
+ {
+ if (check->name.str &&
+ !my_strcasecmp(system_charset_info, buff, check->name.str))
+ break;
+ }
+ if (!check) // Found unique name
+ {
+ name->length= (size_t) (real_end - buff);
+ name->str= thd->strmake(buff, name->length);
+ return;
+ }
+ }
+}
+
/****************************************************************************
** Alter a table definition
@@ -5424,6 +5509,9 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
/*
We have to write the query before we unlock the tables.
*/
+ if (thd->is_current_stmt_binlog_disabled())
+ goto err;
+
if (thd->is_current_stmt_binlog_format_row())
{
/*
@@ -5494,6 +5582,21 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
*/
if (!table->view)
{
+ /*
+ After opening a MERGE table add the children to the query list of
+ tables, so that children tables info can be used on "CREATE TABLE"
+ statement generation by the binary log.
+ Note that placeholders don't have the handler open.
+ */
+ if (table->table->file->extra(HA_EXTRA_ADD_CHILDREN_LIST))
+ goto err;
+
+ /*
+ As the reference table is temporary and may not exist on slave, we must
+ force the ENGINE to be present into CREATE TABLE.
+ */
+ create_info->used_fields|= HA_CREATE_USED_ENGINE;
+
int result __attribute__((unused))=
show_create_table(thd, table, &query, create_info, WITH_DB_NAME);
@@ -5797,7 +5900,19 @@ drop_create_field:
}
}
}
- else /* Alter_drop::KEY */
+ else if (drop->type == Alter_drop::CHECK_CONSTRAINT)
+ {
+ for (uint i=table->s->field_check_constraints; i < table->s->table_check_constraints; i++)
+ {
+ if (my_strcasecmp(system_charset_info, drop->name,
+ table->check_constraints[i]->name.str) == 0)
+ {
+ remove_drop= FALSE;
+ break;
+ }
+ }
+ }
+ else /* Alter_drop::KEY and Alter_drop::FOREIGN_KEY */
{
uint n_key;
if (drop->type != Alter_drop::FOREIGN_KEY)
@@ -5854,7 +5969,7 @@ drop_create_field:
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_CANT_DROP_FIELD_OR_KEY,
ER_THD(thd, ER_CANT_DROP_FIELD_OR_KEY),
- drop->name);
+ drop->type_name(), drop->name);
drop_it.remove();
if (alter_info->drop_list.is_empty())
alter_info->flags&= ~(Alter_info::ALTER_DROP_COLUMN |
@@ -6173,6 +6288,10 @@ static bool fill_alter_inplace_info(THD *thd,
/* Check for: ALTER TABLE FORCE, ALTER TABLE ENGINE and OPTIMIZE TABLE. */
if (alter_info->flags & Alter_info::ALTER_RECREATE)
ha_alter_info->handler_flags|= Alter_inplace_info::RECREATE_TABLE;
+ if (alter_info->flags & Alter_info::ALTER_ADD_CHECK_CONSTRAINT)
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_ADD_CHECK_CONSTRAINT;
+ if (alter_info->flags & Alter_info::ALTER_DROP_CHECK_CONSTRAINT)
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_DROP_CHECK_CONSTRAINT;
/*
If we altering table with old VARCHAR fields we will be automatically
@@ -6920,6 +7039,14 @@ static bool is_inplace_alter_impossible(TABLE *table,
if (!table->s->mysql_version)
DBUG_RETURN(true);
+ /*
+ If we are using a MySQL 5.7 table with virtual fields, ALTER TABLE must
+ recreate the table as we need to rewrite generated fields
+ */
+ if (table->s->mysql_version > 50700 && table->s->mysql_version < 100000 &&
+ table->s->virtual_fields)
+ DBUG_RETURN(TRUE);
+
DBUG_RETURN(false);
}
@@ -7143,7 +7270,8 @@ static bool mysql_inplace_alter_table(THD *thd,
HA_EXTRA_NOT_USED,
NULL);
table_list->table= table= NULL;
- close_temporary_table(thd, altered_table, true, false);
+
+ thd->drop_temporary_table(altered_table, NULL, false);
/*
Replace the old .FRM with the new .FRM, but keep the old name for now.
@@ -7233,7 +7361,7 @@ static bool mysql_inplace_alter_table(THD *thd,
thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
/* QQ; do something about metadata locks ? */
}
- close_temporary_table(thd, altered_table, true, false);
+ thd->drop_temporary_table(altered_table, NULL, false);
// Delete temporary .frm/.par
(void) quick_rm_table(thd, create_info->db_type, alter_ctx->new_db,
alter_ctx->tmp_name, FN_IS_TMP | NO_HA_TABLE);
@@ -7329,6 +7457,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
List_iterator<Create_field> find_it(new_create_list);
List_iterator<Create_field> field_it(new_create_list);
List<Key_part_spec> key_parts;
+ List<Virtual_column_info> new_constraint_list;
uint db_create_options= (table->s->db_create_options
& ~(HA_OPTION_PACK_RECORD));
uint used_fields;
@@ -7473,12 +7602,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
}
if (alter)
{
- if (def->sql_type == MYSQL_TYPE_BLOB)
- {
- my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
- goto err;
- }
- if ((def->def=alter->def)) // Use new default
+ if ((def->default_value= alter->default_value))
def->flags&= ~NO_DEFAULT_VALUE_FLAG;
else
def->flags|= NO_DEFAULT_VALUE_FLAG;
@@ -7752,6 +7876,33 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
}
}
+ /* Add all table level constraints which are not in the drop list */
+ if (table->s->table_check_constraints)
+ {
+ TABLE_SHARE *share= table->s;
+
+ for (uint i= share->field_check_constraints;
+ i < share->table_check_constraints ; i++)
+ {
+ Virtual_column_info *check= table->check_constraints[i];
+ Alter_drop *drop;
+ drop_it.rewind();
+ while ((drop=drop_it++))
+ {
+ if (drop->type == Alter_drop::CHECK_CONSTRAINT &&
+ !my_strcasecmp(system_charset_info, check->name.str, drop->name))
+ {
+ drop_it.remove();
+ break;
+ }
+ }
+ if (!drop)
+ new_constraint_list.push_back(check, thd->mem_root);
+ }
+ }
+ /* Add new constraints */
+ new_constraint_list.append(&alter_info->check_constraint_list);
+
if (alter_info->drop_list.elements)
{
Alter_drop *drop;
@@ -7760,8 +7911,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
switch (drop->type) {
case Alter_drop::KEY:
case Alter_drop::COLUMN:
- my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
- alter_info->drop_list.head()->name);
+ case Alter_drop::CHECK_CONSTRAINT:
+ my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), drop->type_name(),
+ alter_info->drop_list.head()->name);
goto err;
case Alter_drop::FOREIGN_KEY:
// Leave the DROP FOREIGN KEY names in the alter_info->drop_list.
@@ -7769,12 +7921,6 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
}
}
}
- if (alter_info->alter_list.elements)
- {
- my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
- alter_info->alter_list.head()->name);
- goto err;
- }
if (!create_info->comment.str)
{
@@ -7807,6 +7953,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
rc= FALSE;
alter_info->create_list.swap(new_create_list);
alter_info->key_list.swap(new_key_list);
+ alter_info->check_constraint_list.swap(new_constraint_list);
err:
DBUG_RETURN(rc);
}
@@ -8129,6 +8276,72 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table,
DBUG_RETURN(false);
}
+/**
+ Rename temporary table and/or turn indexes on/off without touching .FRM.
+ Its a variant of simple_rename_or_index_change() to be used exclusively
+ for temporary tables.
+
+ @param thd Thread handler
+ @param table_list TABLE_LIST for the table to change
+ @param keys_onoff ENABLE or DISABLE KEYS?
+ @param alter_ctx ALTER TABLE runtime context.
+
+ @return Operation status
+ @retval false Success
+ @retval true Failure
+*/
+static bool
+simple_tmp_rename_or_index_change(THD *thd, TABLE_LIST *table_list,
+ Alter_info::enum_enable_or_disable keys_onoff,
+ Alter_table_ctx *alter_ctx)
+{
+ DBUG_ENTER("simple_tmp_rename_or_index_change");
+
+ TABLE *table= table_list->table;
+ bool error= false;
+
+ DBUG_ASSERT(table->s->tmp_table);
+
+ if (keys_onoff != Alter_info::LEAVE_AS_IS)
+ {
+ THD_STAGE_INFO(thd, stage_manage_keys);
+ error= alter_table_manage_keys(table, table->file->indexes_are_disabled(),
+ keys_onoff);
+ }
+
+ if (!error && alter_ctx->is_table_renamed())
+ {
+ THD_STAGE_INFO(thd, stage_rename);
+
+ /*
+ If THD::rename_temporary_table() fails, there is no need to rename it
+ back to the original name (unlike the case for non-temporary tables),
+ as it was an allocation error and the table was not renamed.
+ */
+ error= thd->rename_temporary_table(table, alter_ctx->new_db,
+ alter_ctx->new_alias);
+ }
+
+ if (!error)
+ {
+ int res= 0;
+ /*
+ We do not replicate alter table statement on temporary tables under
+ ROW-based replication.
+ */
+ if (!thd->is_current_stmt_binlog_format_row())
+ {
+ res= write_bin_log(thd, true, thd->query(), thd->query_length());
+ }
+ if (res != 0)
+ error= true;
+ else
+ my_ok(thd);
+ }
+
+ DBUG_RETURN(error);
+}
+
/**
Rename table and/or turn indexes on/off without touching .FRM
@@ -8165,6 +8378,7 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list,
if (lock_tables(thd, table_list, alter_ctx->tables_opened, 0))
DBUG_RETURN(true);
+ THD_STAGE_INFO(thd, stage_manage_keys);
error= alter_table_manage_keys(table,
table->file->indexes_are_disabled(),
keys_onoff);
@@ -8385,7 +8599,12 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
{
if (table->s->tmp_table != NO_TMP_TABLE)
{
- if (find_temporary_table(thd, alter_ctx.new_db, alter_ctx.new_name))
+ /*
+ Check whether a temporary table exists with same requested new name.
+ If such table exists, there must be a corresponding TABLE_SHARE in
+ THD::all_temp_tables list.
+ */
+ if (thd->find_tmp_table_share(alter_ctx.new_db, alter_ctx.new_name))
{
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias);
DBUG_RETURN(true);
@@ -8480,7 +8699,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
till this point for the alter operation.
*/
if ((alter_info->flags & Alter_info::ADD_FOREIGN_KEY) &&
- check_fk_parent_table_access(thd, create_info, alter_info))
+ check_fk_parent_table_access(thd, create_info, alter_info, new_db))
DBUG_RETURN(true);
/*
@@ -8538,29 +8757,48 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
thd->get_stmt_da()->current_statement_warn_count());
my_ok(thd, 0L, 0L, alter_ctx.tmp_name);
- if (write_bin_log(thd, true, thd->query(), thd->query_length()))
- DBUG_RETURN(true);
+ /* We don't replicate alter table statement on temporary tables */
+ if (table->s->tmp_table == NO_TMP_TABLE ||
+ !thd->is_current_stmt_binlog_format_row())
+ {
+ if (write_bin_log(thd, true, thd->query(), thd->query_length()))
+ DBUG_RETURN(true);
+ }
DBUG_RETURN(false);
}
+ /*
+ Test if we are only doing RENAME or KEYS ON/OFF. This works
+ as we are testing if flags == 0 above.
+ */
if (!(alter_info->flags & ~(Alter_info::ALTER_RENAME |
Alter_info::ALTER_KEYS_ONOFF)) &&
alter_info->requested_algorithm !=
- Alter_info::ALTER_TABLE_ALGORITHM_COPY &&
- !table->s->tmp_table) // no need to touch frm
+ Alter_info::ALTER_TABLE_ALGORITHM_COPY) // No need to touch frm.
{
- // This requires X-lock, no other lock levels supported.
- if (alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_DEFAULT &&
- alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE)
+ bool res;
+
+ if (!table->s->tmp_table)
{
- my_error(ER_ALTER_OPERATION_NOT_SUPPORTED, MYF(0),
- "LOCK=NONE/SHARED", "LOCK=EXCLUSIVE");
- DBUG_RETURN(true);
+ // This requires X-lock, no other lock levels supported.
+ if (alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_DEFAULT &&
+ alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE)
+ {
+ my_error(ER_ALTER_OPERATION_NOT_SUPPORTED, MYF(0),
+ "LOCK=NONE/SHARED", "LOCK=EXCLUSIVE");
+ DBUG_RETURN(true);
+ }
+ res= simple_rename_or_index_change(thd, table_list,
+ alter_info->keys_onoff,
+ &alter_ctx);
+ }
+ else
+ {
+ res= simple_tmp_rename_or_index_change(thd, table_list,
+ alter_info->keys_onoff,
+ &alter_ctx);
}
- bool res= simple_rename_or_index_change(thd, table_list,
- alter_info->keys_onoff,
- &alter_ctx);
DBUG_RETURN(res);
}
@@ -8821,11 +9059,11 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
// We assume that the table is non-temporary.
DBUG_ASSERT(!table->s->tmp_table);
- if (!(altered_table= open_table_uncached(thd, new_db_type, &frm,
- alter_ctx.get_tmp_path(),
- alter_ctx.new_db,
- alter_ctx.tmp_name,
- true, false)))
+ if (!(altered_table=
+ thd->create_and_open_tmp_table(new_db_type, &frm,
+ alter_ctx.get_tmp_path(),
+ alter_ctx.new_db, alter_ctx.tmp_name,
+ false)))
goto err_new_table_cleanup;
/* Set markers for fields in TABLE object for altered table. */
@@ -8839,7 +9077,10 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
altered_table->column_bitmaps_set_no_signal(&altered_table->s->all_set,
&altered_table->s->all_set);
restore_record(altered_table, s->default_values); // Create empty record
- if (altered_table->default_field && altered_table->update_default_fields())
+ /* Check that we can call default functions with default field values */
+ altered_table->reset_default_fields();
+ if (altered_table->default_field &&
+ altered_table->update_default_fields(0, 1))
goto err_new_table_cleanup;
// Ask storage engine whether to use copy or in-place
@@ -8865,7 +9106,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
{
ha_alter_info.report_unsupported_error("LOCK=NONE/SHARED",
"LOCK=EXCLUSIVE");
- close_temporary_table(thd, altered_table, true, false);
+ thd->drop_temporary_table(altered_table, NULL, false);
goto err_new_table_cleanup;
}
break;
@@ -8876,7 +9117,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
Alter_info::ALTER_TABLE_LOCK_NONE)
{
ha_alter_info.report_unsupported_error("LOCK=NONE", "LOCK=SHARED");
- close_temporary_table(thd, altered_table, true, false);
+ thd->drop_temporary_table(altered_table, NULL, false);
goto err_new_table_cleanup;
}
break;
@@ -8890,7 +9131,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
{
ha_alter_info.report_unsupported_error("ALGORITHM=INPLACE",
"ALGORITHM=COPY");
- close_temporary_table(thd, altered_table, true, false);
+ thd->drop_temporary_table(altered_table, NULL, false);
goto err_new_table_cleanup;
}
// COPY with LOCK=NONE is not supported, no point in trying.
@@ -8898,7 +9139,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
Alter_info::ALTER_TABLE_LOCK_NONE)
{
ha_alter_info.report_unsupported_error("LOCK=NONE", "LOCK=SHARED");
- close_temporary_table(thd, altered_table, true, false);
+ thd->drop_temporary_table(altered_table, NULL, false);
goto err_new_table_cleanup;
}
// Otherwise use COPY
@@ -8906,7 +9147,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
break;
case HA_ALTER_ERROR:
default:
- close_temporary_table(thd, altered_table, true, false);
+ thd->drop_temporary_table(altered_table, NULL, false);
goto err_new_table_cleanup;
}
@@ -8925,7 +9166,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
}
else
{
- close_temporary_table(thd, altered_table, true, false);
+ thd->drop_temporary_table(altered_table, NULL, false);
}
}
@@ -8978,13 +9219,18 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
if (create_info->tmp_table())
{
- if (!open_table_uncached(thd, new_db_type, &frm,
- alter_ctx.get_tmp_path(),
- alter_ctx.new_db, alter_ctx.tmp_name,
- true, true))
+ TABLE *tmp_table=
+ thd->create_and_open_tmp_table(new_db_type, &frm,
+ alter_ctx.get_tmp_path(),
+ alter_ctx.new_db, alter_ctx.tmp_name,
+ true);
+ if (!tmp_table)
+ {
goto err_new_table_cleanup;
+ }
}
+
/* Open the table since we need to copy the data. */
if (table->s->tmp_table != NO_TMP_TABLE)
{
@@ -8992,18 +9238,24 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
tbl.init_one_table(alter_ctx.new_db, strlen(alter_ctx.new_db),
alter_ctx.tmp_name, strlen(alter_ctx.tmp_name),
alter_ctx.tmp_name, TL_READ_NO_INSERT);
- /* Table is in thd->temporary_tables */
- (void) open_temporary_table(thd, &tbl);
+ /*
+ Table can be found in the list of open tables in THD::all_temp_tables
+ list.
+ */
+ tbl.table= thd->find_temporary_table(&tbl);
new_table= tbl.table;
}
else
{
- /* table is a normal table: Create temporary table in same directory */
- /* Open our intermediate table. */
- new_table= open_table_uncached(thd, new_db_type, &frm,
- alter_ctx.get_tmp_path(),
- alter_ctx.new_db, alter_ctx.tmp_name,
- true, true);
+ /*
+ table is a normal table: Create temporary table in same directory.
+ Open our intermediate table.
+ */
+ new_table=
+ thd->create_and_open_tmp_table(new_db_type, &frm,
+ alter_ctx.get_tmp_path(),
+ alter_ctx.new_db, alter_ctx.tmp_name,
+ true);
}
if (!new_table)
goto err_new_table_cleanup;
@@ -9071,10 +9323,10 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
new_table->s->table_creation_was_logged=
table->s->table_creation_was_logged;
/* Remove link to old table and rename the new one */
- close_temporary_table(thd, table, true, true);
+ thd->drop_temporary_table(table, NULL, true);
/* Should pass the 'new_name' as we store table name in the cache */
- if (rename_temporary_table(thd, new_table,
- alter_ctx.new_db, alter_ctx.new_name))
+ if (thd->rename_temporary_table(new_table, alter_ctx.new_db,
+ alter_ctx.new_name))
goto err_new_table_cleanup;
/* We don't replicate alter table statement on temporary tables */
if (!thd->is_current_stmt_binlog_format_row() &&
@@ -9086,10 +9338,10 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
/*
Close the intermediate table that will be the new table, but do
- not delete it! Even altough MERGE tables do not have their children
- attached here it is safe to call close_temporary_table().
+ not delete it! Even though MERGE tables do not have their children
+ attached here it is safe to call THD::drop_temporary_table().
*/
- close_temporary_table(thd, new_table, true, false);
+ thd->drop_temporary_table(new_table, NULL, false);
new_table= NULL;
DEBUG_SYNC(thd, "alter_table_before_rename_result_table");
@@ -9231,8 +9483,7 @@ err_new_table_cleanup:
my_free(const_cast<uchar*>(frm.str));
if (new_table)
{
- /* close_temporary_table() frees the new_table pointer. */
- close_temporary_table(thd, new_table, true, true);
+ thd->drop_temporary_table(new_table, NULL, true);
}
else
(void) quick_rm_table(thd, new_db_type,
@@ -9278,6 +9529,9 @@ err_new_table_cleanup:
err_with_mdl_after_alter:
/* the table was altered. binlog the operation */
+ DBUG_ASSERT(!(mysql_bin_log.is_open() &&
+ thd->is_current_stmt_binlog_format_row() &&
+ (create_info->tmp_table())));
write_bin_log(thd, true, thd->query(), thd->query_length());
err_with_mdl:
@@ -9416,7 +9670,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
Old fields keep their current values, and therefore should not be
present in the set of autoupdate fields.
*/
- if ((*ptr)->has_insert_default_function())
+ if ((*ptr)->default_value)
{
*(dfield_ptr++)= *ptr;
++to->s->default_fields;
@@ -9463,7 +9717,9 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
THD_STAGE_INFO(thd, stage_copy_to_tmp_table);
/* Tell handler that we have values for all columns in the to table */
to->use_all_columns();
- to->mark_virtual_columns_for_write(TRUE);
+ /* Add virtual columns to vcol_set to ensure they are updated */
+ if (to->vfield)
+ to->mark_virtual_columns_for_write(TRUE);
if (init_read_record(&info, thd, from, (SQL_SELECT *) 0, file_sort, 1, 1,
FALSE))
goto err;
@@ -9473,8 +9729,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
thd->get_stmt_da()->reset_current_row_for_warning();
restore_record(to, s->default_values); // Create empty record
- if (to->default_field && to->update_default_fields())
- goto err;
+ to->reset_default_fields();
thd->progress.max_counter= from->file->records();
time_to_report_progress= MY_HOW_OFTEN_TO_WRITE/10;
@@ -9515,8 +9770,14 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
copy_ptr->do_copy(copy_ptr);
}
prev_insert_id= to->file->next_insert_id;
+ if (to->default_field)
+ to->update_default_fields(0, ignore);
if (to->vfield)
update_virtual_fields(thd, to, VCOL_UPDATE_FOR_WRITE);
+
+ /* This will set thd->is_error() if fatal failure */
+ if (to->verify_constraints(ignore) == VIEW_CHECK_SKIP)
+ continue;
if (thd->is_error())
{
error= 1;
@@ -9586,7 +9847,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
THD_STAGE_INFO(thd, stage_enabling_keys);
thd_progress_next_stage(thd);
- if (error > 0)
+ if (error > 0 && !from->s->tmp_table)
{
/* We are going to drop the temporary table */
to->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
@@ -9615,7 +9876,8 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
to->file->ha_release_auto_increment();
if (to->file->ha_external_lock(thd,F_UNLCK))
error=1;
- if (error < 0 && to->file->extra(HA_EXTRA_PREPARE_FOR_RENAME))
+ if (error < 0 && !from->s->tmp_table &&
+ to->file->extra(HA_EXTRA_PREPARE_FOR_RENAME))
error= 1;
thd_progress_end(thd);
DBUG_RETURN(error > 0 ? -1 : 0);
@@ -9732,7 +9994,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
/* Allow to open real tables only. */
table->required_type= FRMTYPE_TABLE;
- if (open_temporary_tables(thd, table) ||
+ if (thd->open_temporary_tables(table) ||
open_and_lock_tables(thd, table, FALSE, 0))
{
t= NULL;