-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
IGNITE-24336 SQL Calcite: Add support of user-defined table functions -
Fixes #11832. Signed-off-by: Aleksey Plekhanov <[email protected]>
- Loading branch information
1 parent
405e62b
commit 1a75353
Showing
17 changed files
with
807 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
...pache/ignite/internal/processors/query/calcite/exec/exp/IgniteReflectiveFunctionBase.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF 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. | ||
*/ | ||
package org.apache.ignite.internal.processors.query.calcite.exec.exp; | ||
|
||
import java.lang.reflect.Method; | ||
import org.apache.calcite.schema.impl.ReflectiveFunctionBase; | ||
|
||
/** A base for outer java-method functions. */ | ||
abstract class IgniteReflectiveFunctionBase extends ReflectiveFunctionBase implements ImplementableFunction { | ||
/** */ | ||
protected final CallImplementor implementor; | ||
|
||
/** */ | ||
protected IgniteReflectiveFunctionBase(Method method, CallImplementor implementor) { | ||
super(method); | ||
|
||
this.implementor = implementor; | ||
} | ||
|
||
/** {@inheritDoc} */ | ||
@Override public CallImplementor getImplementor() { | ||
return implementor; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
135 changes: 135 additions & 0 deletions
135
...ava/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteTableFunction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF 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. | ||
*/ | ||
package org.apache.ignite.internal.processors.query.calcite.exec.exp; | ||
|
||
import java.lang.reflect.Method; | ||
import java.lang.reflect.Type; | ||
import java.util.Arrays; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
import org.apache.calcite.adapter.enumerable.NullPolicy; | ||
import org.apache.calcite.adapter.java.JavaTypeFactory; | ||
import org.apache.calcite.rel.type.RelDataType; | ||
import org.apache.calcite.rel.type.RelDataTypeFactory; | ||
import org.apache.calcite.schema.TableFunction; | ||
import org.apache.ignite.cache.query.annotations.QuerySqlTableFunction; | ||
import org.apache.ignite.internal.processors.query.IgniteSQLException; | ||
import org.apache.ignite.internal.util.typedef.F; | ||
|
||
/** | ||
* Holder of user-defined table function. | ||
* | ||
* @see QuerySqlTableFunction | ||
*/ | ||
public class IgniteTableFunction extends IgniteReflectiveFunctionBase implements TableFunction { | ||
/** Column types of the returned table representation. */ | ||
private final Class<?>[] colTypes; | ||
|
||
/** Column names of the returned table representation. */ | ||
private final List<String> colNames; | ||
|
||
/** | ||
* Creates user-defined table function holder. | ||
* | ||
* @param method The implementatng method. | ||
* @param colTypes Column types of the returned table representation. | ||
* @param colNames Column names of the returned table representation. | ||
* @param implementor Call implementor. | ||
*/ | ||
private IgniteTableFunction(Method method, Class<?>[] colTypes, String[] colNames, CallImplementor implementor) { | ||
super(method, implementor); | ||
|
||
validate(method, colTypes, colNames); | ||
|
||
this.colTypes = colTypes; | ||
this.colNames = Arrays.asList(colNames); | ||
} | ||
|
||
/** | ||
* Creates user-defined table function implementor and holder. | ||
* | ||
* @param method The implementating method. | ||
* @param colTypes Column types of the returned table representation. | ||
* @param colNames Column names of the returned table representation. | ||
*/ | ||
public static IgniteTableFunction create(Method method, Class<?>[] colTypes, String[] colNames) { | ||
NotNullImplementor implementor = new ReflectiveCallNotNullImplementor(method); | ||
|
||
CallImplementor impl = RexImpTable.createImplementor(implementor, NullPolicy.NONE, false); | ||
|
||
return new IgniteTableFunction(method, colTypes, colNames, impl); | ||
} | ||
|
||
/** {@inheritDoc} */ | ||
@Override public RelDataType getRowType(RelDataTypeFactory typeFactory, List<?> arguments) { | ||
JavaTypeFactory tf = (JavaTypeFactory)typeFactory; | ||
|
||
List<RelDataType> converted = Stream.of(colTypes).map(cl -> tf.toSql(tf.createType(cl))).collect(Collectors.toList()); | ||
|
||
return typeFactory.createStructType(converted, colNames); | ||
} | ||
|
||
/** {@inheritDoc} */ | ||
@Override public Type getElementType(List<?> arguments) { | ||
// Calcite's {@link TableFunctionImpl} does real invocation ({@link TableFunctionImpl#apply(List)}) to determine | ||
// the type. The call might be long, 'heavy', may affect some metrics and should not be executed at validation/planning. | ||
// We may check the argument number here but not their types. The types might be wrong, but converted further. | ||
if (F.isEmpty(arguments) && !F.isEmpty(method.getParameterTypes()) | ||
|| F.isEmpty(method.getParameterTypes()) && !F.isEmpty(arguments) | ||
|| method.getParameterTypes().length != arguments.size()) { | ||
throw new IllegalArgumentException("Wrong arguments number: " + arguments.size() + ". Expected: " | ||
+ method.getParameterTypes().length + '.'); | ||
} | ||
|
||
return Iterable.class; | ||
} | ||
|
||
/** Validates the parameters and throws an exception if it finds an incorrect parameter. */ | ||
private static void validate(Method mtd, Class<?>[] colTypes, String[] colNames) { | ||
if (F.isEmpty(colTypes)) | ||
raiseValidationError(mtd, "Column types cannot be empty."); | ||
|
||
if (F.isEmpty(colNames)) | ||
raiseValidationError(mtd, "Column names cannot be empty."); | ||
|
||
if (colTypes.length != colNames.length) { | ||
raiseValidationError(mtd, "Number of the table column names [" + colNames.length | ||
+ "] must match the number of column types [" + colTypes.length + "]."); | ||
} | ||
|
||
if (new HashSet<>(Arrays.asList(colNames)).size() != colNames.length) | ||
raiseValidationError(mtd, "One or more column names is not unique."); | ||
|
||
if (!Iterable.class.isAssignableFrom(mtd.getReturnType())) | ||
raiseValidationError(mtd, "The method is expected to return a collection (iterable)."); | ||
} | ||
|
||
/** | ||
* Throws a parameter validation exception with a standard text prefix. | ||
* | ||
* @param method A java-method implementing related user-defined table function. | ||
* @param errPostfix Error text postfix. | ||
*/ | ||
private static void raiseValidationError(Method method, String errPostfix) { | ||
String mtdSign = method.getName() + '(' + Stream.of(method.getParameterTypes()).map(Class::getSimpleName) | ||
.collect(Collectors.joining(", ")) + ')'; | ||
|
||
throw new IgniteSQLException("Unable to create table function for method '" + mtdSign + "'. " + errPostfix); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.