Skip to content

Commit

Permalink
[CALCITE-3726] Allow declaring type objects (ritesh-kapoor)
Browse files Browse the repository at this point in the history
  • Loading branch information
ritesh-kapoor authored and chenyuzhao committed Mar 15, 2020
1 parent f11115a commit 6dfcfb4
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import org.apache.calcite.sql.SqlJsonConstructorNullClause;
import org.apache.calcite.sql.SqlMatchFunction;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.SqlTypeConstructorFunction;
import org.apache.calcite.sql.SqlWindowTableFunction;
import org.apache.calcite.sql.fun.SqlJsonArrayAggAggFunction;
import org.apache.calcite.sql.fun.SqlJsonObjectAggAggFunction;
Expand Down Expand Up @@ -910,6 +911,8 @@ public CallImplementor get(final SqlOperator operator) {
+ " must implement ImplementableFunction");
}
return ((ImplementableFunction) udf).getImplementor();
} else if (operator instanceof SqlTypeConstructorFunction) {
return map.get(SqlStdOperatorTable.ROW);
}
return map.get(operator);
}
Expand Down
15 changes: 11 additions & 4 deletions core/src/main/java/org/apache/calcite/sql/SqlFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -291,13 +291,20 @@ argTypes, argNames, getFunctionType(), SqlSyntax.FUNCTION,
}
}
}

// check if the identifier represents type
final SqlFunction x = (SqlFunction) call.getOperator();
final SqlIdentifier identifier = Util.first(x.getSqlIdentifier(),
new SqlIdentifier(x.getName(), SqlParserPos.ZERO));
RelDataType type = validator.getCatalogReader().getNamedType(identifier);
if (type != null) {
function = new SqlTypeConstructorFunction(identifier, type);
break validCoercionType;
}

// if function doesn't exist within operator table and known function
// handling is turned off then create a more permissive function
if (function == null && validator.isLenientOperatorLookup()) {
final SqlFunction x = (SqlFunction) call.getOperator();
final SqlIdentifier identifier =
Util.first(x.getSqlIdentifier(),
new SqlIdentifier(x.getName(), SqlParserPos.ZERO));
function = new SqlUnresolvedFunction(identifier, null,
null, OperandTypes.VARIADIC, null, x.getFunctionType());
break validCoercionType;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* 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.calcite.sql;

import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.sql.type.ExplicitOperandTypeChecker;

/**
* Type Constructor function.
*
* <p>Created by the parser, then it is rewritten to proper SqlFunction by
* the validator to a function defined in a Calcite schema.</p>
*/
public class SqlTypeConstructorFunction extends SqlFunction {

private RelDataType type;

/**
* Creates a constructor function for types.
*
* @param identifier possibly qualified identifier for function
* @param type type of data
*/
public SqlTypeConstructorFunction(SqlIdentifier identifier,
RelDataType type) {
super(identifier,
null,
null,
new ExplicitOperandTypeChecker(type),
null,
SqlFunctionCategory.SYSTEM);
this.type = type;
}

@Override public RelDataType inferReturnType(SqlOperatorBinding opBinding) {
return type;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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.calcite.sql.type;

import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.sql.SqlCallBinding;
import org.apache.calcite.sql.SqlOperandCountRange;
import org.apache.calcite.sql.SqlOperator;

import java.util.ArrayList;
import java.util.List;

/**
* Parameter type-checking strategy for Explicit Type.
*/
public class ExplicitOperandTypeChecker implements SqlOperandTypeChecker {
//~ Methods ----------------------------------------------------------------

private RelDataType type;

public ExplicitOperandTypeChecker(RelDataType type) {
this.type = type;
}

public boolean isOptional(int i) {
return false;
}

public boolean checkOperandTypes(
SqlCallBinding callBinding,
boolean throwOnFailure) {
List<SqlTypeFamily> families = new ArrayList<>();

List<RelDataTypeField> fieldList = type.getFieldList();
for (int i = 0; i < fieldList.size(); i++) {
RelDataTypeField field = fieldList.get(i);
SqlTypeName sqlTypeName = field.getType().getSqlTypeName();
if (sqlTypeName == SqlTypeName.ROW) {
if (field.getType().equals(callBinding.getOperandType(i))) {
families.add(SqlTypeFamily.ANY);
}
} else {
families.add(field.getType().getSqlTypeName().getFamily());
}
}
return OperandTypes.family(families).checkOperandTypes(callBinding, throwOnFailure);
}

public SqlOperandCountRange getOperandCountRange() {
return SqlOperandCountRanges.of(type.getFieldCount());
}

public String getAllowedSignatures(SqlOperator op, String opName) {
return "<TYPE> " + opName + " <TYPE>";
}

public Consistency getConsistency() {
return Consistency.NONE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1568,7 +1568,7 @@ public void _testLikeAndSimilarFails() {
@Test public void testInvalidMemberFunction() {
expr("myCol.^func()^")
.fails("(?s).*No match found for function signature FUNC().*");
expr("myCol.mySubschema.^memberFunc()^")
expr("customer.mySubschema.^memberFunc()^")
.fails("(?s).*No match found for function signature MEMBERFUNC().*");
}

Expand Down
25 changes: 25 additions & 0 deletions server/src/test/java/org/apache/calcite/test/ServerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Struct;
import java.util.ArrayList;
import java.util.List;

Expand All @@ -39,6 +40,10 @@
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

/**
Expand Down Expand Up @@ -242,6 +247,26 @@ static Connection connect() throws SQLException {
}
}

@Test public void testInsertCreateNewCompositeUdt() throws Exception {
try (Connection c = connect();
Statement s = c.createStatement()) {
boolean b = s.execute("create type mytype as (i int, j int)");
assertFalse(b);
b = s.execute("create table w (i int not null, j mytype)");
assertFalse(b);
int x = s.executeUpdate("insert into w "
+ "values (1, mytype(1, 1))");
assertEquals(x, 1);

try (ResultSet r = s.executeQuery("select * from w")) {
assertTrue(r.next());
assertEquals(r.getInt("i"), 1);
assertArrayEquals(r.getObject("j", Struct.class).getAttributes(), new Object[] {1, 1});
assertFalse(r.next());
}
}
}

@Test public void testStoredGeneratedColumn() throws Exception {
try (Connection c = connect();
Statement s = c.createStatement()) {
Expand Down
34 changes: 34 additions & 0 deletions server/src/test/resources/sql/type.iq
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,43 @@ select * from w;

!ok


# Create type object

create type mytype2 as (ii int, jj char);
(0 rows modified)

!update

create type mytype3 as (ii int, jj mytype2);
(0 rows modified)

!update

create table w2 (i int not null, j mytype2, k mytype3);
(0 rows modified)

!update

insert into w2 values (1, mytype2(2, 'a'), mytype3(1, mytype2(3, 'b')) );
(1 row modified)

!update

select * from w2;
+---+--------+-------------+
| I | J | K |
+---+--------+-------------+
| 1 | {2, a} | {1, {3, b}} |
+---+--------+-------------+
(1 row)

!ok

drop table t;
drop table v;
drop table w;
drop table w2;
(0 rows modified)

!update
Expand Down

0 comments on commit 6dfcfb4

Please sign in to comment.