From: Frazer Clement Date: May 29 2012 5:28pm Subject: bzr push into mysql-5.5-cluster-7.2 branch (frazer.clement:3926 to 3927) List-Archive: http://lists.mysql.com/commits/144011 Message-Id: <201205291728.q4THSYm7031205@acsmt356.oracle.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit 3927 Frazer Clement 2012-05-29 [merge] Merge 7.1->7.2 added: storage/ndb/clusterj/clusterj-test/src/main/java/testsuite/clusterj/QueryLimitsTest.java storage/ndb/clusterj/clusterj-tie/src/test/java/testsuite/clusterj/tie/QueryLimitsTest.java modified: storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/Query.java storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/query/QueryDomainTypeImpl.java storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/query/QueryExecutionContextImpl.java storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/query/QueryImpl.java storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/store/ScanOperation.java storage/ndb/clusterj/clusterj-core/src/main/resources/com/mysql/clusterj/core/Bundle.properties storage/ndb/clusterj/clusterj-jdbc/src/main/java/com/mysql/clusterj/jdbc/SQLExecutor.java storage/ndb/clusterj/clusterj-test/src/main/java/testsuite/clusterj/AbstractQueryTest.java storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/ClusterTransactionImpl.java storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/DbImpl.java storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/NdbRecordScanOperationImpl.java storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/NdbRecordScanResultDataImpl.java storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/ScanOperationImpl.java storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/ScanResultDataImpl.java storage/ndb/include/mgmapi/ndbd_exit_codes.h storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp storage/ndb/src/kernel/error/ndbd_exit_codes.c 3926 magnus.blaudd@stripped 2012-05-23 [merge] Merge 7.1 -> 7.2 Fixed the other place in ha_ndbcluster_cond for BUG#14106592 modified: mysql-test/suite/ndb/r/ndb_condition_pushdown.result mysql-test/suite/ndb/t/ndb_condition_pushdown.test sql/ha_ndbcluster_cond.cc storage/ndb/src/common/util/SocketClient.cpp === modified file 'storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/Query.java' --- a/storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/Query.java 2011-02-03 14:37:50 +0000 +++ b/storage/ndb/clusterj/clusterj-api/src/main/java/com/mysql/clusterj/Query.java 2012-05-23 23:51:17 +0000 @@ -103,4 +103,20 @@ public interface Query { */ Map explain(); + /** + * Set limits on results to return. The execution of the query is + * modified to return only a subset of results. If the filter would + * normally return 100 instances, skip is set to 50, and + * limit is set to 40, then the first 50 results that would have + * been returned are skipped, the next 40 results are returned and the + * remaining 10 results are ignored. + *

+ * Skip must be greater than or equal to 0. Limit must be greater than or equal to 0. + * Limits may not be used with deletePersistentAll. + * @param skip the number of results to skip + * @param limit the number of results to return after skipping; + * use Long.MAX_VALUE for no limit. + */ + void setLimits (long skip, long limit); + } === modified file 'storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/query/QueryDomainTypeImpl.java' --- a/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/query/QueryDomainTypeImpl.java 2012-05-07 07:51:09 +0000 +++ b/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/query/QueryDomainTypeImpl.java 2012-05-29 17:15:08 +0000 @@ -29,6 +29,8 @@ import com.mysql.clusterj.core.spi.Query import com.mysql.clusterj.core.spi.SessionSPI; import com.mysql.clusterj.core.spi.ValueHandlerBatching; +import com.mysql.clusterj.core.store.Blob; +import com.mysql.clusterj.core.store.Column; import com.mysql.clusterj.core.store.Index; import com.mysql.clusterj.core.store.IndexOperation; import com.mysql.clusterj.core.store.IndexScanOperation; @@ -45,6 +47,8 @@ import com.mysql.clusterj.query.Predicat import com.mysql.clusterj.query.QueryDefinition; import com.mysql.clusterj.query.QueryDomainType; +import java.math.BigDecimal; +import java.math.BigInteger; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -147,10 +151,12 @@ public class QueryDomainTypeImpl impl } /** Query.getResultList delegates to this method. + * @param skip the number of rows to skip + * @param limit the limit of rows to return after skipping * * @return the results of executing the query */ - public List getResultList(QueryExecutionContext context) { + public List getResultList(QueryExecutionContext context, long skip, long limit) { assertAllParametersBound(context); SessionSPI session = context.getSession(); @@ -159,7 +165,7 @@ public class QueryDomainTypeImpl impl List resultList = new ArrayList(); try { // execute the query - ResultData resultData = getResultData(context); + ResultData resultData = getResultData(context, skip, limit); // put the result data into the result list while (resultData.next()) { T row = session.newInstance(resultData, domainTypeHandler); @@ -182,10 +188,12 @@ public class QueryDomainTypeImpl impl * depends on the where clause and the bound parameter values. * * @param context the query context, including the bound parameters + * @param skip the number of rows to skip + * @param limit the limit of rows to return after skipping * @return the raw result data from the query * @throws ClusterJUserException if not all parameters are bound */ - public ResultData getResultData(QueryExecutionContext context) { + public ResultData getResultData(QueryExecutionContext context, long skip, long limit) { SessionSPI session = context.getSession(); // execute query based on what kind of scan is needed // if no where clause, scan the entire table @@ -204,6 +212,10 @@ public class QueryDomainTypeImpl impl switch (scanType) { case PRIMARY_KEY: { + // if skipping any results or limit is zero, return no results + if (skip > 0 || limit < 1) { + return resultDataEmpty; + } if (logger.isDetailEnabled()) logger.detail("Using primary key find for query."); // perform a select operation op = session.getSelectOperation(domainTypeHandler.getStoreTable()); @@ -238,7 +250,7 @@ public class QueryDomainTypeImpl impl where.filterCmpValue(context, (IndexScanOperation)op); op.endDefinition(); // execute the scan and get results - result = op.resultData(); + result = ((ScanOperation)op).resultData(true, skip, limit); break; } @@ -255,11 +267,15 @@ public class QueryDomainTypeImpl impl } op.endDefinition(); // execute the scan and get results - result = op.resultData(); + result = ((ScanOperation)op).resultData(true, skip, limit); break; } case UNIQUE_KEY: { + // if skipping any results or limit is zero, return no results + if (skip > 0 || limit < 1) { + return resultDataEmpty; + } storeIndex = index.getStoreIndex(); if (logger.isDetailEnabled()) logger.detail("Using lookup with unique index " + index.getIndexName() + " for query."); // perform a unique lookup operation @@ -581,4 +597,184 @@ public class QueryDomainTypeImpl impl return cls; } + private ResultData resultDataEmpty = new ResultData() { + + public boolean next() { + // this ResultData has no results + return false; + } + + public BigInteger getBigInteger(Column columnName) { + return null; + } + + public BigInteger getBigInteger(int columnNumber) { + return null; + } + + public Blob getBlob(Column storeColumn) { + return null; + } + + public Blob getBlob(int columnNumber) { + return null; + } + + public boolean getBoolean(Column storeColumn) { + return false; + } + + public boolean getBoolean(int columnNumber) { + return false; + } + + public boolean[] getBooleans(Column storeColumn) { + return null; + } + + public boolean[] getBooleans(int columnNumber) { + return null; + } + + public byte getByte(Column storeColumn) { + return 0; + } + + public byte getByte(int columnNumber) { + return 0; + } + + public byte[] getBytes(Column storeColumn) { + return null; + } + + public byte[] getBytes(int columnNumber) { + return null; + } + + public Column[] getColumns() { + return null; + } + + public BigDecimal getDecimal(Column storeColumn) { + return null; + } + + public BigDecimal getDecimal(int columnNumber) { + return null; + } + + public double getDouble(Column storeColumn) { + return 0; + } + + public double getDouble(int columnNumber) { + return 0; + } + + public float getFloat(Column storeColumn) { + return 0; + } + + public float getFloat(int columnNumber) { + return 0; + } + + public int getInt(Column storeColumn) { + return 0; + } + + public int getInt(int columnNumber) { + return 0; + } + + public long getLong(Column storeColumn) { + return 0; + } + + public long getLong(int columnNumber) { + return 0; + } + + public Object getObject(Column storeColumn) { + return null; + } + + public Object getObject(int column) { + return null; + } + + public Boolean getObjectBoolean(Column storeColumn) { + return null; + } + + public Boolean getObjectBoolean(int columnNumber) { + return null; + } + + public Byte getObjectByte(Column storeColumn) { + return null; + } + + public Byte getObjectByte(int columnNumber) { + return null; + } + + public Double getObjectDouble(Column storeColumn) { + return null; + } + + public Double getObjectDouble(int columnNumber) { + return null; + } + + public Float getObjectFloat(Column storeColumn) { + return null; + } + + public Float getObjectFloat(int columnNumber) { + return null; + } + + public Integer getObjectInteger(Column storeColumn) { + return null; + } + + public Integer getObjectInteger(int columnNumber) { + return null; + } + + public Long getObjectLong(Column storeColumn) { + return null; + } + + public Long getObjectLong(int columnNumber) { + return null; + } + + public Short getObjectShort(Column storeColumn) { + return null; + } + + public Short getObjectShort(int columnNumber) { + return null; + } + + public short getShort(Column storeColumn) { + return 0; + } + + public short getShort(int columnNumber) { + return 0; + } + + public String getString(Column storeColumn) { + return null; + } + + public String getString(int columnNumber) { + return null; + } + + }; } === modified file 'storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/query/QueryExecutionContextImpl.java' --- a/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/query/QueryExecutionContextImpl.java 2011-10-20 19:41:56 +0000 +++ b/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/query/QueryExecutionContextImpl.java 2012-05-29 17:15:08 +0000 @@ -127,7 +127,8 @@ public class QueryExecutionContextImpl i } public ResultData getResultData(QueryDomainType queryDomainType) { - return ((QueryDomainTypeImpl)queryDomainType).getResultData(this); + // TODO handle skip and limit + return ((QueryDomainTypeImpl)queryDomainType).getResultData(this, 0, Long.MAX_VALUE); } /** Add a filter to the list of filters created for this query. === modified file 'storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/query/QueryImpl.java' --- a/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/query/QueryImpl.java 2011-02-03 14:37:50 +0000 +++ b/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/query/QueryImpl.java 2012-05-23 23:51:17 +0000 @@ -18,6 +18,7 @@ package com.mysql.clusterj.core.query; +import com.mysql.clusterj.ClusterJUserException; import com.mysql.clusterj.Results; import com.mysql.clusterj.core.*; import com.mysql.clusterj.Query; @@ -45,12 +46,49 @@ public class QueryImpl implements Que /** My query execution context. */ protected QueryExecutionContextImpl context = null; + /** The number to skip */ + protected long skip = 0; + + /** The limit */ + protected long limit = Long.MAX_VALUE; + public QueryImpl(SessionImpl session, QueryDomainTypeImpl dobj) { this.session = session; context = new QueryExecutionContextImpl(session); this.dobj = dobj; } + /** + * Set limits on results to return. The execution of the query is + * modified to return only a subset of results. If the filter would + * normally return 100 instances, skip is set to 50, and + * limit is set to 40, then the first 50 results that would have + * been returned are skipped, the next 40 results are returned and the + * remaining 10 results are ignored. + *

+ * Skip must be greater than or equal to 0. Limit must be greater than or equal to 0. + * Limits may not be used with deletePersistentAll. + *

+ * The limits as specified by the user are converted here into an internal form + * where the limit is the last record to deliver instead of the number of records + * to deliver. So if the user specifies limits of (10, 20) we convert this + * to limits of (10, 30) for the lower layers of the implementation. + * @param skip the number of results to skip + * @param limit the number of results to return after skipping; + * use Long.MAX_VALUE for no limit. + */ + public void setLimits(long skip, long limit) { + if (skip < 0 || limit < 0) { + throw new ClusterJUserException(local.message("ERR_Invalid_Limits", skip, limit)); + } + this.skip = skip; + if (Long.MAX_VALUE - skip < limit) { + limit = Long.MAX_VALUE; + } else { + this.limit = limit + skip; + } + } + public Results execute(Object arg0) { throw new UnsupportedOperationException( local.message("ERR_NotImplemented")); @@ -71,7 +109,7 @@ public class QueryImpl implements Que } public List getResultList() { - List results = dobj.getResultList(context); + List results = dobj.getResultList(context, skip, limit); // create new context, copying the parameters, for another execution context = new QueryExecutionContextImpl(context); return results; @@ -81,6 +119,9 @@ public class QueryImpl implements Que * @return the number of instances deleted */ public int deletePersistentAll() { + if (skip != 0 || limit != Long.MAX_VALUE) { + throw new ClusterJUserException(local.message("ERR_Invalid_Limits", skip, limit)); + } int result = dobj.deletePersistentAll(context); return result; } === modified file 'storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/store/ScanOperation.java' --- a/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/store/ScanOperation.java 2011-07-05 12:46:07 +0000 +++ b/storage/ndb/clusterj/clusterj-core/src/main/java/com/mysql/clusterj/core/store/ScanOperation.java 2012-05-29 17:15:08 +0000 @@ -32,4 +32,6 @@ public interface ScanOperation extends O public int nextResult(boolean fetch); + public ResultData resultData(boolean execute, long skip, long limit); + } === modified file 'storage/ndb/clusterj/clusterj-core/src/main/resources/com/mysql/clusterj/core/Bundle.properties' --- a/storage/ndb/clusterj/clusterj-core/src/main/resources/com/mysql/clusterj/core/Bundle.properties 2011-12-29 14:39:37 +0000 +++ b/storage/ndb/clusterj/clusterj-core/src/main/resources/com/mysql/clusterj/core/Bundle.properties 2012-05-23 23:51:17 +0000 @@ -138,3 +138,5 @@ either an array of Object types or a Lis ERR_Parameter_Too_Big_For_In:For field ''{0}'', the parameter of length {1} for query operator ''in'' \ is too big; it must contain fewer than 4097 items. MSG_Removing_Schema:Removing schema {0} after failure to initialize domain type handler for class {1}. +ERR_Invalid_Limits:The limits {0}, {1} are invalid: the first parameter must be greater than or equal to zero; \ +the second parameter must be greater than or equal to zero; and limits cannot be applied to delete. === modified file 'storage/ndb/clusterj/clusterj-jdbc/src/main/java/com/mysql/clusterj/jdbc/SQLExecutor.java' --- a/storage/ndb/clusterj/clusterj-jdbc/src/main/java/com/mysql/clusterj/jdbc/SQLExecutor.java 2011-11-22 22:53:33 +0000 +++ b/storage/ndb/clusterj/clusterj-jdbc/src/main/java/com/mysql/clusterj/jdbc/SQLExecutor.java 2012-05-23 23:51:17 +0000 @@ -186,7 +186,8 @@ public class SQLExecutor { session.startAutoTransaction(); try { valueHandlerBatching.next(); - ResultData resultData = queryDomainType.getResultData(context); + // TODO get skip and limit from the SQL query + ResultData resultData = queryDomainType.getResultData(context, 0, Long.MAX_VALUE); // session.endAutoTransaction(); return new ResultSetInternalMethodsImpl(resultData, columnNumberToFieldNumberMap, columnNameToFieldNumberMap, session); === modified file 'storage/ndb/clusterj/clusterj-test/src/main/java/testsuite/clusterj/AbstractQueryTest.java' --- a/storage/ndb/clusterj/clusterj-test/src/main/java/testsuite/clusterj/AbstractQueryTest.java 2012-04-15 03:43:47 +0000 +++ b/storage/ndb/clusterj/clusterj-test/src/main/java/testsuite/clusterj/AbstractQueryTest.java 2012-05-23 23:51:17 +0000 @@ -58,6 +58,12 @@ abstract public class AbstractQueryTest private boolean autotransaction; + /** The lower limit (number of returned rows to skip) */ + protected Long skip = null; + + /** The upper limit (number of rows to return) */ + protected Long limit = null; + @Override public void localSetUp() { setAutotransaction(false); @@ -76,6 +82,16 @@ abstract public class AbstractQueryTest autotransaction = b; } + protected void setLimit(long limit) { + this.skip = null; + this.limit = limit; + } + + protected void setLimits(long skip, long limit) { + this.skip = skip; + this.limit = limit; + } + class QueryHolder { public QueryBuilder builder; public QueryDomainType dobj; @@ -246,6 +262,13 @@ abstract public class AbstractQueryTest @SuppressWarnings("unchecked") public void checkResults(String theQuery) { + if (limit != null) { + if (skip != null) { + query.setLimits(skip, limit); + } else { + query.setLimits(0, limit); + } + } Set actualSet = new HashSet(); List resultList = (List) query.getResultList(); for (IdBase result: resultList) { @@ -259,6 +282,13 @@ abstract public class AbstractQueryTest } public void checkDeletePersistentAll(String where, int expectedNumberOfDeletedInstances) { + if (limit != null) { + if (skip != null) { + query.setLimits(skip, limit); + } else { + query.setLimits(0, limit); + } + } int result = query.deletePersistentAll(); errorIfNotEqual("Wrong index used for " + where + " delete query: ", expectedIndex, query.explain().get("IndexUsed")); === added file 'storage/ndb/clusterj/clusterj-test/src/main/java/testsuite/clusterj/QueryLimitsTest.java' --- a/storage/ndb/clusterj/clusterj-test/src/main/java/testsuite/clusterj/QueryLimitsTest.java 1970-01-01 00:00:00 +0000 +++ b/storage/ndb/clusterj/clusterj-test/src/main/java/testsuite/clusterj/QueryLimitsTest.java 2012-05-23 23:51:17 +0000 @@ -0,0 +1,168 @@ +/* +Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +package testsuite.clusterj; + +import com.mysql.clusterj.ClusterJUserException; + +import testsuite.clusterj.model.AllPrimitives; + +/** Query for limits used to skip query results and limit the number + * of results returned. + */ +public class QueryLimitsTest extends AbstractQueryTest { + /* + drop table if exists allprimitives; + create table allprimitives ( + id int not null primary key, + + int_not_null_hash int not null, + int_not_null_btree int not null, + int_not_null_both int not null, + int_not_null_none int not null, + int_null_hash int, + int_null_btree int, + int_null_both int, + int_null_none int, + + byte_not_null_hash tinyint not null, + byte_not_null_btree tinyint not null, + byte_not_null_both tinyint not null, + byte_not_null_none tinyint not null, + byte_null_hash tinyint, + byte_null_btree tinyint, + byte_null_both tinyint, + byte_null_none tinyint, + + short_not_null_hash smallint not null, + short_not_null_btree smallint not null, + short_not_null_both smallint not null, + short_not_null_none smallint not null, + short_null_hash smallint, + short_null_btree smallint, + short_null_both smallint, + short_null_none smallint, + + long_not_null_hash bigint not null, + long_not_null_btree bigint not null, + long_not_null_both bigint not null, + long_not_null_none bigint not null, + long_null_hash bigint, + long_null_btree bigint, + long_null_both bigint, + long_null_none bigint + */ + + @Override + public Class getInstanceType() { + return AllPrimitives.class; + } + + @Override + void createInstances(int number) { + createAllPrimitivesInstances(10); + } + + public void test() { + setLimits(1, 2); + inQuery("int_not_null_both", new Object[] {4, 6, 9}, "idx_int_not_null_both", 6, 9); + setLimits(1, 2); + inQuery("int_not_null_btree", new Object[] {4, 6, 9}, "idx_int_not_null_btree", 6, 9); + setLimits(1, 0); + equalQuery("int_not_null_btree", "idx_int_not_null_btree", 8); + setLimits(1, Long.MAX_VALUE); + greaterEqualQuery("int_not_null_btree", "idx_int_not_null_btree", 7, 8, 9); + setLimits(1, 2); + greaterThanQuery("int_not_null_btree", "idx_int_not_null_btree", 6, 8, 9); + setLimits(2, 2); + lessEqualQuery("int_not_null_btree", "idx_int_not_null_btree", 4, 2, 3); + setLimits(1, 2); + lessThanQuery("int_not_null_btree", "idx_int_not_null_btree", 4, 1, 2); + setLimits(1, 2); + betweenQuery("int_not_null_btree", "idx_int_not_null_btree", 4, 6, 5, 6); + setLimits(0, 3); + equalQuery("int_not_null_hash", "idx_int_not_null_hash", 8, 8); + setLimits(1, 2); + equalQuery("int_not_null_both", "idx_int_not_null_both", 8); + setLimits(1, 2); + greaterEqualQuery("int_not_null_both", "idx_int_not_null_both", 7, 8, 9); + setLimits(1, 2); + greaterThanQuery("int_not_null_both", "idx_int_not_null_both", 6, 8, 9); + setLimits(1, 2); + lessEqualQuery("int_not_null_both", "idx_int_not_null_both", 4, 1, 2); + setLimits(1, 2); + lessThanQuery("int_not_null_both", "idx_int_not_null_both", 4, 1, 2); + setLimits(1, 2); + betweenQuery("int_not_null_both", "idx_int_not_null_both", 4, 6, 5, 6); + setLimits(1, 2); + equalQuery("int_not_null_none", "none", 8); + setLimits(0, 0); + equalQuery("int_not_null_none", "none", 8); + setLimits(1, 0); + equalQuery("int_not_null_none", "none", 8); + failOnError(); + } + + public void testNegative() { + if (session.currentTransaction().isActive()) { + session.currentTransaction().rollback(); + } + try { + // bad limit; first parameter must be greater than or equal to zero + setLimits(-1, 1); + equalQuery("int_not_null_none", "none", 8); + error("Bad limit (-1, 1) should fail."); + } catch (ClusterJUserException ex) { + // good catch + } + if (session.currentTransaction().isActive()) { + session.currentTransaction().rollback(); + } + try { + // bad limit; second parameter must be greater than or equal to zero + setLimits(1, -1); + equalQuery("int_not_null_none", "none", 8); + error("Bad limit (1, -1) should fail."); + } catch (ClusterJUserException ex) { + // good catch + } + if (session.currentTransaction().isActive()) { + session.currentTransaction().rollback(); + } + try { + // bad limit; cannot use limits for delete operations + setLimits(0, 1); + deleteEqualQuery("int_not_null_none", "none", 8, 1); + error("Bad limit for delete should fail."); + } catch (ClusterJUserException ex) { + // good catch + } + if (session.currentTransaction().isActive()) { + session.currentTransaction().rollback(); + } + try { + // bad limit; cannot use limits for delete operations + setLimits(1, 1); + deleteEqualQuery("int_not_null_none", "none", 8, 1); + error("Bad limit for delete should fail."); + } catch (ClusterJUserException ex) { + // good catch + } + failOnError(); + } + +} === modified file 'storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/ClusterTransactionImpl.java' --- a/storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/ClusterTransactionImpl.java 2012-04-02 20:43:14 +0000 +++ b/storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/ClusterTransactionImpl.java 2012-05-24 06:39:40 +0000 @@ -273,10 +273,10 @@ class ClusterTransactionImpl implements handleError(ndbIndex, ndbDictionary); NdbIndexScanOperation ndbOperation = ndbTransaction.getNdbIndexScanOperation(ndbIndex); handleError(ndbOperation, ndbTransaction); - int scanFlags = 0; + int scanFlags = ScanFlag.SF_OrderBy; int lockMode = indexScanLockMode; if (lockMode != com.mysql.ndbjtie.ndbapi.NdbOperationConst.LockMode.LM_CommittedRead) { - scanFlags = ScanFlag.SF_KeyInfo; + scanFlags |= ScanFlag.SF_KeyInfo; } scanFlags |= ScanFlag.SF_MultiRange; int parallel = 0; === modified file 'storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/DbImpl.java' --- a/storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/DbImpl.java 2012-05-17 01:36:46 +0000 +++ b/storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/DbImpl.java 2012-05-23 23:51:17 +0000 @@ -431,9 +431,9 @@ class DbImpl implements com.mysql.cluste IndexBound.delete(ndbIndexBound); } - public NdbInterpretedCode createInterpretedCode(TableConst ndbTable, ByteBuffer buffer, int i) { + public NdbInterpretedCode createInterpretedCode(TableConst ndbTable, int i) { ++numberOfInterpretedCodeCreated; - return NdbInterpretedCode.create(ndbTable, buffer, i); + return NdbInterpretedCode.create(ndbTable, null, i); } public void delete(NdbInterpretedCode ndbInterpretedCode) { === modified file 'storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/NdbRecordScanOperationImpl.java' --- a/storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/NdbRecordScanOperationImpl.java 2012-05-06 13:55:09 +0000 +++ b/storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/NdbRecordScanOperationImpl.java 2012-05-24 13:50:07 +0000 @@ -69,8 +69,14 @@ public abstract class NdbRecordScanOpera */ @Override public ResultData resultData(boolean execute) { + return resultData(execute, 0, Long.MAX_VALUE); + } + + /** Construct a new ResultData and if requested, execute the operation. + */ + public ResultData resultData(boolean execute, long skip, long limit) { NdbRecordResultDataImpl result = - new NdbRecordScanResultDataImpl(this); + new NdbRecordScanResultDataImpl(this, skip, limit); if (execute) { clusterTransaction.executeNoCommit(false, true); } @@ -111,31 +117,24 @@ public abstract class NdbRecordScanOpera /** Create scan options for this scan. * Scan options are used to set a filter into the NdbScanOperation, * set the key info flag if using a lock mode that requires lock takeover, and set the multi range flag. + * always set SF_OrderBy to get ordered scans. */ protected void getScanOptions() { - long options = 0L; - int flags = 0; - if (multiRange | (ndbScanFilter != null) | - (lockMode != com.mysql.ndbjtie.ndbapi.NdbOperationConst.LockMode.LM_CommittedRead)) { - - scanOptions = db.createScanOptions(); - if (multiRange) { - flags |= ScanFlag.SF_MultiRange; - options |= (long)Type.SO_SCANFLAGS; - scanOptions.scan_flags(flags); - } - if (lockMode != com.mysql.ndbjtie.ndbapi.NdbOperationConst.LockMode.LM_CommittedRead) { - flags |= ScanFlag.SF_KeyInfo; - options |= (long)Type.SO_SCANFLAGS; - scanOptions.scan_flags(flags); - } - if (ndbScanFilter != null) { - options |= (long)Type.SO_INTERPRETED; - scanOptions.interpretedCode(ndbScanFilter.getInterpretedCode()); - } - - scanOptions.optionsPresent(options); + long options = (long)Type.SO_SCANFLAGS; + int flags = ScanFlag.SF_OrderBy; + scanOptions = db.createScanOptions(); + if (multiRange) { + flags |= ScanFlag.SF_MultiRange; + } + if (lockMode != com.mysql.ndbjtie.ndbapi.NdbOperationConst.LockMode.LM_CommittedRead) { + flags |= ScanFlag.SF_KeyInfo; + } + if (ndbScanFilter != null) { + options |= (long)Type.SO_INTERPRETED; + scanOptions.interpretedCode(ndbScanFilter.getInterpretedCode()); } + scanOptions.scan_flags(flags); + scanOptions.optionsPresent(options); if (logger.isDebugEnabled()) logger.debug("ScanOptions: " + dumpScanOptions(options, flags)); } @@ -167,7 +166,7 @@ public abstract class NdbRecordScanOpera */ public ScanFilter getScanFilter(QueryExecutionContext context) { - ndbInterpretedCode = db.createInterpretedCode(ndbRecordValues.getNdbTable(), null, 0); + ndbInterpretedCode = db.createInterpretedCode(ndbRecordValues.getNdbTable(), 0); handleError(ndbInterpretedCode, ndbOperation); ndbScanFilter = db.createScanFilter(ndbInterpretedCode); handleError(ndbScanFilter, ndbOperation); === modified file 'storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/NdbRecordScanResultDataImpl.java' --- a/storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/NdbRecordScanResultDataImpl.java 2012-04-02 20:43:14 +0000 +++ b/storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/NdbRecordScanResultDataImpl.java 2012-05-23 23:51:17 +0000 @@ -47,19 +47,37 @@ class NdbRecordScanResultDataImpl extend /** The NdbScanOperation */ private NdbScanOperation ndbScanOperation = null; + /** The number to skip */ + protected long skip = 0; + + /** The limit */ + protected long limit = Long.MAX_VALUE; + + /** The record counter during the scan */ + protected long recordCounter = 0; + /** Construct the ResultDataImpl based on an NdbRecordOperationImpl. * When used with the compatibility operations, delegate to the NdbRecordOperation * to copy data. * @param operation the NdbRecordOperationImpl + * @param skip the number of rows to skip + * @param limit the last row number */ - public NdbRecordScanResultDataImpl(NdbRecordScanOperationImpl scanOperation) { + public NdbRecordScanResultDataImpl(NdbRecordScanOperationImpl scanOperation, long skip, long limit) { super(scanOperation); this.scanOperation = scanOperation; this.ndbScanOperation = (NdbScanOperation)scanOperation.ndbOperation; + this.skip = skip; + this.limit = limit; } @Override public boolean next() { + if (recordCounter >= limit) { + // the next record is past the limit; we have delivered all the rows + ndbScanOperation.close(true, true); + return false; + } // NdbScanOperation may have many results. boolean done = false; boolean fetch = false; @@ -68,9 +86,15 @@ class NdbRecordScanResultDataImpl extend int result = scanOperation.nextResultCopyOut(fetch, force); switch (result) { case RESULT_READY: - // if scanning with locks, grab the lock for the current transaction - scanOperation.lockCurrentTuple(); - return true; + if (++recordCounter > skip) { + // this record is past the skip + // if scanning with locks, grab the lock for the current transaction + scanOperation.lockCurrentTuple(); + return true; + } else { + // skip this record + break; + } case SCAN_FINISHED: ndbScanOperation.close(true, true); return false; === modified file 'storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/ScanOperationImpl.java' --- a/storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/ScanOperationImpl.java 2011-07-05 12:46:07 +0000 +++ b/storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/ScanOperationImpl.java 2012-05-29 17:15:08 +0000 @@ -66,8 +66,12 @@ class ScanOperationImpl extends Operatio @Override public ResultData resultData() { + return resultData(true, 0, Long.MAX_VALUE); + } + + public ResultData resultData(boolean execute, long skip, long limit) { ResultData result = new ScanResultDataImpl(ndbScanOperation, storeColumns, - maximumColumnId, bufferSize, offsets, lengths, maximumColumnLength, bufferManager); + maximumColumnId, bufferSize, offsets, lengths, maximumColumnLength, bufferManager, skip, limit); clusterTransaction.executeNoCommit(false, true); return result; } === modified file 'storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/ScanResultDataImpl.java' --- a/storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/ScanResultDataImpl.java 2012-04-02 20:43:14 +0000 +++ b/storage/ndb/clusterj/clusterj-tie/src/main/java/com/mysql/clusterj/tie/ScanResultDataImpl.java 2012-05-23 23:51:17 +0000 @@ -44,6 +44,14 @@ class ScanResultDataImpl extends ResultD private NdbScanOperation ndbScanOperation = null; + /** The number to skip */ + protected long skip = 0; + + /** The limit */ + protected long limit = Long.MAX_VALUE; + + /** The record counter during the scan */ + protected long recordCounter = 0; /** Flags for iterating a scan */ protected final int RESULT_READY = 0; @@ -52,14 +60,21 @@ class ScanResultDataImpl extends ResultD public ScanResultDataImpl(NdbScanOperation ndbScanOperation, List storeColumns, int maximumColumnId, int bufferSize, int[] offsets, int[] lengths, int maximumColumnLength, - BufferManager bufferManager) { + BufferManager bufferManager, long skip, long limit) { super(ndbScanOperation, storeColumns, maximumColumnId, bufferSize, offsets, lengths, bufferManager, false); this.ndbScanOperation = ndbScanOperation; + this.skip = skip; + this.limit = limit; } @Override public boolean next() { + if (recordCounter >= limit) { + // the next record is past the limit; we have delivered all the rows + ndbScanOperation.close(true, true); + return false; + } // NdbScanOperation may have many results. boolean done = false; boolean fetch = false; @@ -68,10 +83,17 @@ class ScanResultDataImpl extends ResultD int result = ndbScanOperation.nextResult(fetch, force); switch (result) { case RESULT_READY: - if (ndbScanOperation.getLockMode() != LockMode.LM_CommittedRead) { - ndbScanOperation.lockCurrentTuple(); + if (++recordCounter > skip) { + // this record is past the skip + // if scanning with locks, grab the lock for the current transaction + if (ndbScanOperation.getLockMode() != LockMode.LM_CommittedRead) { + ndbScanOperation.lockCurrentTuple(); + } + return true; + } else { + // skip this record + break; } - return true; case SCAN_FINISHED: ndbScanOperation.close(true, true); return false; === added file 'storage/ndb/clusterj/clusterj-tie/src/test/java/testsuite/clusterj/tie/QueryLimitsTest.java' --- a/storage/ndb/clusterj/clusterj-tie/src/test/java/testsuite/clusterj/tie/QueryLimitsTest.java 1970-01-01 00:00:00 +0000 +++ b/storage/ndb/clusterj/clusterj-tie/src/test/java/testsuite/clusterj/tie/QueryLimitsTest.java 2012-05-23 23:51:17 +0000 @@ -0,0 +1,22 @@ +/* + Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package testsuite.clusterj.tie; + +public class QueryLimitsTest extends testsuite.clusterj.QueryLimitsTest { + +} === modified file 'storage/ndb/include/mgmapi/ndbd_exit_codes.h' --- a/storage/ndb/include/mgmapi/ndbd_exit_codes.h 2011-02-01 23:27:25 +0000 +++ b/storage/ndb/include/mgmapi/ndbd_exit_codes.h 2012-05-29 17:04:36 +0000 @@ -129,7 +129,7 @@ typedef ndbd_exit_classification_enum nd /* TUP 6800-> */ #define NDBD_EXIT_SR_OUT_OF_DATAMEMORY 6800 /* LQH 7200-> */ - +#define NDBD_EXIT_LCP_SCAN_WATCHDOG_FAIL 7200 /* Errorcodes for NDB filesystem */ #define NDBD_EXIT_AFS_NOPATH 2801 === modified file 'storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp' --- a/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp 2012-05-21 23:05:17 +0000 +++ b/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp 2012-05-29 17:15:08 +0000 @@ -23507,7 +23507,7 @@ Dblqh::execDUMP_STATE_ORD(Signal* signal { CRASH_INSERTION(5075); - progError(__LINE__, NDBD_EXIT_SYSTEM_ERROR, + progError(__LINE__, NDBD_EXIT_LCP_SCAN_WATCHDOG_FAIL, "Please report this as a bug. " "Provide as much info as possible, expecially all the " "ndb_*_out.log files, Thanks. " === modified file 'storage/ndb/src/kernel/error/ndbd_exit_codes.c' --- a/storage/ndb/src/kernel/error/ndbd_exit_codes.c 2011-10-20 13:01:37 +0000 +++ b/storage/ndb/src/kernel/error/ndbd_exit_codes.c 2012-05-29 17:04:36 +0000 @@ -144,6 +144,10 @@ static const ErrStruct errArray[] = {NDBD_EXIT_SR_OUT_OF_DATAMEMORY, XCR, "Out of data memory during system restart, please increase DataMemory"}, + /* LQH */ + {NDBD_EXIT_LCP_SCAN_WATCHDOG_FAIL, XIE, + "LCP fragment scan watchdog detected a problem. Please report a bug."}, + /* Ndbfs error messages */ /* Most codes will have additional info, such as OS error code */ {NDBD_EXIT_AFS_NOPATH, XIE, "No file system path"}, No bundle (reason: useless for push emails).