Skip to content

Commit

Permalink
[CALCITE-6182] Add LENGTH/LEN functions (enabled in Snowflake library)
Browse files Browse the repository at this point in the history
  • Loading branch information
tanclary authored and julianhyde committed Jan 4, 2024
1 parent 42f0f2d commit 429609c
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 6 deletions.
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 @@ -2769,7 +2769,8 @@ In the following:
| 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 @@ -4111,6 +4111,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() {
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 @@ -4125,7 +4142,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

0 comments on commit 429609c

Please sign in to comment.