diff options
29 files changed, 607 insertions, 365 deletions
diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index be527630078..9d96cef345f 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -434,7 +434,7 @@ d date YES NULL e varchar(1) NO f datetime YES NULL g time YES NULL -h varbinary(23) NO +h longblob NO dd time YES NULL select * from t2; a b c d e f g h dd diff --git a/mysql-test/r/distinct.result b/mysql-test/r/distinct.result index 955ea5b8673..3ad2b73f1d3 100644 --- a/mysql-test/r/distinct.result +++ b/mysql-test/r/distinct.result @@ -464,3 +464,11 @@ SELECT DISTINCT html,SUM(rout)/(SUM(rin)+1) as 'prod' FROM t1 GROUP BY rin; html prod 1 0.0000 drop table t1; +create table t1 (id int, dsc varchar(50)); +insert into t1 values (1, "line number one"), (2, "line number two"), (3, "line number three"); +select distinct id, IFNULL(dsc, '-') from t1; +id IFNULL(dsc, '-') +1 line number one +2 line number two +3 line number three +drop table t1; diff --git a/mysql-test/r/ndb_subquery.result b/mysql-test/r/ndb_subquery.result index f65f09b71b3..b19571b05c1 100644 --- a/mysql-test/r/ndb_subquery.result +++ b/mysql-test/r/ndb_subquery.result @@ -40,3 +40,22 @@ p u o 5 5 5 drop table t1; drop table t2; +create table t1 (p int not null primary key, u int not null) engine=ndb; +insert into t1 values (1,1),(2,2),(3,3); +create table t2 as +select t1.* +from t1 as t1, t1 as t2, t1 as t3, t1 as t4, t1 as t5, t1 as t6, t1 as t7, t1 as t8 +where t1.u = t2.u +and t2.u = t3.u +and t3.u = t4.u +and t4.u = t5.u +and t5.u = t6.u +and t6.u = t7.u +and t7.u = t8.u; +select * from t2 order by 1; +p u +1 1 +2 2 +3 3 +drop table t1; +drop table t2; diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 1dc97124a07..b6ba737a8ba 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -672,3 +672,17 @@ select default(t30.s1) from t30; end| drop procedure bug10969| drop table t1| +prepare stmt from "select 1"; +create procedure p() deallocate prepare stmt; +ERROR 0A000: DEALLOCATE is not allowed in stored procedures +create function f() returns int begin deallocate prepare stmt; +ERROR 0A000: DEALLOCATE is not allowed in stored procedures +create procedure p() prepare stmt from "select 1"; +ERROR 0A000: PREPARE is not allowed in stored procedures +create function f() returns int begin prepare stmt from "select 1"; +ERROR 0A000: PREPARE is not allowed in stored procedures +create procedure p() execute stmt; +ERROR 0A000: EXECUTE is not allowed in stored procedures +create function f() returns int begin execute stmt; +ERROR 0A000: EXECUTE is not allowed in stored procedures +deallocate prepare stmt; diff --git a/mysql-test/r/sp-threads.result b/mysql-test/r/sp-threads.result index a081e520496..a9d50e6e697 100644 --- a/mysql-test/r/sp-threads.result +++ b/mysql-test/r/sp-threads.result @@ -40,3 +40,18 @@ Id User Host db Command Time State Info unlock tables; drop procedure bug9486; drop table t1, t2; +drop procedure if exists bug11158; +create procedure bug11158() delete t1 from t1, t2 where t1.id = t2.id; +create table t1 (id int, j int); +insert into t1 values (1, 1), (2, 2); +create table t2 (id int); +insert into t2 values (1); +call bug11158(); +select * from t1; +id j +2 2 +lock tables t2 read; +call bug11158(); +unlock tables; +drop procedure bug11158; +drop table t1, t2; diff --git a/mysql-test/r/type_newdecimal.result b/mysql-test/r/type_newdecimal.result index 09fbc6b8143..c0693d1585c 100644 --- a/mysql-test/r/type_newdecimal.result +++ b/mysql-test/r/type_newdecimal.result @@ -896,6 +896,8 @@ create table t1( d1 decimal(18) unsigned, d2 decimal(20) unsigned, d3 decimal (2 insert into t1 values(1,-1,-1); ERROR 22003: Out of range value adjusted for column 'd2' at row 1 drop table t1; +set sql_mode=''; +set @sav_dpi= @@div_precision_increment; set @@div_precision_increment=15; create table t1 (col1 int, col2 decimal(30,25), col3 numeric(30,25)); insert into t1 values (1,0.0123456789012345678912345,0.0123456789012345678912345); @@ -909,3 +911,12 @@ select 77777777/7777777; 77777777/7777777 10.000000900000090 drop table t1; +set div_precision_increment= @sav_dpi; +create table t1 (a decimal(4,2)); +insert into t1 values (0.00); +select * from t1 where a > -0.00; +a +select * from t1 where a = -0.00; +a +0.00 +drop table t1; diff --git a/mysql-test/t/distinct.test b/mysql-test/t/distinct.test index a3862786cc3..057c9bd9239 100644 --- a/mysql-test/t/distinct.test +++ b/mysql-test/t/distinct.test @@ -332,3 +332,11 @@ CREATE TABLE t1 ( INSERT INTO t1 VALUES ('1',1,0); SELECT DISTINCT html,SUM(rout)/(SUM(rin)+1) as 'prod' FROM t1 GROUP BY rin; drop table t1; + +# +# Bug 9784 DISTINCT IFNULL truncates data +# +create table t1 (id int, dsc varchar(50)); +insert into t1 values (1, "line number one"), (2, "line number two"), (3, "line number three"); +select distinct id, IFNULL(dsc, '-') from t1; +drop table t1; diff --git a/mysql-test/t/ndb_subquery.test b/mysql-test/t/ndb_subquery.test index 9d3a256a263..095fdbcfa13 100644 --- a/mysql-test/t/ndb_subquery.test +++ b/mysql-test/t/ndb_subquery.test @@ -37,3 +37,26 @@ drop table t1; drop table t2; # bug#5367 ########## + +### +# bug#11205 +create table t1 (p int not null primary key, u int not null) engine=ndb; +insert into t1 values (1,1),(2,2),(3,3); + +create table t2 as +select t1.* +from t1 as t1, t1 as t2, t1 as t3, t1 as t4, t1 as t5, t1 as t6, t1 as t7, t1 as t8 +where t1.u = t2.u + and t2.u = t3.u + and t3.u = t4.u + and t4.u = t5.u + and t5.u = t6.u + and t6.u = t7.u + and t7.u = t8.u; + +select * from t2 order by 1; + +drop table t1; +drop table t2; + + diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index 891e282e335..faf6d8b4de3 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -965,3 +965,24 @@ drop procedure bug10969| drop table t1| delimiter ;| + +# +# Bug#10975, #10605, #7115: Dynamic SQL by means of +# PREPARE/EXECUTE/DEALLOCATE is not supported yet. +# Check that an error message is returned. +# +prepare stmt from "select 1"; +--error ER_SP_BADSTATEMENT +create procedure p() deallocate prepare stmt; +--error ER_SP_BADSTATEMENT +create function f() returns int begin deallocate prepare stmt; +--error ER_SP_BADSTATEMENT +create procedure p() prepare stmt from "select 1"; +--error ER_SP_BADSTATEMENT +create function f() returns int begin prepare stmt from "select 1"; +--error ER_SP_BADSTATEMENT +create procedure p() execute stmt; +--error ER_SP_BADSTATEMENT +create function f() returns int begin execute stmt; +deallocate prepare stmt; + diff --git a/mysql-test/t/sp-threads.test b/mysql-test/t/sp-threads.test index 608ac3e2ee7..8fec5d14bc1 100644 --- a/mysql-test/t/sp-threads.test +++ b/mysql-test/t/sp-threads.test @@ -84,6 +84,32 @@ reap; drop procedure bug9486; drop table t1, t2; +# +# BUG#11158: Can't perform multi-delete in stored procedure +# +--disable_warnings +drop procedure if exists bug11158; +--enable_warnings +create procedure bug11158() delete t1 from t1, t2 where t1.id = t2.id; +create table t1 (id int, j int); +insert into t1 values (1, 1), (2, 2); +create table t2 (id int); +insert into t2 values (1); +# Procedure should work and cause proper effect (delete only first row) +call bug11158(); +select * from t1; +# Also let us test that we obtain only read (and thus non exclusive) lock +# for table from which we are not going to delete rows. +connection con2root; +lock tables t2 read; +connection con1root; +call bug11158(); +connection con2root; +unlock tables; +connection con1root; +# Clean-up +drop procedure bug11158; +drop table t1, t2; # # BUG#NNNN: New bug synopsis diff --git a/mysql-test/t/type_newdecimal.test b/mysql-test/t/type_newdecimal.test index 6bff6c22abc..d1d595285a2 100644 --- a/mysql-test/t/type_newdecimal.test +++ b/mysql-test/t/type_newdecimal.test @@ -934,10 +934,12 @@ create table t1( d1 decimal(18) unsigned, d2 decimal(20) unsigned, d3 decimal (2 --error 1264 insert into t1 values(1,-1,-1); drop table t1; +set sql_mode=''; # # Bug #8425 (insufficient precision of the division) # +set @sav_dpi= @@div_precision_increment; set @@div_precision_increment=15; create table t1 (col1 int, col2 decimal(30,25), col3 numeric(30,25)); insert into t1 values (1,0.0123456789012345678912345,0.0123456789012345678912345); @@ -945,4 +947,13 @@ select col2/9999999999 from t1 where col1=1; select 9999999999/col2 from t1 where col1=1; select 77777777/7777777; drop table t1; +set div_precision_increment= @sav_dpi; +# +# Bug #10896 (0.00 > -0.00) +# +create table t1 (a decimal(4,2)); +insert into t1 values (0.00); +select * from t1 where a > -0.00; +select * from t1 where a = -0.00; +drop table t1; diff --git a/ndb/include/ndbapi/Ndb.hpp b/ndb/include/ndbapi/Ndb.hpp index cefe6477cdd..db2212075e8 100644 --- a/ndb/include/ndbapi/Ndb.hpp +++ b/ndb/include/ndbapi/Ndb.hpp @@ -1616,7 +1616,7 @@ private: Uint32 theNoOfPreparedTransactions; Uint32 theNoOfSentTransactions; Uint32 theNoOfCompletedTransactions; - Uint32 theNoOfAllocatedTransactions; + Uint32 theRemainingStartTransactions; Uint32 theMaxNoOfTransactions; Uint32 theMinNoOfEventsToWakeUp; diff --git a/ndb/src/ndbapi/Ndb.cpp b/ndb/src/ndbapi/Ndb.cpp index 9bf5be259c6..7893aaae15c 100644 --- a/ndb/src/ndbapi/Ndb.cpp +++ b/ndb/src/ndbapi/Ndb.cpp @@ -425,12 +425,20 @@ Ndb::startTransactionLocal(Uint32 aPriority, Uint32 nodeId) DBUG_ENTER("Ndb::startTransactionLocal"); DBUG_PRINT("enter", ("nodeid: %d", nodeId)); + if(unlikely(theRemainingStartTransactions == 0)) + { + theError.code = 4006; + DBUG_RETURN(0); + } + NdbTransaction* tConnection; Uint64 tFirstTransId = theFirstTransId; tConnection = doConnect(nodeId); if (tConnection == NULL) { DBUG_RETURN(NULL); }//if + + theRemainingStartTransactions--; NdbTransaction* tConNext = theTransactionList; tConnection->init(); theTransactionList = tConnection; // into a transaction list. @@ -481,6 +489,7 @@ Ndb::closeTransaction(NdbTransaction* aConnection) CHECK_STATUS_MACRO_VOID; tCon = theTransactionList; + theRemainingStartTransactions++; DBUG_PRINT("info",("close trans: 0x%x transid: 0x%llx", aConnection, aConnection->getTransactionId())); diff --git a/ndb/src/ndbapi/NdbScanOperation.cpp b/ndb/src/ndbapi/NdbScanOperation.cpp index 6898639e059..e0a480e02f7 100644 --- a/ndb/src/ndbapi/NdbScanOperation.cpp +++ b/ndb/src/ndbapi/NdbScanOperation.cpp @@ -89,15 +89,18 @@ int NdbScanOperation::init(const NdbTableImpl* tab, NdbTransaction* myConnection) { m_transConnection = myConnection; - //NdbTransaction* aScanConnection = theNdb->startTransaction(myConnection); + //NdbConnection* aScanConnection = theNdb->startTransaction(myConnection); + theNdb->theRemainingStartTransactions++; // will be checked in hupp... NdbTransaction* aScanConnection = theNdb->hupp(myConnection); if (!aScanConnection){ + theNdb->theRemainingStartTransactions--; setErrorCodeAbort(theNdb->getNdbError().code); return -1; } // NOTE! The hupped trans becomes the owner of the operation if(NdbOperation::init(tab, aScanConnection) != 0){ + theNdb->theRemainingStartTransactions--; return -1; } @@ -675,6 +678,7 @@ void NdbScanOperation::close(bool forceSend, bool releaseOp) tCon->theScanningOp = 0; theNdb->closeTransaction(tCon); + theNdb->theRemainingStartTransactions--; DBUG_VOID_RETURN; } diff --git a/ndb/src/ndbapi/Ndbif.cpp b/ndb/src/ndbapi/Ndbif.cpp index c550701229c..fee6f0930ad 100644 --- a/ndb/src/ndbapi/Ndbif.cpp +++ b/ndb/src/ndbapi/Ndbif.cpp @@ -107,12 +107,10 @@ Ndb::init(int aMaxNoOfTransactions) goto error_handler; } - tMaxNoOfTransactions = aMaxNoOfTransactions * 3; - if (tMaxNoOfTransactions > 1024) { - tMaxNoOfTransactions = 1024; - }//if + + tMaxNoOfTransactions = aMaxNoOfTransactions; theMaxNoOfTransactions = tMaxNoOfTransactions; - + theRemainingStartTransactions= tMaxNoOfTransactions; thePreparedTransactionsArray = new NdbTransaction* [tMaxNoOfTransactions]; theSentTransactionsArray = new NdbTransaction* [tMaxNoOfTransactions]; theCompletedTransactionsArray = new NdbTransaction* [tMaxNoOfTransactions]; diff --git a/ndb/src/ndbapi/Ndbinit.cpp b/ndb/src/ndbapi/Ndbinit.cpp index ccd0cf85c5e..bbc1474f45d 100644 --- a/ndb/src/ndbapi/Ndbinit.cpp +++ b/ndb/src/ndbapi/Ndbinit.cpp @@ -59,7 +59,7 @@ void Ndb::setup(Ndb_cluster_connection *ndb_cluster_connection, theNoOfPreparedTransactions= 0; theNoOfSentTransactions= 0; theNoOfCompletedTransactions= 0; - theNoOfAllocatedTransactions= 0; + theRemainingStartTransactions= 0; theMaxNoOfTransactions= 0; theMinNoOfEventsToWakeUp= 0; theConIdleList= NULL; diff --git a/ndb/src/ndbapi/Ndblist.cpp b/ndb/src/ndbapi/Ndblist.cpp index 6dfd76c160b..96d0f4d7de5 100644 --- a/ndb/src/ndbapi/Ndblist.cpp +++ b/ndb/src/ndbapi/Ndblist.cpp @@ -93,7 +93,6 @@ Ndb::createConIdleList(int aNrOfCon) } tNdbCon->Status(NdbTransaction::NotConnected); } - theNoOfAllocatedTransactions = aNrOfCon; return aNrOfCon; } @@ -193,14 +192,8 @@ Ndb::getNdbCon() { NdbTransaction* tNdbCon; if ( theConIdleList == NULL ) { - if (theNoOfAllocatedTransactions < theMaxNoOfTransactions) { - tNdbCon = new NdbTransaction(this); - if (tNdbCon == NULL) { - return NULL; - }//if - theNoOfAllocatedTransactions++; - } else { - ndbout << "theNoOfAllocatedTransactions = " << theNoOfAllocatedTransactions << " theMaxNoOfTransactions = " << theMaxNoOfTransactions << endl; + tNdbCon = new NdbTransaction(this); + if (tNdbCon == NULL) { return NULL; }//if tNdbCon->next(NULL); diff --git a/ndb/test/ndbapi/testNdbApi.cpp b/ndb/test/ndbapi/testNdbApi.cpp index 65324af6fe6..137a1d51d82 100644 --- a/ndb/test/ndbapi/testNdbApi.cpp +++ b/ndb/test/ndbapi/testNdbApi.cpp @@ -1269,6 +1269,101 @@ int runBug_11133(NDBT_Context* ctx, NDBT_Step* step){ return result; } +int runScan_4006(NDBT_Context* ctx, NDBT_Step* step){ + int result = NDBT_OK; + const Uint32 max= 5; + const NdbDictionary::Table* pTab = ctx->getTab(); + + Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB"); + if (pNdb == NULL){ + ndbout << "pNdb == NULL" << endl; + return NDBT_FAILED; + } + if (pNdb->init(max)){ + ERR(pNdb->getNdbError()); + delete pNdb; + return NDBT_FAILED; + } + + NdbConnection* pCon = pNdb->startTransaction(); + if (pCon == NULL){ + pNdb->closeTransaction(pCon); + delete pNdb; + return NDBT_FAILED; + } + + Uint32 i; + Vector<NdbScanOperation*> scans; + for(i = 0; i<10*max; i++) + { + NdbScanOperation* pOp = pCon->getNdbScanOperation(pTab->getName()); + if (pOp == NULL){ + ERR(pCon->getNdbError()); + pNdb->closeTransaction(pCon); + delete pNdb; + return NDBT_FAILED; + } + + if (pOp->readTuples() != 0){ + pNdb->closeTransaction(pCon); + ERR(pOp->getNdbError()); + delete pNdb; + return NDBT_FAILED; + } + scans.push_back(pOp); + } + + // Dont' call any equal or setValues + + // Execute should not work + int check = pCon->execute(NoCommit); + if (check == 0){ + ndbout << "execute worked" << endl; + } else { + ERR(pCon->getNdbError()); + } + + for(i= 0; i<scans.size(); i++) + { + NdbScanOperation* pOp= scans[i]; + while((check= pOp->nextResult()) == 0); + if(check != 1) + { + ERR(pOp->getNdbError()); + pNdb->closeTransaction(pCon); + delete pNdb; + return NDBT_FAILED; + } + } + + pNdb->closeTransaction(pCon); + + Vector<NdbConnection*> cons; + for(i= 0; i<10*max; i++) + { + pCon= pNdb->startTransaction(); + if(pCon) + cons.push_back(pCon); + else + break; + } + + for(i= 0; i<cons.size(); i++) + { + cons[i]->close(); + } + + if(cons.size() != max) + { + result= NDBT_FAILED; + } + + delete pNdb; + + return result; +} + +template class Vector<NdbScanOperation*>; NDBT_TESTSUITE(testNdbApi); @@ -1351,6 +1446,12 @@ TESTCASE("Bug_11133", INITIALIZER(runBug_11133); FINALIZER(runClearTable); } +TESTCASE("Scan_4006", + "Check that getNdbScanOperation does not get 4006\n"){ + INITIALIZER(runLoadTable); + INITIALIZER(runScan_4006); + FINALIZER(runClearTable); +} NDBT_TESTSUITE_END(testNdbApi); int main(int argc, const char** argv){ diff --git a/ndb/test/run-test/daily-basic-tests.txt b/ndb/test/run-test/daily-basic-tests.txt index ce9f97a9cb2..5d7d7f58f89 100644 --- a/ndb/test/run-test/daily-basic-tests.txt +++ b/ndb/test/run-test/daily-basic-tests.txt @@ -520,6 +520,10 @@ max-time: 500 cmd: testNdbApi args: -n Bug_11133 T1 +max-time: 500 +cmd: testNdbApi +args: -n Scan_4006 T1 + #max-time: 500 #cmd: testInterpreter #args: T1 diff --git a/ndb/test/run-test/ndb-autotest.sh b/ndb/test/run-test/ndb-autotest.sh index 3ba4d1928d5..09087a8a8c7 100755 --- a/ndb/test/run-test/ndb-autotest.sh +++ b/ndb/test/run-test/ndb-autotest.sh @@ -13,7 +13,7 @@ save_args=$* VERSION="ndb-autotest.sh version 1.04" DATE=`date '+%Y-%m-%d'` -HOST=`hostname -s` +HOST=`hostname` export DATE HOST set -e @@ -330,7 +330,10 @@ start(){ tar cfz /tmp/res.$2.$$.tgz `basename $p2`/$DATE scp /tmp/res.$2.$$.tgz \ $result_host:$result_path/res.$DATE.$HOST.$2.$$.tgz - rm -f /tmp/res.$2.$$.tgz + if [ $? -eq 0 ] + then + rm -f /tmp/res.$2.$$.tgz + fi fi } diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index c141bbd882c..da9f019fb35 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -41,7 +41,7 @@ static const int parallelism= 0; // Default value for max number of transactions // createable against NDB from this handler -static const int max_transactions= 256; +static const int max_transactions= 2; static const char *ha_ndb_ext=".ndb"; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 28ab38c5aed..b5b37efaf07 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1109,12 +1109,14 @@ void Item_func_between::print(String *str) void Item_func_ifnull::fix_length_and_dec() { + agg_result_type(&hybrid_type, args, 2); maybe_null=args[1]->maybe_null; decimals= max(args[0]->decimals, args[1]->decimals); - max_length= (max(args[0]->max_length - args[0]->decimals, - args[1]->max_length - args[1]->decimals) + - decimals); - agg_result_type(&hybrid_type, args, 2); + max_length= (hybrid_type == DECIMAL_RESULT || hybrid_type == INT_RESULT) ? + (max(args[0]->max_length - args[0]->decimals, + args[1]->max_length - args[1]->decimals) + decimals) : + max(args[0]->max_length, args[1]->max_length); + switch (hybrid_type) { case STRING_RESULT: agg_arg_charsets(collation, args, arg_count, MY_COLL_CMP_CONV); @@ -1225,16 +1227,7 @@ Item_func_if::fix_length_and_dec() { maybe_null=args[1]->maybe_null || args[2]->maybe_null; decimals= max(args[1]->decimals, args[2]->decimals); - if (decimals == NOT_FIXED_DEC) - { - max_length= max(args[1]->max_length, args[2]->max_length); - } - else - { - max_length= (max(args[1]->max_length - args[1]->decimals, - args[2]->max_length - args[2]->decimals) + - decimals); - } + enum Item_result arg1_type=args[1]->result_type(); enum Item_result arg2_type=args[2]->result_type(); bool null1=args[1]->const_item() && args[1]->null_value; @@ -1263,6 +1256,11 @@ Item_func_if::fix_length_and_dec() collation.set(&my_charset_bin); // Number } } + max_length= + (cached_result_type == DECIMAL_RESULT || cached_result_type == INT_RESULT) ? + (max(args[1]->max_length - args[1]->decimals, + args[2]->max_length - args[2]->decimals) + decimals) : + max(args[1]->max_length, args[2]->max_length); } diff --git a/sql/my_decimal.h b/sql/my_decimal.h index 27fd33cffbe..b65e6aedaa2 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -290,6 +290,11 @@ int int2my_decimal(uint mask, longlong i, my_bool unsigned_flag, my_decimal *d) inline void my_decimal_neg(decimal_t *arg) { + if (decimal_is_zero(arg)) + { + arg->sign= 0; + return; + } decimal_neg(arg); } diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 338e45fa058..4dca5e32c89 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -482,7 +482,7 @@ bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list); bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc); bool multi_update_precheck(THD *thd, TABLE_LIST *tables); -bool multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count); +bool multi_delete_precheck(THD *thd, TABLE_LIST *tables); bool mysql_multi_update_prepare(THD *thd); bool mysql_multi_delete_prepare(THD *thd); bool mysql_insert_select_prepare(THD *thd); @@ -577,6 +577,7 @@ void mysql_init_query(THD *thd, uchar *buf, uint length); bool mysql_new_select(LEX *lex, bool move_down); void create_select_for_variable(const char *var_name); void mysql_init_multi_delete(LEX *lex); +bool multi_delete_set_locks_and_link_aux_tables(LEX *lex); void init_max_user_conn(void); void init_update_queries(void); void free_max_user_conn(void); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index b5df0be1073..a1887996d00 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -42,7 +42,6 @@ static my_bool open_new_frm(const char *path, const char *alias, uint db_stat, uint prgflag, uint ha_open_flags, TABLE *outparam, TABLE_LIST *table_desc, MEM_ROOT *mem_root); -static void relink_tables_for_multidelete(THD *thd); extern "C" byte *table_cache_key(const byte *record,uint *length, my_bool not_used __attribute__((unused))) @@ -2089,7 +2088,6 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) (thd->fill_derived_tables() && mysql_handle_derived(thd->lex, &mysql_derived_filling))) DBUG_RETURN(TRUE); /* purecov: inspected */ - relink_tables_for_multidelete(thd); DBUG_RETURN(0); } @@ -2119,37 +2117,11 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables) if (open_tables(thd, &tables, &counter) || mysql_handle_derived(thd->lex, &mysql_derived_prepare)) DBUG_RETURN(TRUE); /* purecov: inspected */ - relink_tables_for_multidelete(thd); // Not really needed, but DBUG_RETURN(0); } /* - Let us propagate pointers to open tables from global table list - to table lists for multi-delete -*/ - -static void relink_tables_for_multidelete(THD *thd) -{ - if (thd->lex->all_selects_list->next_select_in_list()) - { - for (SELECT_LEX *sl= thd->lex->all_selects_list; - sl; - sl= sl->next_select_in_list()) - { - for (TABLE_LIST *cursor= (TABLE_LIST *) sl->table_list.first; - cursor; - cursor=cursor->next_local) - { - if (cursor->correspondent_table) - cursor->table= cursor->correspondent_table->table; - } - } - } -} - - -/* Mark all real tables in the list as free for reuse. SYNOPSIS diff --git a/sql/sql_lex.h b/sql/sql_lex.h index a6f729d7677..8af416f0ce8 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -751,7 +751,12 @@ typedef struct st_lex uint grant, grant_tot_col, which_columns; uint fk_delete_opt, fk_update_opt, fk_match_option; uint slave_thd_opt, start_transaction_opt; - uint table_count; /* used when usual update transformed in multiupdate */ + /* + In LEX representing update which were transformed to multi-update + stores total number of tables. For LEX representing multi-delete + holds number of tables from which we will delete records. + */ + uint table_count; uint8 describe; uint8 derived_tables; uint8 create_view_algorithm; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index f48bc3713e6..be7ba7d571d 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3291,10 +3291,9 @@ end_with_restore_list: DBUG_ASSERT(first_table == all_tables && first_table != 0); TABLE_LIST *aux_tables= (TABLE_LIST *)thd->lex->auxilliary_table_list.first; - uint table_count; multi_delete *result; - if ((res= multi_delete_precheck(thd, all_tables, &table_count))) + if ((res= multi_delete_precheck(thd, all_tables))) break; /* condition will be TRUE on SP re-excuting */ @@ -3311,7 +3310,7 @@ end_with_restore_list: goto error; if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables, - table_count))) + lex->table_count))) { res= mysql_select(thd, &select_lex->ref_pointer_array, select_lex->get_table_list(), @@ -6801,23 +6800,19 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables) multi_delete_precheck() thd Thread handler tables Global/local table list - table_count Pointer to table counter RETURN VALUE FALSE OK TRUE error */ -bool multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) +bool multi_delete_precheck(THD *thd, TABLE_LIST *tables) { SELECT_LEX *select_lex= &thd->lex->select_lex; TABLE_LIST *aux_tables= (TABLE_LIST *)thd->lex->auxilliary_table_list.first; - TABLE_LIST *target_tbl; DBUG_ENTER("multi_delete_precheck"); - *table_count= 0; - /* sql_yacc guarantees that tables and aux_tables are not zero */ DBUG_ASSERT(aux_tables != 0); if (check_db_used(thd, tables) || check_db_used(thd,aux_tables) || @@ -6830,9 +6825,35 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0)); DBUG_RETURN(TRUE); } - for (target_tbl= aux_tables; target_tbl; target_tbl= target_tbl->next_local) + DBUG_RETURN(FALSE); +} + + +/* + Link tables in auxilary table list of multi-delete with corresponding + elements in main table list, and set proper locks for them. + + SYNOPSIS + multi_delete_set_locks_and_link_aux_tables() + lex - pointer to LEX representing multi-delete + + RETURN VALUE + FALSE - success + TRUE - error +*/ + +bool multi_delete_set_locks_and_link_aux_tables(LEX *lex) +{ + TABLE_LIST *tables= (TABLE_LIST*)lex->select_lex.table_list.first; + TABLE_LIST *target_tbl; + DBUG_ENTER("multi_delete_set_locks_and_link_aux_tables"); + + lex->table_count= 0; + + for (target_tbl= (TABLE_LIST *)lex->auxilliary_table_list.first; + target_tbl; target_tbl= target_tbl->next_local) { - (*table_count)++; + lex->table_count++; /* All tables in aux_tables must be found in FROM PART */ TABLE_LIST *walk; for (walk= tables; walk; walk= walk->next_local) @@ -6850,14 +6871,6 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) } walk->lock_type= target_tbl->lock_type; target_tbl->correspondent_table= walk; // Remember corresponding table - - /* in case of subselects, we need to set lock_type in - * corresponding table in list of all tables */ - if (walk->correspondent_table) - { - target_tbl->correspondent_table= walk->correspondent_table; - walk->correspondent_table->lock_type= walk->lock_type; - } } DBUG_RETURN(FALSE); } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 4440e542434..f1b3c69264c 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -15,18 +15,18 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /********************************************************************** -This file contains the implementation of prepare and executes. +This file contains the implementation of prepare and executes. Prepare: - - Server gets the query from client with command 'COM_PREPARE'; + - Server gets the query from client with command 'COM_PREPARE'; in the following format: [COM_PREPARE:1] [query] - - Parse the query and recognize any parameter markers '?' and + - Parse the query and recognize any parameter markers '?' and store its information list in lex->param_list - - Allocate a new statement for this prepare; and keep this in + - Allocate a new statement for this prepare; and keep this in 'thd->prepared_statements' pool. - - Without executing the query, return back to client the total + - Without executing the query, return back to client the total number of parameters along with result-set metadata information (if any) in the following format: [STMT_ID:4] @@ -34,10 +34,10 @@ Prepare: [Param_count:2] [Columns meta info] (if Column_count > 0) [Params meta info] (if Param_count > 0 ) (TODO : 4.1.1) - + Prepare-execute: - - Server gets the command 'COM_EXECUTE' to execute the + - Server gets the command 'COM_EXECUTE' to execute the previously prepared query. If there is any param markers; then client will send the data in the following format: [COM_EXECUTE:1] @@ -45,12 +45,12 @@ Prepare-execute: [NULL_BITS:(param_count+7)/8)] [TYPES_SUPPLIED_BY_CLIENT(0/1):1] [[length]data] - [[length]data] .. [[length]data]. - (Note: Except for string/binary types; all other types will not be + [[length]data] .. [[length]data]. + (Note: Except for string/binary types; all other types will not be supplied with length field) - - Replace the param items with this new data. If it is a first execute + - Replace the param items with this new data. If it is a first execute or types altered by client; then setup the conversion routines. - - Execute the query without re-parsing and send back the results + - Execute the query without re-parsing and send back the results to client Long data handling: @@ -61,8 +61,8 @@ Long data handling: - data from the packet is appended to long data value buffer for this placeholder. - It's up to the client to check for read data ended. The server doesn't - care; and also server doesn't notify to the client that it got the - data or not; if there is any error; then during execute; the error + care; and also server doesn't notify to the client that it got the + data or not; if there is any error; then during execute; the error will be returned ***********************************************************************/ @@ -97,7 +97,7 @@ public: #else bool (*set_params_data)(Prepared_statement *st, String *expanded_query); #endif - bool (*set_params_from_vars)(Prepared_statement *stmt, + bool (*set_params_from_vars)(Prepared_statement *stmt, List<LEX_STRING>& varnames, String *expanded_query); public: @@ -167,7 +167,7 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns) Send types and names of placeholders to the client XXX: fix this nasty upcast from List<Item_param> to List<Item> */ - DBUG_RETURN(my_net_write(net, buff, sizeof(buff)) || + DBUG_RETURN(my_net_write(net, buff, sizeof(buff)) || (stmt->param_count && stmt->thd->protocol_simple.send_fields((List<Item> *) &stmt->lex->param_list, @@ -220,7 +220,7 @@ static ulong get_param_length(uchar **packet, ulong len) } if (len < 5) return 0; - (*packet)+=9; // Must be 254 when here + (*packet)+=9; // Must be 254 when here /* In our client-server protocol all numbers bigger than 2^24 stored as 8 bytes with uint8korr. Here we always know that @@ -242,7 +242,7 @@ static ulong get_param_length(uchar **packet, ulong len) pos input data buffer len length of data in the buffer - All these functions read the data from pos, convert it to requested type + All these functions read the data from pos, convert it to requested type and assign to param; pos is advanced to predefined length. Make a note that the NULL handling is examined at first execution @@ -260,7 +260,7 @@ static void set_param_tiny(Item_param *param, uchar **pos, ulong len) return; #endif int8 value= (int8) **pos; - param->set_int(param->unsigned_flag ? (longlong) ((uint8) value) : + param->set_int(param->unsigned_flag ? (longlong) ((uint8) value) : (longlong) value, 4); *pos+= 1; } @@ -480,7 +480,7 @@ static void set_param_str(Item_param *param, uchar **pos, ulong len) } -#undef get_param_length +#undef get_param_length static void setup_one_conversion_function(THD *thd, Item_param *param, uchar param_type) @@ -583,12 +583,12 @@ static void setup_one_conversion_function(THD *thd, Item_param *param, #ifndef EMBEDDED_LIBRARY /* - Update the parameter markers by reading data from client packet + Update the parameter markers by reading data from client packet and if binary/update log is set, generate the valid query. */ static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array, - uchar *read_pos, uchar *data_end, + uchar *read_pos, uchar *data_end, String *query) { THD *thd= stmt->thd; @@ -596,14 +596,14 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array, Item_param **end= begin + stmt->param_count; uint32 length= 0; - String str; + String str; const String *res; - DBUG_ENTER("insert_params_withlog"); + DBUG_ENTER("insert_params_withlog"); if (query->copy(stmt->query, stmt->query_length, default_charset_info)) DBUG_RETURN(1); - + for (Item_param **it= begin; it < end; ++it) { Item_param *param= *it; @@ -624,7 +624,7 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array, if (query->replace(param->pos_in_query+length, 1, *res)) DBUG_RETURN(1); - + length+= res->length()-1; } DBUG_RETURN(0); @@ -632,13 +632,13 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array, static bool insert_params(Prepared_statement *stmt, uchar *null_array, - uchar *read_pos, uchar *data_end, + uchar *read_pos, uchar *data_end, String *expanded_query) { Item_param **begin= stmt->param_array; Item_param **end= begin + stmt->param_count; - DBUG_ENTER("insert_params"); + DBUG_ENTER("insert_params"); for (Item_param **it= begin; it < end; ++it) { @@ -672,7 +672,7 @@ static bool setup_conversion_functions(Prepared_statement *stmt, if (*read_pos++) //types supplied / first execute { /* - First execute or types altered by the client, setup the + First execute or types altered by the client, setup the conversion routines for all parameters (one time) */ Item_param **it= stmt->param_array; @@ -720,8 +720,8 @@ static bool emb_insert_params(Prepared_statement *stmt, String *expanded_query) uchar *buff= (uchar*) client_param->buffer; param->unsigned_flag= client_param->is_unsigned; param->set_param_func(param, &buff, - client_param->length ? - *client_param->length : + client_param->length ? + *client_param->length : client_param->buffer_length); } } @@ -747,7 +747,7 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt, String *query) if (query->copy(stmt->query, stmt->query_length, default_charset_info)) DBUG_RETURN(1); - + for (; it < end; ++it, ++client_param) { Item_param *param= *it; @@ -759,10 +759,10 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt, String *query) else { uchar *buff= (uchar*)client_param->buffer; - param->unsigned_flag= client_param->is_unsigned; + param->unsigned_flag= client_param->is_unsigned; param->set_param_func(param, &buff, - client_param->length ? - *client_param->length : + client_param->length ? + *client_param->length : client_param->buffer_length); } } @@ -881,21 +881,21 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, } /* - Validate INSERT statement: + Validate INSERT statement: SYNOPSIS mysql_test_insert() - stmt prepared statemen handler - tables global/local table list + stmt prepared statemen handler + tables global/local table list RETURN VALUE - FALSE OK - TRUE error + FALSE success + TRUE error, error message is set in THD */ static bool mysql_test_insert(Prepared_statement *stmt, TABLE_LIST *table_list, - List<Item> &fields, + List<Item> &fields, List<List_item> &values_list, List<Item> &update_fields, List<Item> &update_values, @@ -905,11 +905,10 @@ static bool mysql_test_insert(Prepared_statement *stmt, LEX *lex= stmt->lex; List_iterator_fast<List_item> its(values_list); List_item *values; - bool res; DBUG_ENTER("mysql_test_insert"); - if ((res= insert_precheck(thd, table_list))) - DBUG_RETURN(res); + if (insert_precheck(thd, table_list)) + goto error; /* open temporary memory pool for temporary data allocated by derived @@ -920,10 +919,7 @@ static bool mysql_test_insert(Prepared_statement *stmt, TL_WRITE_DELAYED as having two such locks can cause table corruption. */ if (open_normal_and_derived_tables(thd, table_list)) - { - DBUG_RETURN(TRUE); - } - + goto error; if ((values= its++)) { @@ -937,17 +933,14 @@ static bool mysql_test_insert(Prepared_statement *stmt, table_list->table->insert_values=(byte *)1; } - if ((res= mysql_prepare_insert(thd, table_list, table_list->table, - fields, values, update_fields, - update_values, duplic, - &unused_conds, FALSE))) + if (mysql_prepare_insert(thd, table_list, table_list->table, fields, + values, update_fields, update_values, duplic, + &unused_conds, FALSE)) goto error; value_count= values->elements; its.rewind(); - res= TRUE; - if (table_list->lock_type == TL_WRITE_DELAYED && !(table_list->table->file->table_flags() & HA_CAN_INSERT_DELAYED)) { @@ -965,15 +958,14 @@ static bool mysql_test_insert(Prepared_statement *stmt, goto error; } if (setup_fields(thd, 0, table_list, *values, 0, 0, 0)) - goto error; + goto error; } } + DBUG_RETURN(FALSE); - res= FALSE; error: - lex->unit.cleanup(); /* insert_values is cleared in open_table */ - DBUG_RETURN(res); + DBUG_RETURN(TRUE); } @@ -982,14 +974,15 @@ error: SYNOPSIS mysql_test_update() - stmt prepared statemen handler - tables list of tables queries + stmt prepared statemen handler + tables list of tables queries RETURN VALUE 0 success + 1 error, error message is set in THD 2 convert to multi_update - 1 error */ + static int mysql_test_update(Prepared_statement *stmt, TABLE_LIST *table_list) { @@ -998,74 +991,65 @@ static int mysql_test_update(Prepared_statement *stmt, uint table_count= 0; SELECT_LEX *select= &stmt->lex->select_lex; #ifndef NO_EMBEDDED_ACCESS_CHECKS - uint want_privilege; + uint want_privilege; #endif DBUG_ENTER("mysql_test_update"); if (update_precheck(thd, table_list)) - DBUG_RETURN(1); + goto error; + + if (open_tables(thd, &table_list, &table_count)) + goto error; - if (!open_tables(thd, &table_list, &table_count)) + if (table_list->multitable_view) { - if (table_list->multitable_view) - { - DBUG_ASSERT(table_list->view != 0); - DBUG_PRINT("info", ("Switch to multi-update")); - /* pass counter value */ - thd->lex->table_count= table_count; - /* convert to multiupdate */ - return 2; - } + DBUG_ASSERT(table_list->view != 0); + DBUG_PRINT("info", ("Switch to multi-update")); + /* pass counter value */ + thd->lex->table_count= table_count; + /* convert to multiupdate */ + DBUG_RETURN(2); + } - /* - thd->fill_derived_tables() is false here for sure (because it is - preparation of PS, so we even do not check it - */ - if (lock_tables(thd, table_list, table_count) || - mysql_handle_derived(thd->lex, &mysql_derived_prepare)) - DBUG_RETURN(1); + /* + thd->fill_derived_tables() is false here for sure (because it is + preparation of PS, so we even do not check it). + */ + if (lock_tables(thd, table_list, table_count) || + mysql_handle_derived(thd->lex, &mysql_derived_prepare)) + goto error; #ifndef NO_EMBEDDED_ACCESS_CHECKS /* TABLE_LIST contain right privilages request */ want_privilege= table_list->grant.want_privilege; #endif - if (!(res= mysql_prepare_update(thd, table_list, - &select->where, - select->order_list.elements, - (ORDER *) select->order_list.first))) - { + if (mysql_prepare_update(thd, table_list, &select->where, + select->order_list.elements, + (ORDER *) select->order_list.first)) + goto error; + #ifndef NO_EMBEDDED_ACCESS_CHECKS - table_list->grant.want_privilege= - table_list->table->grant.want_privilege= - want_privilege; + table_list->grant.want_privilege= want_privilege; + table_list->table->grant.want_privilege= want_privilege; #endif - thd->lex->select_lex.no_wrap_view_item= 1; - if (setup_fields(thd, 0, table_list, select->item_list, 1, 0, 0)) - { - res= 1; - thd->lex->select_lex.no_wrap_view_item= 0; - } - else - { - thd->lex->select_lex.no_wrap_view_item= 0; + thd->lex->select_lex.no_wrap_view_item= 1; + res= setup_fields(thd, 0, table_list, select->item_list, 1, 0, 0); + thd->lex->select_lex.no_wrap_view_item= 0; + if (res) + goto error; #ifndef NO_EMBEDDED_ACCESS_CHECKS - /* Check values */ - table_list->grant.want_privilege= - table_list->table->grant.want_privilege= - (SELECT_ACL & ~table_list->table->grant.privilege); + /* Check values */ + table_list->grant.want_privilege= + table_list->table->grant.want_privilege= + (SELECT_ACL & ~table_list->table->grant.privilege); #endif - if (setup_fields(thd, 0, table_list, - stmt->lex->value_list, 0, 0, 0)) - res= 1; - } - } - stmt->lex->unit.cleanup(); - } - else - res= 1; - /* TODO: here we should send types of placeholders to the client. */ - DBUG_RETURN(res); + if (setup_fields(thd, 0, table_list, stmt->lex->value_list, 0, 0, 0)) + goto error; + /* TODO: here we should send types of placeholders to the client. */ + DBUG_RETURN(0); +error: + DBUG_RETURN(1); } @@ -1074,38 +1058,34 @@ static int mysql_test_update(Prepared_statement *stmt, SYNOPSIS mysql_test_delete() - stmt prepared statemen handler - tables list of tables queries + stmt prepared statemen handler + tables list of tables queries RETURN VALUE FALSE success - TRUE error + TRUE error, error message is set in THD */ -static int mysql_test_delete(Prepared_statement *stmt, - TABLE_LIST *table_list) + +static bool mysql_test_delete(Prepared_statement *stmt, + TABLE_LIST *table_list) { THD *thd= stmt->thd; LEX *lex= stmt->lex; DBUG_ENTER("mysql_test_delete"); - if (delete_precheck(thd, table_list)) - DBUG_RETURN(TRUE); + if (delete_precheck(thd, table_list) || + open_and_lock_tables(thd, table_list)) + goto error; - if (!open_and_lock_tables(thd, table_list)) + if (!table_list->table) { - bool res; - if (!table_list->table) - { - my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - table_list->view_db.str, table_list->view_name.str); - DBUG_RETURN(-1); - } - - res= mysql_prepare_delete(thd, table_list, &lex->select_lex.where); - lex->unit.cleanup(); - DBUG_RETURN(res); + my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), + table_list->view_db.str, table_list->view_name.str); + goto error; } - /* TODO: here we should send types of placeholders to the client. */ + + DBUG_RETURN(mysql_prepare_delete(thd, table_list, &lex->select_lex.where)); +error: DBUG_RETURN(TRUE); } @@ -1113,25 +1093,24 @@ static int mysql_test_delete(Prepared_statement *stmt, /* Validate SELECT statement. In case of success, if this query is not EXPLAIN, send column list info - back to client. + back to client. SYNOPSIS mysql_test_select() - stmt prepared statemen handler - tables list of tables queries + stmt prepared statemen handler + tables list of tables queries RETURN VALUE FALSE success TRUE error, sent to client */ -static int mysql_test_select(Prepared_statement *stmt, - TABLE_LIST *tables, bool text_protocol) +static bool mysql_test_select(Prepared_statement *stmt, + TABLE_LIST *tables, bool text_protocol) { THD *thd= stmt->thd; LEX *lex= stmt->lex; SELECT_LEX_UNIT *unit= &lex->unit; - bool result; DBUG_ENTER("mysql_test_select"); #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -1139,19 +1118,20 @@ static int mysql_test_select(Prepared_statement *stmt, if (tables) { if (check_table_access(thd, privilege, tables,0)) - DBUG_RETURN(TRUE); + goto error; } else if (check_access(thd, privilege, any_db,0,0,0)) - DBUG_RETURN(TRUE); + goto error; #endif - result= TRUE; if (!lex->result && !(lex->result= new (stmt->mem_root) select_send)) - goto err; + { + my_error(ER_OUTOFMEMORY, MYF(0), sizeof(select_send)); + goto error; + } if (open_and_lock_tables(thd, tables)) - goto err; - + goto error; thd->used_tables= 0; // Updated by setup_fields @@ -1161,15 +1141,13 @@ static int mysql_test_select(Prepared_statement *stmt, usual, and we pass 0 as setup_tables_done_option */ if (unit->prepare(thd, 0, 0, "")) - { - goto err_prep; - } + goto error; if (!text_protocol) { if (lex->describe) { if (send_prep_stmt(stmt, 0) || thd->protocol->flush()) - goto err_prep; + goto error; } else { @@ -1179,7 +1157,7 @@ static int mysql_test_select(Prepared_statement *stmt, /* Change columns if a procedure like analyse() */ if (unit->last_procedure && unit->last_procedure->change_columns(fields)) - goto err_prep; + goto error; /* We can use lex->result as it should've been @@ -1188,15 +1166,12 @@ static int mysql_test_select(Prepared_statement *stmt, if (send_prep_stmt(stmt, lex->result->field_count(fields)) || lex->result->send_fields(fields, Protocol::SEND_EOF) || thd->protocol->flush()) - goto err_prep; + goto error; } } - result= FALSE; // ok - -err_prep: - unit->cleanup(); -err: - DBUG_RETURN(result); + DBUG_RETURN(FALSE); +error: + DBUG_RETURN(TRUE); } @@ -1205,32 +1180,28 @@ err: SYNOPSIS mysql_test_do_fields() - stmt prepared statemen handler - tables list of tables queries - values list of expressions + stmt prepared statemen handler + tables list of tables queries + values list of expressions RETURN VALUE FALSE success - TRUE error, sent to client + TRUE error, error message is set in THD */ static bool mysql_test_do_fields(Prepared_statement *stmt, - TABLE_LIST *tables, - List<Item> *values) + TABLE_LIST *tables, + List<Item> *values) { - DBUG_ENTER("mysql_test_do_fields"); THD *thd= stmt->thd; - bool res; + + DBUG_ENTER("mysql_test_do_fields"); if (tables && check_table_access(thd, SELECT_ACL, tables, 0)) DBUG_RETURN(TRUE); if (open_and_lock_tables(thd, tables)) - { DBUG_RETURN(TRUE); - } - res= setup_fields(thd, 0, 0, *values, 0, 0, 0); - stmt->lex->unit.cleanup(); - DBUG_RETURN(res); + DBUG_RETURN(setup_fields(thd, 0, 0, *values, 0, 0, 0)); } @@ -1239,14 +1210,15 @@ static bool mysql_test_do_fields(Prepared_statement *stmt, SYNOPSIS mysql_test_set_fields() - stmt prepared statemen handler - tables list of tables queries - values list of expressions + stmt prepared statemen handler + tables list of tables queries + values list of expressions RETURN VALUE FALSE success TRUE error */ + static bool mysql_test_set_fields(Prepared_statement *stmt, TABLE_LIST *tables, List<set_var_base> *var_list) @@ -1255,25 +1227,19 @@ static bool mysql_test_set_fields(Prepared_statement *stmt, List_iterator_fast<set_var_base> it(*var_list); THD *thd= stmt->thd; set_var_base *var; - bool res; - if (tables && check_table_access(thd, SELECT_ACL, tables, 0)) - DBUG_RETURN(TRUE); - - if ((res= open_and_lock_tables(thd, tables))) + if (tables && check_table_access(thd, SELECT_ACL, tables, 0) || + open_and_lock_tables(thd, tables)) goto error; + while ((var= it++)) { if (var->light_check(thd)) - { - stmt->lex->unit.cleanup(); - res= TRUE; goto error; - } } + DBUG_RETURN(FALSE); error: - stmt->lex->unit.cleanup(); - DBUG_RETURN(res); + DBUG_RETURN(TRUE); } @@ -1294,7 +1260,7 @@ error: RETURN VALUE FALSE success - TRUE error + TRUE error, error message is set in THD */ static bool select_like_stmt_test(Prepared_statement *stmt, @@ -1304,24 +1270,16 @@ static bool select_like_stmt_test(Prepared_statement *stmt, DBUG_ENTER("select_like_stmt_test"); THD *thd= stmt->thd; LEX *lex= stmt->lex; - bool res= FALSE; - if (specific_prepare && (res= (*specific_prepare)(thd))) - goto end; + if (specific_prepare && (*specific_prepare)(thd)) + DBUG_RETURN(TRUE); thd->used_tables= 0; // Updated by setup_fields - // JOIN::prepare calls - if (lex->unit.prepare(thd, 0, setup_tables_done_option, "")) - { - res= TRUE; - } -end: - lex->unit.cleanup(); - DBUG_RETURN(res); + /* Calls JOIN::prepare */ + DBUG_RETURN(lex->unit.prepare(thd, 0, setup_tables_done_option, "")); } - /* Check internal SELECT of the prepared command (with opening and locking tables used). @@ -1365,29 +1323,30 @@ select_like_stmt_test_with_open_n_lock(Prepared_statement *stmt, SYNOPSIS mysql_test_create_table() - stmt prepared statemen handler - tables list of tables queries + stmt prepared statemen handler + tables list of tables queries RETURN VALUE - 0 success - 1 error, sent to client - -1 error, not sent to client + FALSE success + TRUE error, error message is set in THD */ -static int mysql_test_create_table(Prepared_statement *stmt) +static bool mysql_test_create_table(Prepared_statement *stmt) { DBUG_ENTER("mysql_test_create_table"); THD *thd= stmt->thd; LEX *lex= stmt->lex; SELECT_LEX *select_lex= &lex->select_lex; - int res= 0; + bool res= FALSE; /* Skip first table, which is the table we are creating */ bool link_to_local; TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local); TABLE_LIST *tables= lex->query_tables; - if (!(res= create_table_precheck(thd, tables, create_table)) && - select_lex->item_list.elements) + if (create_table_precheck(thd, tables, create_table)) + DBUG_RETURN(TRUE); + + if (select_lex->item_list.elements) { select_lex->resolve_mode= SELECT_LEX::SELECT_MODE; res= select_like_stmt_test_with_open_n_lock(stmt, tables, 0, 0); @@ -1405,8 +1364,8 @@ static int mysql_test_create_table(Prepared_statement *stmt) SYNOPSIS mysql_test_multiupdate() - stmt prepared statemen handler - tables list of tables queries + stmt prepared statemen handler + tables list of tables queries converted converted to multi-update from usual update RETURN VALUE @@ -1415,7 +1374,7 @@ static int mysql_test_create_table(Prepared_statement *stmt) */ static bool mysql_test_multiupdate(Prepared_statement *stmt, - TABLE_LIST *tables, + TABLE_LIST *tables, bool converted) { /* if we switched from normal update, rights are checked */ @@ -1432,37 +1391,38 @@ static bool mysql_test_multiupdate(Prepared_statement *stmt, SYNOPSIS mysql_test_multidelete() - stmt prepared statemen handler - tables list of tables queries + stmt prepared statemen handler + tables list of tables queries RETURN VALUE 0 success - 1 error, sent to client - -1 error, not sent to client + 1 error, error message in THD is set. */ -static int mysql_test_multidelete(Prepared_statement *stmt, - TABLE_LIST *tables) + +static bool mysql_test_multidelete(Prepared_statement *stmt, + TABLE_LIST *tables) { - int res; stmt->thd->lex->current_select= &stmt->thd->lex->select_lex; if (add_item_to_list(stmt->thd, new Item_null())) - return -1; - - uint fake_counter; - if ((res= multi_delete_precheck(stmt->thd, tables, &fake_counter))) - return res; - if ((res= select_like_stmt_test_with_open_n_lock(stmt, tables, - &mysql_multi_delete_prepare, - OPTION_SETUP_TABLES_DONE))) - return res; + { + my_error(ER_OUTOFMEMORY, MYF(0), 0); + goto error; + } + + if (multi_delete_precheck(stmt->thd, tables) || + select_like_stmt_test_with_open_n_lock(stmt, tables, + &mysql_multi_delete_prepare, + OPTION_SETUP_TABLES_DONE)) + goto error; if (!tables->table) { my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0), - tables->view_db.str, tables->view_name.str); - return -1; + tables->view_db.str, tables->view_name.str); + goto error; } - return 0; - + return FALSE; +error: + return TRUE; } @@ -1498,8 +1458,8 @@ static bool mysql_insert_select_prepare_tester(THD *thd) SYNOPSIS mysql_test_insert_select() - stmt prepared statemen handler - tables list of tables of query + stmt prepared statemen handler + tables list of tables of query RETURN VALUE 0 success @@ -1508,7 +1468,7 @@ static bool mysql_insert_select_prepare_tester(THD *thd) */ static int mysql_test_insert_select(Prepared_statement *stmt, - TABLE_LIST *tables) + TABLE_LIST *tables) { int res; LEX *lex= stmt->lex; @@ -1520,8 +1480,8 @@ static int mysql_test_insert_select(Prepared_statement *stmt, tables->table->insert_values=(byte *)1; } - if ((res= insert_precheck(stmt->thd, tables))) - return res; + if (insert_precheck(stmt->thd, tables)) + return 1; /* store it, because mysql_insert_select_prepare_tester change it */ first_local_table= (TABLE_LIST *)lex->select_lex.table_list.first; @@ -1552,12 +1512,12 @@ static int mysql_test_insert_select(Prepared_statement *stmt, by calling fix_fields. RETURN VALUE - 0 success - 1 error, sent to client + FALSE success, statement metadata is sent to client + TRUE error, error message is set (but not sent) */ -static int check_prepared_statement(Prepared_statement *stmt, - bool text_protocol) +static bool check_prepared_statement(Prepared_statement *stmt, + bool text_protocol) { THD *thd= stmt->thd; LEX *lex= stmt->lex; @@ -1576,14 +1536,14 @@ static int check_prepared_statement(Prepared_statement *stmt, case SQLCOM_REPLACE: case SQLCOM_INSERT: res= mysql_test_insert(stmt, tables, lex->field_list, - lex->many_values, - select_lex->item_list, lex->value_list, - lex->duplicates); + lex->many_values, + select_lex->item_list, lex->value_list, + lex->duplicates); break; case SQLCOM_UPDATE: res= mysql_test_update(stmt, tables); - /* mysql_test_update return 2 if we need to switch to multi-update */ + /* mysql_test_update returns 2 if we need to switch to multi-update */ if (res != 2) break; @@ -1599,12 +1559,12 @@ static int check_prepared_statement(Prepared_statement *stmt, if ((res= mysql_test_select(stmt, tables, text_protocol))) goto error; /* Statement and field info has already been sent */ - DBUG_RETURN(0); + DBUG_RETURN(FALSE); case SQLCOM_CREATE_TABLE: res= mysql_test_create_table(stmt); break; - + case SQLCOM_DO: res= mysql_test_do_fields(stmt, tables, lex->insert_list); break; @@ -1650,18 +1610,15 @@ static int check_prepared_statement(Prepared_statement *stmt, break; default: - /* - All other is not supported yet - */ - res= -1; + /* All other statements are not supported yet. */ my_message(ER_UNSUPPORTED_PS, ER(ER_UNSUPPORTED_PS), MYF(0)); goto error; } if (res == 0) - DBUG_RETURN(text_protocol? 0 : (send_prep_stmt(stmt, 0) || - thd->protocol->flush())); + DBUG_RETURN(text_protocol? FALSE : (send_prep_stmt(stmt, 0) || + thd->protocol->flush())); error: - DBUG_RETURN(1); + DBUG_RETURN(TRUE); } /* @@ -1702,30 +1659,30 @@ static bool init_param_array(Prepared_statement *stmt) /* Given a query string with parameter markers, create a Prepared Statement from it and send PS info back to the client. - + SYNOPSIS mysql_stmt_prepare() - packet query to be prepared - packet_length query string length, including ignored trailing NULL or + packet query to be prepared + packet_length query string length, including ignored trailing NULL or quote char. name NULL or statement name. For unnamed statements binary PS - protocol is used, for named statements text protocol is + protocol is used, for named statements text protocol is used. RETURN FALSE OK, statement prepared successfully TRUE Error NOTES - This function parses the query and sends the total number of parameters - and resultset metadata information back to client (if any), without - executing the query i.e. without any log/disk writes. This allows the - queries to be re-executed without re-parsing during execute. + This function parses the query and sends the total number of parameters + and resultset metadata information back to client (if any), without + executing the query i.e. without any log/disk writes. This allows the + queries to be re-executed without re-parsing during execute. If parameter markers are found in the query, then store the information - using Item_param along with maintaining a list in lex->param_array, so - that a fast and direct retrieval can be made without going through all + using Item_param along with maintaining a list in lex->param_array, so + that a fast and direct retrieval can be made without going through all field items. - + */ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, @@ -1803,6 +1760,7 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, thd->lex->sphead= NULL; } lex_end(lex); + lex->unit.cleanup(); close_thread_tables(thd); thd->restore_backup_statement(stmt, &thd->stmt_backup); cleanup_items(stmt->free_list); @@ -1897,8 +1855,8 @@ void reset_stmt_for_execute(THD *thd, LEX *lex) } /* - TODO: When the new table structure is ready, then have a status bit - to indicate the table is altered, and re-do the setup_* + TODO: When the new table structure is ready, then have a status bit + to indicate the table is altered, and re-do the setup_* and open the tables back. */ /* @@ -1907,8 +1865,8 @@ void reset_stmt_for_execute(THD *thd, LEX *lex) they have their own table list). */ for (TABLE_LIST *tables= lex->query_tables; - tables; - tables= tables->next_global) + tables; + tables= tables->next_global) { /* Reset old pointers to TABLEs: they are not valid since the tables @@ -1939,10 +1897,10 @@ void reset_stmt_for_execute(THD *thd, LEX *lex) /* Clears parameters from data left from previous execution or long data - + SYNOPSIS reset_stmt_params() - stmt prepared statement for which parameters should be reset + stmt prepared statement for which parameters should be reset */ static void reset_stmt_params(Prepared_statement *stmt) @@ -2039,8 +1997,8 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) } #else /* - In embedded library we re-install conversion routines each time - we set params, and also we don't need to parse packet. + In embedded library we re-install conversion routines each time + we set params, and also we don't need to parse packet. So we do it in one function. */ if (stmt->param_count && stmt->set_params_data(stmt, &expanded_query)) @@ -2203,9 +2161,9 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt, SYNOPSIS mysql_stmt_fetch() - thd Thread handler - packet Packet from client (with stmt_id & num_rows) - packet_length Length of packet + thd Thread handler + packet Packet from client (with stmt_id & num_rows) + packet_length Length of packet */ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) @@ -2232,7 +2190,7 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(), QUERY_PRIOR); - thd->protocol= &thd->protocol_prep; // Switch to binary protocol + thd->protocol= &thd->protocol_prep; // Switch to binary protocol stmt->cursor->fetch(num_rows); thd->protocol= &thd->protocol_simple; // Use normal protocol @@ -2253,7 +2211,7 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) SYNOPSIS mysql_stmt_reset() thd Thread handle - packet Packet with stmt id + packet Packet with stmt id DESCRIPTION This function resets statement to the state it was right after prepare. @@ -2280,22 +2238,22 @@ void mysql_stmt_reset(THD *thd, char *packet) stmt->state= Item_arena::PREPARED; - /* - Clear parameters from data which could be set by + /* + Clear parameters from data which could be set by mysql_stmt_send_long_data() call. */ reset_stmt_params(stmt); mysql_reset_thd_for_next_command(thd); send_ok(thd); - + DBUG_VOID_RETURN; } /* Delete a prepared statement from memory. - Note: we don't send any reply to that command. + Note: we don't send any reply to that command. */ void mysql_stmt_free(THD *thd, char *packet) @@ -2320,9 +2278,9 @@ void mysql_stmt_free(THD *thd, char *packet) SYNOPSIS mysql_stmt_get_longdata() - thd Thread handle - pos String to append - packet_length Length of string + thd Thread handle + pos String to append + packet_length Length of string DESCRIPTION Get a part of a long data. @@ -2341,7 +2299,7 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length) Prepared_statement *stmt; Item_param *param; char *packet_end= packet + packet_length - 1; - + DBUG_ENTER("mysql_stmt_get_longdata"); #ifndef EMBEDDED_LIBRARY diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 719b42e890f..8ece85b3dee 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -919,6 +919,11 @@ deallocate: yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "DEALLOCATE"); + YYABORT; + } lex->sql_command= SQLCOM_DEALLOCATE_PREPARE; lex->prepared_stmt_name= $3; }; @@ -939,6 +944,11 @@ prepare: yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "PREPARE"); + YYABORT; + } lex->sql_command= SQLCOM_PREPARE; lex->prepared_stmt_name= $2; }; @@ -969,6 +979,11 @@ execute: yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "EXECUTE"); + YYABORT; + } lex->sql_command= SQLCOM_EXECUTE; lex->prepared_stmt_name= $2; } @@ -6132,10 +6147,17 @@ single_multi: | table_wild_list { mysql_init_multi_delete(Lex); } FROM join_table_list where_clause + { + if (multi_delete_set_locks_and_link_aux_tables(Lex)) + YYABORT; + } | FROM table_wild_list { mysql_init_multi_delete(Lex); } USING join_table_list where_clause - {} + { + if (multi_delete_set_locks_and_link_aux_tables(Lex)) + YYABORT; + } ; table_wild_list: |