Skip to content

Commit 28328ce

Browse files
Merge branch 'apache:main' into main
2 parents 8fbed53 + 34989b0 commit 28328ce

File tree

33 files changed

+982
-543
lines changed

33 files changed

+982
-543
lines changed

core/src/main/java/org/apache/calcite/rel/core/TableModify.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,18 @@
5555
* <ul>
5656
* <li>For {@code INSERT}, those rows are the new values;
5757
* <li>for {@code DELETE}, the old values;
58-
* <li>for {@code UPDATE}, all old values plus updated new values.
58+
* <li>for {@code UPDATE}, all old values plus updated new values;
59+
* <li>for {@code MERGE}, the rows may contain fields for both {@code INSERT} and {@code UPDATE}
60+
* operations, depending on the clauses specified:
61+
* <ul>
62+
* <li>If only `WHEN MATCHED THEN UPDATE` is specified, the row contains all old values plus
63+
* updated new values (like {@code UPDATE}).</li>
64+
* <li>If only `WHEN NOT MATCHED THEN INSERT` is specified, the row contains new values to be
65+
* inserted (like {@code INSERT}).</li>
66+
* <li>If both `WHEN MATCHED THEN UPDATE` and `WHEN NOT MATCHED THEN INSERT` are specified,
67+
* the row contains: new values to be inserted, all old values, updated new values.
68+
* </ul>
69+
* </li>
5970
* </ul>
6071
*/
6172
public abstract class TableModify extends SingleRel {

core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,10 +297,13 @@ protected RelMdSize() {}
297297
switch (type.getSqlTypeName()) {
298298
case BOOLEAN:
299299
case TINYINT:
300+
case UTINYINT:
300301
return 1d;
301302
case SMALLINT:
303+
case USMALLINT:
302304
return 2d;
303305
case INTEGER:
306+
case UINTEGER:
304307
case REAL:
305308
case DECIMAL:
306309
case DATE:
@@ -312,6 +315,7 @@ protected RelMdSize() {}
312315
case INTERVAL_MONTH:
313316
return 4d;
314317
case BIGINT:
318+
case UBIGINT:
315319
case DOUBLE:
316320
case FLOAT: // sic
317321
case TIMESTAMP:
@@ -362,10 +366,13 @@ public double typeValueSize(RelDataType type, @Nullable Comparable value) {
362366
switch (type.getSqlTypeName()) {
363367
case BOOLEAN:
364368
case TINYINT:
369+
case UTINYINT:
365370
return 1d;
366371
case SMALLINT:
372+
case USMALLINT:
367373
return 2d;
368374
case INTEGER:
375+
case UINTEGER:
369376
case REAL:
370377
case DATE:
371378
case TIME:
@@ -376,6 +383,7 @@ public double typeValueSize(RelDataType type, @Nullable Comparable value) {
376383
case INTERVAL_MONTH:
377384
return 4d;
378385
case BIGINT:
386+
case UBIGINT:
379387
case FLOAT: // sic
380388
case DOUBLE:
381389
case TIMESTAMP:

core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,9 +1121,6 @@ private static SqlIdentifier getSqlTargetTable(RelNode e) {
11211121

11221122
/** Visits a TableModify; called by {@link #dispatch} via reflection. */
11231123
public Result visit(TableModify modify) {
1124-
final Map<String, RelDataType> pairs = ImmutableMap.of();
1125-
final Context context = aliasContext(pairs, false);
1126-
11271124
// Target Table Name
11281125
final SqlIdentifier sqlTargetTable = getSqlTargetTable(modify);
11291126

@@ -1142,6 +1139,10 @@ public Result visit(TableModify modify) {
11421139
}
11431140
case UPDATE: {
11441141
final Result input = visitInput(modify, 0);
1142+
// SELECT statement has two parts: columns from the target table (old values) and
1143+
// expressions for the UPDATE. See the TableModify documentation for details.
1144+
final SqlSelect select = input.asSelect();
1145+
final Context context = selectListContext(select.getSelectList(), false);
11451146

11461147
final SqlUpdate sqlUpdate =
11471148
new SqlUpdate(POS, sqlTargetTable,
@@ -1151,8 +1152,7 @@ public Result visit(TableModify modify) {
11511152
exprList(context,
11521153
requireNonNull(modify.getSourceExpressionList(),
11531154
() -> "modify.getSourceExpressionList() is null for " + modify)),
1154-
((SqlSelect) input.node).getWhere(), input.asSelect(),
1155-
null);
1155+
select.getWhere(), select, null);
11561156

11571157
return result(sqlUpdate, input.clauses, modify, null);
11581158
}
@@ -1172,7 +1172,7 @@ public Result visit(TableModify modify) {
11721172
// `WHEN NOT MATCHED THEN INSERT` clauses, the selectList consists of three parts:
11731173
// the insert expression, the target table reference, and the update expression.
11741174
// When querying with the `WHEN MATCHED THEN UPDATE` clause, the selectList will not
1175-
// include the update expression.
1175+
// include the insert expression.
11761176
// However, when querying with the `WHEN NOT MATCHED THEN INSERT` clause,
11771177
// the expression list will only contain the insert expression.
11781178
final SqlNodeList selectList = SqlUtil.stripListAs(select.getSelectList());

core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java

Lines changed: 79 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,6 +1633,10 @@ private static int computeFieldCount(
16331633
return x;
16341634
}
16351635

1636+
public Context selectListContext(SqlNodeList sqlNodeList, boolean aliasRef) {
1637+
return new SelectListContext(dialect, sqlNodeList.size(), aliasRef, sqlNodeList);
1638+
}
1639+
16361640
public Context aliasContext(Map<String, RelDataType> aliases,
16371641
boolean qualified) {
16381642
return new AliasContext(dialect, aliases, qualified);
@@ -1810,6 +1814,80 @@ private boolean isWindowTableFunctionRex(RexNode rex) {
18101814
};
18111815
}
18121816

1817+
/**
1818+
* A context that uses a select list.
1819+
*/
1820+
final class SelectListContext extends BaseContext {
1821+
private final boolean aliasRef;
1822+
private final SqlNodeList selectList;
1823+
1824+
/**
1825+
* Creates a SelectListContext.
1826+
*
1827+
* @param dialect SQL dialect.
1828+
* @param fieldCount Number of fields.
1829+
* @param aliasRef If true, and a select-list item at a given ordinal has an alias
1830+
* (e.g. {@code SUM(x) AS sx}), then a call to {@code field(ordinal)}
1831+
* will return the alias ({@code sx}). If false, it will return the
1832+
* full expression ({@code SUM(x)}).
1833+
* @param selectList The list of expressions in a SELECT clause.
1834+
*/
1835+
SelectListContext(
1836+
SqlDialect dialect, int fieldCount, boolean aliasRef, SqlNodeList selectList) {
1837+
super(dialect, fieldCount);
1838+
this.aliasRef = aliasRef;
1839+
this.selectList = selectList;
1840+
}
1841+
1842+
@Override public SqlNode field(int ordinal) {
1843+
final SqlNode selectItem = selectList.get(ordinal);
1844+
switch (selectItem.getKind()) {
1845+
case AS:
1846+
final SqlCall asCall = (SqlCall) selectItem;
1847+
SqlNode alias = asCall.operand(1);
1848+
if (aliasRef && !SqlUtil.isGeneratedAlias(((SqlIdentifier) alias).getSimple())) {
1849+
// For BigQuery, given the query
1850+
// SELECT SUM(x) AS x FROM t HAVING(SUM(t.x) > 0)
1851+
// we can generate
1852+
// SELECT SUM(x) AS x FROM t HAVING(x > 0)
1853+
// because 'x' in HAVING resolves to the 'AS x' not 't.x'.
1854+
return alias;
1855+
}
1856+
return asCall.operand(0);
1857+
default:
1858+
break;
1859+
}
1860+
return selectItem;
1861+
}
1862+
1863+
@Override public SqlNode orderField(int ordinal) {
1864+
// If the field expression is an unqualified column identifier
1865+
// and matches a different alias, use an ordinal.
1866+
// For example, given
1867+
// SELECT deptno AS empno, empno AS x FROM emp ORDER BY emp.empno
1868+
// we generate
1869+
// SELECT deptno AS empno, empno AS x FROM emp ORDER BY 2
1870+
// "ORDER BY empno" would give incorrect result;
1871+
// "ORDER BY x" is acceptable but is not preferred.
1872+
final SqlNode node = super.orderField(ordinal);
1873+
if (node instanceof SqlIdentifier
1874+
&& ((SqlIdentifier) node).isSimple()) {
1875+
final String name = ((SqlIdentifier) node).getSimple();
1876+
for (Ord<SqlNode> selectItem : Ord.zip(selectList)) {
1877+
if (selectItem.i != ordinal) {
1878+
final @Nullable String alias =
1879+
SqlValidatorUtil.alias(selectItem.e);
1880+
if (name.equalsIgnoreCase(alias) && dialect.getConformance().isSortByAlias()) {
1881+
return SqlLiteral.createExactNumeric(
1882+
Integer.toString(ordinal + 1), SqlParserPos.ZERO);
1883+
}
1884+
}
1885+
}
1886+
}
1887+
return node;
1888+
}
1889+
}
1890+
18131891
/** Result of implementing a node. */
18141892
public class Result {
18151893
final SqlNode node;
@@ -1907,7 +1985,7 @@ private Builder builder(RelNode rel, Set<Clause> clauses) {
19071985
if (!selectList.equals(SqlNodeList.SINGLETON_STAR)) {
19081986
final boolean aliasRef = expectedClauses.contains(Clause.HAVING)
19091987
&& dialect.getConformance().isHavingAlias();
1910-
newContext = new SelectListContext(dialect, selectList.size(), aliasRef, selectList);
1988+
newContext = selectListContext(selectList, aliasRef);
19111989
} else {
19121990
boolean qualified =
19131991
!dialect.hasImplicitTableAlias() || aliases.size() > 1;
@@ -2295,69 +2373,6 @@ Result withExpectedClauses(boolean ignoreClauses,
22952373
: new Result(node, clauses, neededAlias, neededType, aliases, anon,
22962374
ignoreClauses, ImmutableSet.copyOf(expectedClauses), expectedRel);
22972375
}
2298-
2299-
/**
2300-
* A context that uses a select list.
2301-
*/
2302-
private final class SelectListContext extends BaseContext {
2303-
private final boolean aliasRef;
2304-
private final SqlNodeList selectList;
2305-
2306-
private SelectListContext(
2307-
SqlDialect dialect, int fieldCount, boolean aliasRef, SqlNodeList selectList) {
2308-
super(dialect, fieldCount);
2309-
this.aliasRef = aliasRef;
2310-
this.selectList = selectList;
2311-
}
2312-
2313-
@Override public SqlNode field(int ordinal) {
2314-
final SqlNode selectItem = selectList.get(ordinal);
2315-
switch (selectItem.getKind()) {
2316-
case AS:
2317-
final SqlCall asCall = (SqlCall) selectItem;
2318-
SqlNode alias = asCall.operand(1);
2319-
if (aliasRef && !SqlUtil.isGeneratedAlias(((SqlIdentifier) alias).getSimple())) {
2320-
// For BigQuery, given the query
2321-
// SELECT SUM(x) AS x FROM t HAVING(SUM(t.x) > 0)
2322-
// we can generate
2323-
// SELECT SUM(x) AS x FROM t HAVING(x > 0)
2324-
// because 'x' in HAVING resolves to the 'AS x' not 't.x'.
2325-
return alias;
2326-
}
2327-
return asCall.operand(0);
2328-
default:
2329-
break;
2330-
}
2331-
return selectItem;
2332-
}
2333-
2334-
@Override public SqlNode orderField(int ordinal) {
2335-
// If the field expression is an unqualified column identifier
2336-
// and matches a different alias, use an ordinal.
2337-
// For example, given
2338-
// SELECT deptno AS empno, empno AS x FROM emp ORDER BY emp.empno
2339-
// we generate
2340-
// SELECT deptno AS empno, empno AS x FROM emp ORDER BY 2
2341-
// "ORDER BY empno" would give incorrect result;
2342-
// "ORDER BY x" is acceptable but is not preferred.
2343-
final SqlNode node = super.orderField(ordinal);
2344-
if (node instanceof SqlIdentifier
2345-
&& ((SqlIdentifier) node).isSimple()) {
2346-
final String name = ((SqlIdentifier) node).getSimple();
2347-
for (Ord<SqlNode> selectItem : Ord.zip(selectList)) {
2348-
if (selectItem.i != ordinal) {
2349-
final @Nullable String alias =
2350-
SqlValidatorUtil.alias(selectItem.e);
2351-
if (name.equalsIgnoreCase(alias) && dialect.getConformance().isSortByAlias()) {
2352-
return SqlLiteral.createExactNumeric(
2353-
Integer.toString(ordinal + 1), SqlParserPos.ZERO);
2354-
}
2355-
}
2356-
}
2357-
}
2358-
return node;
2359-
}
2360-
}
23612376
}
23622377

23632378
/** Builder. */

core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ static ImmutableSortedSet<TimeUnitRange> extractTimeUnits(RexNode e) {
149149
@VisibleForTesting
150150
public static RexNode replaceTimeUnits(RexBuilder rexBuilder, RexNode e,
151151
String timeZone) {
152+
e = RexUtil.expandSearch(rexBuilder, null, e);
152153
ImmutableSortedSet<TimeUnitRange> timeUnits = extractTimeUnits(e);
153154
if (!timeUnits.contains(TimeUnitRange.YEAR)) {
154155
// Case when we have FLOOR or CEIL but no extract on YEAR.

core/src/main/java/org/apache/calcite/rex/RexSimplify.java

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3175,13 +3175,28 @@ static class SargCollector {
31753175
this.negate = negate;
31763176
}
31773177

3178+
/**
3179+
* Accepts an expression and converts it to a Sarg if possible.
3180+
*
3181+
* @param term the expression to convert
3182+
* @param newTerms the list holding the result of the conversion or the
3183+
* original term if it cannot be converted
3184+
*/
31783185
private void accept(RexNode term, List<RexNode> newTerms) {
31793186
if (!accept_(term, newTerms)) {
31803187
newTerms.add(term);
31813188
}
31823189
newTermsCount = newTerms.size();
31833190
}
31843191

3192+
/**
3193+
* Accepts an expression and converts it to a Sarg if possible.
3194+
* Only certain kinds of expressions can be converted to Sargs.
3195+
*
3196+
* @param e the expression to convert
3197+
* @param newTerms the list to which the Sarg will be added if the expression is accepted
3198+
* @return true if the expression can be converted to a Sarg, false otherwise
3199+
*/
31853200
private boolean accept_(RexNode e, List<RexNode> newTerms) {
31863201
switch (e.getKind()) {
31873202
case LESS_THAN:
@@ -3204,31 +3219,25 @@ private boolean accept_(RexNode e, List<RexNode> newTerms) {
32043219
}
32053220
}
32063221

3222+
/**
3223+
* Accepts two operands from a binary comparison operator and converts them to a Sarg if
3224+
* possible. Only comparisons between a literal and a deterministic expression can be converted.
3225+
* Simplifications with non-deterministic expressions are generally avoided to ensure consistent
3226+
* results.
3227+
*
3228+
* @param left the left operand of the comparison
3229+
* @param right the right operand of the comparison
3230+
* @param kind the kind of comparison operator
3231+
* @param newTerms the list to which the Sarg will be added if accepted
3232+
* @return true if the operands can be converted to a Sarg, false otherwise
3233+
*/
32073234
private boolean accept2(RexNode left, RexNode right, SqlKind kind,
32083235
List<RexNode> newTerms) {
3209-
switch (left.getKind()) {
3210-
case INPUT_REF:
3211-
case FIELD_ACCESS:
3212-
case CAST:
3213-
switch (right.getKind()) {
3214-
case LITERAL:
3215-
return accept2b(left, kind, (RexLiteral) right, newTerms);
3216-
default:
3217-
break;
3218-
}
3219-
return false;
3220-
case LITERAL:
3221-
switch (right.getKind()) {
3222-
case INPUT_REF:
3223-
case FIELD_ACCESS:
3224-
case CAST:
3225-
return accept2b(right, kind.reverse(), (RexLiteral) left, newTerms);
3226-
default:
3227-
break;
3228-
}
3229-
return false;
3230-
default:
3231-
break;
3236+
if (right.isA(SqlKind.LITERAL) && RexUtil.isDeterministic(left)) {
3237+
return accept2b(left, kind, (RexLiteral) right, newTerms);
3238+
}
3239+
if (left.isA(SqlKind.LITERAL) && RexUtil.isDeterministic(right)) {
3240+
return accept2b(right, kind.reverse(), (RexLiteral) left, newTerms);
32323241
}
32333242
return false;
32343243
}
@@ -3238,7 +3247,14 @@ private static <E> E addFluent(List<? super E> list, E e) {
32383247
return e;
32393248
}
32403249

3241-
// always returns true
3250+
/**
3251+
* Accepts an operand from a null predicate and converts it to a Sarg.
3252+
*
3253+
* @param e the operand of the null predicate
3254+
* @param kind the kind of the null predicate
3255+
* @param newTerms the list to which the Sarg is added
3256+
* @return true since the operand is always converted to a Sarg
3257+
*/
32423258
private boolean accept1(RexNode e, SqlKind kind, List<RexNode> newTerms) {
32433259
final RexSargBuilder b =
32443260
map.computeIfAbsent(e, e2 ->
@@ -3256,6 +3272,17 @@ private boolean accept1(RexNode e, SqlKind kind, List<RexNode> newTerms) {
32563272
}
32573273
}
32583274

3275+
/**
3276+
* Accepts two operands from a binary comparison operator and converts them to a Sarg when the
3277+
* literal is not null. The conversion depends on the kind of comparison operator and only
3278+
* certain operators are supported.
3279+
*
3280+
* @param e any kind of deterministic expression
3281+
* @param kind the kind of comparison operator
3282+
* @param literal the literal operand of the comparison
3283+
* @param newTerms the list to which the Sarg is added if accepted
3284+
* @return false if the literal operand is null, true otherwise
3285+
*/
32593286
private boolean accept2b(RexNode e, SqlKind kind,
32603287
RexLiteral literal, List<RexNode> newTerms) {
32613288
if (literal.getValue() == null) {

0 commit comments

Comments
 (0)