List:Commits« Previous MessageNext Message »
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)
View as plain text  
 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<E> {
      */
     Map<String, Object> 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.
+     * <p>
+     * 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<T> 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<T> getResultList(QueryExecutionContext context) {
+    public List<T> getResultList(QueryExecutionContext context, long skip, long limit) {
         assertAllParametersBound(context);
 
         SessionSPI session = context.getSession();
@@ -159,7 +165,7 @@ public class QueryDomainTypeImpl<T> impl
         List<T> resultList = new ArrayList<T>();
         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<T> 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<T> 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<T> 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<T> 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<T> 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<E> 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<E> 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.
+     * <p>
+     * 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.
+     * <p>
+     * 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<E> execute(Object arg0) {
             throw new UnsupportedOperationException(
                     local.message("ERR_NotImplemented"));
@@ -71,7 +109,7 @@ public class QueryImpl<E> implements Que
     }
 
     public List<E> getResultList() {
-        List<E> results = dobj.getResultList(context);
+        List<E> 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<E> 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<Integer> actualSet = new HashSet<Integer>();
             List<IdBase> resultList = (List<IdBase>) 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<Column> 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).
Thread
bzr push into mysql-5.5-cluster-7.2 branch (frazer.clement:3926 to 3927) Frazer Clement29 May