/*
* Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. Crate licenses
* this file to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* However, if you have executed another commercial license agreement
* with Crate these terms will supersede the license and you may use the
* software solely pursuant to the terms of the relevant commercial agreement.
*/
package io.crate.analyze.where;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.crate.action.sql.SessionContext;
import io.crate.analyze.EvaluatingNormalizer;
import io.crate.analyze.symbol.Symbol;
import io.crate.metadata.ColumnIdent;
import io.crate.metadata.ReplaceMode;
import io.crate.metadata.TransactionContext;
import io.crate.test.integration.CrateUnitTest;
import io.crate.testing.SqlExpressions;
import io.crate.testing.T3;
import org.junit.Test;
import java.util.List;
import static io.crate.testing.SymbolMatchers.isLiteral;
import static io.crate.testing.TestingHelpers.getFunctions;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.core.Is.is;
@SuppressWarnings("unchecked")
public class EqualityExtractorTest extends CrateUnitTest {
private TransactionContext transactionContext = new TransactionContext(SessionContext.SYSTEM_SESSION);
private SqlExpressions expressions = new SqlExpressions(ImmutableMap.of(T3.T1, T3.TR_1), T3.TR_1);
private EvaluatingNormalizer normalizer = EvaluatingNormalizer.functionOnlyNormalizer(getFunctions(), ReplaceMode.COPY);
private EqualityExtractor ee = new EqualityExtractor(normalizer);
private ColumnIdent x = new ColumnIdent("x");
private ColumnIdent i = new ColumnIdent("i");
private List<List<Symbol>> analyzeParentX(Symbol query) {
return ee.extractParentMatches(ImmutableList.of(x), query, transactionContext);
}
private List<List<Symbol>> analyzeExactX(Symbol query) {
return analyzeExact(query, ImmutableList.of(x));
}
private List<List<Symbol>> analyzeExactXI(Symbol query) {
return analyzeExact(query, ImmutableList.of(x, i));
}
private List<List<Symbol>> analyzeExact(Symbol query, List<ColumnIdent> primaryKeys) {
return ee.extractExactMatches(primaryKeys, query, transactionContext);
}
private Symbol query(String expression) {
return expressions.normalize(expressions.asSymbol(expression));
}
@Test
public void testNoExtract2ColPKWithOr() throws Exception {
Symbol query = query("x = 1 or i = 2");
List<List<Symbol>> matches = analyzeExactXI(query);
assertNull(matches);
}
@Test
public void testExtract2ColPKWithAndAndNestedOr() throws Exception {
Symbol query = query("x = 1 and (i = 2 or i = 3 or i = 4)");
List<List<Symbol>> matches = analyzeExactXI(query);
assertThat(matches.size(), is(3));
assertThat(matches, containsInAnyOrder(
contains(isLiteral(1), isLiteral(2)),
contains(isLiteral(1), isLiteral(3)),
contains(isLiteral(1), isLiteral(4)))
);
}
@Test
public void testExtract2ColPKWithOrFullDistinctKeys() throws Exception {
Symbol query = query("(x = 1 and i = 2) or (x = 3 and i =4)");
List<List<Symbol>> matches = analyzeExactXI(query);
assertThat(matches.size(), is(2));
assertThat(matches, containsInAnyOrder(
contains(isLiteral(1), isLiteral(2)),
contains(isLiteral(3), isLiteral(4))
));
}
@Test
public void testExtract2ColPKWithOrFullDuplicateKeys() throws Exception {
Symbol query = query("(x = 1 and i = 2) or (x = 1 and i = 4)");
List<List<Symbol>> matches = analyzeExactXI(query);
assertThat(matches.size(), is(2));
assertThat(matches, containsInAnyOrder(
contains(isLiteral(1), isLiteral(2)),
contains(isLiteral(1), isLiteral(4))
));
}
@Test
public void testExtractRoutingFromAnd() throws Exception {
Symbol query = query("x = 1 and i = 2");
List<List<Symbol>> matches = analyzeParentX(query);
assertThat(matches.size(), is(1));
assertThat(matches, contains(
contains(isLiteral(1))
));
}
@Test
public void testExtractNoRoutingFromForeignOnly() throws Exception {
Symbol query = query("i = 2");
List<List<Symbol>> matches = analyzeParentX(query);
assertNull(matches);
}
@Test
public void testExtractRoutingFromOr() throws Exception {
Symbol query = query("x = 1 or x = 2");
List<List<Symbol>> matches = analyzeParentX(query);
assertThat(matches.size(), is(2));
assertThat(matches, containsInAnyOrder(
contains(isLiteral(1)),
contains(isLiteral(2))
));
}
@Test
public void testNoExtractSinglePKFromAnd() throws Exception {
Symbol query = query("x = 1 and x = 2");
List<List<Symbol>> matches = analyzeExactX(query);
assertNull(matches);
}
@Test
public void testExtractRoutingFromNestedOr() throws Exception {
Symbol query = query("x =1 or x =2 or x = 3 or x = 4");
List<List<Symbol>> matches = analyzeParentX(query);
assertThat(matches.size(), is(4));
assertThat(matches, containsInAnyOrder(
contains(isLiteral(1)),
contains(isLiteral(2)),
contains(isLiteral(3)),
contains(isLiteral(4))
));
}
@Test
public void testExtractNoRoutingFromOrWithForeignColumn() throws Exception {
Symbol query = query("x = 1 or i = 2");
List<List<Symbol>> matches = analyzeParentX(query);
assertNull(matches);
}
@Test
public void testNoExtractSinglePKFromAndWithForeignColumn() throws Exception {
Symbol query = query("x = 1 or (x = 2 and i = 2)");
List<List<Symbol>> matches = analyzeExactX(query);
assertNull(matches);
}
@Test
public void testExtract2ColPKFromNestedOrWithDuplicates() throws Exception {
Symbol query = query("x = 1 and (i = 2 or i = 2 or i = 4)");
List<List<Symbol>> matches = analyzeExactXI(query);
assertThat(matches.size(), is(2));
assertThat(matches, containsInAnyOrder(
contains(isLiteral(1), isLiteral(2)),
contains(isLiteral(1), isLiteral(4))
));
}
@Test
public void testNoExtract2ColPKFromAndEq1PartAnd2ForeignColumns() throws Exception {
Symbol query = query("x = 1 and (i = 2 or a = 'a')");
List<List<Symbol>> matches = analyzeExactXI(query);
assertNull(matches);
}
/**
* x=1 and (y=2 or ?)
* and(x1, or(y2, ?)
* <p>
* x = 1 and (y=2 or x=3)
* and(x1, or(y2, x3)
* <p>
* x=1 and (y=2 or y=3)
* and(x1, or(or(y2, y3), y4))
* <p>
* branches: x1,
* <p>
* <p>
* <p>
* x=1 and (y=2 or F)
* 1,2 1=1 and (2=2 or z=3) T
*/
@Test
public void testNoExtract2ColPKFromAndWithEq1PartAnd1ForeignColumnInOr() throws Exception {
Symbol query = query("x = 1 and (i = 2 or a = 'a')");
List<List<Symbol>> matches = analyzeExactXI(query);
assertNull(matches);
}
@Test
public void testExtract2ColPKFrom1PartAndOtherPart2EqOr() throws Exception {
Symbol query = query("x = 1 and (i = 2 or i = 3)");
List<List<Symbol>> matches = analyzeExactXI(query);
assertThat(matches.size(), is(2));
assertThat(matches, containsInAnyOrder(
contains(isLiteral(1), isLiteral(2)),
contains(isLiteral(1), isLiteral(3))
));
}
@Test
public void testNoExtract2ColPKFromOnly1Part() throws Exception {
Symbol query = query("x = 1");
List<List<Symbol>> matches = analyzeExactXI(query);
assertNull(matches);
}
@Test
public void testExtractSinglePKFromAnyEq() throws Exception {
Symbol query = query("x = any([1, 2, 3])");
List<List<Symbol>> matches = analyzeExactX(query);
assertThat(matches.size(), is(3));
assertThat(matches, containsInAnyOrder(
contains(isLiteral(1)),
contains(isLiteral(2)),
contains(isLiteral(3))
));
}
@Test
public void testExtract2ColPKFromAnyEq() throws Exception {
Symbol query = query("i = 4 and x = any([1, 2, 3])");
List<List<Symbol>> matches = analyzeExactXI(query);
assertThat(matches.size(), is(3));
assertThat(matches, containsInAnyOrder(
contains(isLiteral(1), isLiteral(4)),
contains(isLiteral(2), isLiteral(4)),
contains(isLiteral(3), isLiteral(4))
));
}
@Test
public void testExtractSinglePKFromAnyEqInOr() throws Exception {
Symbol query = query("x = any([1, 2, 3]) or x = any([4, 5, 3])");
List<List<Symbol>> matches = analyzeExactX(query);
assertThat(matches.size(), is(5));
assertThat(matches, containsInAnyOrder(
contains(isLiteral(1)),
contains(isLiteral(2)),
contains(isLiteral(3)),
contains(isLiteral(4)),
contains(isLiteral(5))
));
}
@Test
public void testExtractSinglePKFromOrInAnd() throws Exception {
Symbol query = query("(x = 1 or x = 2 or x = 3) and (x = 1 or x = 4 or x = 5)");
List<List<Symbol>> matches = analyzeExactX(query);
assertThat(matches.size(), is(1));
assertThat(matches, contains(
contains(isLiteral(1))
));
}
@Test
public void testExtractSinglePK1FromAndAnyEq() throws Exception {
Symbol query = query("x = any([1, 2, 3]) and x = any([4, 5, 3])");
List<List<Symbol>> matches = analyzeExactX(query);
assertThat(matches, is(notNullValue()));
assertThat(matches.size(), is(1)); // 3
assertThat(matches, contains(
contains(isLiteral(3))
));
}
@Test
public void testExtract2ColPKFromAnyEqAnd() throws Exception {
Symbol query = query("x = any([1, 2, 3]) and i = any([1, 2, 3])");
List<List<Symbol>> matches = analyzeExactXI(query);
assertThat(matches.size(), is(9)); // cartesian product: 3 * 3
assertThat(matches, containsInAnyOrder(
contains(isLiteral(1), isLiteral(1)),
contains(isLiteral(1), isLiteral(2)),
contains(isLiteral(1), isLiteral(3)),
contains(isLiteral(2), isLiteral(1)),
contains(isLiteral(2), isLiteral(2)),
contains(isLiteral(2), isLiteral(3)),
contains(isLiteral(3), isLiteral(1)),
contains(isLiteral(3), isLiteral(2)),
contains(isLiteral(3), isLiteral(3))
));
}
@Test
public void testNoPKExtractionIfMatchIsPresent() throws Exception {
Symbol query = query("x in (1, 2, 3) and match(a, 'Hello World')");
List<List<Symbol>> matches = analyzeExactX(query);
assertThat(matches, nullValue());
}
@Test
public void testNoPKExtractionIfFunctionUsingPKIsPresent() throws Exception {
Symbol query = query("x in (1, 2, 3) and substr(cast(x as string), 0) = 4");
List<List<Symbol>> matches = analyzeExactX(query);
assertThat(matches, nullValue());
}
}