summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/create.result2
-rw-r--r--mysql-test/r/distinct.result8
-rw-r--r--mysql-test/r/ndb_subquery.result19
-rw-r--r--mysql-test/r/sp-error.result14
-rw-r--r--mysql-test/r/sp-threads.result15
-rw-r--r--mysql-test/r/type_newdecimal.result11
-rw-r--r--mysql-test/t/distinct.test8
-rw-r--r--mysql-test/t/ndb_subquery.test23
-rw-r--r--mysql-test/t/sp-error.test21
-rw-r--r--mysql-test/t/sp-threads.test26
-rw-r--r--mysql-test/t/type_newdecimal.test11
-rw-r--r--ndb/include/ndbapi/Ndb.hpp2
-rw-r--r--ndb/src/ndbapi/Ndb.cpp9
-rw-r--r--ndb/src/ndbapi/NdbScanOperation.cpp6
-rw-r--r--ndb/src/ndbapi/Ndbif.cpp8
-rw-r--r--ndb/src/ndbapi/Ndbinit.cpp2
-rw-r--r--ndb/src/ndbapi/Ndblist.cpp11
-rw-r--r--ndb/test/ndbapi/testNdbApi.cpp101
-rw-r--r--ndb/test/run-test/daily-basic-tests.txt4
-rwxr-xr-xndb/test/run-test/ndb-autotest.sh7
-rw-r--r--sql/ha_ndbcluster.cc2
-rw-r--r--sql/item_cmpfunc.cc26
-rw-r--r--sql/my_decimal.h5
-rw-r--r--sql/mysql_priv.h3
-rw-r--r--sql/sql_base.cc28
-rw-r--r--sql/sql_lex.h7
-rw-r--r--sql/sql_parse.cc49
-rw-r--r--sql/sql_prepare.cc520
-rw-r--r--sql/sql_yacc.yy24
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: