Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -136,24 +136,19 @@ else if (!requiredCollation.satisfies(relCollation))

/** {@inheritDoc} */
@Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
double inputRowCount = mq.getRowCount(getInput());

double lim = fetch != null ? doubleFromRex(fetch, inputRowCount * FETCH_IS_PARAM_FACTOR) : inputRowCount;
double off = offset != null ? doubleFromRex(offset, inputRowCount * OFFSET_IS_PARAM_FACTOR) : 0;

double rows = Math.min(lim + off, inputRowCount);
double rows = estimateRowCount(mq);

return planner.getCostFactory().makeCost(rows, rows * IgniteCost.ROW_PASS_THROUGH_COST, 0);
}

/** {@inheritDoc} */
@Override public double estimateRowCount(RelMetadataQuery mq) {
double inputRowCount = mq.getRowCount(getInput());
double inputRowCnt = mq.getRowCount(getInput());

double lim = fetch != null ? doubleFromRex(fetch, inputRowCount * FETCH_IS_PARAM_FACTOR) : inputRowCount;
double off = offset != null ? doubleFromRex(offset, inputRowCount * OFFSET_IS_PARAM_FACTOR) : 0;
double lim = fetch != null ? doubleFromRex(fetch, inputRowCnt * FETCH_IS_PARAM_FACTOR) : inputRowCnt;
double off = offset != null ? doubleFromRex(offset, inputRowCnt * OFFSET_IS_PARAM_FACTOR) : 0;

return Math.min(lim, inputRowCount - off);
return Math.max(0, Math.min(lim, inputRowCnt - off));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@
*/
@WithSystemProperty(key = "calcite.debug", value = "false")
public class AbstractBasicIntegrationTest extends GridCommonAbstractTest {
/** */
protected static final Object[] NULL_RESULT = new Object[] { null };

/** */
protected static final String TABLE_NAME = "person";

Expand Down Expand Up @@ -147,8 +150,8 @@ protected List<List<?>> executeSql(String sql, Object... args) {
* @param cls Exception class.
* @param msg Error message.
*/
protected void assertThrows(String sql, Class<? extends Exception> cls, String msg) {
assertThrowsAnyCause(log, () -> executeSql(sql), cls, msg);
protected void assertThrows(String sql, Class<? extends Exception> cls, String msg, Object... args) {
assertThrowsAnyCause(log, () -> executeSql(sql, args), cls, msg);
}

/** */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@
* Test Ignite SQL functions.
*/
public class FunctionsTest extends AbstractBasicIntegrationTest {
/** */
private static final Object[] NULL_RESULT = new Object[] { null };

/** */
@Test
public void testTimestampDiffWithFractionsOfSecond() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* limitations under the License.
*/

package org.apache.ignite.internal.processors.query.calcite;
package org.apache.ignite.internal.processors.query.calcite.integration;

import java.math.BigDecimal;
import java.util.Arrays;
Expand All @@ -24,38 +24,36 @@
import org.apache.ignite.IgniteCache;
import org.apache.ignite.cache.CacheMode;
import org.apache.ignite.cache.QueryEntity;
import org.apache.ignite.cache.query.FieldsQueryCursor;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.processors.query.QueryEngine;
import org.apache.ignite.internal.processors.query.calcite.exec.rel.AbstractNode;
import org.apache.ignite.internal.processors.query.calcite.util.Commons;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.junit.Test;

import static java.util.Collections.singletonList;

/**
* Limit / offset tests.
*/
public class LimitOffsetTest extends GridCommonAbstractTest {
public class LimitOffsetIntegrationTest extends AbstractBasicIntegrationTest {
/** */
private static IgniteCache<Integer, String> cacheRepl;

/** */
private static IgniteCache<Integer, String> cachePart;

/** {@inheritDoc} */
@Override protected void beforeTestsStarted() throws Exception {
startGrids(2);
@Override protected void beforeTest() throws Exception {
cacheRepl = client.cache("TEST_REPL");
cachePart = client.cache("TEST_PART");
}

cacheRepl = grid(0).cache("TEST_REPL");
cachePart = grid(0).cache("TEST_PART");
/** {@inheritDoc} */
@Override protected void afterTest() throws Exception {
// Override method to keep caches after tests.
}

/** {@inheritDoc} */
Expand All @@ -76,7 +74,7 @@ public class LimitOffsetTest extends GridCommonAbstractTest {
.setKeyFieldName("id")
.setValueFieldName("val")
.addQueryField("id", Integer.class.getName(), null)
.addQueryField("val", String.class.getName(), null);;
.addQueryField("val", String.class.getName(), null);

return super.getConfiguration(igniteInstanceName)
.setCacheConfiguration(
Expand All @@ -93,117 +91,35 @@ public class LimitOffsetTest extends GridCommonAbstractTest {
/** Tests correctness of fetch / offset params. */
@Test
public void testInvalidLimitOffset() {
QueryEngine engine = engine(grid(0));

String bigInt = BigDecimal.valueOf(10000000000L).toString();

try {
List<FieldsQueryCursor<List<?>>> cursors =
engine.query(null, "PUBLIC",
"SELECT * FROM TEST_REPL OFFSET " + bigInt + " ROWS");
cursors.get(0).getAll();

fail();
}
catch (Throwable e) {
assertTrue(e.toString(), X.hasCause(e, "Illegal value of offset", SqlValidatorException.class));
}

try {
List<FieldsQueryCursor<List<?>>> cursors =
engine.query(null, "PUBLIC",
"SELECT * FROM TEST_REPL FETCH FIRST " + bigInt + " ROWS ONLY");
cursors.get(0).getAll();

fail();
}
catch (Throwable e) {
assertTrue(e.toString(), X.hasCause(e, "Illegal value of fetch / limit", SqlValidatorException.class));
}

try {
List<FieldsQueryCursor<List<?>>> cursors =
engine.query(null, "PUBLIC", "SELECT * FROM TEST_REPL LIMIT " + bigInt);
cursors.get(0).getAll();
assertThrows("SELECT * FROM TEST_REPL OFFSET " + bigInt + " ROWS",
SqlValidatorException.class, "Illegal value of offset");

fail();
}
catch (Throwable e) {
assertTrue(e.toString(), X.hasCause(e, "Illegal value of fetch / limit", SqlValidatorException.class));
}
assertThrows("SELECT * FROM TEST_REPL FETCH FIRST " + bigInt + " ROWS ONLY",
SqlValidatorException.class, "Illegal value of fetch / limit");

try {
List<FieldsQueryCursor<List<?>>> cursors =
engine.query(null, "PUBLIC",
"SELECT * FROM TEST_REPL OFFSET -1 ROWS FETCH FIRST -1 ROWS ONLY");
cursors.get(0).getAll();
assertThrows("SELECT * FROM TEST_REPL LIMIT " + bigInt,
SqlValidatorException.class, "Illegal value of fetch / limit");

fail();
}
catch (Throwable e) {
assertTrue(e.toString(), X.hasCause(e, IgniteSQLException.class));
}
assertThrows("SELECT * FROM TEST_REPL OFFSET -1 ROWS FETCH FIRST -1 ROWS ONLY",
IgniteSQLException.class, null);

try {
List<FieldsQueryCursor<List<?>>> cursors =
engine.query(null, "PUBLIC",
"SELECT * FROM TEST_REPL OFFSET -1 ROWS");
cursors.get(0).getAll();
assertThrows("SELECT * FROM TEST_REPL OFFSET -1 ROWS",
IgniteSQLException.class, null);

fail();
}
catch (Throwable e) {
assertTrue(e.toString(), X.hasCause(e, IgniteSQLException.class));
}

try {
List<FieldsQueryCursor<List<?>>> cursors =
engine.query(null, "PUBLIC",
"SELECT * FROM TEST_REPL OFFSET 2+1 ROWS");
cursors.get(0).getAll();

fail();
}
catch (Throwable e) {
assertTrue(e.toString(), X.hasCause(e, IgniteSQLException.class));
}
assertThrows("SELECT * FROM TEST_REPL OFFSET 2+1 ROWS",
IgniteSQLException.class, null);

// Check with parameters
try {
List<FieldsQueryCursor<List<?>>> cursors =
engine.query(null, "PUBLIC",
"SELECT * FROM TEST_REPL OFFSET ? ROWS FETCH FIRST ? ROWS ONLY", -1, -1);
cursors.get(0).getAll();
assertThrows("SELECT * FROM TEST_REPL OFFSET ? ROWS FETCH FIRST ? ROWS ONLY",
SqlValidatorException.class, "Illegal value of fetch / limit", -1, -1);

fail();
}
catch (Throwable e) {
assertTrue(e.toString(), X.hasCause(e, "Illegal value of fetch / limit", SqlValidatorException.class));
}

try {
List<FieldsQueryCursor<List<?>>> cursors =
engine.query(null, "PUBLIC",
"SELECT * FROM TEST_REPL OFFSET ? ROWS", -1);
cursors.get(0).getAll();

fail();
}
catch (Throwable e) {
assertTrue(e.toString(), X.hasCause(e, "Illegal value of offset", SqlValidatorException.class));
}
assertThrows("SELECT * FROM TEST_REPL OFFSET ? ROWS",
SqlValidatorException.class, "Illegal value of offset", -1);

try {
List<FieldsQueryCursor<List<?>>> cursors =
engine.query(null, "PUBLIC",
"SELECT * FROM TEST_REPL FETCH FIRST ? ROWS ONLY", -1);
cursors.get(0).getAll();

fail();
}
catch (Throwable e) {
assertTrue(e.toString(), X.hasCause(e, "Illegal value of fetch / limit", SqlValidatorException.class));
}
assertThrows("SELECT * FROM TEST_REPL FETCH FIRST ? ROWS ONLY",
SqlValidatorException.class, "Illegal value of fetch / limit", -1);
}

/**
Expand Down Expand Up @@ -239,13 +155,8 @@ public void testLimitOffset() throws Exception {
public void testLimitDistributed() throws Exception {
fillCache(cachePart, 10_000);

QueryEngine engine = engine(grid(0));

for (String order : F.asArray("id", "val")) { // Order by ID - without explicit IgniteSort node.
FieldsQueryCursor<List<?>> cur = engine.query(null, "PUBLIC",
"SELECT id FROM TEST_PART ORDER BY " + order + " LIMIT 1000 OFFSET 5000").get(0);

List<List<?>> res = cur.getAll();
List<List<?>> res = sql("SELECT id FROM TEST_PART ORDER BY " + order + " LIMIT 1000 OFFSET 5000");

assertEquals(1000, res.size());

Expand All @@ -254,11 +165,19 @@ public void testLimitDistributed() throws Exception {
}
}

/** */
@Test
public void testOffsetOutOfRange() throws Exception {
fillCache(cachePart, 5);

assertQuery("SELECT (SELECT id FROM TEST_PART ORDER BY id LIMIT 1 OFFSET 10)").returns(NULL_RESULT).check();
}

/**
* @param c Cache.
* @param rows Rows count.
*/
private void fillCache(IgniteCache c, int rows) throws InterruptedException {
private void fillCache(IgniteCache<Integer, String> c, int rows) throws InterruptedException {
c.clear();

for (int i = 0; i < rows; ++i)
Expand All @@ -277,8 +196,6 @@ private void fillCache(IgniteCache c, int rows) throws InterruptedException {
* @param sorted Use sorted query (adds ORDER BY).
*/
void checkQuery(int rows, int lim, int off, boolean param, boolean sorted) {
QueryEngine engine = engine(grid(0));

String sql = createSql(lim, off, param, sorted);

Object[] params;
Expand All @@ -293,10 +210,7 @@ else if (off >= 0)

log.info("SQL: " + sql + (param ? "params=" + Arrays.toString(params) : ""));

List<FieldsQueryCursor<List<?>>> cursors =
engine.query(null, "PUBLIC", sql, param ? params : X.EMPTY_OBJECT_ARRAY);

List<List<?>> res = cursors.get(0).getAll();
List<List<?>> res = sql(sql, params);

assertEquals("Invalid results size. [rows=" + rows + ", limit=" + lim + ", off=" + off
+ ", res=" + res.size() + ']', expectedSize(rows, lim, off), res.size());
Expand Down Expand Up @@ -341,9 +255,4 @@ private String createSql(int lim, int off, boolean param, boolean sorted) {

return sb.toString();
}

/** */
private QueryEngine engine(IgniteEx grid) {
return Commons.lookupComponent(grid.context(), QueryEngine.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ public void testEstimateRows() throws Exception {
assertPlan("SELECT * FROM TEST ORDER BY ID OFFSET 60", publicSchema,
nodeOrAnyChild(isInstanceOf(IgniteLimit.class)
.and(l -> l.getCluster().getMetadataQuery().getRowCount(l) == ROW_CNT - 60d)));

assertPlan("SELECT * FROM TEST ORDER BY ID LIMIT 1 OFFSET " + ROW_CNT * 2, publicSchema,
nodeOrAnyChild(isInstanceOf(IgniteLimit.class)
// Estimated row count returned by IgniteLimit node is 0, but after validation it becomes 1
// if it less than 1.
.and(l -> l.getCluster().getMetadataQuery().getRowCount(l) == 1)));
}

/** */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import org.apache.ignite.internal.processors.query.calcite.CalciteQueryProcessorTest;
import org.apache.ignite.internal.processors.query.calcite.CancelTest;
import org.apache.ignite.internal.processors.query.calcite.DateTimeTest;
import org.apache.ignite.internal.processors.query.calcite.LimitOffsetTest;
import org.apache.ignite.internal.processors.query.calcite.SqlFieldsQueryUsageTest;
import org.apache.ignite.internal.processors.query.calcite.UnstableTopologyTest;
import org.apache.ignite.internal.processors.query.calcite.integration.AggregatesIntegrationTest;
Expand All @@ -39,6 +38,7 @@
import org.apache.ignite.internal.processors.query.calcite.integration.KeepBinaryIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.KillCommandDdlIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.KillQueryCommandDdlIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.LimitOffsetIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.LocalDateTimeSupportTest;
import org.apache.ignite.internal.processors.query.calcite.integration.MemoryQuotasIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.integration.MetadataIntegrationTest;
Expand Down Expand Up @@ -78,7 +78,7 @@
CalciteBasicSecondaryIndexIntegrationTest.class,
CancelTest.class,
DateTimeTest.class,
LimitOffsetTest.class,
LimitOffsetIntegrationTest.class,
SqlFieldsQueryUsageTest.class,
AggregatesIntegrationTest.class,
MetadataIntegrationTest.class,
Expand Down