Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
e9ed8ff
In MockCatalogReaderSimple, rename column 'name' to 'dname' to be con…
julianhyde Feb 27, 2026
83c0a6b
Continue "In MockCatalogReaderSimple"
julianhyde Feb 28, 2026
f6a7edf
[CALCITE-7420] Convert RelOptRulesTest to Quidem scripts (first test)
julianhyde Feb 27, 2026
b23a74d
[CALCITE-7420] Migrate five aggregate rule tests to aggregate.iq
julianhyde Feb 27, 2026
b17c629
[CALCITE-7420] Migrate 10 rule tests to Quidem scripts
julianhyde Feb 28, 2026
cfc866c
[CALCITE-7420] Migrate ~49 tests from RelOptRulesTest to Quidem .iq f…
julianhyde Feb 28, 2026
754025b
[CALCITE-7420] Migrate ~86 tests from RelOptRulesTest to Quidem .iq f…
julianhyde Feb 28, 2026
f350288
[CALCITE-7420] Migrate ~205 tests from RelOptRulesTest to Quidem .iq …
julianhyde Mar 1, 2026
a1a7a95
[CALCITE-7420] Add config tokens to !sub-plan; migrate testReduceCase…
julianhyde Mar 1, 2026
1b6ea30
[CALCITE-7420] Migrate ~30 tests using new config tokens to Quidem .i…
julianhyde Mar 1, 2026
759c03a
Lint
julianhyde Mar 1, 2026
acc3119
[CALCITE-7420] Enable !ok for ~290 tests in Quidem .iq files
julianhyde Mar 1, 2026
1f976b0
[CALCITE-7420] Some !ok directives in .iq files overflow or cause typ…
julianhyde Mar 1, 2026
4672ab2
[CALCITE-7420] Migrate remaining 7 tests from RelOptRulesTest to Quid…
julianhyde Mar 1, 2026
dc23322
Disable query whose result depends on current date
julianhyde Mar 2, 2026
95e34b5
Lint
julianhyde Mar 2, 2026
05be8b1
Fix line endings on Windows
julianhyde Mar 2, 2026
416f3f8
Fix line endings on Windows (2nd attempt)
julianhyde Mar 2, 2026
2906e3c
[CALCITE-7420] Migrate tests to Quidem .iq format (batch)
julianhyde Mar 3, 2026
3ca9ead
Add 'Not using ok' comments
julianhyde Mar 3, 2026
a1a5a38
Convert many 'Not using ok' comments to '!ok'
julianhyde Mar 3, 2026
2365b5e
[CALCITE-7420] Migrate tests to Quidem .iq format (batch)
julianhyde Mar 3, 2026
09c6153
Reformat SQL in .iq test files: wrap long lines (>80 chars), each cla…
julianhyde Mar 3, 2026
9c07396
[CALCITE-7420] Migrate filter tests to Quidem .iq format
julianhyde Mar 3, 2026
9a16c9e
[CALCITE-7420] Migrate tests to Quidem .iq format (batch)
julianhyde Mar 3, 2026
5e4cf91
[CALCITE-7420] Migrate tests to Quidem .iq format (batch)
julianhyde Mar 4, 2026
f8973f7
[CALCITE-7420] Migrate tests to Quidem .iq format (batch)
julianhyde Mar 4, 2026
fc66680
Add !ok to testAggregateMerge tests in aggregate.iq
julianhyde Mar 4, 2026
1b8f0ca
[CALCITE-7420] Improve SQL formatting and aliases in .iq test files
julianhyde Mar 4, 2026
eafc6e4
[CALCITE-7420] Migrate tests to Quidem .iq format (batch)
julianhyde Mar 4, 2026
5c99d95
Following [CALCITE-7424], Lint should print the target line number wh…
julianhyde Mar 4, 2026
af9b80b
[CALCITE-7420] Migrate tests to Quidem .iq format (batch)
julianhyde Mar 4, 2026
4361dfd
Lint should ensure that test names in .iq files are unique
julianhyde Mar 4, 2026
b6f8cfe
[CALCITE-7420] Migrate tests to Quidem .iq format (batch)
julianhyde Mar 5, 2026
5e74c80
[CALCITE-7420] Migrate tests to Quidem .iq format (batch)
julianhyde Mar 5, 2026
ebe6e40
Remove orphaned Javadoc from RelOptRulesTest.java; update .iq files
julianhyde Mar 6, 2026
c321fcd
Rename Quidem command from '!sub-plan' to '!transform'
julianhyde Mar 19, 2026
0b95a95
Move and rename SubPlanCommand to a top-level class TransformCommand
julianhyde Mar 19, 2026
5b4c4a0
Refactor SubPlanCommand.execute() into focused helper methods
julianhyde Mar 19, 2026
14fd6bf
Introduce parameterised rule syntax in !transform command
julianhyde Mar 19, 2026
2fdc41f
[CALCITE-7420] Add RelBuilder(...) and Sql2Rel(...) config groups to …
julianhyde Mar 19, 2026
1152756
[CALCITE-7420] Migrate testDecorrelateTwoScalar, testExpandJoinIn, te…
julianhyde Mar 20, 2026
eb63904
[CALCITE-7420] Migrate testUnnestInternalMissingParameter, testExists…
julianhyde Mar 21, 2026
fba8384
[CALCITE-7420] Migrate testReduceConstantsCalc, testReducingConstants…
julianhyde Mar 21, 2026
771fe43
[CALCITE-7420] Migrate testReplace, testAggregateWithDynamicParam, te…
julianhyde Mar 21, 2026
2be1a55
Fix expected output in misc-rules.iq and reduce-expressions.iq after …
julianhyde Mar 21, 2026
5b4430e
[CALCITE-7420] Migrate 10 DPhyp/HyperGraph tests to hypergraph.iq
julianhyde Mar 21, 2026
46a0373
Clean up EXPR\$N column names in .iq test files
julianhyde Mar 26, 2026
2bcff24
Format SQL in .iq test files
julianhyde Mar 26, 2026
c539e70
Review and fix '# Not using !ok' comments in .iq test files
julianhyde Mar 26, 2026
c51a2e3
Normalize profit margin to 3dp in top-down-decorrelate.iq
julianhyde Mar 26, 2026
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
18 changes: 9 additions & 9 deletions babel/src/test/java/org/apache/calcite/test/BabelTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ names, is(
+ " from emp e join dept d on e.deptno = d.deptno")
.fails("SELECT \\* EXCLUDE/EXCEPT list contains unknown column\\(s\\): D.DEPTNO");

fixture.withSql("select e.* exclude(e.empno, e.ename, e.job, e.mgr), d.* exclude(d.name)"
fixture.withSql("select e.* exclude(e.empno, e.ename, e.job, e.mgr), d.* exclude(d.dname)"
+ " from emp e join dept d on e.deptno = d.deptno")
.type(type -> {
final List<String> names = type.getFieldList().stream()
Expand All @@ -267,11 +267,11 @@ names, is(
final List<String> names = type.getFieldList().stream()
.map(RelDataTypeField::getName)
.collect(Collectors.toList());
assertThat(names, is(ImmutableList.of("NAME")));
assertThat(names, is(ImmutableList.of("DNAME")));
});

// To verify that the exclude list contains all columns in the table
fixture.withSql("select ^*^ exclude(deptno, name) from dept")
fixture.withSql("select ^*^ exclude(deptno, dname) from dept")
.fails("SELECT \\* EXCLUDE/EXCEPT list cannot exclude all columns");
}

Expand Down Expand Up @@ -364,7 +364,7 @@ names, is(
.withConformance(SqlConformanceEnum.BABEL);

v.withSql("SELECT * FROM dept LEFT SEMI JOIN emp ON emp.deptno = dept.deptno")
.type("RecordType(INTEGER NOT NULL DEPTNO, VARCHAR(10) NOT NULL NAME) NOT NULL");
.type("RecordType(INTEGER NOT NULL DEPTNO, VARCHAR(10) NOT NULL DNAME) NOT NULL");

v.withSql("SELECT deptno FROM dept LEFT SEMI JOIN emp ON emp.deptno = dept.deptno")
.type("RecordType(INTEGER NOT NULL DEPTNO) NOT NULL");
Expand All @@ -379,10 +379,10 @@ names, is(
.withConformance(SqlConformanceEnum.BABEL);

v.withSql("SELECT * FROM dept LEFT ANTI JOIN emp ON emp.deptno = dept.deptno")
.type("RecordType(INTEGER NOT NULL DEPTNO, VARCHAR(10) NOT NULL NAME) NOT NULL");
.type("RecordType(INTEGER NOT NULL DEPTNO, VARCHAR(10) NOT NULL DNAME) NOT NULL");

v.withSql("SELECT name FROM dept LEFT ANTI JOIN emp ON emp.deptno = dept.deptno")
.type("RecordType(VARCHAR(10) NOT NULL NAME) NOT NULL");
v.withSql("SELECT dname FROM dept LEFT ANTI JOIN emp ON emp.deptno = dept.deptno")
.type("RecordType(VARCHAR(10) NOT NULL DNAME) NOT NULL");
}

/** Test case for
Expand All @@ -402,8 +402,8 @@ names, is(
// Test BY clause with multiple columns
v.withSql("select ename, empno by deptno, job from emp").ok();
// Test complex BY clause example from the feature proposal
v.withSql("select e.ename, e.empno by d.name as dept DESC, e.job as title "
+ "from emp as e join dept as d on e.deptno = d.deptno where d.name = 'SALES'")
v.withSql("select e.ename, e.empno by d.dname as dept DESC, e.job as title "
+ "from emp as e join dept as d on e.deptno = d.deptno where d.dname = 'SALES'")
.ok();

// Test SELECT BY cannot be used with GROUP BY
Expand Down
12 changes: 12 additions & 0 deletions core/src/main/java/org/apache/calcite/rel/rules/CoreRules.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ private CoreRules() {}
AGGREGATE_PROJECT_PULL_UP_CONSTANTS =
AggregateProjectPullUpConstantsRule.Config.DEFAULT.toRule();

/** Rule that replaces constant group-by keys in an {@link Aggregate}
* with a dummy join to a single-row {@link org.apache.calcite.rel.core.Values},
* allowing the aggregate to be applied to a partitioned relation. */
public static final AggregateProjectConstantToDummyJoinRule
AGGREGATE_PROJECT_CONSTANT_TO_DUMMY_JOIN =
AggregateProjectConstantToDummyJoinRule.Config.DEFAULT.toRule();

/** More general form of {@link #AGGREGATE_PROJECT_PULL_UP_CONSTANTS}
* that matches any relational expression. */
public static final AggregateProjectPullUpConstantsRule
Expand Down Expand Up @@ -327,6 +334,11 @@ private CoreRules() {}
public static final FilterMultiJoinMergeRule FILTER_MULTI_JOIN_MERGE =
FilterMultiJoinMergeRule.Config.DEFAULT.toRule();

/** Rule that flattens uncorrelated calls in a correlated {@link Filter}. */
public static final FilterFlattenCorrelatedConditionRule
FILTER_FLATTEN_CORRELATED_CONDITION =
FilterFlattenCorrelatedConditionRule.Config.DEFAULT.toRule();

/** Rule that replaces {@code IS NOT DISTINCT FROM}
* in a {@link Filter} with logically equivalent operations. */
public static final FilterRemoveIsNotDistinctFromRule
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ class SqlAdvisorTest extends SqlValidatorTestCase {
private static final List<String> DEPT_COLUMNS =
Arrays.asList(
"COLUMN(DEPTNO)",
"COLUMN(NAME)");
"COLUMN(DNAME)");

protected static final List<String> PREDICATE_KEYWORDS =
Arrays.asList(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,8 @@ public void testSqlMisc(String path) throws Exception {
return new ExtendedCommandHandler();
}

/** Command handler that adds a "!explain-validated-on dialect..." command
* (see {@link QuidemTest.ExplainValidatedCommand}). */
/** Command handler that adds "!explain-validated-on dialect..." and
* "!transform" commands (see {@link TransformCommand}). */
private static class ExtendedCommandHandler implements CommandHandler {
@Override public @Nullable Command parseCommand(List<String> lines,
List<String> content, String line) {
Expand All @@ -226,6 +226,10 @@ private static class ExtendedCommandHandler implements CommandHandler {
SqlParserImpl.FACTORY, lines, content, set.build());
}
}
if (line.startsWith("transform ")) {
String args = line.substring("transform ".length());
return new TransformCommand(lines, content, args);
}
return null;
}
}
Expand Down
33 changes: 18 additions & 15 deletions core/src/test/java/org/apache/calcite/test/HepPlannerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class HepPlannerTest {
//~ Static fields/initializers ---------------------------------------------

private static final String UNION_TREE =
"(select name from dept union select ename from emp)"
"(select dname from dept union select ename from emp)"
+ " union (select ename from bonus)";

private static final String COMPLEX_UNION_TREE = "select * from (\n"
Expand Down Expand Up @@ -134,7 +134,7 @@ public final RelOptFixture sql(String sql) {
.withDescription("CoerceInputsRule:Intersection") // TODO
.toRule());

final String sql = "(select name from dept union select ename from emp)\n"
final String sql = "(select dname from dept union select ename from emp)\n"
+ "intersect (select fname from customer.contact)";
sql(sql).withPlanner(planner).checkUnchanged();
}
Expand All @@ -151,7 +151,7 @@ public final RelOptFixture sql(String sql) {

planner.addRule(CoreRules.FILTER_TO_CALC);

final String sql = "select name from sales.dept where deptno=12";
final String sql = "select dname from sales.dept where deptno=12";
sql(sql).withPlanner(planner).check();
}

Expand Down Expand Up @@ -182,9 +182,9 @@ public final RelOptFixture sql(String sql) {
private static String buildUnion(int n) {
StringBuilder sb = new StringBuilder();
sb.append("select * from (");
sb.append("select name from sales.dept");
sb.append("select dname from sales.dept");
for (int i = 0; i < n; i++) {
sb.append(" union all select name from sales.dept");
sb.append(" union all select dname from sales.dept");
}
sb.append(")");
return sb.toString();
Expand All @@ -195,7 +195,7 @@ private static String buildUnion(int n) {
HepPlanner planner =
new HepPlanner(
programBuilder.build());
RelNode root = sql("select name from sales.dept").toRel();
RelNode root = sql("select dname from sales.dept").toRel();
planner.setRoot(root);

StringWriter sw = new StringWriter();
Expand All @@ -205,13 +205,16 @@ private static String buildUnion(int n) {
final RelNode root1 = planner.getRoot();
assertThat(root1, notNullValue());
root1.explain(planWriter);
String planStr = sw.toString();

assertThat(
planStr, isLinux("digraph {\n"
+ "\"LogicalTableScan\\ntable = [CATALOG, SA\\nLES, DEPT]\\n\" -> "
+ "\"LogicalProject\\nNAME = $1\\n\" [label=\"0\"]\n"
+ "}\n"));
String expected =
"digraph {\n"
+ "\"LogicalTableScan\\n"
+ "table = [CATALOG, SA\\n"
+ "LES, DEPT]\\n"
+ "\" -> \"LogicalProject\\nDNAME = $1\\n"
+ "\" [label=\"0\"]\n"
+ "}\n";
assertThat(sw.toString(), isLinux(expected));
}

private void assertIncludesExactlyOnce(String message, String digest,
Expand Down Expand Up @@ -329,7 +332,7 @@ private void assertIncludesExactlyOnce(String message, String digest,
programBuilder.addRuleInstance(CoreRules.FILTER_TO_CALC);
programBuilder.addGroupEnd();

final String sql = "select upper(name) from dept where deptno=20";
final String sql = "select upper(dname) from dept where deptno=20";
sql(sql).withProgram(programBuilder.build()).check();
}

Expand All @@ -342,11 +345,11 @@ private void assertIncludesExactlyOnce(String message, String digest,

HepPlanner planner = new HepPlanner(programBuilder.build());
planner.setRoot(
sql("select upper(name) from dept where deptno=20").toRel());
sql("select upper(dname) from dept where deptno=20").toRel());
planner.findBestExp();
// Reuse of HepPlanner (should trigger GC).
planner.setRoot(
sql("select upper(name) from dept where deptno=20").toRel());
sql("select upper(dname) from dept where deptno=20").toRel());
planner.findBestExp();
}

Expand Down
80 changes: 65 additions & 15 deletions core/src/test/java/org/apache/calcite/test/LintTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package org.apache.calcite.test;

import org.apache.calcite.runtime.PairList;
import org.apache.calcite.util.Puffin;
import org.apache.calcite.util.Source;
import org.apache.calcite.util.Sources;
Expand All @@ -35,8 +36,10 @@
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Consumer;
Expand All @@ -63,6 +66,10 @@ class LintTest {
private static final Pattern CALCITE_PATTERN =
compile("^(\\[CALCITE-[0-9]{1,4}][ ]).*");
private static final Pattern PATTERN = compile("^ *(// )?");
/** Pattern matching a test header in a .iq file, e.g.
* {@code # testFoo --...--} (80 chars total). */
private static final Pattern IQ_TEST_HEADER =
compile("^# test[A-Za-z][A-Za-z0-9]* -*$");

private static final String TERMINOLOGY_ERROR_MSG =
"Message contains '%s' word; use one of the following instead: %s";
Expand Down Expand Up @@ -233,6 +240,39 @@ && isJava(line.filename()),
}
})

// In .iq files, lines matching '# test[A-Za-z]+' must be exactly
// 80 chars long (padded with '-') and have a name unique across files.
.add(line -> line.filename().endsWith(".iq")
&& line.matches("^# test[A-Za-z].*"),
line -> {
final String s = line.line();
// Check format: must match '# test[A-Za-z]+ -*' exactly
if (!IQ_TEST_HEADER.matcher(s).matches()) {
line.state().message(
"Test header must match '# test[A-Za-z]+ -*'", line);
} else if (s.length() != 80) {
line.state().message(
"Test header must be exactly 80 chars (is " + s.length()
+ ")",
line);
} else {
// Extract the test name (the word beginning with 'test')
final String name = s.substring(2, s.indexOf(' ', 2));
final Message msg =
new Message(line.source(), line.fnr(),
"Duplicate .iq test name '" + name + "'");
final GlobalState g = line.globalState();
synchronized (g) {
final Message prev = g.iqTestNames.put(name, msg);
if (prev != null) {
// Duplicate: report both occurrences
g.messages.add(prev);
g.messages.add(msg);
}
}
}
})

.build();
}

Expand Down Expand Up @@ -563,6 +603,9 @@ private static class Message {
private static class GlobalState {
int fileCount = 0;
final List<Message> messages = new ArrayList<>();
/** Test names seen in .iq files: name to first-occurrence location.
* Guarded by {@code this}. */
final Map<String, Message> iqTestNames = new LinkedHashMap<>();
}

/** Internal state of the lint rules, per file. */
Expand Down Expand Up @@ -606,7 +649,8 @@ public boolean inJavadoc() {
+ " }\n"
+ "}\n",
"GuavaCharSource{memory}:7:"
+ "Lines must be sorted; ' case b' should be before ' case c'");
+ "Lines must be sorted; ' case b' should be before ' case c'"
+ " (move to line 5)");

// Cases after "until" are checked against the same sorted list.
checkSortSpec(
Expand All @@ -622,7 +666,8 @@ public boolean inJavadoc() {
+ " }\n"
+ "}\n",
"GuavaCharSource{memory}:9:"
+ "Lines must be sorted; ' case a' should be before ' case x'");
+ "Lines must be sorted; ' case a' should be before ' case x'"
+ " (move to line 4)");

// Change '#}' to '##}': consumer stops at the same-indent '}', so
// the second switch's cases are not compared. No violations.
Expand All @@ -649,7 +694,8 @@ public boolean inJavadoc() {
+ " }\n"
+ "}\n",
"GuavaCharSource{memory}:4:"
+ "Lines must be sorted; 'a' should be before 'c'");
+ "Lines must be sorted; 'a' should be before 'c'"
+ " (move to line 3)");

// Specification spread over multiple lines using '\' continuation.
checkSortSpec(
Expand All @@ -663,7 +709,8 @@ public boolean inJavadoc() {
+ " }\n"
+ "}\n",
"GuavaCharSource{memory}:6:"
+ "Lines must be sorted; 'a' should be before 'c'");
+ "Lines must be sorted; 'a' should be before 'c'"
+ " (move to line 5)");
}

private void checkSortSpec(String code, String... expectedMessages) {
Expand Down Expand Up @@ -754,7 +801,7 @@ private static class SortConsumer
implements Consumer<Puffin.Line<GlobalState, FileState>> {
final Sort sort;
final Comparator<String> comparator = String.CASE_INSENSITIVE_ORDER;
final List<String> lines = new ArrayList<>();
final PairList<String, Integer> lines = PairList.of();
boolean done = false;

SortConsumer(Sort sort) {
Expand Down Expand Up @@ -791,19 +838,22 @@ private static class SortConsumer
private void addLine(Puffin.Line<GlobalState, FileState> line,
String thisLine) {
if (!lines.isEmpty()) {
final String prevLine = lines.get(lines.size() - 1);
final String prevLine = lines.left(lines.size() - 1);
if (comparator.compare(prevLine, thisLine) > 0) {
final String earlierLine =
Util.filter(lines, s -> comparator.compare(s, thisLine) > 0)
.iterator().next();
line.state().message(
String.format(Locale.ROOT,
"Lines must be sorted; '%s' should be before '%s'",
thisLine, earlierLine),
line);
for (int i = 0; i < lines.size(); i++) {
if (comparator.compare(lines.left(i), thisLine) > 0) {
line.state().message(
String.format(Locale.ROOT,
"Lines must be sorted; '%s' should be before '%s'"
+ " (move to line %d)",
thisLine, lines.left(i), lines.right(i)),
line);
break;
}
}
}
}
lines.add(thisLine);
lines.add(thisLine, line.fnr());
}
}
}
Loading