Skip to content

Commit 37b7c51

Browse files
authored
[FLINK-39010][transform] YAML Pipeline supports item subscription of Variant type (#4261)
1 parent 97baee4 commit 37b7c51

File tree

9 files changed

+434
-13
lines changed

9 files changed

+434
-13
lines changed

docs/content.zh/docs/core-concept/transform.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,11 +228,15 @@ You can use `CAST( <EXPR> AS <T> )` syntax to convert any valid expression `<EXP
228228

229229
## Struct Functions
230230

231+
结构函数用于使用下标语法访问 ARRAY、MAP、ROW 和 VARIANT 类型中的元素。
232+
231233
| Function | Janino Code | Description |
232234
| -------- | ----------- | ----------- |
233235
| array[index] | itemAccess(array, index) | 返回数组中位置 `index` 的元素。索引从 1 开始(SQL 标准)。如果索引超出范围或数组为 NULL,则返回 NULL。 |
234236
| map[key] | itemAccess(map, key) | 返回 map 中与 `key` 关联的值。如果 key 不存在或 map 为 NULL,则返回 NULL。 |
235237
| row[index] | itemAccess(row, index) | 返回 row 中位置 `index` 的字段。索引从 1 开始。索引必须是常量(不能是计算表达式),因为返回类型必须在静态阶段确定。 |
238+
| variant[index] | itemAccess(variant, index) | 返回 variant 中位置 `index` 的元素(如果是数组)。索引从 1 开始(SQL 标准)。如果索引超出范围或 variant 不是数组,则返回 NULL。 |
239+
| variant[key] | itemAccess(variant, key) | 返回 variant 中与 `key` 关联的值(如果是对象)。如果 key 不存在或 variant 不是对象,则返回 NULL。 |
236240

237241
# 示例
238242
## 添加计算列

docs/content/docs/core-concept/transform.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,13 +229,15 @@ You can use `CAST( <EXPR> AS <T> )` syntax to convert any valid expression `<EXP
229229

230230
## Struct Functions
231231

232-
Struct functions are used to access elements in ARRAY, MAP and ROW types using subscript syntax.
232+
Struct functions are used to access elements in ARRAY, MAP, ROW, and VARIANT types using subscript syntax.
233233

234234
| Function | Janino Code | Description |
235235
| -------- | ----------- | ----------- |
236236
| array[index] | itemAccess(array, index) | Returns the element at position `index` in the array. Index is 1-based (SQL standard). Returns NULL if the index is out of bounds or if the array is NULL. |
237237
| map[key] | itemAccess(map, key) | Returns the value associated with `key` in the map. Returns NULL if the key does not exist or if the map is NULL. |
238238
| row[index] | itemAccess(row, index) | Returns the field at position `index` in the row. Index is 1-based. The index must be a constant (not a computed expression) since the return type must be statically determined. |
239+
| variant[index] | itemAccess(variant, index) | Returns the element at position `index` in the variant (if it's an array). Index is 1-based (SQL standard). Returns NULL if the index is out of bounds or if the variant is not an array. |
240+
| variant[key] | itemAccess(variant, key) | Returns the value associated with `key` in the variant (if it's an object). Returns NULL if the key does not exist or if the variant is not an object. |
239241

240242
# Example
241243
## Add computed columns

flink-cdc-composer/src/test/resources/specs/nested.yaml

Lines changed: 86 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
id_
3636
PARSE_JSON(id_) AS variant_id_
3737
PARSE_JSON('[' || id_ || ',' || bool_ || ']') AS variant_array_
38-
PARSE_JSON('{\"id\": ' || id_ || ', \"name\": \"' || string_ || '\"}') AS variant_doc_
38+
PARSE_JSON('{"id": ' || id_ || ', "name": "' || string_ || '"}') AS variant_doc_
3939
primary-key: id_
4040
expect: |-
4141
CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT NULL 'Identifier',`variant_id_` VARIANT,`variant_array_` VARIANT,`variant_doc_` VARIANT}, primaryKeys=id_, options=()}
@@ -55,7 +55,7 @@
5555
id_
5656
TRY_PARSE_JSON(id_) AS variant_id_
5757
TRY_PARSE_JSON('[' || id_ || ',' || bool_ || ']') AS variant_array_
58-
TRY_PARSE_JSON('{\"id\": ' || id_ || ', \"name\": \"' || string_ || '\"}') AS variant_doc_
58+
TRY_PARSE_JSON('{"id": ' || id_ || ', "name": "' || string_ || '"}') AS variant_doc_
5959
primary-key: id_
6060
expect: |-
6161
CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT NULL 'Identifier',`variant_id_` VARIANT,`variant_array_` VARIANT,`variant_doc_` VARIANT}, primaryKeys=id_, options=()}
@@ -79,13 +79,13 @@
7979
- do: Parse Variant from JSON with duplicate key rejected
8080
projection: |-
8181
id_
82-
PARSE_JSON('{\"id\": 1, \"id\": 2}') AS variant_
82+
PARSE_JSON('{"id": 1, "id": 2}') AS variant_
8383
primary-key: id_
8484
expect-error: 'org.apache.flink.cdc.common.types.variant.VariantTypeException: VARIANT_DUPLICATE_KEY'
8585
- do: Parse Variant from JSON with duplicate key allowed
8686
projection: |-
8787
id_
88-
PARSE_JSON('{\"id\": 1, \"id\": 2}', TRUE) AS variant_
88+
PARSE_JSON('{"id": 1, "id": 2}', TRUE) AS variant_
8989
primary-key: id_
9090
expect: |-
9191
CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT NULL 'Identifier',`variant_` VARIANT}, primaryKeys=id_, options=()}
@@ -97,7 +97,7 @@
9797
- do: Try Parse Variant from JSON with duplicate key rejected
9898
projection: |-
9999
id_
100-
TRY_PARSE_JSON('{\"id\": 1, \"id\": 2}') AS variant_
100+
TRY_PARSE_JSON('{"id": 1, "id": 2}') AS variant_
101101
primary-key: id_
102102
expect: |-
103103
CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT NULL 'Identifier',`variant_` VARIANT}, primaryKeys=id_, options=()}
@@ -109,7 +109,7 @@
109109
- do: Try Parse Variant from JSON with duplicate key allowed
110110
projection: |-
111111
id_
112-
TRY_PARSE_JSON('{\"id\": 1, \"id\": 2}', TRUE) AS variant_
112+
TRY_PARSE_JSON('{"id": 1, "id": 2}', TRUE) AS variant_
113113
primary-key: id_
114114
expect: |-
115115
CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT NULL 'Identifier',`variant_` VARIANT}, primaryKeys=id_, options=()}
@@ -283,4 +283,83 @@
283283
expect: |-
284284
CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT NULL 'Identifier',`complex_row_` ROW<`name` STRING, `length` INT>}, primaryKeys=id_, options=()}
285285
DataChangeEvent{tableId=foo.bar.baz, before=[1, {name: STRING -> Alice, length: INT -> 5}], after=[-1, {name: STRING -> Derrida, length: INT -> 7}], op=UPDATE, meta=()}
286-
DataChangeEvent{tableId=foo.bar.baz, before=[-1, {name: STRING -> Derrida, length: INT -> 7}], after=[], op=DELETE, meta=()}
286+
DataChangeEvent{tableId=foo.bar.baz, before=[-1, {name: STRING -> Derrida, length: INT -> 7}], after=[], op=DELETE, meta=()}
287+
- do: Variant Object Subscripting With String Key
288+
projection: |-
289+
id_
290+
variant_
291+
variant_['k'] AS variant_k
292+
primary-key: id_
293+
expect: |-
294+
CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT NULL 'Identifier',`variant_` VARIANT,`variant_k` VARIANT}, primaryKeys=id_, options=()}
295+
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, {"array":[1,2,{"kk":1.123}],"k":1,"object":{"k":"hello"}}, 1], op=INSERT, meta=()}
296+
DataChangeEvent{tableId=foo.bar.baz, before=[1, {"array":[1,2,{"kk":1.123}],"k":1,"object":{"k":"hello"}}, 1], after=[-1, [{"k":1},"hello",{"k":2}], null], op=UPDATE, meta=()}
297+
DataChangeEvent{tableId=foo.bar.baz, before=[-1, [{"k":1},"hello",{"k":2}], null], after=[], op=DELETE, meta=()}
298+
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null, null], op=INSERT, meta=()}
299+
DataChangeEvent{tableId=foo.bar.baz, before=[0, null, null], after=[], op=DELETE, meta=()}
300+
- do: Variant Array Subscripting With Integer Index
301+
projection: |-
302+
id_
303+
variant_
304+
variant_[1] AS variant_first
305+
variant_[2] AS variant_second
306+
primary-key: id_
307+
expect: |-
308+
CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT NULL 'Identifier',`variant_` VARIANT,`variant_first` VARIANT,`variant_second` VARIANT}, primaryKeys=id_, options=()}
309+
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, {"array":[1,2,{"kk":1.123}],"k":1,"object":{"k":"hello"}}, null, null], op=INSERT, meta=()}
310+
DataChangeEvent{tableId=foo.bar.baz, before=[1, {"array":[1,2,{"kk":1.123}],"k":1,"object":{"k":"hello"}}, null, null], after=[-1, [{"k":1},"hello",{"k":2}], {"k":1}, "hello"], op=UPDATE, meta=()}
311+
DataChangeEvent{tableId=foo.bar.baz, before=[-1, [{"k":1},"hello",{"k":2}], {"k":1}, "hello"], after=[], op=DELETE, meta=()}
312+
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null, null, null], op=INSERT, meta=()}
313+
DataChangeEvent{tableId=foo.bar.baz, before=[0, null, null, null], after=[], op=DELETE, meta=()}
314+
- do: Variant Nested Subscripting
315+
projection: |-
316+
id_
317+
variant_['object']['k'] AS nested_object_k
318+
variant_['array'][1] AS nested_array_first
319+
primary-key: id_
320+
expect: |-
321+
CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT NULL 'Identifier',`nested_object_k` VARIANT,`nested_array_first` VARIANT}, primaryKeys=id_, options=()}
322+
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, "hello", 1], op=INSERT, meta=()}
323+
DataChangeEvent{tableId=foo.bar.baz, before=[1, "hello", 1], after=[-1, null, null], op=UPDATE, meta=()}
324+
DataChangeEvent{tableId=foo.bar.baz, before=[-1, null, null], after=[], op=DELETE, meta=()}
325+
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null, null], op=INSERT, meta=()}
326+
DataChangeEvent{tableId=foo.bar.baz, before=[0, null, null], after=[], op=DELETE, meta=()}
327+
- do: Variant Subscripting With Absent Key
328+
projection: |-
329+
id_
330+
variant_['nonexistent'] AS absent_key
331+
variant_[1000] AS absent_index
332+
primary-key: id_
333+
expect: |-
334+
CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT NULL 'Identifier',`absent_key` VARIANT,`absent_index` VARIANT}, primaryKeys=id_, options=()}
335+
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, null, null], op=INSERT, meta=()}
336+
DataChangeEvent{tableId=foo.bar.baz, before=[1, null, null], after=[-1, null, null], op=UPDATE, meta=()}
337+
DataChangeEvent{tableId=foo.bar.baz, before=[-1, null, null], after=[], op=DELETE, meta=()}
338+
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null, null], op=INSERT, meta=()}
339+
DataChangeEvent{tableId=foo.bar.baz, before=[0, null, null], after=[], op=DELETE, meta=()}
340+
- do: Variant Subscripting With Parsed JSON
341+
projection: |-
342+
id_
343+
PARSE_JSON('[1, 2, 3]')[2] AS array_second
344+
PARSE_JSON('[10, 20, 30]')[1] AS array_first
345+
primary-key: id_
346+
expect: |-
347+
CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT NULL 'Identifier',`array_second` VARIANT,`array_first` VARIANT}, primaryKeys=id_, options=()}
348+
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, 2, 10], op=INSERT, meta=()}
349+
DataChangeEvent{tableId=foo.bar.baz, before=[1, 2, 10], after=[-1, 2, 10], op=UPDATE, meta=()}
350+
DataChangeEvent{tableId=foo.bar.baz, before=[-1, 2, 10], after=[], op=DELETE, meta=()}
351+
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, 2, 10], op=INSERT, meta=()}
352+
DataChangeEvent{tableId=foo.bar.baz, before=[0, 2, 10], after=[], op=DELETE, meta=()}
353+
- do: Variant Subscripting With Parsed JSON Object
354+
projection: |-
355+
id_
356+
PARSE_JSON('{"key": "value"}')['key'] AS json_key
357+
PARSE_JSON('{"nested": {"inner": 42}}')['nested']['inner'] AS nested_value
358+
primary-key: id_
359+
expect: |-
360+
CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT NULL 'Identifier',`json_key` VARIANT,`nested_value` VARIANT}, primaryKeys=id_, options=()}
361+
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, "value", 42], op=INSERT, meta=()}
362+
DataChangeEvent{tableId=foo.bar.baz, before=[1, "value", 42], after=[-1, "value", 42], op=UPDATE, meta=()}
363+
DataChangeEvent{tableId=foo.bar.baz, before=[-1, "value", 42], after=[], op=DELETE, meta=()}
364+
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, "value", 42], op=INSERT, meta=()}
365+
DataChangeEvent{tableId=foo.bar.baz, before=[0, "value", 42], after=[], op=DELETE, meta=()}

flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/functions/impl/StructFunctions.java

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,16 @@
1717

1818
package org.apache.flink.cdc.runtime.functions.impl;
1919

20+
import org.apache.flink.cdc.common.types.variant.Variant;
21+
2022
import java.util.List;
2123
import java.util.Map;
2224

2325
/**
2426
* Built-in functions for collection and struct data types.
2527
*
26-
* <p>These functions support accessing elements from collections (ARRAY, MAP) and structured data
27-
* types (ROW).
28+
* <p>These functions support accessing elements from collections (ARRAY, MAP), structured data
29+
* types (ROW), and semi-structured data types (VARIANT).
2830
*/
2931
public class StructFunctions {
3032

@@ -67,4 +69,49 @@ public static <K, V> V itemAccess(Map<K, V> map, K key) {
6769
}
6870
return map.get(key);
6971
}
72+
73+
/**
74+
* Accesses an element from a VARIANT array by index (1-based, SQL standard).
75+
*
76+
* <p>variant[1] returns the first element.
77+
*
78+
* @param variant the variant (must be an array) to access
79+
* @param index the 1-based index
80+
* @return the element at the specified index as a Variant, or null if the variant is not an
81+
* array or index is out of bounds
82+
*/
83+
public static Variant itemAccess(Variant variant, Integer index) {
84+
if (variant == null || index == null) {
85+
return null;
86+
}
87+
if (!variant.isArray()) {
88+
return null;
89+
}
90+
// Convert 1-based index to 0-based (SQL standard uses 1-based indexing)
91+
int zeroBasedIndex = index - 1;
92+
if (zeroBasedIndex < 0 || zeroBasedIndex >= variant.arraySize()) {
93+
return null;
94+
}
95+
return variant.getElement(zeroBasedIndex);
96+
}
97+
98+
/**
99+
* Accesses a field from a VARIANT object by field name.
100+
*
101+
* <p>variant['fieldName'] returns the value of the specified field.
102+
*
103+
* @param variant the variant (must be an object) to access
104+
* @param fieldName the name of the field to look up
105+
* @return the field value as a Variant, or null if the variant is not an object or field is not
106+
* found
107+
*/
108+
public static Variant itemAccess(Variant variant, String fieldName) {
109+
if (variant == null || fieldName == null) {
110+
return null;
111+
}
112+
if (!variant.isObject()) {
113+
return null;
114+
}
115+
return variant.getField(fieldName);
116+
}
70117
}

flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/parser/JaninoCompiler.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,10 @@ private static Java.Rvalue translateSqlSqlLiteral(Context context, SqlLiteral sq
174174
Object value = sqlLiteral.getValue();
175175
if (sqlLiteral instanceof SqlCharStringLiteral) {
176176
// Double quotation marks represent strings in Janino.
177-
value = "\"" + sqlLiteral.getValueAs(NlsString.class).getValue() + "\"";
177+
// Escape backslashes first, then double quotes for proper Janino string literals.
178+
String stringValue = sqlLiteral.getValueAs(NlsString.class).getValue();
179+
stringValue = stringValue.replace("\\", "\\\\").replace("\"", "\\\"");
180+
value = "\"" + stringValue + "\"";
178181
} else if (sqlLiteral instanceof SqlNumericLiteral) {
179182
if (((SqlNumericLiteral) sqlLiteral).isInteger()) {
180183
long longValue = sqlLiteral.longValue(true);

flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/parser/metadata/TransformSqlOperatorTable.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,8 +401,8 @@ public SqlSyntax getSyntax() {
401401
// ---------------------
402402
// Struct Functions
403403
// ---------------------
404-
// Supports accessing elements of ARRAY[index], ROW[index] and MAP[key]
405-
public static final SqlOperator ITEM = SqlStdOperatorTable.ITEM;
404+
// Supports accessing elements of ARRAY[index], ROW[index], MAP[key], and VARIANT[index/key]
405+
public static final SqlOperator ITEM = new VariantAwareItemOperator();
406406

407407
public static final SqlFunction AI_CHAT_PREDICT =
408408
new SqlFunction(
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.flink.cdc.runtime.parser.metadata;
19+
20+
import org.apache.calcite.rel.type.RelDataType;
21+
import org.apache.calcite.rel.type.RelDataTypeFactory;
22+
import org.apache.calcite.sql.SqlCallBinding;
23+
import org.apache.calcite.sql.SqlKind;
24+
import org.apache.calcite.sql.SqlOperandCountRange;
25+
import org.apache.calcite.sql.SqlOperatorBinding;
26+
import org.apache.calcite.sql.SqlSpecialOperator;
27+
import org.apache.calcite.sql.SqlWriter;
28+
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
29+
import org.apache.calcite.sql.type.SqlTypeName;
30+
import org.apache.calcite.sql.type.SqlTypeUtil;
31+
import org.apache.calcite.sql.validate.SqlValidator;
32+
33+
/**
34+
* A variant-aware implementation of the ITEM operator.
35+
*
36+
* <p>This extends the standard Calcite ITEM operator to support VARIANT type. For ARRAY, MAP, and
37+
* ROW types, it delegates to the standard Calcite ITEM operator.
38+
*
39+
* <p>When accessing elements of a VARIANT:
40+
*
41+
* <ul>
42+
* <li>VARIANT[INTEGER] - accesses array elements (1-based index)
43+
* <li>VARIANT[STRING] - accesses object fields by name
44+
* </ul>
45+
*
46+
* <p>The return type is always VARIANT when the source is VARIANT.
47+
*/
48+
public class VariantAwareItemOperator extends SqlSpecialOperator {
49+
50+
public VariantAwareItemOperator() {
51+
super("ITEM", SqlKind.ITEM, 100, true, null, null, null);
52+
}
53+
54+
@Override
55+
public SqlOperandCountRange getOperandCountRange() {
56+
return SqlStdOperatorTable.ITEM.getOperandCountRange();
57+
}
58+
59+
@Override
60+
public boolean checkOperandTypes(SqlCallBinding callBinding, boolean throwOnFailure) {
61+
final SqlValidator validator = callBinding.getValidator();
62+
final RelDataType operandType =
63+
validator.deriveType(callBinding.getScope(), callBinding.operand(0));
64+
final SqlTypeName typeName = operandType.getSqlTypeName();
65+
66+
// Only handle VARIANT specially, delegate others to standard ITEM
67+
if (typeName == SqlTypeName.VARIANT) {
68+
final RelDataType keyType =
69+
validator.deriveType(callBinding.getScope(), callBinding.operand(1));
70+
// VARIANT accepts integer or string keys
71+
boolean valid =
72+
SqlTypeUtil.isIntType(keyType)
73+
|| SqlTypeUtil.isCharacter(keyType)
74+
|| keyType.getSqlTypeName() == SqlTypeName.ANY;
75+
if (!valid && throwOnFailure) {
76+
throw callBinding.newValidationSignatureError();
77+
}
78+
return valid;
79+
}
80+
81+
// Delegate to standard ITEM for ARRAY, MAP, ROW, etc.
82+
return SqlStdOperatorTable.ITEM.checkOperandTypes(callBinding, throwOnFailure);
83+
}
84+
85+
@Override
86+
public RelDataType inferReturnType(SqlOperatorBinding opBinding) {
87+
final RelDataType operandType = opBinding.getOperandType(0);
88+
final SqlTypeName typeName = operandType.getSqlTypeName();
89+
90+
// Only handle VARIANT specially
91+
if (typeName == SqlTypeName.VARIANT) {
92+
final RelDataTypeFactory typeFactory = opBinding.getTypeFactory();
93+
return typeFactory.createTypeWithNullability(
94+
typeFactory.createSqlType(SqlTypeName.VARIANT), true);
95+
}
96+
97+
// Delegate to standard ITEM for other types
98+
return SqlStdOperatorTable.ITEM.inferReturnType(opBinding);
99+
}
100+
101+
@Override
102+
public String getAllowedSignatures(String opName) {
103+
// Include VARIANT in addition to standard signatures
104+
return SqlStdOperatorTable.ITEM.getAllowedSignatures(opName)
105+
+ "\n<VARIANT>[<INTEGER>|<CHARACTER>]";
106+
}
107+
108+
@Override
109+
public void unparse(
110+
SqlWriter writer, org.apache.calcite.sql.SqlCall call, int leftPrec, int rightPrec) {
111+
SqlStdOperatorTable.ITEM.unparse(writer, call, leftPrec, rightPrec);
112+
}
113+
}

0 commit comments

Comments
 (0)