Skip to content

Commit 78a0063

Browse files
committed
Could be...
1 parent 9871f14 commit 78a0063

File tree

6 files changed

+389
-15
lines changed

6 files changed

+389
-15
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.drill.exec.planner.sql;
19+
20+
import org.apache.calcite.sql.SqlCall;
21+
import org.apache.calcite.sql.SqlFunction;
22+
import org.apache.calcite.sql.SqlNode;
23+
import org.apache.calcite.sql.SqlOperator;
24+
import org.apache.calcite.sql.SqlOperatorBinding;
25+
import org.apache.calcite.sql.SqlSyntax;
26+
import org.apache.calcite.sql.SqlWriter;
27+
import org.apache.calcite.sql.validate.SqlMonotonicity;
28+
import org.apache.calcite.sql.validate.SqlValidator;
29+
import org.apache.calcite.util.Litmus;
30+
31+
/**
32+
* Wrapper for Calcite's EXTRACT function that provides custom type inference.
33+
* In Calcite 1.35, EXTRACT returns BIGINT by default, but Drill returns DOUBLE
34+
* for SECOND to support fractional seconds.
35+
*/
36+
public class DrillCalciteSqlExtractWrapper extends SqlFunction implements DrillCalciteSqlWrapper {
37+
private final SqlFunction operator;
38+
39+
public DrillCalciteSqlExtractWrapper(SqlFunction wrappedFunction) {
40+
super(wrappedFunction.getName(),
41+
wrappedFunction.getSqlIdentifier(),
42+
wrappedFunction.getKind(),
43+
// Use Drill's custom EXTRACT type inference which returns DOUBLE for SECOND
44+
TypeInferenceUtils.getDrillSqlReturnTypeInference("EXTRACT", java.util.Collections.emptyList()),
45+
wrappedFunction.getOperandTypeInference(),
46+
wrappedFunction.getOperandTypeChecker(),
47+
wrappedFunction.getParamTypes(),
48+
wrappedFunction.getFunctionType());
49+
this.operator = wrappedFunction;
50+
}
51+
52+
@Override
53+
public SqlNode rewriteCall(SqlValidator validator, SqlCall call) {
54+
return operator.rewriteCall(validator, call);
55+
}
56+
57+
@Override
58+
public SqlOperator getOperator() {
59+
return operator;
60+
}
61+
62+
@Override
63+
public boolean validRexOperands(int count, Litmus litmus) {
64+
return true;
65+
}
66+
67+
@Override
68+
public String getAllowedSignatures(String opNameToUse) {
69+
return operator.getAllowedSignatures(opNameToUse);
70+
}
71+
72+
@Override
73+
public SqlMonotonicity getMonotonicity(SqlOperatorBinding call) {
74+
return operator.getMonotonicity(call);
75+
}
76+
77+
@Override
78+
public boolean isDeterministic() {
79+
return operator.isDeterministic();
80+
}
81+
82+
@Override
83+
public boolean isDynamicFunction() {
84+
return operator.isDynamicFunction();
85+
}
86+
87+
@Override
88+
public SqlSyntax getSyntax() {
89+
return operator.getSyntax();
90+
}
91+
92+
@Override
93+
public void unparse(SqlWriter writer, SqlCall call, int leftPrec, int rightPrec) {
94+
operator.unparse(writer, call, leftPrec, rightPrec);
95+
}
96+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.drill.exec.planner.sql;
19+
20+
import org.apache.calcite.rel.type.RelDataType;
21+
import org.apache.calcite.rel.type.RelDataTypeFactory;
22+
import org.apache.calcite.sql.SqlCall;
23+
import org.apache.calcite.sql.SqlFunction;
24+
import org.apache.calcite.sql.SqlNode;
25+
import org.apache.calcite.sql.SqlOperator;
26+
import org.apache.calcite.sql.SqlOperatorBinding;
27+
import org.apache.calcite.sql.SqlSyntax;
28+
import org.apache.calcite.sql.SqlWriter;
29+
import org.apache.calcite.sql.type.SqlReturnTypeInference;
30+
import org.apache.calcite.sql.type.SqlTypeName;
31+
import org.apache.calcite.sql.validate.SqlMonotonicity;
32+
import org.apache.calcite.sql.validate.SqlValidator;
33+
import org.apache.calcite.util.Litmus;
34+
35+
/**
36+
* Wrapper for Calcite's TIMESTAMPADD function that provides custom type inference.
37+
* Fixes Calcite 1.35 issue where DATE types incorrectly get precision added,
38+
* causing "typeName.allowsPrecScale(true, false): DATE" assertion errors.
39+
*/
40+
public class DrillCalciteSqlTimestampAddWrapper extends SqlFunction implements DrillCalciteSqlWrapper {
41+
private final SqlFunction operator;
42+
43+
private static final SqlReturnTypeInference TIMESTAMP_ADD_INFERENCE = opBinding -> {
44+
RelDataTypeFactory typeFactory = opBinding.getTypeFactory();
45+
46+
// Get operand types
47+
RelDataType intervalType = opBinding.getOperandType(0);
48+
RelDataType datetimeType = opBinding.getOperandType(2);
49+
50+
// Extract time unit from interval qualifier
51+
org.apache.calcite.avatica.util.TimeUnit timeUnit =
52+
intervalType.getIntervalQualifier().getStartUnit();
53+
54+
SqlTypeName returnTypeName;
55+
int precision = -1;
56+
57+
// Match logic from DrillConvertletTable.timestampAddConvertlet()
58+
switch (timeUnit) {
59+
case DAY:
60+
case WEEK:
61+
case MONTH:
62+
case QUARTER:
63+
case YEAR:
64+
case NANOSECOND:
65+
returnTypeName = datetimeType.getSqlTypeName();
66+
// Only set precision for types that support it (TIMESTAMP, TIME)
67+
if (returnTypeName == SqlTypeName.TIMESTAMP || returnTypeName == SqlTypeName.TIME) {
68+
precision = 3;
69+
}
70+
break;
71+
case MICROSECOND:
72+
case MILLISECOND:
73+
returnTypeName = SqlTypeName.TIMESTAMP;
74+
precision = 3;
75+
break;
76+
case SECOND:
77+
case MINUTE:
78+
case HOUR:
79+
if (datetimeType.getSqlTypeName() == SqlTypeName.TIME) {
80+
returnTypeName = SqlTypeName.TIME;
81+
} else {
82+
returnTypeName = SqlTypeName.TIMESTAMP;
83+
}
84+
precision = 3;
85+
break;
86+
default:
87+
returnTypeName = datetimeType.getSqlTypeName();
88+
precision = datetimeType.getPrecision();
89+
}
90+
91+
RelDataType returnType;
92+
if (precision >= 0 && (returnTypeName == SqlTypeName.TIMESTAMP || returnTypeName == SqlTypeName.TIME)) {
93+
returnType = typeFactory.createSqlType(returnTypeName, precision);
94+
} else {
95+
returnType = typeFactory.createSqlType(returnTypeName);
96+
}
97+
98+
// Apply nullability
99+
boolean isNullable = opBinding.getOperandType(1).isNullable() ||
100+
opBinding.getOperandType(2).isNullable();
101+
return typeFactory.createTypeWithNullability(returnType, isNullable);
102+
};
103+
104+
public DrillCalciteSqlTimestampAddWrapper(SqlFunction wrappedFunction) {
105+
super(wrappedFunction.getName(),
106+
wrappedFunction.getSqlIdentifier(),
107+
wrappedFunction.getKind(),
108+
TIMESTAMP_ADD_INFERENCE,
109+
wrappedFunction.getOperandTypeInference(),
110+
wrappedFunction.getOperandTypeChecker(),
111+
wrappedFunction.getParamTypes(),
112+
wrappedFunction.getFunctionType());
113+
this.operator = wrappedFunction;
114+
}
115+
116+
@Override
117+
public SqlNode rewriteCall(SqlValidator validator, SqlCall call) {
118+
return operator.rewriteCall(validator, call);
119+
}
120+
121+
@Override
122+
public SqlOperator getOperator() {
123+
return operator;
124+
}
125+
126+
@Override
127+
public boolean validRexOperands(int count, Litmus litmus) {
128+
return true;
129+
}
130+
131+
@Override
132+
public String getAllowedSignatures(String opNameToUse) {
133+
return operator.getAllowedSignatures(opNameToUse);
134+
}
135+
136+
@Override
137+
public SqlMonotonicity getMonotonicity(SqlOperatorBinding call) {
138+
return operator.getMonotonicity(call);
139+
}
140+
141+
@Override
142+
public boolean isDeterministic() {
143+
return operator.isDeterministic();
144+
}
145+
146+
@Override
147+
public boolean isDynamicFunction() {
148+
return operator.isDynamicFunction();
149+
}
150+
151+
@Override
152+
public SqlSyntax getSyntax() {
153+
return operator.getSyntax();
154+
}
155+
156+
@Override
157+
public void unparse(SqlWriter writer, SqlCall call, int leftPrec, int rightPrec) {
158+
operator.unparse(writer, call, leftPrec, rightPrec);
159+
}
160+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.drill.exec.planner.sql;
19+
20+
import org.apache.calcite.rel.type.RelDataType;
21+
import org.apache.calcite.rel.type.RelDataTypeFactory;
22+
import org.apache.calcite.sql.SqlCall;
23+
import org.apache.calcite.sql.SqlFunction;
24+
import org.apache.calcite.sql.SqlNode;
25+
import org.apache.calcite.sql.SqlOperator;
26+
import org.apache.calcite.sql.SqlOperatorBinding;
27+
import org.apache.calcite.sql.SqlSyntax;
28+
import org.apache.calcite.sql.SqlWriter;
29+
import org.apache.calcite.sql.type.SqlReturnTypeInference;
30+
import org.apache.calcite.sql.type.SqlTypeName;
31+
import org.apache.calcite.sql.validate.SqlMonotonicity;
32+
import org.apache.calcite.sql.validate.SqlValidator;
33+
import org.apache.calcite.util.Litmus;
34+
35+
/**
36+
* Wrapper for Calcite's TIMESTAMPDIFF function that provides custom type inference.
37+
* Returns BIGINT to match Calcite 1.35 validation expectations.
38+
*/
39+
public class DrillCalciteSqlTimestampDiffWrapper extends SqlFunction implements DrillCalciteSqlWrapper {
40+
private final SqlFunction operator;
41+
42+
private static final SqlReturnTypeInference TIMESTAMP_DIFF_INFERENCE = opBinding -> {
43+
RelDataTypeFactory typeFactory = opBinding.getTypeFactory();
44+
45+
// TIMESTAMPDIFF returns BIGINT in Calcite 1.35
46+
RelDataType returnType = typeFactory.createSqlType(SqlTypeName.BIGINT);
47+
48+
// Apply nullability from operands
49+
boolean isNullable = opBinding.getOperandType(1).isNullable() ||
50+
opBinding.getOperandType(2).isNullable();
51+
return typeFactory.createTypeWithNullability(returnType, isNullable);
52+
};
53+
54+
public DrillCalciteSqlTimestampDiffWrapper(SqlFunction wrappedFunction) {
55+
super(wrappedFunction.getName(),
56+
wrappedFunction.getSqlIdentifier(),
57+
wrappedFunction.getKind(),
58+
TIMESTAMP_DIFF_INFERENCE,
59+
wrappedFunction.getOperandTypeInference(),
60+
wrappedFunction.getOperandTypeChecker(),
61+
wrappedFunction.getParamTypes(),
62+
wrappedFunction.getFunctionType());
63+
this.operator = wrappedFunction;
64+
}
65+
66+
@Override
67+
public SqlNode rewriteCall(SqlValidator validator, SqlCall call) {
68+
return operator.rewriteCall(validator, call);
69+
}
70+
71+
@Override
72+
public SqlOperator getOperator() {
73+
return operator;
74+
}
75+
76+
@Override
77+
public boolean validRexOperands(int count, Litmus litmus) {
78+
return true;
79+
}
80+
81+
@Override
82+
public String getAllowedSignatures(String opNameToUse) {
83+
return operator.getAllowedSignatures(opNameToUse);
84+
}
85+
86+
@Override
87+
public SqlMonotonicity getMonotonicity(SqlOperatorBinding call) {
88+
return operator.getMonotonicity(call);
89+
}
90+
91+
@Override
92+
public boolean isDeterministic() {
93+
return operator.isDeterministic();
94+
}
95+
96+
@Override
97+
public boolean isDynamicFunction() {
98+
return operator.isDynamicFunction();
99+
}
100+
101+
@Override
102+
public SqlSyntax getSyntax() {
103+
return operator.getSyntax();
104+
}
105+
106+
@Override
107+
public void unparse(SqlWriter writer, SqlCall call, int leftPrec, int rightPrec) {
108+
operator.unparse(writer, call, leftPrec, rightPrec);
109+
}
110+
}

0 commit comments

Comments
 (0)