Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions core/src/main/java/org/apache/calcite/sql/SqlKind.java
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,9 @@ public enum SqlKind {
/** {@code SUBSTR} function (PostgreSQL semantics). */
SUBSTR_POSTGRESQL,

/** {@code CHAR_LENGTH} function. */
CHAR_LENGTH,

/** {@code ENDS_WITH} function. */
ENDS_WITH,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public SnowflakeSqlDialect(Context context) {
@Override public void unparseCall(final SqlWriter writer, final SqlCall call, final int leftPrec,
final int rightPrec) {
switch (call.getKind()) {
case CHAR_LENGTH:
SqlCall lengthCall = SqlLibraryOperators.LENGTH
.createCall(SqlParserPos.ZERO, call.getOperandList());
super.unparseCall(writer, lengthCall, leftPrec, rightPrec);
break;
case ENDS_WITH:
SqlCall endsWithCall = SqlLibraryOperators.ENDSWITH
.createCall(SqlParserPos.ZERO, call.getOperandList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,13 @@ private static SqlCall transformConvert(SqlValidator validator, SqlCall call) {
@LibraryOperator(libraries = {BIG_QUERY})
public static final SqlFunction IFNULL = NVL.withName("IFNULL");

/** The "LEN(string)" function. */
@LibraryOperator(libraries = {SNOWFLAKE})
public static final SqlFunction LEN =
SqlStdOperatorTable.CHAR_LENGTH.withName("LEN");

/** The "LENGTH(string)" function. */
@LibraryOperator(libraries = {BIG_QUERY})
@LibraryOperator(libraries = {BIG_QUERY, SNOWFLAKE})
public static final SqlFunction LENGTH =
SqlStdOperatorTable.CHAR_LENGTH.withName("LENGTH");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1613,10 +1613,9 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
public static final SqlFunction POSITION = new SqlPositionFunction("POSITION");

public static final SqlBasicFunction CHAR_LENGTH =
SqlBasicFunction.create("CHAR_LENGTH",
SqlBasicFunction.create(SqlKind.CHAR_LENGTH,
ReturnTypes.INTEGER_NULLABLE,
OperandTypes.CHARACTER,
SqlFunctionCategory.NUMERIC);
OperandTypes.CHARACTER);

/** Alias for {@link #CHAR_LENGTH}. */
public static final SqlFunction CHARACTER_LENGTH =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ private StandardConvertletTable() {

// Register aliases (operators which have a different name but
// identical behavior to other operators).
addAlias(SqlLibraryOperators.LEN,
SqlStdOperatorTable.CHAR_LENGTH);
addAlias(SqlLibraryOperators.LENGTH,
SqlStdOperatorTable.CHAR_LENGTH);
addAlias(SqlStdOperatorTable.CHARACTER_LENGTH,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6696,6 +6696,23 @@ private void checkLiteral2(String expression, String expected) {
sql(query).withLibrary(SqlLibrary.SNOWFLAKE).withSnowflake().ok(expectedSnowflake);
}

/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-6182">[CALCITE-6182]
* Add LENGTH/LEN functions (enabled in Snowflake library)</a>. */
@Test void testSnowflakeLength() {
final String query = "select CHAR_LENGTH(\"brand_name\")\n"
+ "from \"product\"";
final String expectedBigQuery = "SELECT CHAR_LENGTH(brand_name)\n"
+ "FROM foodmart.product";
// Snowflake would accept either LEN or LENGTH, but we currently unparse into "LENGTH"
// since it seems to be used across more dialects.
final String expectedSnowflake = "SELECT LENGTH(\"brand_name\")\n"
+ "FROM \"foodmart\".\"product\"";
Sql sql = sql(query).withLibrary(SqlLibrary.BIG_QUERY);
sql.withBigQuery().ok(expectedBigQuery);
sql.withSnowflake().ok(expectedSnowflake);
}

@Test void testSubstringInSpark() {
final String query = "select substring(\"brand_name\" from 2) "
+ "from \"product\"\n";
Expand Down
3 changes: 2 additions & 1 deletion site/_docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2765,7 +2765,8 @@ BigQuery's type system uses confusingly different names for types and functions:
| m | JSON_STORAGE_SIZE(jsonValue) | Returns the number of bytes used to store the binary representation of *jsonValue*
| b o | LEAST(expr [, expr ]* ) | Returns the least of the expressions
| b m p | LEFT(string, length) | Returns the leftmost *length* characters from the *string*
| b | LENGTH(string) | Equivalent to `CHAR_LENGTH(string)`
| f | LEN(string) | Equivalent to `CHAR_LENGTH(string)`
| b f | LENGTH(string) | Equivalent to `CHAR_LENGTH(string)`
| h s | LEVENSHTEIN(string1, string2) | Returns the Levenshtein distance between *string1* and *string2*
| b | LOG(numeric1 [, numeric2 ]) | Returns the logarithm of *numeric1* to base *numeric2*, or base e if *numeric2* is not present
| b o | LPAD(string, length [, pattern ]) | Returns a string or bytes value that consists of *string* prepended to *length* with *pattern*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4108,6 +4108,23 @@ static void checkRlikeFails(SqlOperatorFixture f) {
f.checkNull("CHARACTER_LENGTH(cast(null as varchar(1)))");
}

/** Tests {@code LEN} function from Snowflake. {@code LEN} is a
* Snowflake-specific alias for {@code CHAR_LENGTH}. */
@Test void testLenFunc() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use a comment - that LEN is a snowflake-specific alias for CHAR_LENGTH, and that snowflake also supports LENGTH

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

final SqlOperatorFixture f0 = fixture().setFor(SqlLibraryOperators.LEN);
f0.checkFails("^len('hello')^",
"No match found for function signature LEN\\(<CHARACTER>\\)",
false);
final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.SNOWFLAKE);
f.checkScalar("len('hello')", "5", "INTEGER NOT NULL");
f.checkScalar("len('')", "0", "INTEGER NOT NULL");
f.checkScalar("len(CAST('x' as CHAR(3)))", "3", "INTEGER NOT NULL");
f.checkScalar("len(CAST('x' as VARCHAR(4)))", "1", "INTEGER NOT NULL");
f.checkNull("len(CAST(NULL as CHAR(5)))");
}

/** Tests {@code LENGTH} function from Big Query/Snowflake. {@code LENGTH} is a
* BQ/Snowflake-specific alias for {@code CHAR_LENGTH}. */
@Test void testLengthFunc() {
final SqlOperatorFixture f0 = fixture().setFor(SqlLibraryOperators.LENGTH);
f0.checkFails("^length('hello')^",
Expand All @@ -4122,7 +4139,7 @@ static void checkRlikeFails(SqlOperatorFixture f) {
f.checkNull("length(CAST(NULL as CHAR(5)))");
};

f0.forEachLibrary(list(SqlLibrary.BIG_QUERY), consumer);
f0.forEachLibrary(list(SqlLibrary.BIG_QUERY, SqlLibrary.SNOWFLAKE), consumer);
}

@Test void testOctetLengthFunc() {
Expand Down