diff options
author | unknown <pappa@c-8b0ae253.1238-1-64736c10.cust.bredbandsbolaget.se> | 2005-08-05 18:01:34 -0400 |
---|---|---|
committer | unknown <pappa@c-8b0ae253.1238-1-64736c10.cust.bredbandsbolaget.se> | 2005-08-05 18:01:34 -0400 |
commit | aca6f88ebb4fb6118b8f12a696111fce167762d3 (patch) | |
tree | f2bca76ddf241e417c5d8e8f0ab325105e97cbae /sql | |
parent | 9216f5f25926197a4b40ac4fd4aeb9ad48a2a247 (diff) | |
parent | e13fa8ebbb095970b9a5a206001eeb06273567de (diff) | |
download | mariadb-git-aca6f88ebb4fb6118b8f12a696111fce167762d3.tar.gz |
Merge mronstrom@bk-internal.mysql.com:/home/bk/mysql-5.0
into c-8b0ae253.1238-1-64736c10.cust.bredbandsbolaget.se:/home/pappa/mysql-5.1
BitKeeper/etc/config:
Auto merged
mysql-test/mysql-test-run.pl:
Auto merged
mysql-test/r/information_schema_db.result:
Auto merged
mysql-test/t/disabled.def:
Auto merged
mysys/default.c:
Auto merged
scripts/Makefile.am:
Auto merged
scripts/mysql_create_system_tables.sh:
Auto merged
scripts/mysql_fix_privilege_tables.sql:
Auto merged
sql/field.cc:
Auto merged
sql/field.h:
Auto merged
sql/ha_federated.cc:
Auto merged
sql/ha_innodb.cc:
Auto merged
sql/ha_innodb.h:
Auto merged
sql/ha_ndbcluster.cc:
Auto merged
sql/ha_ndbcluster.h:
Auto merged
sql/handler.cc:
Auto merged
sql/item.cc:
Auto merged
sql/lock.cc:
Auto merged
sql/log.cc:
Auto merged
sql/mysql_priv.h:
Auto merged
sql/mysqld.cc:
Auto merged
sql/set_var.cc:
Auto merged
sql/set_var.h:
Auto merged
sql/slave.cc:
Auto merged
sql/slave.h:
Auto merged
sql/sp.cc:
Auto merged
sql/sql_acl.cc:
Auto merged
sql/sql_base.cc:
Auto merged
sql/sql_cache.cc:
Auto merged
sql/sql_class.h:
Auto merged
sql/sql_insert.cc:
Auto merged
sql/sql_lex.cc:
Auto merged
sql/sql_parse.cc:
Auto merged
sql/sql_prepare.cc:
Auto merged
sql/sql_select.cc:
Auto merged
sql/sql_show.cc:
Auto merged
sql/sql_table.cc:
Auto merged
sql/table.cc:
Auto merged
sql/table.h:
Auto merged
sql/tztime.cc:
Auto merged
sql/unireg.cc:
Auto merged
storage/innobase/buf/buf0buf.c:
Auto merged
storage/innobase/include/os0file.h:
Auto merged
storage/innobase/include/read0read.h:
Auto merged
storage/innobase/include/read0types.h:
Auto merged
storage/innobase/include/trx0trx.h:
Auto merged
storage/innobase/os/os0file.c:
Auto merged
storage/innobase/read/read0read.c:
Auto merged
storage/innobase/row/row0sel.c:
Auto merged
storage/innobase/srv/srv0srv.c:
Auto merged
storage/innobase/srv/srv0start.c:
Auto merged
storage/innobase/trx/trx0sys.c:
Auto merged
storage/innobase/trx/trx0trx.c:
Auto merged
storage/myisam/mi_create.c:
Auto merged
storage/myisam/sort.c:
Auto merged
storage/ndb/include/mgmapi/mgmapi.h:
Auto merged
storage/ndb/include/mgmapi/mgmapi_config_parameters.h:
Auto merged
storage/ndb/src/common/portlib/NdbMutex.c:
Auto merged
storage/ndb/src/common/portlib/NdbThread.c:
Auto merged
storage/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp:
Auto merged
storage/ndb/src/kernel/blocks/dbtup/DbtupGen.cpp:
Auto merged
storage/ndb/src/mgmapi/mgmapi.cpp:
Auto merged
storage/ndb/src/mgmsrv/ConfigInfo.cpp:
Auto merged
storage/ndb/src/mgmsrv/ConfigInfo.hpp:
Auto merged
strings/ctype-big5.c:
Auto merged
strings/ctype-ucs2.c:
Auto merged
support-files/mysql.spec.sh:
Auto merged
configure.in:
Manual merge 5.0 -> 5.1
mysql-test/t/alter_table.test:
Manual merge 5.0 -> 5.1
sql/share/errmsg.txt:
Manual merge 5.0 -> 5.1
storage/ndb/tools/Makefile.am:
Manual merge 5.0 -> 5.1
Diffstat (limited to 'sql')
55 files changed, 1423 insertions, 666 deletions
diff --git a/sql/field.cc b/sql/field.cc index 4e26616a0fc..270214d4350 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4470,7 +4470,7 @@ int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) my_time_t tmp= 0; int error; bool have_smth_to_conv; - bool in_dst_time_gap; + my_bool in_dst_time_gap; THD *thd= table->in_use; /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ @@ -4541,14 +4541,14 @@ int Field_timestamp::store(longlong nr) TIME l_time; my_time_t timestamp= 0; int error; - bool in_dst_time_gap; + my_bool in_dst_time_gap; THD *thd= table->in_use; /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ longlong tmp= number_to_datetime(nr, &l_time, (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | MODE_NO_ZERO_IN_DATE, &error); - if (tmp < 0) + if (tmp == LL(-1)) { error= 2; } @@ -5217,7 +5217,7 @@ int Field_date::store(longlong nr) MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error); - if (nr < 0) + if (nr == LL(-1)) { nr= 0; error= 2; @@ -5393,12 +5393,12 @@ int Field_newdate::store(longlong nr) TIME l_time; longlong tmp; int error; - if ((tmp= number_to_datetime(nr, &l_time, - (TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & - (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | - MODE_INVALID_DATES))), - &error) < 0)) + if (number_to_datetime(nr, &l_time, + (TIME_FUZZY_DATE | + (table->in_use->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | + MODE_INVALID_DATES))), + &error) == LL(-1)) { tmp= 0L; error= 2; @@ -5595,7 +5595,7 @@ int Field_datetime::store(longlong nr) MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error); - if (nr < 0) + if (nr == LL(-1)) { nr= 0; error= 2; @@ -7901,7 +7901,10 @@ int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs) { set_rec_bits(0xff, bit_ptr, bit_ofs, bit_len); memset(ptr, 0xff, field_length); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + if (table->in_use->really_abort_on_warning()) + set_warning(MYSQL_ERROR::WARN_LEVEL_ERROR, ER_DATA_TOO_LONG, 1); + else + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); return 1; } /* delta is >= -1 here */ @@ -8147,7 +8150,10 @@ int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs) memset(ptr, 0xff, field_length); if (bits) *ptr&= ((1 << bits) - 1); /* set first byte */ - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + if (table->in_use->really_abort_on_warning()) + set_warning(MYSQL_ERROR::WARN_LEVEL_ERROR, ER_DATA_TOO_LONG, 1); + else + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); return 1; } bzero(ptr, delta); @@ -8546,7 +8552,10 @@ create_field::create_field(Field *old_field,Field *orig_field) def=0; if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) && !old_field->is_real_null() && - old_field->ptr && orig_field) + old_field->ptr && orig_field && + (sql_type != FIELD_TYPE_TIMESTAMP || /* set def only if */ + old_field->table->timestamp_field != old_field || /* timestamp field */ + unireg_check == Field::TIMESTAMP_UN_FIELD)) /* has default val */ { char buff[MAX_FIELD_WIDTH],*pos; String tmp(buff,sizeof(buff), charset), *res; diff --git a/sql/field.h b/sql/field.h index 7c55bcf7066..c31d70dd651 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1232,6 +1232,7 @@ public: int store(longlong nr); int store_decimal(const my_decimal *); void get_key_image(char *buff,uint length,imagetype type); + uint size_of() const { return sizeof(*this); } }; #endif /*HAVE_SPATIAL*/ diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc index f36df13c5e9..392aca33965 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -765,7 +765,6 @@ uint ha_federated::convert_row_to_internal_format(byte *record, MYSQL_ROW row) (*field)->move_field(-old_ptr); } - DBUG_DUMP("record", record, table->s->reclength); DBUG_RETURN(0); } diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index a1f85da16d3..93d830d9fb1 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -216,10 +216,10 @@ static handlerton innobase_hton = { innobase_xa_recover, /* recover */ innobase_commit_by_xid, /* commit_by_xid */ innobase_rollback_by_xid, /* rollback_by_xid */ - NULL, - NULL, - NULL, - HTON_CLOSE_CURSORS_AT_COMMIT + innobase_create_cursor_view, + innobase_set_cursor_view, + innobase_close_cursor_view, + HTON_NO_FLAGS }; /********************************************************************* @@ -538,7 +538,7 @@ innobase_mysql_prepare_print_arbitrary_thd(void) } /***************************************************************** -Relases the mutex reserved by innobase_mysql_prepare_print_arbitrary_thd(). +Releases the mutex reserved by innobase_mysql_prepare_print_arbitrary_thd(). NOTE that /mysql/innobase/lock/lock0lock.c must contain the prototype for this function! */ extern "C" @@ -1700,7 +1700,7 @@ innobase_store_binlog_offset_and_flush_log( /* Commits the mini-transaction */ mtr_commit(&mtr); - /* Syncronous flush of the log buffer to disk */ + /* Synchronous flush of the log buffer to disk */ log_buffer_flush_to_disk(); } #endif @@ -2132,15 +2132,34 @@ innobase_savepoint( /********************************************************************* Frees a possible InnoDB trx object associated with the current THD. */ - -static int +static +int innobase_close_connection( /*======================*/ /* out: 0 or error number */ THD* thd) /* in: handle to the MySQL thread of the user whose resources should be free'd */ { - trx_free_for_mysql((trx_t*)thd->ha_data[innobase_hton.slot]); + trx_t* trx; + + trx = (trx_t*)thd->ha_data[innobase_hton.slot]; + + ut_a(trx); + + if (trx->conc_state != TRX_NOT_STARTED) { + ut_print_timestamp(stderr); + + fprintf(stderr, +" InnoDB: Warning: MySQL is closing a connection" +"InnoDB: that has an active InnoDB transaction. We roll back that\n" +"InnoDB: transaction. %lu row modifications to roll back.\n", + (ulong)trx->undo_no.low); + } + + innobase_rollback_trx(trx); + + trx_free_for_mysql(trx); + return(0); } @@ -2834,7 +2853,7 @@ ha_innobase::store_key_val_for_row( /* All indexes on BLOB and TEXT are column prefix indexes, and we may need to truncate the data to be - stored in the kay value: */ + stored in the key value: */ if (blob_len > key_part->length) { blob_len = key_part->length; @@ -5881,7 +5900,7 @@ ha_innobase::start_stmt( innobase_release_stat_resources(trx); if (trx->isolation_level <= TRX_ISO_READ_COMMITTED - && trx->read_view) { + && trx->global_read_view) { /* At low transaction isolation levels we let each consistent read set its own snapshot */ @@ -6102,7 +6121,7 @@ ha_innobase::external_lock( } } else { if (trx->isolation_level <= TRX_ISO_READ_COMMITTED - && trx->read_view) { + && trx->global_read_view) { /* At low transaction isolation levels we let each consistent read set its own snapshot */ @@ -7119,7 +7138,7 @@ int innobase_rollback_by_xid( /*=====================*/ /* out: 0 or error number */ - XID *xid) /* in: X/Open XA transaction idenfification */ + XID *xid) /* in: X/Open XA transaction identification */ { trx_t* trx; @@ -7132,6 +7151,50 @@ innobase_rollback_by_xid( } } +/*********************************************************************** +Create a consistent view for a cursor based on current transaction +which is created if the corresponding MySQL thread still lacks one. +This consistent view is then used inside of MySQL when accessing records +using a cursor. */ + +void* +innobase_create_cursor_view(void) +/*=============================*/ + /* out: Pointer to cursor view or NULL */ +{ + return(read_cursor_view_create_for_mysql( + check_trx_exists(current_thd))); +} + +/*********************************************************************** +Close the given consistent cursor view of a transaction and restore +global read view to a transaction read view. Transaction is created if the +corresponding MySQL thread still lacks one. */ + +void +innobase_close_cursor_view( +/*=======================*/ + void* curview)/* in: Consistent read view to be closed */ +{ + read_cursor_view_close_for_mysql(check_trx_exists(current_thd), + (cursor_view_t*) curview); +} + +/*********************************************************************** +Set the given consistent cursor view to a transaction which is created +if the corresponding MySQL thread still lacks one. If the given +consistent cursor view is NULL global read view of a transaction is +restored to a transaction read view. */ + +void +innobase_set_cursor_view( +/*=====================*/ + void* curview)/* in: Consistent cursor view to be set */ +{ + read_cursor_set_for_mysql(check_trx_exists(current_thd), + (cursor_view_t*) curview); +} + bool ha_innobase::check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes) diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index 1883885a805..0ce1cfeaab2 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -314,7 +314,7 @@ which is in the prepared state */ int innobase_rollback_by_xid( /* out: 0 or error number */ - XID *xid); /* in : X/Open XA Transaction Idenfification */ + XID *xid); /* in : X/Open XA Transaction Identification */ int innobase_xa_end(THD *thd); @@ -322,3 +322,35 @@ int innobase_xa_end(THD *thd); int innobase_repl_report_sent_binlog(THD *thd, char *log_file_name, my_off_t end_offset); + +/*********************************************************************** +Create a consistent view for a cursor based on current transaction +which is created if the corresponding MySQL thread still lacks one. +This consistent view is then used inside of MySQL when accessing records +using a cursor. */ + +void* +innobase_create_cursor_view(void); +/*=============================*/ + /* out: Pointer to cursor view or NULL */ + +/*********************************************************************** +Close the given consistent cursor view of a transaction and restore +global read view to a transaction read view. Transaction is created if the +corresponding MySQL thread still lacks one. */ + +void +innobase_close_cursor_view( +/*=======================*/ + void* curview); /* in: Consistent read view to be closed */ + +/*********************************************************************** +Set the given consistent cursor view to a transaction which is created +if the corresponding MySQL thread still lacks one. If the given +consistent cursor view is NULL global read view of a transaction is +restored to a transaction read view. */ + +void +innobase_set_cursor_view( +/*=====================*/ + void* curview); /* in: Consistent read view to be closed */ diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index bddf442c34e..5d8cfd9913e 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -6312,12 +6312,14 @@ void ndb_serialize_cond(const Item *item, void *arg) // result type if (context->expecting(Item::FIELD_ITEM) && (context->expecting_field_result(field->result_type()) || - // Date and year can be written as strings + // Date and year can be written as string or int ((type == MYSQL_TYPE_TIME || type == MYSQL_TYPE_DATE || type == MYSQL_TYPE_YEAR || type == MYSQL_TYPE_DATETIME) - ? context->expecting_field_result(STRING_RESULT) : true)) && + ? (context->expecting_field_result(STRING_RESULT) || + context->expecting_field_result(INT_RESULT)) + : true)) && // Bit fields no yet supported in scan filter type != MYSQL_TYPE_BIT) { @@ -6385,8 +6387,8 @@ void ndb_serialize_cond(const Item *item, void *arg) } else { - DBUG_PRINT("info", ("Was not expecting field of type %u", - field->result_type())); + DBUG_PRINT("info", ("Was not expecting field of type %u(%u)", + field->result_type(), type)); context->supported= FALSE; } } @@ -6527,17 +6529,6 @@ void ndb_serialize_cond(const Item *item, void *arg) context->expect(Item::FUNC_ITEM); break; } - case Item_func::NOTLIKE_FUNC: - { - DBUG_PRINT("info", ("NOTLIKE_FUNC")); - curr_cond->ndb_item= new Ndb_item(func_item->functype(), - func_item); - context->expect(Item::STRING_ITEM); - context->expect(Item::FIELD_ITEM); - context->expect_field_result(STRING_RESULT); - context->expect(Item::FUNC_ITEM); - break; - } case Item_func::ISNULL_FUNC: { DBUG_PRINT("info", ("ISNULL_FUNC")); @@ -6982,7 +6973,7 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, switch ((negated) ? Ndb_item::negate(cond->ndb_item->qualification.function_type) : cond->ndb_item->qualification.function_type) { - case Item_func::EQ_FUNC: + case NDB_EQ_FUNC: { if (!value || !field) break; // Save value in right format for the field type @@ -6996,7 +6987,7 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, cond= cond->next->next->next; DBUG_RETURN(0); } - case Item_func::NE_FUNC: + case NDB_NE_FUNC: { if (!value || !field) break; // Save value in right format for the field type @@ -7010,7 +7001,7 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, cond= cond->next->next->next; DBUG_RETURN(0); } - case Item_func::LT_FUNC: + case NDB_LT_FUNC: { if (!value || !field) break; // Save value in right format for the field type @@ -7036,7 +7027,7 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, cond= cond->next->next->next; DBUG_RETURN(0); } - case Item_func::LE_FUNC: + case NDB_LE_FUNC: { if (!value || !field) break; // Save value in right format for the field type @@ -7062,7 +7053,7 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, cond= cond->next->next->next; DBUG_RETURN(0); } - case Item_func::GE_FUNC: + case NDB_GE_FUNC: { if (!value || !field) break; // Save value in right format for the field type @@ -7088,7 +7079,7 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, cond= cond->next->next->next; DBUG_RETURN(0); } - case Item_func::GT_FUNC: + case NDB_GT_FUNC: { if (!value || !field) break; // Save value in right format for the field type @@ -7114,7 +7105,7 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, cond= cond->next->next->next; DBUG_RETURN(0); } - case Item_func::LIKE_FUNC: + case NDB_LIKE_FUNC: { if (!value || !field) break; if ((value->qualification.value_type != Item::STRING_ITEM) && @@ -7133,12 +7124,12 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, cond= cond->next->next->next; DBUG_RETURN(0); } - case Item_func::NOTLIKE_FUNC: + case NDB_NOTLIKE_FUNC: { if (!value || !field) break; if ((value->qualification.value_type != Item::STRING_ITEM) && (value->qualification.value_type != Item::VARBIN_ITEM)) - break; + break; // Save value in right format for the field type value->save_in_field(field); DBUG_PRINT("info", ("Generating NOTLIKE filter: notlike(%d,%s,%d)", @@ -7152,7 +7143,7 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, cond= cond->next->next->next; DBUG_RETURN(0); } - case Item_func::ISNULL_FUNC: + case NDB_ISNULL_FUNC: if (!field) break; DBUG_PRINT("info", ("Generating ISNULL filter")); @@ -7160,7 +7151,7 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond, DBUG_RETURN(1); cond= cond->next->next; DBUG_RETURN(0); - case Item_func::ISNOTNULL_FUNC: + case NDB_ISNOTNULL_FUNC: { if (!field) break; @@ -7197,7 +7188,7 @@ ha_ndbcluster::build_scan_filter_group(Ndb_cond* &cond, NdbScanFilter *filter) case NDB_FUNCTION: { switch (cond->ndb_item->qualification.function_type) { - case Item_func::COND_AND_FUNC: + case NDB_COND_AND_FUNC: { level++; DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NAND":"AND", @@ -7209,7 +7200,7 @@ ha_ndbcluster::build_scan_filter_group(Ndb_cond* &cond, NdbScanFilter *filter) cond= cond->next; break; } - case Item_func::COND_OR_FUNC: + case NDB_COND_OR_FUNC: { level++; DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NOR":"OR", @@ -7221,7 +7212,7 @@ ha_ndbcluster::build_scan_filter_group(Ndb_cond* &cond, NdbScanFilter *filter) cond= cond->next; break; } - case Item_func::NOT_FUNC: + case NDB_NOT_FUNC: { DBUG_PRINT("info", ("Generating negated query")); cond= cond->next; @@ -7264,8 +7255,8 @@ ha_ndbcluster::build_scan_filter(Ndb_cond * &cond, NdbScanFilter *filter) switch (cond->ndb_item->type) { case NDB_FUNCTION: switch (cond->ndb_item->qualification.function_type) { - case Item_func::COND_AND_FUNC: - case Item_func::COND_OR_FUNC: + case NDB_COND_AND_FUNC: + case NDB_COND_OR_FUNC: simple_cond= FALSE; break; default: diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index 59ad2556c11..4ba66e959d4 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -72,10 +72,28 @@ typedef enum ndb_item_type { NDB_END_COND = 3 // End marker for condition group } NDB_ITEM_TYPE; +typedef enum ndb_func_type { + NDB_EQ_FUNC = 0, + NDB_NE_FUNC = 1, + NDB_LT_FUNC = 2, + NDB_LE_FUNC = 3, + NDB_GT_FUNC = 4, + NDB_GE_FUNC = 5, + NDB_ISNULL_FUNC = 6, + NDB_ISNOTNULL_FUNC = 7, + NDB_LIKE_FUNC = 8, + NDB_NOTLIKE_FUNC = 9, + NDB_NOT_FUNC = 10, + NDB_UNKNOWN_FUNC = 11, + NDB_COND_AND_FUNC = 12, + NDB_COND_OR_FUNC = 13, + NDB_UNSUPPORTED_FUNC = 14 +} NDB_FUNC_TYPE; + typedef union ndb_item_qualification { Item::Type value_type; - enum_field_types field_type; // Instead of Item::FIELD_ITEM - Item_func::Functype function_type; // Instead of Item::FUNC_ITEM + enum_field_types field_type; // Instead of Item::FIELD_ITEM + NDB_FUNC_TYPE function_type; // Instead of Item::FUNC_ITEM } NDB_ITEM_QUALIFICATION; typedef struct ndb_item_field_value { @@ -91,23 +109,31 @@ typedef union ndb_item_value { struct negated_function_mapping { - Item_func::Functype pos_fun; - Item_func::Functype neg_fun; + NDB_FUNC_TYPE pos_fun; + NDB_FUNC_TYPE neg_fun; }; +/* + Define what functions can be negated in condition pushdown. + Note, these HAVE to be in the same order as in definition enum +*/ static const negated_function_mapping neg_map[]= { - {Item_func::EQ_FUNC, Item_func::NE_FUNC}, - {Item_func::NE_FUNC, Item_func::EQ_FUNC}, - {Item_func::LT_FUNC, Item_func::GE_FUNC}, - {Item_func::LE_FUNC, Item_func::GT_FUNC}, - {Item_func::GT_FUNC, Item_func::LE_FUNC}, - {Item_func::GE_FUNC, Item_func::LT_FUNC}, - {Item_func::LIKE_FUNC, Item_func::NOTLIKE_FUNC}, - {Item_func::NOTLIKE_FUNC, Item_func::LIKE_FUNC}, - {Item_func::ISNULL_FUNC, Item_func::ISNOTNULL_FUNC}, - {Item_func::ISNOTNULL_FUNC, Item_func::ISNULL_FUNC}, - {Item_func::UNKNOWN_FUNC, Item_func::NOT_FUNC} + {NDB_EQ_FUNC, NDB_NE_FUNC}, + {NDB_NE_FUNC, NDB_EQ_FUNC}, + {NDB_LT_FUNC, NDB_GE_FUNC}, + {NDB_LE_FUNC, NDB_GT_FUNC}, + {NDB_GT_FUNC, NDB_LE_FUNC}, + {NDB_GE_FUNC, NDB_LT_FUNC}, + {NDB_ISNULL_FUNC, NDB_ISNOTNULL_FUNC}, + {NDB_ISNOTNULL_FUNC, NDB_ISNULL_FUNC}, + {NDB_LIKE_FUNC, NDB_NOTLIKE_FUNC}, + {NDB_NOTLIKE_FUNC, NDB_LIKE_FUNC}, + {NDB_NOT_FUNC, NDB_UNSUPPORTED_FUNC}, + {NDB_UNKNOWN_FUNC, NDB_UNSUPPORTED_FUNC}, + {NDB_COND_AND_FUNC, NDB_UNSUPPORTED_FUNC}, + {NDB_COND_OR_FUNC, NDB_UNSUPPORTED_FUNC}, + {NDB_UNSUPPORTED_FUNC, NDB_UNSUPPORTED_FUNC} }; /* @@ -162,14 +188,14 @@ class Ndb_item { Ndb_item(Item_func::Functype func_type, const Item *item_value) : type(NDB_FUNCTION) { - qualification.function_type= func_type; + qualification.function_type= item_func_to_ndb_func(func_type); value.item= item_value; value.arg_count= ((Item_func *) item_value)->argument_count(); }; Ndb_item(Item_func::Functype func_type, uint no_args) : type(NDB_FUNCTION) { - qualification.function_type= func_type; + qualification.function_type= item_func_to_ndb_func(func_type); value.arg_count= no_args; }; ~Ndb_item() @@ -231,13 +257,30 @@ class Ndb_item { ((Item *)item)->save_in_field(field, false); }; - static Item_func::Functype negate(Item_func::Functype fun) + static NDB_FUNC_TYPE item_func_to_ndb_func(Item_func::Functype fun) + { + switch (fun) { + case (Item_func::EQ_FUNC): { return NDB_EQ_FUNC; } + case (Item_func::NE_FUNC): { return NDB_NE_FUNC; } + case (Item_func::LT_FUNC): { return NDB_LT_FUNC; } + case (Item_func::LE_FUNC): { return NDB_LE_FUNC; } + case (Item_func::GT_FUNC): { return NDB_GT_FUNC; } + case (Item_func::GE_FUNC): { return NDB_GE_FUNC; } + case (Item_func::ISNULL_FUNC): { return NDB_ISNULL_FUNC; } + case (Item_func::ISNOTNULL_FUNC): { return NDB_ISNOTNULL_FUNC; } + case (Item_func::LIKE_FUNC): { return NDB_LIKE_FUNC; } + case (Item_func::NOT_FUNC): { return NDB_NOT_FUNC; } + case (Item_func::UNKNOWN_FUNC): { return NDB_UNKNOWN_FUNC; } + case (Item_func::COND_AND_FUNC): { return NDB_COND_AND_FUNC; } + case (Item_func::COND_OR_FUNC): { return NDB_COND_OR_FUNC; } + default: { return NDB_UNSUPPORTED_FUNC; } + } + }; + + static NDB_FUNC_TYPE negate(NDB_FUNC_TYPE fun) { - uint i; - for (i=0; - fun != neg_map[i].pos_fun && - neg_map[i].pos_fun != Item_func::UNKNOWN_FUNC; - i++); + uint i= (uint) fun; + DBUG_ASSERT(fun == neg_map[i].pos_fun); return neg_map[i].neg_fun; }; diff --git a/sql/handler.cc b/sql/handler.cc index 89883d038d6..3c64d33c371 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -660,7 +660,7 @@ int ha_commit_trans(THD *thd, bool all) my_xid xid= thd->transaction.xid.get_my_xid(); DBUG_ENTER("ha_commit_trans"); - if (thd->transaction.in_sub_stmt) + if (thd->in_sub_stmt) { /* Since we don't support nested statement transactions in 5.0, @@ -779,7 +779,7 @@ int ha_rollback_trans(THD *thd, bool all) THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt; bool is_real_trans=all || thd->transaction.all.nht == 0; DBUG_ENTER("ha_rollback_trans"); - if (thd->transaction.in_sub_stmt) + if (thd->in_sub_stmt) { /* If we are inside stored function or trigger we should not commit or diff --git a/sql/hostname.cc b/sql/hostname.cc index 12b69a97859..3b1eeb63d37 100644 --- a/sql/hostname.cc +++ b/sql/hostname.cc @@ -143,8 +143,8 @@ my_string ip_to_hostname(struct in_addr *in, uint *errors) *errors=0; /* We always treat the loopback address as "localhost". */ - if (in->s_addr == INADDR_LOOPBACK) - return (char *)my_localhost; + if (in->s_addr == htonl(INADDR_LOOPBACK)) // is expanded inline by gcc + DBUG_RETURN((char *)my_localhost); /* Check first if we have name in cache */ if (!(specialflag & SPECIAL_NO_HOST_CACHE)) diff --git a/sql/item.cc b/sql/item.cc index 604f2f6dd2c..d00fcb75d32 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1082,6 +1082,176 @@ bool DTCollation::aggregate(DTCollation &dt, uint flags) return 0; } +/******************************/ +static +void my_coll_agg_error(DTCollation &c1, DTCollation &c2, const char *fname) +{ + my_error(ER_CANT_AGGREGATE_2COLLATIONS,MYF(0), + c1.collation->name,c1.derivation_name(), + c2.collation->name,c2.derivation_name(), + fname); +} + + +static +void my_coll_agg_error(DTCollation &c1, DTCollation &c2, DTCollation &c3, + const char *fname) +{ + my_error(ER_CANT_AGGREGATE_3COLLATIONS,MYF(0), + c1.collation->name,c1.derivation_name(), + c2.collation->name,c2.derivation_name(), + c3.collation->name,c3.derivation_name(), + fname); +} + + +static +void my_coll_agg_error(Item** args, uint count, const char *fname) +{ + if (count == 2) + my_coll_agg_error(args[0]->collation, args[1]->collation, fname); + else if (count == 3) + my_coll_agg_error(args[0]->collation, args[1]->collation, + args[2]->collation, fname); + else + my_error(ER_CANT_AGGREGATE_NCOLLATIONS,MYF(0),fname); +} + + +bool agg_item_collations(DTCollation &c, const char *fname, + Item **av, uint count, uint flags) +{ + uint i; + c.set(av[0]->collation); + for (i= 1; i < count; i++) + { + if (c.aggregate(av[i]->collation, flags)) + { + my_coll_agg_error(av, count, fname); + return TRUE; + } + } + if ((flags & MY_COLL_DISALLOW_NONE) && + c.derivation == DERIVATION_NONE) + { + my_coll_agg_error(av, count, fname); + return TRUE; + } + return FALSE; +} + + +bool agg_item_collations_for_comparison(DTCollation &c, const char *fname, + Item **av, uint count, uint flags) +{ + return (agg_item_collations(c, fname, av, count, + flags | MY_COLL_DISALLOW_NONE)); +} + + +/* + Collect arguments' character sets together. + We allow to apply automatic character set conversion in some cases. + The conditions when conversion is possible are: + - arguments A and B have different charsets + - A wins according to coercibility rules + (i.e. a column is stronger than a string constant, + an explicit COLLATE clause is stronger than a column) + - character set of A is either superset for character set of B, + or B is a string constant which can be converted into the + character set of A without data loss. + + If all of the above is true, then it's possible to convert + B into the character set of A, and then compare according + to the collation of A. + + For functions with more than two arguments: + + collect(A,B,C) ::= collect(collect(A,B),C) +*/ + +bool agg_item_charsets(DTCollation &coll, const char *fname, + Item **args, uint nargs, uint flags) +{ + Item **arg, **last, *safe_args[2]; + if (agg_item_collations(coll, fname, args, nargs, flags)) + return TRUE; + + /* + For better error reporting: save the first and the second argument. + We need this only if the the number of args is 3 or 2: + - for a longer argument list, "Illegal mix of collations" + doesn't display each argument's characteristics. + - if nargs is 1, then this error cannot happen. + */ + if (nargs >=2 && nargs <= 3) + { + safe_args[0]= args[0]; + safe_args[1]= args[1]; + } + + THD *thd= current_thd; + Query_arena *arena, backup; + bool res= FALSE; + /* + In case we're in statement prepare, create conversion item + in its memory: it will be reused on each execute. + */ + arena= thd->change_arena_if_needed(&backup); + + for (arg= args, last= args + nargs; arg < last; arg++) + { + Item* conv; + uint32 dummy_offset; + if (!String::needs_conversion(0, coll.collation, + (*arg)->collation.collation, + &dummy_offset)) + continue; + + if (!(conv= (*arg)->safe_charset_converter(coll.collation))) + { + if (nargs >=2 && nargs <= 3) + { + /* restore the original arguments for better error message */ + args[0]= safe_args[0]; + args[1]= safe_args[1]; + } + my_coll_agg_error(args, nargs, fname); + res= TRUE; + break; // we cannot return here, we need to restore "arena". + } + if ((*arg)->type() == Item::FIELD_ITEM) + ((Item_field *)(*arg))->no_const_subst= 1; + /* + If in statement prepare, then we create a converter for two + constant items, do it once and then reuse it. + If we're in execution of a prepared statement, arena is NULL, + and the conv was created in runtime memory. This can be + the case only if the argument is a parameter marker ('?'), + because for all true constants the charset converter has already + been created in prepare. In this case register the change for + rollback. + */ + if (arena) + *arg= conv; + else + thd->change_item_tree(arg, conv); + /* + We do not check conv->fixed, because Item_func_conv_charset which can + be return by safe_charset_converter can't be fixed at creation + */ + conv->fix_fields(thd, arg); + } + if (arena) + thd->restore_backup_item_arena(arena, &backup); + return res; +} + + + + +/**********************************************/ + Item_field::Item_field(Field *f) :Item_ident(0, NullS, *f->table_name, f->field_name), item_equal(0), no_const_subst(0), @@ -2018,6 +2188,7 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry) void Item_param::reset() { + DBUG_ENTER("Item_param::reset"); /* Shrink string buffer if it's bigger than max possible CHAR column */ if (str_value.alloced_length() > MAX_CHAR_WIDTH) str_value.free(); @@ -2042,6 +2213,7 @@ void Item_param::reset() DBUG_ASSERTS(state != NO_VALUE) in all Item_param::get_* methods). */ + DBUG_VOID_RETURN; } diff --git a/sql/item.h b/sql/item.h index 5a1cf193806..f0ffb160553 100644 --- a/sql/item.h +++ b/sql/item.h @@ -767,6 +767,15 @@ public: }; +bool agg_item_collations(DTCollation &c, const char *name, + Item **items, uint nitems, uint flags= 0); +bool agg_item_collations_for_comparison(DTCollation &c, const char *name, + Item **items, uint nitems, + uint flags= 0); +bool agg_item_charsets(DTCollation &c, const char *name, + Item **items, uint nitems, uint flags= 0); + + class Item_num: public Item { public: @@ -1141,7 +1150,7 @@ class Item_uint :public Item_int { public: Item_uint(const char *str_arg, uint length); - Item_uint(uint32 i) :Item_int((ulonglong) i, 10) {} + Item_uint(ulonglong i) :Item_int((ulonglong) i, 10) {} Item_uint(const char *str_arg, longlong i, uint length); double val_real() { DBUG_ASSERT(fixed == 1); return ulonglong2double((ulonglong)value); } @@ -1464,7 +1473,13 @@ public: void save_org_in_field(Field *field) { (*ref)->save_org_in_field(field); } enum Item_result result_type () const { return (*ref)->result_type(); } enum_field_types field_type() const { return (*ref)->field_type(); } - Field *get_tmp_table_field() { return result_field; } + Field *get_tmp_table_field() + { return result_field ? result_field : (*ref)->get_tmp_table_field(); } + Item *get_tmp_table_item(THD *thd) + { + return (result_field ? new Item_field(result_field) : + (*ref)->get_tmp_table_item(thd)); + } table_map used_tables() const { return depended_from ? OUTER_REF_TABLE_BIT : (*ref)->used_tables(); @@ -1702,7 +1717,7 @@ class Cached_item_field :public Cached_item public: Cached_item_field(Item_field *item) { - field=item->field; + field= item->field; buff= (char*) sql_calloc(length=field->pack_length()); } bool cmp(void); diff --git a/sql/item_buff.cc b/sql/item_buff.cc index a67e420170a..9db2f465080 100644 --- a/sql/item_buff.cc +++ b/sql/item_buff.cc @@ -25,9 +25,9 @@ Cached_item *new_Cached_item(THD *thd, Item *item) { - if (item->type() == Item::FIELD_ITEM && - !(((Item_field *) item)->field->flags & BLOB_FLAG)) - return new Cached_item_field((Item_field *) item); + if (item->real_item()->type() == Item::FIELD_ITEM && + !(((Item_field *) (item->real_item()))->field->flags & BLOB_FLAG)) + return new Cached_item_field((Item_field *) (item->real_item())); switch (item->result_type()) { case STRING_RESULT: return new Cached_item_str(thd, (Item_field *) item); diff --git a/sql/item_func.cc b/sql/item_func.cc index 53895cc7331..71e0f29ffc7 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -42,73 +42,6 @@ bool check_reserved_words(LEX_STRING *name) } -static void my_coll_agg_error(DTCollation &c1, DTCollation &c2, - const char *fname) -{ - my_error(ER_CANT_AGGREGATE_2COLLATIONS, MYF(0), - c1.collation->name, c1.derivation_name(), - c2.collation->name, c2.derivation_name(), - fname); -} - -static void my_coll_agg_error(DTCollation &c1, - DTCollation &c2, - DTCollation &c3, - const char *fname) -{ - my_error(ER_CANT_AGGREGATE_3COLLATIONS, MYF(0), - c1.collation->name, c1.derivation_name(), - c2.collation->name, c2.derivation_name(), - c3.collation->name, c3.derivation_name(), - fname); -} - - -static void my_coll_agg_error(Item** args, uint count, const char *fname) -{ - if (count == 2) - my_coll_agg_error(args[0]->collation, args[1]->collation, fname); - else if (count == 3) - my_coll_agg_error(args[0]->collation, - args[1]->collation, - args[2]->collation, - fname); - else - my_error(ER_CANT_AGGREGATE_NCOLLATIONS, MYF(0), fname); -} - - -bool Item_func::agg_arg_collations(DTCollation &c, Item **av, uint count, - uint flags) -{ - uint i; - c.set(av[0]->collation); - for (i= 1; i < count; i++) - { - if (c.aggregate(av[i]->collation, flags)) - { - my_coll_agg_error(av, count, func_name()); - return TRUE; - } - } - if ((flags & MY_COLL_DISALLOW_NONE) && - c.derivation == DERIVATION_NONE) - { - my_coll_agg_error(av, count, func_name()); - return TRUE; - } - return FALSE; -} - - -bool Item_func::agg_arg_collations_for_comparison(DTCollation &c, - Item **av, uint count, - uint flags) -{ - return (agg_arg_collations(c, av, count, flags | MY_COLL_DISALLOW_NONE)); -} - - /* return TRUE if item is a constant */ bool @@ -118,107 +51,6 @@ eval_const_cond(COND *cond) } - -/* - Collect arguments' character sets together. - We allow to apply automatic character set conversion in some cases. - The conditions when conversion is possible are: - - arguments A and B have different charsets - - A wins according to coercibility rules - (i.e. a column is stronger than a string constant, - an explicit COLLATE clause is stronger than a column) - - character set of A is either superset for character set of B, - or B is a string constant which can be converted into the - character set of A without data loss. - - If all of the above is true, then it's possible to convert - B into the character set of A, and then compare according - to the collation of A. - - For functions with more than two arguments: - - collect(A,B,C) ::= collect(collect(A,B),C) -*/ - -bool Item_func::agg_arg_charsets(DTCollation &coll, - Item **args, uint nargs, uint flags) -{ - Item **arg, **last, *safe_args[2]; - if (agg_arg_collations(coll, args, nargs, flags)) - return TRUE; - - /* - For better error reporting: save the first and the second argument. - We need this only if the the number of args is 3 or 2: - - for a longer argument list, "Illegal mix of collations" - doesn't display each argument's characteristics. - - if nargs is 1, then this error cannot happen. - */ - if (nargs >=2 && nargs <= 3) - { - safe_args[0]= args[0]; - safe_args[1]= args[1]; - } - - THD *thd= current_thd; - Query_arena *arena, backup; - bool res= FALSE; - /* - In case we're in statement prepare, create conversion item - in its memory: it will be reused on each execute. - */ - arena= thd->change_arena_if_needed(&backup); - - for (arg= args, last= args + nargs; arg < last; arg++) - { - Item* conv; - uint32 dummy_offset; - if (!String::needs_conversion(0, coll.collation, - (*arg)->collation.collation, - &dummy_offset)) - continue; - - if (!(conv= (*arg)->safe_charset_converter(coll.collation))) - { - if (nargs >=2 && nargs <= 3) - { - /* restore the original arguments for better error message */ - args[0]= safe_args[0]; - args[1]= safe_args[1]; - } - my_coll_agg_error(args, nargs, func_name()); - res= TRUE; - break; // we cannot return here, we need to restore "arena". - } - if ((*arg)->type() == FIELD_ITEM) - ((Item_field *)(*arg))->no_const_subst= 1; - /* - If in statement prepare, then we create a converter for two - constant items, do it once and then reuse it. - If we're in execution of a prepared statement, arena is NULL, - and the conv was created in runtime memory. This can be - the case only if the argument is a parameter marker ('?'), - because for all true constants the charset converter has already - been created in prepare. In this case register the change for - rollback. - */ - if (arena) - *arg= conv; - else - thd->change_item_tree(arg, conv); - /* - We do not check conv->fixed, because Item_func_conv_charset which can - be return by safe_charset_converter can't be fixed at creation - */ - conv->fix_fields(thd, arg); - } - if (arena) - thd->restore_backup_item_arena(arena, &backup); - return res; -} - - - void Item_func::set_arguments(List<Item> &list) { allowed_arg_cols= 1; @@ -1536,8 +1368,6 @@ my_decimal *Item_func_abs::decimal_op(my_decimal *decimal_value) void Item_func_abs::fix_length_and_dec() { Item_func_num1::fix_length_and_dec(); - if (hybrid_type == INT_RESULT) - unsigned_flag= 1; } @@ -4605,7 +4435,7 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, if (component.str == 0 && !my_strcasecmp(system_charset_info, name.str, "VERSION")) - return new Item_string("@@VERSION", server_version, + return new Item_string(NULL, server_version, (uint) strlen(server_version), system_charset_info, DERIVATION_SYSCONST); @@ -4632,28 +4462,10 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, } thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - buff[0]='@'; - buff[1]='@'; - pos=buff+2; - if (var_type == OPT_SESSION) - pos=strmov(pos,"session."); - else if (var_type == OPT_GLOBAL) - pos=strmov(pos,"global."); - set_if_smaller(component_name->length, MAX_SYS_VAR_LENGTH); - set_if_smaller(base_name->length, MAX_SYS_VAR_LENGTH); - - if (component_name->str) - { - memcpy(pos, component_name->str, component_name->length); - pos+= component_name->length; - *pos++= '.'; - } - memcpy(pos, base_name->str, base_name->length); - pos+= base_name->length; return new Item_func_get_system_var(var, var_type, component_name, - buff, pos - buff); + NULL, 0); } @@ -4844,7 +4656,7 @@ Item_func_sp::execute(Item **itp) THD *thd= current_thd; ulong old_client_capabilites; int res= -1; - bool save_in_sub_stmt= thd->transaction.in_sub_stmt; + bool save_in_sub_stmt= thd->in_sub_stmt; my_bool save_no_send_ok; #ifndef NO_EMBEDDED_ACCESS_CHECKS st_sp_security_context save_ctx; @@ -4882,11 +4694,11 @@ Item_func_sp::execute(Item **itp) */ tmp_disable_binlog(thd); /* don't binlog the substatements */ - thd->transaction.in_sub_stmt= TRUE; + thd->in_sub_stmt= TRUE; res= m_sp->execute_function(thd, args, arg_count, itp); - thd->transaction.in_sub_stmt= save_in_sub_stmt; + thd->in_sub_stmt= save_in_sub_stmt; reenable_binlog(thd); if (res && mysql_bin_log.is_open() && (m_sp->m_chistics->daccess == SP_CONTAINS_SQL || diff --git a/sql/item_func.h b/sql/item_func.h index 43a85e6aa0b..e8db9d70ae7 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -43,7 +43,7 @@ public: bool const_item_cache; enum Functype { UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC, GE_FUNC,GT_FUNC,FT_FUNC, - LIKE_FUNC,NOTLIKE_FUNC,ISNULL_FUNC,ISNOTNULL_FUNC, + LIKE_FUNC,ISNULL_FUNC,ISNOTNULL_FUNC, COND_AND_FUNC, COND_OR_FUNC, COND_XOR_FUNC, BETWEEN, IN_FUNC, MULT_EQUAL_FUNC, INTERVAL_FUNC, ISNOTNULLTEST_FUNC, @@ -166,12 +166,22 @@ public: my_decimal *val_decimal(my_decimal *); bool agg_arg_collations(DTCollation &c, Item **items, uint nitems, - uint flags= 0); + uint flags= 0) + { + return agg_item_collations(c, func_name(), items, nitems, flags); + } bool agg_arg_collations_for_comparison(DTCollation &c, Item **items, uint nitems, - uint flags= 0); + uint flags= 0) + { + return agg_item_collations_for_comparison(c, func_name(), + items, nitems, flags); + } bool agg_arg_charsets(DTCollation &c, Item **items, uint nitems, - uint flags= 0); + uint flags= 0) + { + return agg_item_charsets(c, func_name(), items, nitems, flags); + } bool walk(Item_processor processor, byte *arg); Item *transform(Item_transformer transformer, byte *arg); void traverse_cond(Cond_traverser traverser, diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index eb37609c28e..256090d3e61 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2076,7 +2076,6 @@ void Item_func_rpad::fix_length_and_dec() { ulonglong length= ((ulonglong) args[1]->val_int() * collation.collation->mbmaxlen); - length= max((ulonglong) args[0]->max_length, length); if (length >= MAX_BLOB_WIDTH) { length= MAX_BLOB_WIDTH; @@ -2164,7 +2163,6 @@ void Item_func_lpad::fix_length_and_dec() { ulonglong length= ((ulonglong) args[1]->val_int() * collation.collation->mbmaxlen); - length= max((ulonglong) args[0]->max_length, length); if (length >= MAX_BLOB_WIDTH) { length= MAX_BLOB_WIDTH; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index d2f1016891b..bbcea4705fa 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -321,6 +321,22 @@ Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table, field->flags&= ~NOT_NULL_FLAG; return field; } + /* + DATE/TIME fields have STRING_RESULT result types. + In order to preserve field type, it's needed to handle DATE/TIME + fields creations separately. + */ + switch (args[0]->field_type()) { + case MYSQL_TYPE_DATE: + return new Field_date(maybe_null, name, table, collation.collation); + case MYSQL_TYPE_TIME: + return new Field_time(maybe_null, name, table, collation.collation); + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATETIME: + return new Field_datetime(maybe_null, name, table, collation.collation); + default: + break; + } return Item_sum::create_tmp_field(group, table, convert_blob_length); } @@ -2632,7 +2648,11 @@ int group_concat_key_cmp_with_distinct(void* arg, byte* key1, the temporary table, not the original field */ Field *field= (*field_item)->get_tmp_table_field(); - if (field) + /* + If field_item is a const item then either get_tp_table_field returns 0 + or it is an item over a const table. + */ + if (field && !(*field_item)->const_item()) { int res; uint offset= field->offset() - table->s->null_bytes; @@ -2666,8 +2686,11 @@ int group_concat_key_cmp_with_order(void* arg, byte* key1, byte* key2) the temporary table, not the original field */ Field *field= item->get_tmp_table_field(); - /* If the item is a constant, there is no tmp table field */ - if (field) + /* + If item is a const item then either get_tp_table_field returns 0 + or it is an item over a const table. + */ + if (field && !item->const_item()) { int res; uint offset= field->offset() - table->s->null_bytes; @@ -2976,6 +2999,10 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref) maybe_null|= args[i]->maybe_null; } + if (agg_item_charsets(collation, func_name(), + args, arg_count, MY_COLL_ALLOW_CONV)) + return 1; + result_field= 0; null_value= 1; thd->allow_sum_func= 1; diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index aa377d0bdd8..a6fbbee6f23 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1070,7 +1070,7 @@ longlong Item_func_year::val_int() longlong Item_func_unix_timestamp::val_int() { TIME ltime; - bool not_used; + my_bool not_used; DBUG_ASSERT(fixed == 1); if (arg_count == 0) @@ -1798,7 +1798,6 @@ bool Item_func_convert_tz::get_date(TIME *ltime, uint fuzzy_date __attribute__((unused))) { my_time_t my_time_tmp; - bool not_used; String str; if (!from_tz_cached) @@ -1824,6 +1823,7 @@ bool Item_func_convert_tz::get_date(TIME *ltime, ltime->year==TIMESTAMP_MAX_YEAR && ltime->month==1 && ltime->day==1 || ltime->year==TIMESTAMP_MIN_YEAR && ltime->month==12 && ltime->day==31) { + my_bool not_used; my_time_tmp= from_tz->TIME_to_gmt_sec(ltime, ¬_used); if (my_time_tmp >= TIMESTAMP_MIN_VALUE && my_time_tmp <= TIMESTAMP_MAX_VALUE) to_tz->gmt_sec_to_TIME(ltime, my_time_tmp); diff --git a/sql/lock.cc b/sql/lock.cc index 802639b2559..29b6473d7d4 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -99,14 +99,15 @@ static void print_lock_error(int error, const char *); NULL on error. */ +static int thr_lock_errno_to_mysql[]= +{ 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK }; + MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags) { MYSQL_LOCK *sql_lock; TABLE *write_lock_used; int rc; /* Map the return value of thr_lock to an error from errmsg.txt */ - const static int thr_lock_errno_to_mysql[]= - { 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK }; DBUG_ENTER("mysql_lock_tables"); for (;;) @@ -347,7 +348,18 @@ void mysql_lock_abort(THD *thd, TABLE *table) } -/* Abort one thread / table combination */ +/* + Abort one thread / table combination + + SYNOPSIS + mysql_lock_abort_for_thread() + thd Thread handler + table Table that should be removed from lock queue + + RETURN + 0 Table was not locked by another thread + 1 Table was locked by at least one other thread +*/ bool mysql_lock_abort_for_thread(THD *thd, TABLE *table) { @@ -360,10 +372,9 @@ bool mysql_lock_abort_for_thread(THD *thd, TABLE *table) { for (uint i=0; i < locked->lock_count; i++) { - bool found; - found= thr_abort_locks_for_thread(locked->locks[i]->lock, - table->in_use->real_id); - result|= found; + if (thr_abort_locks_for_thread(locked->locks[i]->lock, + table->in_use->real_id)) + result= TRUE; } my_free((gptr) locked,MYF(0)); } @@ -600,6 +611,7 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list) DBUG_RETURN(-1); table->s= &table->share_not_to_be_used; memcpy((table->s->table_cache_key= (char*) (table+1)), key, key_length); + table->s->db= table->s->table_cache_key; table->s->key_length=key_length; table->in_use=thd; table->locked_by_name=1; @@ -611,14 +623,9 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list) DBUG_RETURN(-1); } - { - if (remove_table_from_cache(thd, db, - table_list->table_name, RTFC_NO_FLAG)) - { - DBUG_RETURN(1); // Table is in use - } - } - DBUG_RETURN(0); + /* Return 1 if table is in use */ + DBUG_RETURN(test(remove_table_from_cache(thd, db, table_list->table_name, + RTFC_NO_FLAG))); } @@ -982,7 +989,7 @@ bool make_global_read_lock_block_commit(THD *thd) make_global_read_lock_block_commit(), do nothing. */ if (thd->global_read_lock != GOT_GLOBAL_READ_LOCK) - DBUG_RETURN(1); + DBUG_RETURN(0); pthread_mutex_lock(&LOCK_global_read_lock); /* increment this BEFORE waiting on cond (otherwise race cond) */ global_read_lock_blocks_commit++; diff --git a/sql/log.cc b/sql/log.cc index 1ef522588ff..374b1a7a94e 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -59,7 +59,11 @@ static handlerton binlog_hton = { binlog_prepare, NULL, /* recover */ NULL, /* commit_by_xid */ - NULL /* rollback_by_xid */ + NULL, /* rollback_by_xid */ + NULL, /* create_cursor_read_view */ + NULL, /* set_cursor_read_view */ + NULL, /* close_cursor_read_view */ + HTON_NO_FLAGS }; /* @@ -2211,7 +2215,7 @@ bool flush_error_log() On Windows is necessary a temporary file for to rename the current error file. */ - strmov(strmov(err_temp, err_renamed),"-tmp"); + strxmov(err_temp, err_renamed,"-tmp",NullS); (void) my_delete(err_temp, MYF(0)); if (freopen(err_temp,"a+",stdout)) { diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 710eb30e26b..8add46cc552 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -533,6 +533,7 @@ struct Query_cache_query_flags { unsigned int client_long_flag:1; unsigned int client_protocol_41:1; + unsigned int more_results_exists:1; uint character_set_client_num; uint character_set_results_num; uint collation_connection_num; @@ -844,8 +845,6 @@ bool mysqld_show_column_types(THD *thd); bool mysqld_help (THD *thd, const char *text); void calc_sum_of_all_status(STATUS_VAR *to); - - /* information schema */ extern LEX_STRING information_schema_name; LEX_STRING *make_lex_string(THD *thd, LEX_STRING *lex_str, @@ -973,12 +972,16 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db, const char *table_name); void remove_db_from_cache(const char *db); void flush_tables(); +bool is_equal(const LEX_STRING *a, const LEX_STRING *b); + +/* bits for last argument to remove_table_from_cache() */ #define RTFC_NO_FLAG 0x0000 #define RTFC_OWNED_BY_THD_FLAG 0x0001 #define RTFC_WAIT_OTHER_THREAD_FLAG 0x0002 #define RTFC_CHECK_KILLED_FLAG 0x0004 bool remove_table_from_cache(THD *thd, const char *db, const char *table, uint flags); + bool close_cached_tables(THD *thd, bool wait_for_refresh, TABLE_LIST *tables); void copy_field_from_tmp_record(Field *field,int offset); bool fill_record(THD *thd, Field **field, List<Item> &values, @@ -1176,6 +1179,7 @@ extern uint opt_large_page_size; extern MYSQL_LOG mysql_log,mysql_slow_log,mysql_bin_log; extern FILE *bootstrap_file; extern int bootstrap_error; +extern FILE *stderror_file; extern pthread_key(MEM_ROOT**,THR_MALLOC); extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open, LOCK_thread_count,LOCK_mapped_file,LOCK_user_locks, LOCK_status, @@ -1209,6 +1213,7 @@ extern HASH open_cache; extern TABLE *unused_tables; extern const char* any_db; extern struct my_option my_long_options[]; +extern const LEX_STRING view_type; /* optional things, have_* variables */ @@ -1268,10 +1273,13 @@ void unlock_table_names(THD *thd, TABLE_LIST *table_list, void unireg_init(ulong options); void unireg_end(void); bool mysql_create_frm(THD *thd, my_string file_name, + const char *db, const char *table, HA_CREATE_INFO *create_info, List<create_field> &create_field, uint key_count,KEY *key_info,handler *db_type); -int rea_create_table(THD *thd, my_string file_name,HA_CREATE_INFO *create_info, +int rea_create_table(THD *thd, my_string file_name, + const char *db, const char *table, + HA_CREATE_INFO *create_info, List<create_field> &create_field, uint key_count,KEY *key_info, handler *file); int format_number(uint inputflag,uint max_length,my_string pos,uint length, @@ -1289,7 +1297,7 @@ ulong convert_period_to_month(ulong period); ulong convert_month_to_period(ulong month); void get_date_from_daynr(long daynr,uint *year, uint *month, uint *day); -my_time_t TIME_to_timestamp(THD *thd, const TIME *t, bool *not_exist); +my_time_t TIME_to_timestamp(THD *thd, const TIME *t, my_bool *not_exist); bool str_to_time_with_warn(const char *str,uint length,TIME *l_time); timestamp_type str_to_datetime_with_warn(const char *str, uint length, TIME *l_time, uint flags); @@ -1338,7 +1346,8 @@ ulong make_new_entry(File file,uchar *fileinfo,TYPELIB *formnames, const char *newname); ulong next_io_size(ulong pos); void append_unescaped(String *res, const char *pos, uint length); -int create_frm(THD *thd, char *name,uint reclength,uchar *fileinfo, +int create_frm(THD *thd, char *name, const char *db, const char *table, + uint reclength,uchar *fileinfo, HA_CREATE_INFO *create_info, uint keys); void update_create_info_from_table(HA_CREATE_INFO *info, TABLE *form); int rename_file_ext(const char * from,const char * to,const char * ext); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 89d324a73de..24d308e969c 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -221,21 +221,58 @@ extern "C" int gethostname(char *name, int namelen); /* Constants */ const char *show_comp_option_name[]= {"YES", "NO", "DISABLED"}; -static const char *sql_mode_names[] = +static const char *sql_mode_names[]= { "REAL_AS_FLOAT", "PIPES_AS_CONCAT", "ANSI_QUOTES", "IGNORE_SPACE", "?", "ONLY_FULL_GROUP_BY", "NO_UNSIGNED_SUBTRACTION", "NO_DIR_IN_CREATE", "POSTGRESQL", "ORACLE", "MSSQL", "DB2", "MAXDB", "NO_KEY_OPTIONS", "NO_TABLE_OPTIONS", "NO_FIELD_OPTIONS", "MYSQL323", "MYSQL40", "ANSI", - "NO_AUTO_VALUE_ON_ZERO", "NO_BACKSLASH_ESCAPES", "STRICT_TRANS_TABLES", "STRICT_ALL_TABLES", - "NO_ZERO_IN_DATE", "NO_ZERO_DATE", "ALLOW_INVALID_DATES", "ERROR_FOR_DIVISION_BY_ZERO", + "NO_AUTO_VALUE_ON_ZERO", "NO_BACKSLASH_ESCAPES", "STRICT_TRANS_TABLES", + "STRICT_ALL_TABLES", + "NO_ZERO_IN_DATE", "NO_ZERO_DATE", "ALLOW_INVALID_DATES", + "ERROR_FOR_DIVISION_BY_ZERO", "TRADITIONAL", "NO_AUTO_CREATE_USER", "HIGH_NOT_PRECEDENCE", "NO_ENGINE_SUBSTITUTION", NullS }; +static const unsigned int sql_mode_names_len[]= +{ + /*REAL_AS_FLOAT*/ 13, + /*PIPES_AS_CONCAT*/ 15, + /*ANSI_QUOTES*/ 11, + /*IGNORE_SPACE*/ 12, + /*?*/ 1, + /*ONLY_FULL_GROUP_BY*/ 18, + /*NO_UNSIGNED_SUBTRACTION*/ 23, + /*NO_DIR_IN_CREATE*/ 16, + /*POSTGRESQL*/ 10, + /*ORACLE*/ 6, + /*MSSQL*/ 5, + /*DB2*/ 3, + /*MAXDB*/ 5, + /*NO_KEY_OPTIONS*/ 14, + /*NO_TABLE_OPTIONS*/ 16, + /*NO_FIELD_OPTIONS*/ 16, + /*MYSQL323*/ 8, + /*MYSQL40*/ 7, + /*ANSI*/ 4, + /*NO_AUTO_VALUE_ON_ZERO*/ 21, + /*NO_BACKSLASH_ESCAPES*/ 20, + /*STRICT_TRANS_TABLES*/ 19, + /*STRICT_ALL_TABLES*/ 17, + /*NO_ZERO_IN_DATE*/ 15, + /*NO_ZERO_DATE*/ 12, + /*ALLOW_INVALID_DATES*/ 19, + /*ERROR_FOR_DIVISION_BY_ZERO*/ 26, + /*TRADITIONAL*/ 11, + /*NO_AUTO_CREATE_USER*/ 19, + /*HIGH_NOT_PRECEDENCE*/ 19, + /*NO_ENGINE_SUBSTITUTION*/ 22 +}; TYPELIB sql_mode_typelib= { array_elements(sql_mode_names)-1,"", - sql_mode_names, NULL }; + sql_mode_names, + (unsigned int *)sql_mode_names_len }; static const char *tc_heuristic_recover_names[]= { "COMMIT", "ROLLBACK", NullS @@ -412,6 +449,7 @@ Le_creator le_creator; FILE *bootstrap_file; int bootstrap_error; +FILE *stderror_file=0; I_List<THD> threads; I_List<NAMED_LIST> key_caches; @@ -2317,6 +2355,19 @@ static int my_message_sql(uint error, const char *str, myf MyFlags) DBUG_RETURN(0); } + +static void *my_str_malloc_mysqld(size_t size) +{ + return my_malloc(size, MYF(MY_FAE)); +} + + +static void my_str_free_mysqld(void *ptr) +{ + my_free((gptr)ptr, MYF(MY_FAE)); +} + + #ifdef __WIN__ struct utsname @@ -2598,6 +2649,50 @@ static int init_common_variables(const char *conf_file_name, int argc, if (my_dbopt_init()) return 1; + /* + Ensure that lower_case_table_names is set on system where we have case + insensitive names. If this is not done the users MyISAM tables will + get corrupted if accesses with names of different case. + */ + DBUG_PRINT("info", ("lower_case_table_names: %d", lower_case_table_names)); + if (!lower_case_table_names && + (lower_case_file_system= + (test_if_case_insensitive(mysql_real_data_home) == 1))) + { + if (lower_case_table_names_used) + { + if (global_system_variables.log_warnings) + sql_print_warning("\ +You have forced lower_case_table_names to 0 through a command-line \ +option, even though your file system '%s' is case insensitive. This means \ +that you can corrupt a MyISAM table by accessing it with different cases. \ +You should consider changing lower_case_table_names to 1 or 2", + mysql_real_data_home); + } + else + { + if (global_system_variables.log_warnings) + sql_print_warning("Setting lower_case_table_names=2 because file system for %s is case insensitive", mysql_real_data_home); + lower_case_table_names= 2; + } + } + else if (lower_case_table_names == 2 && + !(lower_case_file_system= + (test_if_case_insensitive(mysql_real_data_home) == 1))) + { + if (global_system_variables.log_warnings) + sql_print_warning("lower_case_table_names was set to 2, even though your " + "the file system '%s' is case sensitive. Now setting " + "lower_case_table_names to 0 to avoid future problems.", + mysql_real_data_home); + lower_case_table_names= 0; + } + + /* Reset table_alias_charset, now that lower_case_table_names is set. */ + table_alias_charset= (lower_case_table_names ? + files_charset_info : + &my_charset_bin); + return 0; } @@ -2782,7 +2877,7 @@ server."); #ifndef EMBEDDED_LIBRARY if (freopen(log_error_file, "a+", stdout)) #endif - freopen(log_error_file, "a+", stderr); + stderror_file= freopen(log_error_file, "a+", stderr); } } @@ -3084,50 +3179,6 @@ int main(int argc, char **argv) (void) thr_setconcurrency(concurrency); // 10 by default - /* - Ensure that lower_case_table_names is set on system where we have case - insensitive names. If this is not done the users MyISAM tables will - get corrupted if accesses with names of different case. - */ - DBUG_PRINT("info", ("lower_case_table_names: %d", lower_case_table_names)); - if (!lower_case_table_names && - (lower_case_file_system= - (test_if_case_insensitive(mysql_real_data_home) == 1))) - { - if (lower_case_table_names_used) - { - if (global_system_variables.log_warnings) - sql_print_warning("\ -You have forced lower_case_table_names to 0 through a command-line \ -option, even though your file system '%s' is case insensitive. This means \ -that you can corrupt a MyISAM table by accessing it with different cases. \ -You should consider changing lower_case_table_names to 1 or 2", - mysql_real_data_home); - } - else - { - if (global_system_variables.log_warnings) - sql_print_warning("Setting lower_case_table_names=2 because file system for %s is case insensitive", mysql_real_data_home); - lower_case_table_names= 2; - } - } - else if (lower_case_table_names == 2 && - !(lower_case_file_system= - (test_if_case_insensitive(mysql_real_data_home) == 1))) - { - if (global_system_variables.log_warnings) - sql_print_warning("lower_case_table_names was set to 2, even though your " - "the file system '%s' is case sensitive. Now setting " - "lower_case_table_names to 0 to avoid future problems.", - mysql_real_data_home); - lower_case_table_names= 0; - } - - /* Reset table_alias_charset, now that lower_case_table_names is set. */ - table_alias_charset= (lower_case_table_names ? - files_charset_info : - &my_charset_bin); - select_thread=pthread_self(); select_thread_in_use=1; init_ssl(); @@ -3183,10 +3234,16 @@ we force server id to 2, but this MySQL server will not act as a slave."); #endif /* + Initialize my_str_malloc() and my_str_free() + */ + my_str_malloc= &my_str_malloc_mysqld; + my_str_free= &my_str_free_mysqld; + + /* init signals & alarm After this we can't quit by a simple unireg_abort */ - error_handler_hook = my_message_sql; + error_handler_hook= my_message_sql; start_signal_handler(); // Creates pidfile if (acl_init((THD *)0, opt_noacl) || my_tz_init((THD *)0, default_tz_name, opt_bootstrap)) diff --git a/sql/parse_file.cc b/sql/parse_file.cc index abca8736916..82ce2f2d7b5 100644 --- a/sql/parse_file.cc +++ b/sql/parse_file.cc @@ -166,6 +166,25 @@ write_parameter(IO_CACHE *file, gptr base, File_option *parameter, } break; } + case FILE_OPTIONS_ULLLIST: + { + List_iterator_fast<ulonglong> it(*((List<ulonglong>*) + (base + parameter->offset))); + bool first= 1; + ulonglong *val; + while ((val= it++)) + { + num.set(*val, &my_charset_bin); + // We need ' ' after string to detect list continuation + if ((!first && my_b_append(file, (const byte *)" ", 1)) || + my_b_append(file, (const byte *)num.ptr(), num.length())) + { + DBUG_RETURN(TRUE); + } + first= 0; + } + break; + } default: DBUG_ASSERT(0); // never should happened } @@ -615,6 +634,8 @@ File_parser::parse(gptr base, MEM_ROOT *mem_root, char *eol; LEX_STRING *str; List<LEX_STRING> *list; + ulonglong *num; + List<ulonglong> *nlist; DBUG_ENTER("File_parser::parse"); while (ptr < end && found < required) @@ -719,7 +740,7 @@ File_parser::parse(gptr base, MEM_ROOT *mem_root, case FILE_OPTIONS_STRLIST: { list= (List<LEX_STRING>*)(base + parameter->offset); - + list->empty(); // list parsing while (ptr < end) @@ -741,17 +762,56 @@ File_parser::parse(gptr base, MEM_ROOT *mem_root, goto list_err_w_message; } } - end_of_list: + +end_of_list: if (*(ptr++) != '\n') goto list_err; break; - list_err_w_message: +list_err_w_message: my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0), parameter->name.str, line); - list_err: +list_err: DBUG_RETURN(TRUE); } + case FILE_OPTIONS_ULLLIST: + { + nlist= (List<ulonglong>*)(base + parameter->offset); + nlist->empty(); + // list parsing + while (ptr < end) + { + int not_used; + char *num_end= end; + if (!(num= (ulonglong*)alloc_root(mem_root, sizeof(ulonglong))) || + nlist->push_back(num, mem_root)) + goto nlist_err; + *num= my_strtoll10(ptr, &num_end, ¬_used); + ptr= num_end; + switch (*ptr) { + case '\n': + goto end_of_nlist; + case ' ': + // we cant go over buffer bounds, because we have \0 at the end + ptr++; + break; + default: + goto nlist_err_w_message; + } + } + +end_of_nlist: + if (*(ptr++) != '\n') + goto nlist_err; + break; + +nlist_err_w_message: + my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0), + parameter->name.str, line); +nlist_err: + DBUG_RETURN(TRUE); + + } default: DBUG_ASSERT(0); // never should happened } diff --git a/sql/parse_file.h b/sql/parse_file.h index 82a89dffd18..cc0aa6556f6 100644 --- a/sql/parse_file.h +++ b/sql/parse_file.h @@ -27,8 +27,10 @@ enum file_opt_type { FILE_OPTIONS_REV, /* Revision version number (ulonglong) */ FILE_OPTIONS_TIMESTAMP, /* timestamp (LEX_STRING have to be allocated with length 20 (19+1) */ - FILE_OPTIONS_STRLIST /* list of escaped strings + FILE_OPTIONS_STRLIST, /* list of escaped strings (List<LEX_STRING>) */ + FILE_OPTIONS_ULLLIST /* list of ulonglong values + (List<ulonglong>) */ }; struct File_option diff --git a/sql/protocol_cursor.cc b/sql/protocol_cursor.cc index ed2d0b583d0..093a2bf2b90 100644 --- a/sql/protocol_cursor.cc +++ b/sql/protocol_cursor.cc @@ -114,8 +114,7 @@ bool Protocol_cursor::write() for (; cur_field < fields_end; cur_field++, data_tmp++) { - if ((len= net_field_length((uchar **)&cp)) == 0 || - len == NULL_LENGTH) + if ((len= net_field_length((uchar **)&cp)) == NULL_LENGTH) { *data_tmp= 0; } diff --git a/sql/set_var.cc b/sql/set_var.cc index c0102b8be1b..d5c3496eae6 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -1431,6 +1431,12 @@ bool sys_var_thd_ulong::update(THD *thd, set_var *var) if ((ulong) tmp > max_system_variables.*offset) tmp= max_system_variables.*offset; +#if SIZEOF_LONG == 4 + /* Avoid overflows on 32 bit systems */ + if (tmp > (ulonglong) ~(ulong) 0) + tmp= ((ulonglong) ~(ulong) 0); +#endif + if (option_limits) tmp= (ulong) getopt_ull_limit_value(tmp, option_limits); if (var->type == OPT_GLOBAL) @@ -1684,7 +1690,7 @@ Item *sys_var::item(THD *thd, enum_var_type var_type, LEX_STRING *base) pthread_mutex_lock(&LOCK_global_system_variables); value= *(uint*) value_ptr(thd, var_type, base); pthread_mutex_unlock(&LOCK_global_system_variables); - return new Item_uint((int32) value); + return new Item_uint((ulonglong) value); } case SHOW_LONG: { @@ -1692,7 +1698,7 @@ Item *sys_var::item(THD *thd, enum_var_type var_type, LEX_STRING *base) pthread_mutex_lock(&LOCK_global_system_variables); value= *(ulong*) value_ptr(thd, var_type, base); pthread_mutex_unlock(&LOCK_global_system_variables); - return new Item_uint((int32) value); + return new Item_uint((ulonglong) value); } case SHOW_LONGLONG: { @@ -3201,27 +3207,50 @@ bool sys_var_thd_table_type::update(THD *thd, set_var *var) Functions to handle sql_mode ****************************************************************************/ -byte *sys_var_thd_sql_mode::value_ptr(THD *thd, enum_var_type type, - LEX_STRING *base) +/* + Make string representation of mode + + SYNOPSIS + thd in thread handler + val in sql_mode value + len out pointer on length of string + + RETURN + pointer to string with sql_mode representation +*/ + +byte *sys_var_thd_sql_mode::symbolic_mode_representation(THD *thd, ulong val, + ulong *len) { - ulong val; char buff[256]; String tmp(buff, sizeof(buff), &my_charset_latin1); + ulong length; tmp.length(0); - val= ((type == OPT_GLOBAL) ? global_system_variables.*offset : - thd->variables.*offset); for (uint i= 0; val; val>>= 1, i++) { if (val & 1) { - tmp.append(enum_names->type_names[i]); + tmp.append(sql_mode_typelib.type_names[i], + sql_mode_typelib.type_lengths[i]); tmp.append(','); } } - if (tmp.length()) - tmp.length(tmp.length() - 1); - return (byte*) thd->strmake(tmp.ptr(), tmp.length()); + + if ((length= tmp.length())) + length--; + *len= length; + return (byte*) thd->strmake(tmp.ptr(), length); +} + + +byte *sys_var_thd_sql_mode::value_ptr(THD *thd, enum_var_type type, + LEX_STRING *base) +{ + ulong val= ((type == OPT_GLOBAL) ? global_system_variables.*offset : + thd->variables.*offset); + ulong length_unused; + return symbolic_mode_representation(thd, val, &length_unused); } @@ -3233,6 +3262,7 @@ void sys_var_thd_sql_mode::set_default(THD *thd, enum_var_type type) thd->variables.*offset= global_system_variables.*offset; } + void fix_sql_mode_var(THD *thd, enum_var_type type) { if (type == OPT_GLOBAL) diff --git a/sql/set_var.h b/sql/set_var.h index 3462908a5d9..b1c847973c1 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -361,6 +361,8 @@ public: } void set_default(THD *thd, enum_var_type type); byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); + static byte *symbolic_mode_representation(THD *thd, ulong sql_mode, + ulong *length); }; diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index d6108a8d436..b0dc75b98dc 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5462,3 +5462,11 @@ ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF ER_NO_PARTS_ERROR eng "Number of %s = 0 is not an allowed value" swe "Antal %s = 0 är inte ett tillåten värde" +ER_CANT_CREATE_FEDERATED_TABLE + eng "Can't create federated table. Foreign data src error : '%-.64s'" +ER_TRG_IN_WRONG_SCHEMA + eng "Trigger in wrong schema" +ER_STACK_OVERRUN_NEED_MORE + eng "Thread stack overrun: %ld bytes used of a %ld byte stack, and %ld bytes needed. Use 'mysqld -O thread_stack=#' to specify a bigger stack." +ER_TOO_LONG_BODY 42000 S1009 + eng "Routine body for '%-.100s' is too long" diff --git a/sql/slave.cc b/sql/slave.cc index e86fbbeece4..2e751f9ddab 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2018,7 +2018,8 @@ bool show_master_info(THD* thd, MASTER_INFO* mi) &my_charset_bin); protocol->store((ulonglong) mi->rli.group_relay_log_pos); protocol->store(mi->rli.group_master_log_name, &my_charset_bin); - protocol->store(mi->slave_running ? "Yes":"No", &my_charset_bin); + protocol->store(mi->slave_running == MYSQL_SLAVE_RUN_CONNECT + ? "Yes":"No", &my_charset_bin); protocol->store(mi->rli.slave_running ? "Yes":"No", &my_charset_bin); protocol->store(rpl_filter->get_do_db()); protocol->store(rpl_filter->get_ignore_db()); @@ -3254,9 +3255,9 @@ err: mi->abort_slave = 0; // TODO: check if this is needed DBUG_ASSERT(thd->net.buff != 0); net_end(&thd->net); // destructor will not free it, because net.vio is 0 + close_thread_tables(thd, 0); pthread_mutex_lock(&LOCK_thread_count); THD_CHECK_SENTRY(thd); - close_thread_tables(thd); delete thd; pthread_mutex_unlock(&LOCK_thread_count); pthread_cond_broadcast(&mi->stop_cond); // tell the world we are done diff --git a/sql/sp.cc b/sql/sp.cc index 0c9af895fb6..e45a360e85b 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -503,6 +503,11 @@ db_create_routine(THD *thd, int type, sp_head *sp) ret= SP_BAD_IDENTIFIER; goto done; } + if (sp->m_body.length > table->field[MYSQL_PROC_FIELD_BODY]->field_length) + { + ret= SP_BODY_TOO_LONG; + goto done; + } table->field[MYSQL_PROC_FIELD_DB]-> store(sp->m_db.str, sp->m_db.length, system_charset_info); table->field[MYSQL_PROC_FIELD_NAME]-> @@ -1176,6 +1181,43 @@ extern "C" byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first) /* + Check if + - current statement (the one in thd->lex) needs table prelocking + - first routine in thd->lex->sroutines_list needs to execute its body in + prelocked mode. + + SYNOPSIS + sp_get_prelocking_info() + thd Current thread, thd->lex is the statement to be + checked. + need_prelocking OUT TRUE - prelocked mode should be activated + before executing the statement + FALSE - Don't activate prelocking + first_no_prelocking OUT TRUE - Tables used by first routine in + thd->lex->sroutines_list should be + prelocked. + FALSE - Otherwise. + NOTES + This function assumes that for any "CALL proc(...)" statement routines_list + will have 'proc' as first element (it may have several, consider e.g. + "proc(sp_func(...)))". This property is currently guaranted by the parser. +*/ + +void sp_get_prelocking_info(THD *thd, bool *need_prelocking, + bool *first_no_prelocking) +{ + Sroutine_hash_entry *routine; + routine= (Sroutine_hash_entry*)thd->lex->sroutines_list.first; + + DBUG_ASSERT(routine); + bool first_is_procedure= (routine->key.str[0] == TYPE_ENUM_PROCEDURE); + + *first_no_prelocking= first_is_procedure; + *need_prelocking= !first_is_procedure || test(routine->next); +} + + +/* Auxilary function that adds new element to the set of stored routines used by statement. @@ -1312,11 +1354,13 @@ static void sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src) SYNOPSIS sp_cache_routines_and_add_tables_aux() - thd - thread context - lex - LEX representing statement - start - first routine from the list of routines to be cached - (this list defines mentioned sub-set). - + thd - thread context + lex - LEX representing statement + start - first routine from the list of routines to be cached + (this list defines mentioned sub-set). + first_no_prelock - If true, don't add tables or cache routines used by + the body of the first routine (i.e. *start) + will be executed in non-prelocked mode. NOTE If some function is missing this won't be reported here. Instead this fact will be discovered during query execution. @@ -1328,10 +1372,11 @@ static void sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src) static bool sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, - Sroutine_hash_entry *start) + Sroutine_hash_entry *start, + bool first_no_prelock) { bool result= FALSE; - + bool first= TRUE; DBUG_ENTER("sp_cache_routines_and_add_tables_aux"); for (Sroutine_hash_entry *rt= start; rt; rt= rt->next) @@ -1367,9 +1412,13 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, } if (sp) { - sp_update_stmt_used_routines(thd, lex, &sp->m_sroutines); - result|= sp->add_used_tables_to_table_list(thd, &lex->query_tables_last); + if (!(first && first_no_prelock)) + { + sp_update_stmt_used_routines(thd, lex, &sp->m_sroutines); + result|= sp->add_used_tables_to_table_list(thd, &lex->query_tables_last); + } } + first= FALSE; } DBUG_RETURN(result); } @@ -1382,20 +1431,22 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, SYNOPSIS sp_cache_routines_and_add_tables() - thd - thread context - lex - LEX representing statement - + thd - thread context + lex - LEX representing statement + first_no_prelock - If true, don't add tables or cache routines used by + the body of the first routine (i.e. *start) + RETURN VALUE TRUE - some tables were added FALSE - no tables were added. */ bool -sp_cache_routines_and_add_tables(THD *thd, LEX *lex) +sp_cache_routines_and_add_tables(THD *thd, LEX *lex, bool first_no_prelock) { - return sp_cache_routines_and_add_tables_aux(thd, lex, - (Sroutine_hash_entry *)lex->sroutines_list.first); + (Sroutine_hash_entry *)lex->sroutines_list.first, + first_no_prelock); } @@ -1417,8 +1468,8 @@ sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, LEX *aux_lex) Sroutine_hash_entry **last_cached_routine_ptr= (Sroutine_hash_entry **)lex->sroutines_list.next; sp_update_stmt_used_routines(thd, lex, &aux_lex->sroutines); - (void)sp_cache_routines_and_add_tables_aux(thd, lex, - *last_cached_routine_ptr); + (void)sp_cache_routines_and_add_tables_aux(thd, lex, + *last_cached_routine_ptr, FALSE); } @@ -1443,7 +1494,9 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, Sroutine_hash_entry **last_cached_routine_ptr= (Sroutine_hash_entry **)lex->sroutines_list.next; for (int i= 0; i < (int)TRG_EVENT_MAX; i++) + { for (int j= 0; j < (int)TRG_ACTION_MAX; j++) + { if (triggers->bodies[i][j]) { (void)triggers->bodies[i][j]->add_used_tables_to_table_list(thd, @@ -1451,9 +1504,11 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, sp_update_stmt_used_routines(thd, lex, &triggers->bodies[i][j]->m_sroutines); } - + } + } (void)sp_cache_routines_and_add_tables_aux(thd, lex, - *last_cached_routine_ptr); + *last_cached_routine_ptr, + FALSE); } } @@ -29,6 +29,7 @@ #define SP_INTERNAL_ERROR -7 #define SP_NO_DB_ERROR -8 #define SP_BAD_IDENTIFIER -9 +#define SP_BODY_TOO_LONG -10 /* Drop all routines in database 'db' */ int @@ -79,10 +80,13 @@ sp_show_status_function(THD *thd, const char *wild); Procedures for pre-caching of stored routines and building table list for prelocking. */ +void sp_get_prelocking_info(THD *thd, bool *need_prelocking, + bool *first_no_prelocking); void sp_add_used_routine(LEX *lex, Query_arena *arena, sp_name *rt, char rt_type); void sp_update_sp_used_routines(HASH *dst, HASH *src); -bool sp_cache_routines_and_add_tables(THD *thd, LEX *lex); +bool sp_cache_routines_and_add_tables(THD *thd, LEX *lex, + bool first_no_prelock); void sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, LEX *aux_lex); void sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, diff --git a/sql/sp_cache.h b/sql/sp_cache.h index e9efe5b2a8c..14b2db97f5f 100644 --- a/sql/sp_cache.h +++ b/sql/sp_cache.h @@ -22,6 +22,13 @@ #pragma interface /* gcc class implementation */ #endif +/* + Stored procedures/functions cache. This is used as follows: + * Each thread has its own cache. + * Each sp_head object is put into its thread cache before it is used, and + then remains in the cache until deleted. +*/ + class sp_head; class sp_cache; @@ -31,16 +38,20 @@ void sp_cache_init(); /* Clear the cache *cp and set *cp to NULL */ void sp_cache_clear(sp_cache **cp); -/* Insert an SP to cache. If 'cp' points to NULL, it's set to a new cache */ +/* Insert an SP into cache. If 'cp' points to NULL, it's set to a new cache */ void sp_cache_insert(sp_cache **cp, sp_head *sp); /* Lookup an SP in cache */ sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name); -/* Remove an SP from cache. Returns true if something was removed */ +/* + Remove an SP from cache, and also bump the Cversion number so all other + caches are invalidated. + Returns true if something was removed. +*/ bool sp_cache_remove(sp_cache **cp, sp_name *name); -/* Invalidate a cache */ +/* Invalidate all existing SP caches by bumping Cversion number. */ void sp_cache_invalidate(); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 02c006d01ee..3a386356335 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -574,6 +574,7 @@ sp_head::execute(THD *thd) sp_rcontext *ctx; int ret= 0; uint ip= 0; + ulong save_sql_mode; Query_arena *old_arena; query_id_t old_query_id; TABLE *old_derived_tables; @@ -626,6 +627,8 @@ sp_head::execute(THD *thd) old_query_id= thd->query_id; old_derived_tables= thd->derived_tables; thd->derived_tables= 0; + save_sql_mode= thd->variables.sql_mode; + thd->variables.sql_mode= m_sql_mode; /* It is also more efficient to save/restore current thd->lex once when do it in each instruction @@ -715,6 +718,7 @@ sp_head::execute(THD *thd) thd->query_id= old_query_id; DBUG_ASSERT(!thd->derived_tables); thd->derived_tables= old_derived_tables; + thd->variables.sql_mode= save_sql_mode; thd->current_arena= old_arena; state= EXECUTED; @@ -879,7 +883,10 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) octx= new sp_rcontext(csize, hmax, cmax); tmp_octx= TRUE; } + + /* Evaluate SP arguments (i.e. get the values passed as parameters) */ // QQ: Should do type checking? + DBUG_PRINT("info",(" %.*s: eval args", m_name.length, m_name.str)); for (i = 0 ; (it= li++) && i < params ; i++) { sp_pvar_t *pvar= m_pcont->find_pvar(i); @@ -916,6 +923,15 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) } } + /* + Okay, got values for all arguments. Close tables that might be used by + arguments evaluation. If arguments evaluation required prelocking mode, + we'll leave it here. + */ + if (!thd->in_sub_stmt) + close_thread_tables(thd, 0, 0, 0); + + DBUG_PRINT("info",(" %.*s: eval args done", m_name.length, m_name.str)); // The rest of the frame are local variables which are all IN. // Default all variables to null (those with default clauses will // be set by an set instruction). @@ -1245,8 +1261,6 @@ sp_head::show_create_procedure(THD *thd) String buffer(buff, sizeof(buff), system_charset_info); int res; List<Item> field_list; - ulong old_sql_mode; - sys_var *sql_mode_var; byte *sql_mode_str; ulong sql_mode_len; bool full_access; @@ -1258,19 +1272,13 @@ sp_head::show_create_procedure(THD *thd) if (check_show_routine_access(thd, this, &full_access)) return 1; - - old_sql_mode= thd->variables.sql_mode; - thd->variables.sql_mode= m_sql_mode; - sql_mode_var= find_sys_var("SQL_MODE", 8); - if (sql_mode_var) - { - sql_mode_str= sql_mode_var->value_ptr(thd, OPT_SESSION, 0); - sql_mode_len= strlen((char*) sql_mode_str); - } + sql_mode_str= + sys_var_thd_sql_mode::symbolic_mode_representation(thd, + m_sql_mode, + &sql_mode_len); field_list.push_back(new Item_empty_string("Procedure", NAME_LEN)); - if (sql_mode_var) - field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len)); + field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len)); // 1024 is for not to confuse old clients field_list.push_back(new Item_empty_string("Create Procedure", max(buffer.length(), 1024))); @@ -1282,15 +1290,13 @@ sp_head::show_create_procedure(THD *thd) } protocol->prepare_for_resend(); protocol->store(m_name.str, m_name.length, system_charset_info); - if (sql_mode_var) - protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info); + protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info); if (full_access) protocol->store(m_defstr.str, m_defstr.length, system_charset_info); res= protocol->write(); send_eof(thd); done: - thd->variables.sql_mode= old_sql_mode; DBUG_RETURN(res); } @@ -1326,8 +1332,6 @@ sp_head::show_create_function(THD *thd) String buffer(buff, sizeof(buff), system_charset_info); int res; List<Item> field_list; - ulong old_sql_mode; - sys_var *sql_mode_var; byte *sql_mode_str; ulong sql_mode_len; bool full_access; @@ -1339,18 +1343,12 @@ sp_head::show_create_function(THD *thd) if (check_show_routine_access(thd, this, &full_access)) return 1; - old_sql_mode= thd->variables.sql_mode; - thd->variables.sql_mode= m_sql_mode; - sql_mode_var= find_sys_var("SQL_MODE", 8); - if (sql_mode_var) - { - sql_mode_str= sql_mode_var->value_ptr(thd, OPT_SESSION, 0); - sql_mode_len= strlen((char*) sql_mode_str); - } - + sql_mode_str= + sys_var_thd_sql_mode::symbolic_mode_representation(thd, + m_sql_mode, + &sql_mode_len); field_list.push_back(new Item_empty_string("Function",NAME_LEN)); - if (sql_mode_var) - field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len)); + field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len)); field_list.push_back(new Item_empty_string("Create Function", max(buffer.length(),1024))); if (protocol->send_fields(&field_list, @@ -1361,15 +1359,13 @@ sp_head::show_create_function(THD *thd) } protocol->prepare_for_resend(); protocol->store(m_name.str, m_name.length, system_charset_info); - if (sql_mode_var) - protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info); + protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info); if (full_access) protocol->store(m_defstr.str, m_defstr.length, system_charset_info); res= protocol->write(); send_eof(thd); done: - thd->variables.sql_mode= old_sql_mode; DBUG_RETURN(res); } @@ -1480,8 +1476,27 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, implemented at the same time as ability not to store LEX for instruction if it is not really used. */ - reinit_stmt_before_use(thd, m_lex); + if (thd->prelocked_mode == NON_PRELOCKED) + { + /* + This statement will enter/leave prelocked mode on its own. + Entering prelocked mode changes table list and related members + of LEX, so we'll need to restore them. + */ + if (lex_query_tables_own_last) + { + /* + We've already entered/left prelocked mode with this statement. + Attach the list of tables that need to be prelocked and mark m_lex + as having such list attached. + */ + *lex_query_tables_own_last= prelocking_tables; + m_lex->mark_as_requiring_prelocking(lex_query_tables_own_last); + } + } + + reinit_stmt_before_use(thd, m_lex); /* If requested check whenever we have access to tables in LEX's table list and open and lock them before executing instructtions core function. @@ -1499,6 +1514,26 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, thd->proc_info="closing tables"; close_thread_tables(thd); + if (m_lex->query_tables_own_last) + { + /* + We've entered and left prelocking mode when executing statement + stored in m_lex. + m_lex->query_tables(->next_global)* list now has a 'tail' - a list + of tables that are added for prelocking. (If this is the first + execution, the 'tail' was added by open_tables(), otherwise we've + attached it above in this function). + Now we'll save the 'tail', and detach it. + */ + DBUG_ASSERT(!lex_query_tables_own_last || + lex_query_tables_own_last == m_lex->query_tables_own_last && + prelocking_tables == *(m_lex->query_tables_own_last)); + + lex_query_tables_own_last= m_lex->query_tables_own_last; + prelocking_tables= *lex_query_tables_own_last; + *lex_query_tables_own_last= NULL; + m_lex->mark_as_requiring_prelocking(NULL); + } thd->rollback_item_tree_changes(); /* diff --git a/sql/sp_head.h b/sql/sp_head.h index 32dc4449174..e15b68be158 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -121,7 +121,7 @@ public: uchar *m_tmp_query; // Temporary pointer to sub query string uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value st_sp_chistics *m_chistics; - ulong m_sql_mode; // For SHOW CREATE + ulong m_sql_mode; // For SHOW CREATE and execution LEX_STRING m_qname; // db.name LEX_STRING m_db; LEX_STRING m_name; @@ -282,6 +282,10 @@ private: /* Multi-set representing optimized list of tables to be locked by this routine. Does not include tables which are used by invoked routines. + + Note: for prelocking-free SPs this multiset is constructed too. + We do so because the same instance of sp_head may be called both + in prelocked mode and in non-prelocked mode. */ HASH m_sptabs; @@ -383,7 +387,8 @@ class sp_lex_keeper public: sp_lex_keeper(LEX *lex, bool lex_resp) - : m_lex(lex), m_lex_resp(lex_resp) + : m_lex(lex), m_lex_resp(lex_resp), + lex_query_tables_own_last(NULL) { lex->sp_lex_in_use= TRUE; } @@ -418,6 +423,25 @@ private: for LEX deletion. */ bool m_lex_resp; + + /* + Support for being able to execute this statement in two modes: + a) inside prelocked mode set by the calling procedure or its ancestor. + b) outside of prelocked mode, when this statement enters/leaves + prelocked mode itself. + */ + + /* + List of additional tables this statement needs to lock when it + enters/leaves prelocked mode on its own. + */ + TABLE_LIST *prelocking_tables; + + /* + The value m_lex->query_tables_own_last should be set to this when the + statement enters/leaves prelocked mode on its own. + */ + TABLE_LIST **lex_query_tables_own_last; }; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index fdb7f7f069c..5d2f5d55a7b 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3547,9 +3547,9 @@ bool check_grant_db(THD *thd,const char *db) { char helping [NAME_LEN+USERNAME_LENGTH+2]; uint len; - bool error=1; + bool error= 1; - len = (uint) (strmov(strmov(helping,thd->priv_user)+1,db)-helping)+ 1; + len= (uint) (strmov(strmov(helping,thd->priv_user)+1,db)-helping)+ 1; rw_rdlock(&LOCK_grant); for (uint idx=0 ; idx < column_priv_hash.records ; idx++) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 710bb2c0a85..dfed0e36b70 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -391,6 +391,8 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table) LOCK_open skip_derived Set to 1 (0 = default) if we should not free derived tables. + stopper When closing tables from thd->open_tables(->next)*, + don't close/remove tables starting from stopper. IMPLEMENTATION Unlocks tables and frees derived tables. @@ -474,6 +476,7 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived, We are in prelocked mode, so we have to leave it now with doing implicit UNLOCK TABLES if need. */ + DBUG_PRINT("info",("thd->prelocked_mode= NON_PRELOCKED")); thd->prelocked_mode= NON_PRELOCKED; if (prelocked_mode == PRELOCKED_UNDER_LOCK_TABLES) @@ -1043,26 +1046,26 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, if (thd->locked_tables || thd->prelocked_mode) { // Using table locks TABLE *best_table= 0; - int best_distance= INT_MIN, distance; + int best_distance= INT_MIN; for (table=thd->open_tables; table ; table=table->next) { if (table->s->key_length == key_length && !memcmp(table->s->table_cache_key, key, key_length) && !my_strcasecmp(system_charset_info, table->alias, alias) && - table->query_id != thd->query_id && /* skip tables already used by this query */ + table->query_id != thd->query_id && /* skip tables already used */ !(thd->prelocked_mode && table->query_id)) { - distance= ((int) table->reginfo.lock_type - - (int) table_list->lock_type); + int distance= ((int) table->reginfo.lock_type - + (int) table_list->lock_type); /* Find a table that either has the exact lock type requested, or has the best suitable lock. In case there is no locked table that has an equal or higher lock than requested, - we still maitain the best_table to produce an error message - about wrong lock mode on the table. The best_table is changed + we us the closest matching lock to be able to produce an error + message about wrong lock mode on the table. The best_table is changed if bd < 0 <= d or bd < d < 0 or 0 <= d < bd. - distance < 0 - we have not enough high lock mode + distance < 0 - No suitable lock found distance > 0 - we have lock mode higher then we require distance == 0 - we have lock mode exactly which we need */ @@ -1071,7 +1074,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, { best_distance= distance; best_table= table; - if (best_distance == 0) + if (best_distance == 0) // Found perfect lock break; } } @@ -1792,6 +1795,7 @@ err: DBUG_RETURN(1); } + /* Open all tables in list @@ -1843,10 +1847,6 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter) statement for which table list for prelocking is already built, let us cache routines and try to build such table list. - NOTE: If we want queries with functions to work under explicit - LOCK TABLES we have to additionaly lock mysql.proc table in it. - At least until Monty will fix SP loading :) - NOTE: We can't delay prelocking until we will met some sub-statement which really uses tables, since this will imply that we have to restore its table list to be able execute it in some other context. @@ -1860,16 +1860,23 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter) mode we will have some locked tables, because queries which use only derived/information schema tables and views possible. Thus "counter" may be still zero for prelocked statement... + + NOTE: The above notes may be out of date. Please wait for psergey to + document new prelocked behavior. */ - if (!thd->prelocked_mode && !thd->lex->requires_prelocking() && - thd->lex->sroutines.records) + + if (!thd->prelocked_mode && !thd->lex->requires_prelocking() && + thd->lex->sroutines_list.elements) { + bool first_no_prelocking, need_prelocking; TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last; DBUG_ASSERT(thd->lex->query_tables == *start); + sp_get_prelocking_info(thd, &need_prelocking, &first_no_prelocking); - if (sp_cache_routines_and_add_tables(thd, thd->lex) || - *start) + if ((sp_cache_routines_and_add_tables(thd, thd->lex, + first_no_prelocking) || + *start) && need_prelocking) { query_tables_last_own= save_query_tables_last; *start= thd->lex->query_tables; @@ -1891,14 +1898,32 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter) DBUG_RETURN(-1); } (*counter)++; + if (!tables->table && !(tables->table= open_table(thd, tables, &new_frm_mem, &refresh, 0))) { free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); + if (tables->view) { /* VIEW placeholder */ (*counter)--; + + /* + tables->next_global list consists of two parts: + 1) Query tables and underlying tables of views. + 2) Tables used by all stored routines that this statement invokes on + execution. + We need to know where the bound between these two parts is. If we've + just opened a view, which was the last table in part #1, and it + has added its base tables after itself, adjust the boundary pointer + accordingly. + */ + if (query_tables_last_own && + query_tables_last_own == &(tables->next_global) && + tables->view->query_tables) + query_tables_last_own= tables->view->query_tables_last; + /* Again if needed we have to get cache all routines used by this view and add tables used by them to table list. @@ -2323,6 +2348,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count) and was marked as occupied during open_tables() as free for reuse. */ mark_real_tables_as_free_for_reuse(first_not_own); + DBUG_PRINT("info",("prelocked_mode= PRELOCKED")); thd->prelocked_mode= PRELOCKED; } } @@ -2346,6 +2372,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count) if (thd->lex->requires_prelocking()) { mark_real_tables_as_free_for_reuse(first_not_own); + DBUG_PRINT("info", ("thd->prelocked_mode= PRELOCKED_UNDER_LOCK_TABLES")); thd->prelocked_mode= PRELOCKED_UNDER_LOCK_TABLES; } } @@ -4128,6 +4155,9 @@ void flush_tables() The table will be closed (not stored in cache) by the current thread when close_thread_tables() is called. + PREREQUISITES + Lock on LOCK_open() + RETURN 0 This thread now have exclusive access to this table and no other thread can access the table until close_thread_tables() is called. @@ -4143,6 +4173,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, bool result=0, signalled= 0; DBUG_ENTER("remove_table_from_cache"); + key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; for (;;) { @@ -4200,15 +4231,12 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, { if (!(flags & RTFC_CHECK_KILLED_FLAG) || !thd->killed) { + dropping_tables++; if (likely(signalled)) - { - dropping_tables++; (void) pthread_cond_wait(&COND_refresh, &LOCK_open); - dropping_tables--; - continue; - } else { + struct timespec abstime; /* It can happen that another thread has opened the table but has not yet locked any table at all. Since @@ -4219,11 +4247,11 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, and then we retry another loop in the remove_table_from_cache routine. */ - pthread_mutex_unlock(&LOCK_open); - my_sleep(10); - pthread_mutex_lock(&LOCK_open); - continue; + set_timespec(abstime, 10); + pthread_cond_timedwait(&COND_refresh, &LOCK_open, &abstime); } + dropping_tables--; + continue; } } break; @@ -4303,7 +4331,7 @@ open_new_frm(const char *path, const char *alias, if ((parser= sql_parse_prepare(&pathstr, mem_root, 1))) { - if (!strncmp("VIEW", parser->type()->str, parser->type()->length)) + if (is_equal(&view_type, parser->type())) { if (table_desc == 0 || table_desc->required_type == FRMTYPE_TABLE) { @@ -4326,3 +4354,9 @@ err: bzero(outparam, sizeof(TABLE)); // do not run repair DBUG_RETURN(1); } + + +bool is_equal(const LEX_STRING *a, const LEX_STRING *b) +{ + return a->length == b->length && !strncmp(a->str, b->str, a->length); +} diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index e70ab38ee43..d03d4c381fa 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -774,10 +774,11 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) Query_cache_query_flags flags; // fill all gaps between fields with 0 to get repeatable key bzero(&flags, QUERY_CACHE_FLAGS_SIZE); - flags.client_long_flag= (thd->client_capabilities & CLIENT_LONG_FLAG ? - 1 : 0); - flags.client_protocol_41= (thd->client_capabilities & CLIENT_PROTOCOL_41 ? - 1 : 0); + flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG); + flags.client_protocol_41= test(thd->client_capabilities & + CLIENT_PROTOCOL_41); + flags.more_results_exists= test(thd->server_status & + SERVER_MORE_RESULTS_EXISTS); flags.character_set_client_num= thd->variables.character_set_client->number; flags.character_set_results_num= @@ -791,6 +792,20 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) flags.sql_mode= thd->variables.sql_mode; flags.max_sort_length= thd->variables.max_sort_length; flags.group_concat_max_len= thd->variables.group_concat_max_len; + DBUG_PRINT("qcache", ("long %d, 4.1: %d, more results %d, \ +CS client: %u, CS result: %u, CS conn: %u, limit: %lu, TZ: 0x%lx, \ +sql mode: 0x%lx, sort len: %lu, conncat len: %lu", + (int)flags.client_long_flag, + (int)flags.client_protocol_41, + (int)flags.more_results_exists, + flags.character_set_client_num, + flags.character_set_results_num, + flags.collation_connection_num, + flags.limit, + (ulong)flags.time_zone, + flags.sql_mode, + flags.max_sort_length, + flags.group_concat_max_len)); STRUCT_LOCK(&structure_guard_mutex); if (query_cache_size == 0) @@ -973,10 +988,11 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) // fill all gaps between fields with 0 to get repeatable key bzero(&flags, QUERY_CACHE_FLAGS_SIZE); - flags.client_long_flag= (thd->client_capabilities & CLIENT_LONG_FLAG ? - 1 : 0); - flags.client_protocol_41= (thd->client_capabilities & CLIENT_PROTOCOL_41 ? - 1 : 0); + flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG); + flags.client_protocol_41= test(thd->client_capabilities & + CLIENT_PROTOCOL_41); + flags.more_results_exists= test(thd->server_status & + SERVER_MORE_RESULTS_EXISTS); flags.character_set_client_num= thd->variables.character_set_client->number; flags.character_set_results_num= (thd->variables.character_set_results ? @@ -988,6 +1004,20 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) flags.sql_mode= thd->variables.sql_mode; flags.max_sort_length= thd->variables.max_sort_length; flags.group_concat_max_len= thd->variables.group_concat_max_len; + DBUG_PRINT("qcache", ("long %d, 4.1: %d, more results %d, \ +CS client: %u, CS result: %u, CS conn: %u, limit: %lu, TZ: 0x%lx, \ +sql mode: 0x%lx, sort len: %lu, conncat len: %lu", + (int)flags.client_long_flag, + (int)flags.client_protocol_41, + (int)flags.more_results_exists, + flags.character_set_client_num, + flags.character_set_results_num, + flags.collation_connection_num, + flags.limit, + (ulong)flags.time_zone, + flags.sql_mode, + flags.max_sort_length, + flags.group_concat_max_len)); memcpy((void *)(sql + (tot_length - QUERY_CACHE_FLAGS_SIZE)), &flags, QUERY_CACHE_FLAGS_SIZE); query_block = (Query_cache_block *) hash_search(&queries, (byte*) sql, diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 89d5b543dfc..56d3194765b 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -174,7 +174,7 @@ THD::THD() :Statement(CONVENTIONAL_EXECUTION, 0, ALLOC_ROOT_MIN_BLOCK_SIZE, 0), Open_tables_state(), lock_id(&main_lock_id), - user_time(0), global_read_lock(0), is_fatal_error(0), + user_time(0), in_sub_stmt(FALSE), global_read_lock(0), is_fatal_error(0), rand_used(0), time_zone_used(0), last_insert_id_used(0), insert_id_used(0), clear_next_insert_id(0), in_lock_tables(0), bootstrap(0), derived_tables_processing(FALSE), @@ -523,6 +523,10 @@ bool THD::store_globals() if this is the slave SQL thread. */ variables.pseudo_thread_id= thread_id; + /* + We have to call thr_lock_info_init() again here as THD may have been + created in another thread + */ thr_lock_info_init(&lock_info); return 0; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 1c255ccaadc..d2aea640245 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1141,6 +1141,10 @@ public: thr_lock_type update_lock_default; delayed_insert *di; my_bool tablespace_op; /* This is TRUE in DISCARD/IMPORT TABLESPACE */ + + /* TRUE if we are inside of trigger or stored function. */ + bool in_sub_stmt; + /* container for handler's private per-connection data */ void *ha_data[MAX_HA]; struct st_transactions { @@ -1148,8 +1152,6 @@ public: THD_TRANS all; // Trans since BEGIN WORK THD_TRANS stmt; // Trans for current statement bool on; // see ha_enable_transaction() - /* TRUE if we are inside of trigger or stored function. */ - bool in_sub_stmt; XID xid; // transaction identifier enum xa_states xa_state; // used by external XA only /* @@ -1719,8 +1721,8 @@ public: TMP_TABLE_PARAM() :copy_field(0), group_parts(0), - group_length(0), group_null_parts(0), convert_blob_length(0), - schema_table(0) + group_length(0), group_null_parts(0), convert_blob_length(0), + schema_table(0) {} ~TMP_TABLE_PARAM() { diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index fa5930e5205..6e83017a4e4 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1101,7 +1101,9 @@ ok_or_after_trg_err: err: info->last_errno= error; - thd->lex->current_select->no_error= 0; // Give error + /* current_select is NULL if this is a delayed insert */ + if (thd->lex->current_select) + thd->lex->current_select->no_error= 0; // Give error table->file->print_error(error,MYF(0)); before_trg_err: diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 73aaecd39aa..3fdda5d31a7 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -564,7 +564,7 @@ int yylex(void *arg, void *yythd) grammatically correct. */ else if (c == '?' && ((THD*) yythd)->command == COM_STMT_PREPARE && - !ident_map[cs, yyPeek()]) + !ident_map[yyPeek()]) return(PARAM_MARKER); return((int) c); @@ -2009,6 +2009,7 @@ void st_lex::cleanup_after_one_table_open() time_zone_tables_used= 0; if (sroutines.records) my_hash_reset(&sroutines); + sroutines_list.empty(); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 3a7defebddd..341cc84e90c 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -28,6 +28,7 @@ #include "sp_head.h" #include "sp.h" +#include "sp_cache.h" #ifdef HAVE_OPENSSL /* @@ -125,7 +126,7 @@ static bool end_active_trans(THD *thd) { int error=0; DBUG_ENTER("end_active_trans"); - if (unlikely(thd->transaction.in_sub_stmt)) + if (unlikely(thd->in_sub_stmt)) { my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); DBUG_RETURN(1); @@ -148,11 +149,7 @@ static bool end_active_trans(THD *thd) static bool begin_trans(THD *thd) { int error=0; - /* - QQ: May be it is better to simply prohibit COMMIT and ROLLBACK in - stored routines as SQL2003 suggests? - */ - if (unlikely(thd->transaction.in_sub_stmt)) + if (unlikely(thd->in_sub_stmt)) { my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); return 1; @@ -197,7 +194,7 @@ static int get_or_create_user_conn(THD *thd, const char *user, const char *host, USER_RESOURCES *mqh) { - int return_val=0; + int return_val= 0; uint temp_len, user_len; char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2]; struct user_conn *uc; @@ -205,7 +202,7 @@ static int get_or_create_user_conn(THD *thd, const char *user, DBUG_ASSERT(user != 0); DBUG_ASSERT(host != 0); - user_len=strlen(user); + user_len= strlen(user); temp_len= (strmov(strmov(temp_user, user)+1, host) - temp_user)+1; (void) pthread_mutex_lock(&LOCK_user_conn); if (!(uc = (struct user_conn *) hash_search(&hash_user_connections, @@ -217,21 +214,21 @@ static int get_or_create_user_conn(THD *thd, const char *user, MYF(MY_WME))))) { net_send_error(thd, 0, NullS); // Out of memory - return_val=1; + return_val= 1; goto end; } uc->user=(char*) (uc+1); memcpy(uc->user,temp_user,temp_len+1); uc->host= uc->user + user_len + 1; - uc->len = temp_len; + uc->len= temp_len; uc->connections= uc->questions= uc->updates= uc->conn_per_hour= 0; - uc->user_resources=*mqh; - uc->intime=thd->thr_create_time; + uc->user_resources= *mqh; + uc->intime= thd->thr_create_time; if (my_hash_insert(&hash_user_connections, (byte*) uc)) { my_free((char*) uc,0); net_send_error(thd, 0, NullS); // Out of memory - return_val=1; + return_val= 1; goto end; } } @@ -1344,11 +1341,7 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion) int res= 0; DBUG_ENTER("end_trans"); - /* - QQ: May be it is better to simply prohibit COMMIT and ROLLBACK in - stored routines as SQL2003 suggests? - */ - if (unlikely(thd->transaction.in_sub_stmt)) + if (unlikely(thd->in_sub_stmt)) { my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); DBUG_RETURN(1); @@ -3649,6 +3642,7 @@ end_with_restore_list: if (!(res = mysql_create_function(thd, &lex->udf))) send_ok(thd); #else + net_printf_error(thd, ER_CANT_OPEN_LIBRARY, lex->udf.dl, 0, "feature disabled"); res= TRUE; #endif break; @@ -4110,6 +4104,12 @@ end_with_restore_list: delete lex->sphead; lex->sphead= 0; goto error; + case SP_BODY_TOO_LONG: + my_error(ER_TOO_LONG_BODY, MYF(0), name); + lex->unit.cleanup(); + delete lex->sphead; + lex->sphead= 0; + goto error; default: my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(lex), name); lex->unit.cleanup(); @@ -4132,9 +4132,8 @@ end_with_restore_list: goto error; /* - By this moment all needed SPs should be in cache so no need - to look into DB. Moreover we may be unable to do it becuase - we may don't have read lock on mysql.proc + By this moment all needed SPs should be in cache so no need to look + into DB. */ if (!(sp= sp_find_procedure(thd, lex->spname, TRUE))) { @@ -4199,7 +4198,7 @@ end_with_restore_list: select_limit= thd->variables.select_limit; thd->variables.select_limit= HA_POS_ERROR; - thd->row_count_func= 0; + thd->row_count_func= 0; tmp_disable_binlog(thd); /* don't binlog the substatements */ res= sp->execute_procedure(thd, &lex->value_list); reenable_binlog(thd); @@ -5091,8 +5090,9 @@ bool check_stack_overrun(THD *thd, long margin, if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >= (long) (thread_stack - margin)) { - sprintf(errbuff[0],ER(ER_STACK_OVERRUN),stack_used,thread_stack); - my_message(ER_STACK_OVERRUN,errbuff[0],MYF(0)); + 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)); thd->fatal_error(); return 1; } @@ -5307,6 +5307,8 @@ void create_select_for_variable(const char *var_name) THD *thd; LEX *lex; LEX_STRING tmp, null_lex_string; + Item *var; + char buff[MAX_SYS_VAR_LENGTH*2+4+8], *end; DBUG_ENTER("create_select_for_variable"); thd= current_thd; @@ -5316,8 +5318,14 @@ void create_select_for_variable(const char *var_name) tmp.str= (char*) var_name; tmp.length=strlen(var_name); bzero((char*) &null_lex_string.str, sizeof(null_lex_string)); - add_item_to_list(thd, get_system_var(thd, OPT_SESSION, tmp, - null_lex_string)); + /* + We set the name of Item to @@session.var_name because that then is used + as the column name in the output. + */ + 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); DBUG_VOID_RETURN; } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 87366fe157d..a601e7c68b7 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1682,10 +1682,12 @@ static bool init_param_array(Prepared_statement *stmt) static void cleanup_stmt_and_thd_after_use(Statement *stmt, THD *thd) { + DBUG_ENTER("cleanup_stmt_and_thd_after_use"); stmt->lex->unit.cleanup(); cleanup_items(stmt->free_list); thd->rollback_item_tree_changes(); thd->cleanup_after_query(); + DBUG_VOID_RETURN; } /* @@ -1982,7 +1984,8 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_execute"))) DBUG_VOID_RETURN; - DBUG_PRINT("exec_query:", ("%s", stmt->query)); + DBUG_PRINT("exec_query", ("%s", stmt->query)); + DBUG_PRINT("info",("stmt: %p", stmt)); /* Check if we got an error when sending long data */ if (stmt->state == Query_arena::ERROR) @@ -2079,6 +2082,10 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) reset_stmt_params(stmt); } + log_slow_statement(thd); + /* Prevent from second logging in the end of dispatch_command */ + thd->enable_slow_log= FALSE; + thd->set_statement(&stmt_backup); thd->lock_id= &thd->main_lock_id; thd->current_arena= thd; @@ -2125,6 +2132,8 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name) DBUG_VOID_RETURN; } + DBUG_PRINT("info",("stmt: %p", stmt)); + /* Must go before setting variables, as it clears thd->user_var_events */ mysql_reset_thd_for_next_command(thd); thd->set_n_backup_statement(stmt, &stmt_backup); @@ -2445,19 +2454,26 @@ void Prepared_statement::setup_set_params() Prepared_statement::~Prepared_statement() { + DBUG_ENTER("Prepared_statement::~Prepared_statement"); + DBUG_PRINT("enter",("stmt: %p cursor: %p", this, cursor)); if (cursor) { if (cursor->is_open()) { cursor->close(FALSE); - free_items(); + cleanup_items(free_list); + thd->rollback_item_tree_changes(); free_root(cursor->mem_root, MYF(0)); } cursor->Cursor::~Cursor(); } - else - free_items(); + /* + We have to call free on the items even if cleanup is called as some items, + like Item_param, don't free everything until free_items() + */ + free_items(); delete lex->result; + DBUG_VOID_RETURN; } @@ -2469,6 +2485,9 @@ Query_arena::Type Prepared_statement::type() const void Prepared_statement::close_cursor() { + DBUG_ENTER("Prepared_statement::close_cursor"); + DBUG_PRINT("enter",("stmt: %p", this)); + if (cursor && cursor->is_open()) { thd->change_list= cursor->change_list; @@ -2483,4 +2502,5 @@ void Prepared_statement::close_cursor() mysql_stmt_send_long_data() call. */ reset_stmt_params(this); + DBUG_VOID_RETURN; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c2d547261ff..21277dfc1af 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1194,7 +1194,14 @@ JOIN::exec() { result->send_fields(fields_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); - if (cond_value != Item::COND_FALSE && (!having || having->val_int())) + /* + We have to test for 'conds' here as the WHERE may not be constant + even if we don't have any tables for prepared statements or if + conds uses something like 'rand()'. + */ + if (cond_value != Item::COND_FALSE && + (!conds || conds->val_int()) && + (!having || having->val_int())) { if (do_send_rows && (procedure ? (procedure->send_row(fields_list) || procedure->end_of_records()) @@ -1767,7 +1774,7 @@ Cursor::init_from_thd(THD *thd) for (handlerton **pht= thd->transaction.stmt.ht; *pht; pht++) { const handlerton *ht= *pht; - close_at_commit|= (ht->flags & HTON_CLOSE_CURSORS_AT_COMMIT); + close_at_commit|= test(ht->flags & HTON_CLOSE_CURSORS_AT_COMMIT); if (ht->create_cursor_read_view) { info->ht= ht; @@ -1854,6 +1861,7 @@ Cursor::fetch(ulong num_rows) JOIN_TAB *join_tab= join->join_tab + join->const_tables; enum_nested_loop_state error= NESTED_LOOP_OK; Query_arena backup_arena; + Engine_info *info; DBUG_ENTER("Cursor::fetch"); DBUG_PRINT("enter",("rows: %lu", num_rows)); @@ -1868,7 +1876,7 @@ Cursor::fetch(ulong num_rows) /* save references to memory, allocated during fetch */ thd->set_n_backup_item_arena(this, &backup_arena); - for (Engine_info *info= ht_info; info->read_view ; info++) + for (info= ht_info; info->read_view ; info++) (info->ht->set_cursor_read_view)(info->read_view); join->fetch_limit+= num_rows; @@ -1887,7 +1895,7 @@ Cursor::fetch(ulong num_rows) /* Grab free_list here to correctly free it in close */ thd->restore_backup_item_arena(this, &backup_arena); - for (Engine_info *info= ht_info; info->read_view; info++) + for (info= ht_info; info->read_view; info++) (info->ht->set_cursor_read_view)(0); if (error == NESTED_LOOP_CURSOR_LIMIT) @@ -2762,9 +2770,9 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, We use null_rejecting in add_not_null_conds() to add 'othertbl.field IS NOT NULL' to tab->select_cond. */ - (*key_fields)->null_rejecting= (cond->functype() == Item_func::EQ_FUNC) && - ((*value)->type() == Item::FIELD_ITEM) && - ((Item_field*)*value)->field->maybe_null(); + (*key_fields)->null_rejecting= ((cond->functype() == Item_func::EQ_FUNC) && + ((*value)->type() == Item::FIELD_ITEM) && + ((Item_field*)*value)->field->maybe_null()); (*key_fields)++; } @@ -7951,7 +7959,15 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, item->name, table, item->unsigned_flag); break; case STRING_RESULT: - if (item->max_length > 255 && convert_blob_length) + enum enum_field_types type; + /* + DATE/TIME fields have STRING_RESULT result type. To preserve + type they needed to be handled separately. + */ + if ((type= item->field_type()) == MYSQL_TYPE_DATETIME || + type == MYSQL_TYPE_TIME || type == MYSQL_TYPE_DATE) + new_field= item->tmp_table_field_from_field_type(table); + else if (item->max_length > 255 && convert_blob_length) new_field= new Field_varstring(convert_blob_length, maybe_null, item->name, table, item->collation.collation); @@ -8041,6 +8057,14 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, bool table_cant_handle_bit_fields, uint convert_blob_length) { + if (type != Item::FIELD_ITEM && + item->real_item()->type() == Item::FIELD_ITEM && + (item->type() != Item::REF_ITEM || + !((Item_ref *) item)->depended_from)) + { + item= item->real_item(); + type= Item::FIELD_ITEM; + } switch (type) { case Item::SUM_FUNC_ITEM: { @@ -8054,30 +8078,31 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, case Item::DEFAULT_VALUE_ITEM: { Item_field *field= (Item_field*) item; - if (table_cant_handle_bit_fields && field->field->type() == FIELD_TYPE_BIT) + /* + If item have to be able to store NULLs but underlaid field can't do it, + create_tmp_field_from_field() can't be used for tmp field creation. + */ + if (field->maybe_null && !field->field->maybe_null()) + { + Field *res= create_tmp_field_from_item(thd, item, table, NULL, + modify_item, convert_blob_length); + *from_field= field->field; + if (res && modify_item) + ((Item_field*)item)->result_field= res; + return res; + } + + if (table_cant_handle_bit_fields && + field->field->type() == FIELD_TYPE_BIT) return create_tmp_field_from_item(thd, item, table, copy_func, modify_item, convert_blob_length); return create_tmp_field_from_field(thd, (*from_field= field->field), item->name, table, - modify_item ? (Item_field*) item : NULL, + modify_item ? (Item_field*) item : + NULL, convert_blob_length); } - case Item::REF_ITEM: - { - Item *tmp_item; - if ((tmp_item= item->real_item())->type() == Item::FIELD_ITEM) - { - Item_field *field= (Item_field*) tmp_item; - Field *new_field= create_tmp_field_from_field(thd, - (*from_field= field->field), - item->name, table, - NULL, - convert_blob_length); - if (modify_item) - item->set_result_field(new_field); - return new_field; - } - } + /* Fall through */ case Item::FUNC_ITEM: case Item::COND_ITEM: case Item::FIELD_AVG_ITEM: @@ -8089,6 +8114,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, case Item::REAL_ITEM: case Item::DECIMAL_ITEM: case Item::STRING_ITEM: + case Item::REF_ITEM: case Item::NULL_ITEM: case Item::VARBIN_ITEM: return create_tmp_field_from_item(thd, item, table, copy_func, modify_item, @@ -9881,6 +9907,11 @@ join_read_always_key(JOIN_TAB *tab) int error; TABLE *table= tab->table; + for (uint i= 0 ; i < tab->ref.key_parts ; i++) + { + if ((tab->ref.null_rejecting & 1 << i) && tab->ref.items[i]->is_null()) + return -1; + } if (!table->file->inited) { table->file->ha_index_init(tab->ref.key, tab->sorted); @@ -10936,13 +10967,13 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, usable_keys.set_all(); for (ORDER *tmp_order=order; tmp_order ; tmp_order=tmp_order->next) { - if ((*tmp_order->item)->type() != Item::FIELD_ITEM) + Item *item= (*tmp_order->item)->real_item(); + if (item->type() != Item::FIELD_ITEM) { usable_keys.clear_all(); DBUG_RETURN(0); } - usable_keys.intersect(((Item_field*) (*tmp_order->item))-> - field->part_of_sortkey); + usable_keys.intersect(((Item_field*) item)->field->part_of_sortkey); if (usable_keys.is_clear_all()) DBUG_RETURN(0); // No usable keys } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index d56c14e2836..e0c40b3466e 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3005,9 +3005,13 @@ static bool store_trigger(THD *thd, TABLE *table, const char *db, const char *tname, LEX_STRING *trigger_name, enum trg_event_type event, enum trg_action_time_type timing, - LEX_STRING *trigger_stmt) + LEX_STRING *trigger_stmt, + ulong sql_mode) { CHARSET_INFO *cs= system_charset_info; + byte *sql_mode_str; + ulong sql_mode_len; + restore_record(table, s->default_values); table->field[1]->store(db, strlen(db), cs); table->field[2]->store(trigger_name->str, trigger_name->length, cs); @@ -3021,6 +3025,12 @@ static bool store_trigger(THD *thd, TABLE *table, const char *db, trg_action_time_type_names[timing].length, cs); table->field[14]->store("OLD", 3, cs); table->field[15]->store("NEW", 3, cs); + + sql_mode_str= + sys_var_thd_sql_mode::symbolic_mode_representation(thd, + sql_mode, + &sql_mode_len); + table->field[17]->store((const char*)sql_mode_str, sql_mode_len, cs); return schema_table_store_record(thd, table); } @@ -3053,13 +3063,16 @@ static int get_schema_triggers_record(THD *thd, struct st_table_list *tables, { LEX_STRING trigger_name; LEX_STRING trigger_stmt; + ulong sql_mode; if (triggers->get_trigger_info(thd, (enum trg_event_type) event, (enum trg_action_time_type)timing, - &trigger_name, &trigger_stmt)) + &trigger_name, &trigger_stmt, + &sql_mode)) continue; if (store_trigger(thd, table, base_name, file_name, &trigger_name, (enum trg_event_type) event, - (enum trg_action_time_type) timing, &trigger_stmt)) + (enum trg_action_time_type) timing, &trigger_stmt, + sql_mode)) DBUG_RETURN(1); } } @@ -3565,9 +3578,8 @@ int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list) if (table_list->schema_table_reformed) // show command { SELECT_LEX *sel= lex->current_select; - uint i= 0; Item *item; - Field_translator *transl; + Field_translator *transl, *org_transl; if (table_list->field_translation) { @@ -3588,16 +3600,17 @@ int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list) { DBUG_RETURN(1); } - while ((item= it++)) + for (org_transl= transl; (item= it++); transl++) { - char *name= item->name; - transl[i].item= item; - if (!item->fixed && item->fix_fields(thd, &transl[i].item)) + transl->item= item; + transl->name= item->name; + if (!item->fixed && item->fix_fields(thd, &transl->item)) + { DBUG_RETURN(1); - transl[i++].name= name; + } } - table_list->field_translation= transl; - table_list->field_translation_end= transl + sel->item_list.elements; + table_list->field_translation= org_transl; + table_list->field_translation_end= transl; } DBUG_RETURN(0); @@ -3971,6 +3984,7 @@ ST_FIELD_INFO triggers_fields_info[]= {"ACTION_REFERENCE_OLD_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0}, {"ACTION_REFERENCE_NEW_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0}, {"CREATED", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Created"}, + {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, "sql_mode"}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; @@ -3989,46 +4003,46 @@ ST_FIELD_INFO variables_fields_info[]= ST_SCHEMA_TABLE schema_tables[]= { - {"SCHEMATA", schema_fields_info, create_schema_table, - fill_schema_shemata, make_schemata_old_format, 0, 1, -1, 0}, - {"TABLES", tables_fields_info, create_schema_table, - get_all_tables, make_old_format, get_schema_tables_record, 1, 2, 0}, - {"COLUMNS", columns_fields_info, create_schema_table, - get_all_tables, make_columns_old_format, get_schema_column_record, 1, 2, 0}, {"CHARACTER_SETS", charsets_fields_info, create_schema_table, fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0}, {"COLLATIONS", collation_fields_info, create_schema_table, fill_schema_collation, make_old_format, 0, -1, -1, 0}, {"COLLATION_CHARACTER_SET_APPLICABILITY", coll_charset_app_fields_info, create_schema_table, fill_schema_coll_charset_app, 0, 0, -1, -1, 0}, - {"ROUTINES", proc_fields_info, create_schema_table, - fill_schema_proc, make_proc_old_format, 0, -1, -1, 0}, - {"STATISTICS", stat_fields_info, create_schema_table, - get_all_tables, make_old_format, get_schema_stat_record, 1, 2, 0}, - {"VIEWS", view_fields_info, create_schema_table, - get_all_tables, 0, get_schema_views_record, 1, 2, 0}, - {"USER_PRIVILEGES", user_privileges_fields_info, create_schema_table, - fill_schema_user_privileges, 0, 0, -1, -1, 0}, - {"SCHEMA_PRIVILEGES", schema_privileges_fields_info, create_schema_table, - fill_schema_schema_privileges, 0, 0, -1, -1, 0}, - {"TABLE_PRIVILEGES", table_privileges_fields_info, create_schema_table, - fill_schema_table_privileges, 0, 0, -1, -1, 0}, + {"COLUMNS", columns_fields_info, create_schema_table, + get_all_tables, make_columns_old_format, get_schema_column_record, 1, 2, 0}, {"COLUMN_PRIVILEGES", column_privileges_fields_info, create_schema_table, fill_schema_column_privileges, 0, 0, -1, -1, 0}, - {"TABLE_CONSTRAINTS", table_constraints_fields_info, create_schema_table, - get_all_tables, 0, get_schema_constraints_record, 3, 4, 0}, {"KEY_COLUMN_USAGE", key_column_usage_fields_info, create_schema_table, get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0}, - {"TABLE_NAMES", table_names_fields_info, create_schema_table, - get_all_tables, make_table_names_old_format, 0, 1, 2, 1}, {"OPEN_TABLES", open_tables_fields_info, create_schema_table, fill_open_tables, make_old_format, 0, -1, -1, 1}, + {"ROUTINES", proc_fields_info, create_schema_table, + fill_schema_proc, make_proc_old_format, 0, -1, -1, 0}, + {"SCHEMATA", schema_fields_info, create_schema_table, + fill_schema_shemata, make_schemata_old_format, 0, 1, -1, 0}, + {"SCHEMA_PRIVILEGES", schema_privileges_fields_info, create_schema_table, + fill_schema_schema_privileges, 0, 0, -1, -1, 0}, + {"STATISTICS", stat_fields_info, create_schema_table, + get_all_tables, make_old_format, get_schema_stat_record, 1, 2, 0}, {"STATUS", variables_fields_info, create_schema_table, fill_status, make_old_format, 0, -1, -1, 1}, - {"TRIGGERS", triggers_fields_info, create_schema_table, + {"TABLES", tables_fields_info, create_schema_table, + get_all_tables, make_old_format, get_schema_tables_record, 1, 2, 0}, + {"TABLE_CONSTRAINTS", table_constraints_fields_info, create_schema_table, + get_all_tables, 0, get_schema_constraints_record, 3, 4, 0}, + {"TABLE_NAMES", table_names_fields_info, create_schema_table, + get_all_tables, make_table_names_old_format, 0, 1, 2, 1}, + {"TABLE_PRIVILEGES", table_privileges_fields_info, create_schema_table, + fill_schema_table_privileges, 0, 0, -1, -1, 0}, + {"TRIGGERS", triggers_fields_info, create_schema_table, get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0}, {"VARIABLES", variables_fields_info, create_schema_table, fill_variables, make_old_format, 0, -1, -1, 1}, + {"VIEWS", view_fields_info, create_schema_table, + get_all_tables, 0, get_schema_views_record, 1, 2, 0}, + {"USER_PRIVILEGES", user_privileges_fields_info, create_schema_table, + fill_schema_user_privileges, 0, 0, -1, -1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0} }; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 4e685badd76..695c2300bc1 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -230,7 +230,6 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, for (table= tables; table; table= table->next_local) { char *db=table->db; - uint flags; mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL); if (!close_temporary_table(thd, db, table->table_name)) { @@ -241,10 +240,11 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, error=0; if (!drop_temporary) { - abort_locked_tables(thd,db,table->table_name); - flags= RTFC_WAIT_OTHER_THREAD_FLAG | RTFC_CHECK_KILLED_FLAG; - remove_table_from_cache(thd,db,table->table_name,flags); - drop_locked_tables(thd,db,table->table_name); + abort_locked_tables(thd, db, table->table_name); + remove_table_from_cache(thd, db, table->table_name, + RTFC_WAIT_OTHER_THREAD_FLAG | + RTFC_CHECK_KILLED_FLAG); + drop_locked_tables(thd, db, table->table_name); if (thd->killed) { thd->no_warnings_for_error= 0; @@ -1695,12 +1695,10 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name, create_info->data_file_name= create_info->index_file_name= 0; create_info->table_options=db_options; - if (rea_create_table(thd, path, create_info, fields, key_count, + if (rea_create_table(thd, path, db, table_name, + create_info, fields, key_count, key_info_buffer, file)) - { - /* my_error(ER_CANT_CREATE_TABLE,MYF(0),table_name,my_errno); */ goto end; - } if (create_info->options & HA_LEX_CREATE_TMP_TABLE) { /* Open table and put in temporary table list */ @@ -2333,14 +2331,14 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, /* Close all instances of the table to allow repair to rename files */ if (lock_type == TL_WRITE && table->table->s->version) { - uint flags; pthread_mutex_lock(&LOCK_open); const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open, "Waiting to get writelock"); mysql_lock_abort(thd,table->table); - flags= RTFC_WAIT_OTHER_THREAD_FLAG | RTFC_CHECK_KILLED_FLAG; remove_table_from_cache(thd, table->table->s->db, - table->table->s->table_name, flags); + table->table->s->table_name, + RTFC_WAIT_OTHER_THREAD_FLAG | + RTFC_CHECK_KILLED_FLAG); thd->exit_cond(old_message); if (thd->killed) goto err; @@ -2714,6 +2712,15 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, } } + /* + create like should be not allowed for Views, Triggers, ... + */ + if (mysql_frm_type(src_path) != FRMTYPE_TABLE) + { + my_error(ER_WRONG_OBJECT, MYF(0), src_db, src_table, "BASE TABLE"); + goto err; + } + /* Validate the destination table @@ -2743,8 +2750,14 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, /* Create a new table by copying from source table */ - if (my_copy(src_path, dst_path, MYF(MY_WME|MY_DONT_OVERWRITE_FILE))) + if (my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE))) + { + if (my_errno == ENOENT) + my_error(ER_BAD_DB_ERROR,MYF(0),db); + else + my_error(ER_CANT_CREATE_FILE,MYF(0),dst_path,my_errno); goto err; + } /* As mysql_truncate don't work on a new table at this stage of @@ -4046,9 +4059,10 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, if (table) { VOID(table->file->extra(HA_EXTRA_FORCE_REOPEN)); // Use new file + /* Mark in-use copies old */ remove_table_from_cache(thd,db,table_name,RTFC_NO_FLAG); - // Mark in-use copies old - mysql_lock_abort(thd,table); // end threads waiting on lock + /* end threads waiting on lock */ + mysql_lock_abort(thd,table); } VOID(quick_rm_table(old_db_type,db,old_name)); if (close_data_tables(thd,db,table_name) || diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index a7aee95197e..09eeff02de6 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -32,8 +32,12 @@ const char * const triggers_file_ext= ".TRG"; */ static File_option triggers_file_parameters[]= { - {{(char*)"triggers", 8}, offsetof(class Table_triggers_list, definitions_list), - FILE_OPTIONS_STRLIST}, + {{(char*)"triggers", 8}, + offsetof(class Table_triggers_list, definitions_list), + FILE_OPTIONS_STRLIST}, + {{(char*)"sql_modes", 13}, + offsetof(class Table_triggers_list, definition_modes_list), + FILE_OPTIONS_ULLLIST}, {{0, 0}, 0, FILE_OPTIONS_STRING} }; @@ -127,12 +131,13 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) DBUG_RETURN(TRUE); /* - We do not allow creation of triggers on views or temporary tables. - We have to do this check here and not in - Table_triggers_list::create_trigger() because we want to avoid messing - with table cash for views and temporary tables. + We do not allow creation of triggers on temporary tables. We also don't + allow creation of triggers on views but fulfilment of this restriction + is guaranteed by open_ltable(). It is better to have this check here + than do it in Table_triggers_list::create_trigger() and mess with table + cache. */ - if (tables->view || table->s->tmp_table != NO_TMP_TABLE) + if (table->s->tmp_table != NO_TMP_TABLE) { my_error(ER_TRG_ON_VIEW_OR_TEMP_TABLE, MYF(0), tables->alias); DBUG_RETURN(TRUE); @@ -221,12 +226,13 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables) trigname_path[FN_REFLEN]; LEX_STRING dir, file, trigname_file; LEX_STRING *trg_def, *name; + ulonglong *trg_sql_mode; Item_trigger_field *trg_field; struct st_trigname trigname; /* Trigger must be in the same schema as target table. */ - if (my_strcasecmp(system_charset_info, table->s->db, + if (my_strcasecmp(table_alias_charset, table->s->db, lex->spname->m_db.str ? lex->spname->m_db.str : thd->db)) { @@ -307,11 +313,15 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables) */ if (!(trg_def= (LEX_STRING *)alloc_root(&table->mem_root, sizeof(LEX_STRING))) || - definitions_list.push_back(trg_def, &table->mem_root)) + definitions_list.push_back(trg_def, &table->mem_root) || + !(trg_sql_mode= (ulonglong*)alloc_root(&table->mem_root, + sizeof(ulonglong))) || + definition_modes_list.push_back(trg_sql_mode, &table->mem_root)) goto err_with_cleanup; trg_def->str= thd->query; trg_def->length= thd->query_length; + *trg_sql_mode= thd->variables.sql_mode; if (!sql_create_definition_file(&dir, &file, &triggers_file_type, (gptr)this, triggers_file_parameters, 3)) @@ -390,13 +400,15 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables) LEX_STRING *name; List_iterator_fast<LEX_STRING> it_name(names_list); List_iterator<LEX_STRING> it_def(definitions_list); + List_iterator<ulonglong> it_mod(definition_modes_list); char path[FN_REFLEN]; while ((name= it_name++)) { it_def++; + it_mod++; - if (my_strcasecmp(system_charset_info, lex->spname->m_name.str, + if (my_strcasecmp(table_alias_charset, lex->spname->m_name.str, name->str) == 0) { /* @@ -404,6 +416,7 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables) clean trigger removing since table will be reopened anyway. */ it_def.remove(); + it_mod.remove(); if (definitions_list.is_empty()) { @@ -520,6 +533,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, char path_buff[FN_REFLEN]; LEX_STRING path; File_parser *parser; + LEX_STRING save_db; DBUG_ENTER("Table_triggers_list::check_n_load"); @@ -540,8 +554,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, if ((parser= sql_parse_prepare(&path, &table->mem_root, 1))) { - if (!strncmp(triggers_file_type.str, parser->type()->str, - parser->type()->length)) + if (is_equal(&triggers_file_type, parser->type())) { Table_triggers_list *triggers= new (&table->mem_root) Table_triggers_list(table); @@ -549,10 +562,48 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, if (!triggers) DBUG_RETURN(1); + /* + We don't have sql_modes in old versions of .TRG file, so we should + initialize list for safety. + */ + triggers->definition_modes_list.empty(); + if (parser->parse((gptr)triggers, &table->mem_root, - triggers_file_parameters, 1)) + triggers_file_parameters, 2)) DBUG_RETURN(1); + List_iterator_fast<LEX_STRING> it(triggers->definitions_list); + LEX_STRING *trg_create_str, *trg_name_str; + ulonglong *trg_sql_mode; + + if (triggers->definition_modes_list.is_empty() && + !triggers->definitions_list.is_empty()) + { + /* + It is old file format => we should fill list of sql_modes. + + We use one mode (current) for all triggers, because we have not + information about mode in old format. + */ + if (!(trg_sql_mode= (ulonglong*)alloc_root(&table->mem_root, + sizeof(ulonglong)))) + { + DBUG_RETURN(1); // EOM + } + *trg_sql_mode= global_system_variables.sql_mode; + while ((trg_create_str= it++)) + { + if (triggers->definition_modes_list.push_back(trg_sql_mode, + &table->mem_root)) + { + DBUG_RETURN(1); // EOM + } + } + it.rewind(); + } + + DBUG_ASSERT(triggers->definition_modes_list.elements == + triggers->definitions_list.elements); table->triggers= triggers; /* @@ -564,8 +615,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, alloc_root(&table->mem_root, triggers->sroutines_key.length))) DBUG_RETURN(1); triggers->sroutines_key.str[0]= TYPE_ENUM_TRIGGER; - strmov(strmov(strmov(triggers->sroutines_key.str+1, db), "."), - table_name); + strxmov(triggers->sroutines_key.str+1, db, ".", table_name, NullS); /* TODO: This could be avoided if there is no triggers @@ -574,15 +624,21 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, if (!names_only && triggers->prepare_record1_accessors(table)) DBUG_RETURN(1); - List_iterator_fast<LEX_STRING> it(triggers->definitions_list); - LEX_STRING *trg_create_str, *trg_name_str; char *trg_name_buff; + List_iterator_fast<ulonglong> itm(triggers->definition_modes_list); LEX *old_lex= thd->lex, lex; + ulong save_sql_mode= thd->variables.sql_mode; thd->lex= &lex; + save_db.str= thd->db; + save_db.length= thd->db_length; + thd->db_length= strlen(db); + thd->db= (char *) db; while ((trg_create_str= it++)) { + trg_sql_mode= itm++; + thd->variables.sql_mode= (ulong)*trg_sql_mode; lex_start(thd, (uchar*)trg_create_str->str, trg_create_str->length); if (yyparse((void *)thd) || thd->is_fatal_error) @@ -595,9 +651,11 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, goto err_with_lex_cleanup; } + lex.sphead->m_sql_mode= *trg_sql_mode; triggers->bodies[lex.trg_chistics.event] [lex.trg_chistics.action_time]= lex.sphead; - if (triggers->names_list.push_back(&lex.sphead->m_name, &table->mem_root)) + if (triggers->names_list.push_back(&lex.sphead->m_name, + &table->mem_root)) goto err_with_lex_cleanup; if (names_only) @@ -611,8 +669,9 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, in old/new versions of row in trigger to Field objects in table being opened. - We ignore errors here, because if even something is wrong we still will - be willing to open table to perform some operations (e.g. SELECT)... + We ignore errors here, because if even something is wrong we still + will be willing to open table to perform some operations (e.g. + SELECT)... Anyway some things can be checked only during trigger execution. */ for (Item_trigger_field *trg_field= @@ -623,7 +682,10 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, lex_end(&lex); } + thd->db= save_db.str; + thd->db_length= save_db.length; thd->lex= old_lex; + thd->variables.sql_mode= save_sql_mode; DBUG_RETURN(0); @@ -631,6 +693,9 @@ err_with_lex_cleanup: // QQ: anything else ? lex_end(&lex); thd->lex= old_lex; + thd->variables.sql_mode= save_sql_mode; + thd->db= save_db.str; + thd->db_length= save_db.length; DBUG_RETURN(1); } @@ -639,7 +704,7 @@ err_with_lex_cleanup: be merged into .FRM anyway. */ my_error(ER_WRONG_OBJECT, MYF(0), - table_name, triggers_file_ext, "TRIGGER"); + table_name, triggers_file_ext+1, "TRIGGER"); DBUG_RETURN(1); } @@ -657,6 +722,7 @@ err_with_lex_cleanup: time_type - trigger action time name - returns name of trigger stmt - returns statement of trigger + sql_mode - returns sql_mode of trigger RETURN VALUE False - success @@ -666,7 +732,8 @@ err_with_lex_cleanup: bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event, trg_action_time_type time_type, LEX_STRING *trigger_name, - LEX_STRING *trigger_stmt) + LEX_STRING *trigger_stmt, + ulong *sql_mode) { sp_head *body; DBUG_ENTER("get_trigger_info"); @@ -674,6 +741,7 @@ bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event, { *trigger_name= body->m_name; *trigger_stmt= body->m_body; + *sql_mode= body->m_sql_mode; DBUG_RETURN(0); } DBUG_RETURN(1); @@ -718,10 +786,9 @@ static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig) if (!(parser= sql_parse_prepare(&path, thd->mem_root, 1))) DBUG_RETURN(0); - if (strncmp(trigname_file_type.str, parser->type()->str, - parser->type()->length)) + if (!is_equal(&trigname_file_type, parser->type())) { - my_error(ER_WRONG_OBJECT, MYF(0), trig->m_name.str, trigname_file_ext, + my_error(ER_WRONG_OBJECT, MYF(0), trig->m_name.str, trigname_file_ext+1, "TRIGGERNAME"); DBUG_RETURN(0); } diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index e751741fa34..e2ed4bcc0f4 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -60,6 +60,10 @@ public: It have to be public because we are using it directly from parser. */ List<LEX_STRING> definitions_list; + /* + List of sql modes for triggers + */ + List<ulonglong> definition_modes_list; Table_triggers_list(TABLE *table_arg): record1_field(0), table(table_arg) @@ -78,7 +82,7 @@ public: if (bodies[event][time_type]) { - bool save_in_sub_stmt= thd->transaction.in_sub_stmt; + bool save_in_sub_stmt= thd->in_sub_stmt; #ifndef EMBEDDED_LIBRARY /* Surpress OK packets in case if we will execute statements */ my_bool nsok= thd->net.no_send_ok; @@ -107,11 +111,11 @@ public: does NOT go into binlog. */ tmp_disable_binlog(thd); - thd->transaction.in_sub_stmt= TRUE; + thd->in_sub_stmt= TRUE; res= bodies[event][time_type]->execute_function(thd, 0, 0, 0); - thd->transaction.in_sub_stmt= save_in_sub_stmt; + thd->in_sub_stmt= save_in_sub_stmt; reenable_binlog(thd); #ifndef EMBEDDED_LIBRARY @@ -123,7 +127,8 @@ public: } bool get_trigger_info(THD *thd, trg_event_type event, trg_action_time_type time_type, - LEX_STRING *trigger_name, LEX_STRING *trigger_stmt); + LEX_STRING *trigger_name, LEX_STRING *trigger_stmt, + ulong *sql_mode); static bool check_n_load(THD *thd, const char *db, const char *table_name, TABLE *table, bool names_only); diff --git a/sql/sql_union.cc b/sql/sql_union.cc index f2b637dc5f4..c414f5e9e72 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -235,7 +235,13 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, if ((res= (res || thd_arg->is_fatal_error))) goto err; - if (sl == first_select) + /* + Use items list of underlaid select for derived tables to preserve + information about fields lengths and exact types + */ + if (!is_union) + types= first_select_in_union()->item_list; + else if (sl == first_select) { /* We need to create an empty table object. It is used diff --git a/sql/sql_view.cc b/sql/sql_view.cc index a60bf80a6d8..aedff648e5c 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -24,6 +24,8 @@ #define MD5_BUFF_LENGTH 33 +const LEX_STRING view_type= { (char*) STRING_WITH_LEN("VIEW") }; + static int mysql_register_view(THD *thd, TABLE_LIST *view, enum_view_create_mode mode); @@ -431,7 +433,7 @@ static File_option view_parameters[]= FILE_OPTIONS_STRING} }; -static LEX_STRING view_file_type[]= {{(char*)"VIEW", 4}}; +static LEX_STRING view_file_type[]= {{(char*) STRING_WITH_LEN("VIEW") }}; /* @@ -470,7 +472,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, thd->variables.sql_mode|= sql_mode; } str.append('\0'); - DBUG_PRINT("VIEW", ("View: %s", str.ptr())); + DBUG_PRINT("info", ("View: %s", str.ptr())); /* print file name */ (void) my_snprintf(dir_buff, FN_REFLEN, "%s/%s/", @@ -507,8 +509,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, if (!(parser= sql_parse_prepare(&path, thd->mem_root, 0))) DBUG_RETURN(1); - if (!parser->ok() || - strncmp("VIEW", parser->type()->str, parser->type()->length)) + if (!parser->ok() || !is_equal(&view_type, parser->type())) { my_error(ER_WRONG_OBJECT, MYF(0), (view->db ? view->db : thd->db), view->table_name, "VIEW"); @@ -691,7 +692,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) view_select= &lex->select_lex; view_select->select_number= ++thd->select_number; { - ulong options= thd->options; + ulong save_mode= thd->variables.sql_mode; /* switch off modes which can prevent normal parsing of VIEW - MODE_REAL_AS_FLOAT affect only CREATE TABLE parsing + MODE_PIPES_AS_CONCAT affect expression parsing @@ -716,13 +717,13 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) ? MODE_NO_AUTO_VALUE_ON_ZERO affect UPDATEs + MODE_NO_BACKSLASH_ESCAPES affect expression parsing */ - thd->options&= ~(MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES | - MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES); + thd->variables.sql_mode&= ~(MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES | + MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES); CHARSET_INFO *save_cs= thd->variables.character_set_client; thd->variables.character_set_client= system_charset_info; res= yyparse((void *)thd); thd->variables.character_set_client= save_cs; - thd->options= options; + thd->variables.sql_mode= save_mode; } if (!res && !thd->is_fatal_error) { diff --git a/sql/table.cc b/sql/table.cc index 8852e1fa9dd..7c1701cc4d3 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -714,10 +714,9 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, key_part->key_part_flag|= HA_BIT_PART; if (i == 0 && key != primary_key) - field->flags |= - ((keyinfo->flags & HA_NOSAME) && - field->key_length() == - keyinfo->key_length ? UNIQUE_KEY_FLAG : MULTIPLE_KEY_FLAG); + field->flags |= ((keyinfo->flags & HA_NOSAME) && + (keyinfo->key_parts == 1)) ? + UNIQUE_KEY_FLAG : MULTIPLE_KEY_FLAG; if (i == 0) field->key_start.set_bit(key); if (field->key_length() == key_part->length && @@ -1378,8 +1377,9 @@ void append_unescaped(String *res, const char *pos, uint length) /* Create a .frm file */ -File create_frm(THD *thd, register my_string name, uint reclength, - uchar *fileinfo, HA_CREATE_INFO *create_info, uint keys) +File create_frm(THD *thd, my_string name, const char *db, + const char *table, uint reclength, uchar *fileinfo, + HA_CREATE_INFO *create_info, uint keys) { register File file; ulong length; @@ -1402,7 +1402,7 @@ File create_frm(THD *thd, register my_string name, uint reclength, */ set_if_smaller(create_info->raid_chunks, 255); - if ((file= my_create(name, CREATE_MODE, create_flags, MYF(MY_WME))) >= 0) + if ((file= my_create(name, CREATE_MODE, create_flags, MYF(0))) >= 0) { uint key_length, tmp_key_length; uint tmp; @@ -1449,6 +1449,13 @@ File create_frm(THD *thd, register my_string name, uint reclength, } } } + else + { + if (my_errno == ENOENT) + my_error(ER_BAD_DB_ERROR,MYF(0),db); + else + my_error(ER_CANT_CREATE_TABLE,MYF(0),table,my_errno); + } return (file); } /* create_frm */ diff --git a/sql/table.h b/sql/table.h index 8c0040b0fa4..30462d1b173 100644 --- a/sql/table.h +++ b/sql/table.h @@ -285,11 +285,26 @@ typedef struct st_foreign_key_info enum enum_schema_tables { - SCH_SCHEMATA= 0, SCH_TABLES, SCH_COLUMNS, SCH_CHARSETS, SCH_COLLATIONS, - SCH_COLLATION_CHARACTER_SET_APPLICABILITY, SCH_PROCEDURES, SCH_STATISTICS, - SCH_VIEWS, SCH_USER_PRIVILEGES, SCH_SCHEMA_PRIVILEGES, SCH_TABLE_PRIVILEGES, - SCH_COLUMN_PRIVILEGES, SCH_TABLE_CONSTRAINTS, SCH_KEY_COLUMN_USAGE, - SCH_TABLE_NAMES, SCH_OPEN_TABLES, SCH_STATUS, SCH_TRIGGERS, SCH_VARIABLES + SCH_CHARSETS= 0, + SCH_COLLATIONS, + SCH_COLLATION_CHARACTER_SET_APPLICABILITY, + SCH_COLUMNS, + SCH_COLUMN_PRIVILEGES, + SCH_KEY_COLUMN_USAGE, + SCH_OPEN_TABLES, + SCH_PROCEDURES, + SCH_SCHEMATA, + SCH_SCHEMA_PRIVILEGES, + SCH_STATISTICS, + SCH_STATUS, + SCH_TABLES, + SCH_TABLE_CONSTRAINTS, + SCH_TABLE_NAMES, + SCH_TABLE_PRIVILEGES, + SCH_TRIGGERS, + SCH_VARIABLES, + SCH_VIEWS, + SCH_USER_PRIVILEGES }; diff --git a/sql/time.cc b/sql/time.cc index a3ec2283860..5069031081d 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -223,7 +223,7 @@ str_to_datetime_with_warn(const char *str, uint length, TIME *l_time, 0 - t contains datetime value which is out of TIMESTAMP range. */ -my_time_t TIME_to_timestamp(THD *thd, const TIME *t, bool *in_dst_time_gap) +my_time_t TIME_to_timestamp(THD *thd, const TIME *t, my_bool *in_dst_time_gap) { my_time_t timestamp; diff --git a/sql/tztime.cc b/sql/tztime.cc index bb516731440..99a009968a7 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -880,12 +880,12 @@ sec_since_epoch(int year, int mon, int mday, int hour, int min ,int sec) 0 in case of error. */ static my_time_t -TIME_to_gmt_sec(const TIME *t, const TIME_ZONE_INFO *sp, bool *in_dst_time_gap) +TIME_to_gmt_sec(const TIME *t, const TIME_ZONE_INFO *sp, + my_bool *in_dst_time_gap) { my_time_t local_t; uint saved_seconds; uint i; - DBUG_ENTER("TIME_to_gmt_sec"); /* We need this for correct leap seconds handling */ @@ -962,7 +962,7 @@ class Time_zone_system : public Time_zone { public: virtual my_time_t TIME_to_gmt_sec(const TIME *t, - bool *in_dst_time_gap) const; + my_bool *in_dst_time_gap) const; virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const; virtual const String * get_name() const; }; @@ -994,7 +994,7 @@ public: Corresponding my_time_t value or 0 in case of error */ my_time_t -Time_zone_system::TIME_to_gmt_sec(const TIME *t, bool *in_dst_time_gap) const +Time_zone_system::TIME_to_gmt_sec(const TIME *t, my_bool *in_dst_time_gap) const { long not_used; return my_system_gmt_sec(t, ¬_used, in_dst_time_gap); @@ -1055,7 +1055,7 @@ class Time_zone_utc : public Time_zone { public: virtual my_time_t TIME_to_gmt_sec(const TIME *t, - bool *in_dst_time_gap) const; + my_bool *in_dst_time_gap) const; virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const; virtual const String * get_name() const; }; @@ -1081,7 +1081,7 @@ public: 0 */ my_time_t -Time_zone_utc::TIME_to_gmt_sec(const TIME *t, bool *in_dst_time_gap) const +Time_zone_utc::TIME_to_gmt_sec(const TIME *t, my_bool *in_dst_time_gap) const { /* Should be never called */ DBUG_ASSERT(0); @@ -1144,7 +1144,7 @@ class Time_zone_db : public Time_zone public: Time_zone_db(TIME_ZONE_INFO *tz_info_arg, const String * tz_name_arg); virtual my_time_t TIME_to_gmt_sec(const TIME *t, - bool *in_dst_time_gap) const; + my_bool *in_dst_time_gap) const; virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const; virtual const String * get_name() const; private: @@ -1193,7 +1193,7 @@ Time_zone_db::Time_zone_db(TIME_ZONE_INFO *tz_info_arg, Corresponding my_time_t value or 0 in case of error */ my_time_t -Time_zone_db::TIME_to_gmt_sec(const TIME *t, bool *in_dst_time_gap) const +Time_zone_db::TIME_to_gmt_sec(const TIME *t, my_bool *in_dst_time_gap) const { return ::TIME_to_gmt_sec(t, tz_info, in_dst_time_gap); } @@ -1240,7 +1240,7 @@ class Time_zone_offset : public Time_zone public: Time_zone_offset(long tz_offset_arg); virtual my_time_t TIME_to_gmt_sec(const TIME *t, - bool *in_dst_time_gap) const; + my_bool *in_dst_time_gap) const; virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const; virtual const String * get_name() const; /* @@ -1292,7 +1292,7 @@ Time_zone_offset::Time_zone_offset(long tz_offset_arg): Corresponding my_time_t value or 0 in case of error */ my_time_t -Time_zone_offset::TIME_to_gmt_sec(const TIME *t, bool *in_dst_time_gap) const +Time_zone_offset::TIME_to_gmt_sec(const TIME *t, my_bool *in_dst_time_gap) const { return sec_since_epoch(t->year, t->month, t->day, t->hour, t->minute, t->second) - @@ -2549,8 +2549,6 @@ main(int argc, char **argv) time_t t, t1, t2; char fullname[FN_REFLEN+1]; char *str_end; - long not_used; - bool not_used_2; MEM_ROOT tz_storage; MY_INIT(argv[0]); @@ -2660,14 +2658,21 @@ main(int argc, char **argv) dates. */ for (time_tmp.year= 1980; time_tmp.year < 2010; time_tmp.year++) + { for (time_tmp.month= 1; time_tmp.month < 13; time_tmp.month++) + { for (time_tmp.day= 1; time_tmp.day < mon_lengths[isleap(time_tmp.year)][time_tmp.month-1]; time_tmp.day++) + { for (time_tmp.hour= 0; time_tmp.hour < 24; time_tmp.hour++) + { for (time_tmp.minute= 0; time_tmp.minute < 60; time_tmp.minute+= 5) + { for (time_tmp.second=0; time_tmp.second<60; time_tmp.second+=25) { + long not_used; + my_bool not_used_2; t= (time_t)my_system_gmt_sec(&time_tmp, ¬_used, ¬_used_2); t1= (time_t)TIME_to_gmt_sec(&time_tmp, &tz_info, ¬_used_2); if (t != t1) @@ -2699,6 +2704,11 @@ main(int argc, char **argv) return 1; } } + } + } + } + } + } printf("TIME_to_gmt_sec = my_system_gmt_sec for test range\n"); diff --git a/sql/tztime.h b/sql/tztime.h index cbf359e8961..a168fe4fb73 100644 --- a/sql/tztime.h +++ b/sql/tztime.h @@ -37,7 +37,7 @@ public: falls into spring time-gap (or lefts it untouched otherwise). */ virtual my_time_t TIME_to_gmt_sec(const TIME *t, - bool *in_dst_time_gap) const = 0; + my_bool *in_dst_time_gap) const = 0; /* Converts time in my_time_t representation to local time in broken down TIME representation. diff --git a/sql/unireg.cc b/sql/unireg.cc index cdbae4f1eb9..d423e1bee4b 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -56,6 +56,8 @@ static bool make_empty_rec(THD *thd, int file, enum db_type table_type, mysql_create_frm() thd Thread handler file_name Name of file (including database and .frm) + db Name of database + table Name of table create_info create info parameters create_fields Fields to create keys number of keys to create @@ -68,6 +70,7 @@ static bool make_empty_rec(THD *thd, int file, enum db_type table_type, */ bool mysql_create_frm(THD *thd, my_string file_name, + const char *db, const char *table, HA_CREATE_INFO *create_info, List<create_field> &create_fields, uint keys, KEY *key_info, @@ -119,7 +122,7 @@ bool mysql_create_frm(THD *thd, my_string file_name, } reclength=uint2korr(forminfo+266); - if ((file=create_frm(thd, file_name, reclength, fileinfo, + if ((file=create_frm(thd, file_name, db, table, reclength, fileinfo, create_info, keys)) < 0) { my_free((gptr) screen_buff,MYF(0)); @@ -235,6 +238,8 @@ err3: rea_create_table() thd Thread handler file_name Name of file (including database and .frm) + db Name of database + table Name of table create_info create info parameters create_fields Fields to create keys number of keys to create @@ -246,13 +251,14 @@ err3: */ int rea_create_table(THD *thd, my_string file_name, + const char *db, const char *table, HA_CREATE_INFO *create_info, List<create_field> &create_fields, uint keys, KEY *key_info, handler *file) { DBUG_ENTER("rea_create_table"); - if (mysql_create_frm(thd, file_name, create_info, + if (mysql_create_frm(thd, file_name, db, table, create_info, create_fields, keys, key_info, file)) DBUG_RETURN(1); if (file->create_handler_files(file_name)) |