Skip to content

Commit

Permalink
[CALCITE-5548] Add MSSQL-style CONVERT function (enabled in MSSql lib…
Browse files Browse the repository at this point in the history
…rary)

Microsoft SQL Server's `CONVERT(type, exp [, style])`
function is equivalent to `CAST(exp AS type)` and the JDBC
standard function `{fn CONVERT(value, type)}`.

This function is not to be confused with standard SQL's
`CONVERT` function, which converts a character string from
one character set to another.

Close apache#3100
  • Loading branch information
julianhyde committed Mar 20, 2023
1 parent 9db81c2 commit 60c6269
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeTransforms;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.util.Litmus;
import org.apache.calcite.util.Optionality;

Expand Down Expand Up @@ -142,26 +143,31 @@ private SqlLibraryOperators() {
@LibraryOperator(libraries = {MSSQL})
public static final SqlFunction MSSQL_CONVERT =
SqlBasicFunction.create(SqlKind.CAST,
ReturnTypes.andThen(opBinding -> {
// Guaranteed to be a SqlCallBinding, with 2 or 3 arguments
final SqlCallBinding binding = (SqlCallBinding) opBinding;
final SqlCall call = binding.getCall();
return new SqlCallBinding(binding.getValidator(),
binding.getScope(),
SqlStdOperatorTable.CAST.createCall(
call.getParserPosition(), call.operand(1),
call.operand(0)));
},
ReturnTypes.andThen(SqlLibraryOperators::transformConvert,
SqlCastFunction::inferReturnTypeImpl),
OperandTypes.repeat(SqlOperandCountRanges.between(2, 3),
OperandTypes.ANY))
.withName("CONVERT")
.withFunctionType(SqlFunctionCategory.SYSTEM)
.withOperandTypeInference(InferTypes.FIRST_KNOWN)
.withOperandHandler(
OperandHandlers.of((validator, call) ->
SqlStdOperatorTable.CAST.createCall(call.getParserPosition(),
call.operand(1), call.operand(0))));
OperandHandlers.of(SqlLibraryOperators::transformConvert));

/** Transforms a call binding of {@code CONVERT} to an equivalent binding for
* {@code CAST}. */
private static SqlCallBinding transformConvert(SqlOperatorBinding opBinding) {
// Guaranteed to be a SqlCallBinding, with 2 or 3 arguments
final SqlCallBinding binding = (SqlCallBinding) opBinding;
return new SqlCallBinding(binding.getValidator(), binding.getScope(),
transformConvert(binding.getValidator(), binding.getCall()));
}

/** Transforms a call to {@code CONVERT} to an equivalent call to
* {@code CAST}. */
private static SqlCall transformConvert(SqlValidator validator, SqlCall call) {
return SqlStdOperatorTable.CAST.createCall(call.getParserPosition(),
call.operand(1), call.operand(0));
}

/** The "DATE_PART(timeUnit, datetime)" function
* (Databricks, Postgres, Redshift, Snowflake). */
Expand Down
4 changes: 2 additions & 2 deletions site/_docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2635,8 +2635,8 @@ BigQuery's type system uses confusingly different names for types and functions:
| b o | COSH(numeric) | Returns the hyperbolic cosine of *numeric*
| o | CONCAT(string, string) | Concatenates two strings
| b m p | CONCAT(string [, string ]*) | Concatenates two or more strings
| m | COMPRESS(string) | Compresses a string using zlib compression and returns the result as a binary string.
| q | CONVERT(data_type[ ( length ) ] , expression [ , style ]) | In MSSQL, a function very similar to `CAST` with optional style operand. In Calcite implementation, however, it's simply a wrapper around CAST which parses, but ultimately ignores "style" argument.
| m | COMPRESS(string) | Compresses a string using zlib compression and returns the result as a binary string
| q | CONVERT(type, expression [ , style ]) | Equivalent to `CAST(expression AS type)`; ignores the *style* operand
| p | CONVERT_TIMEZONE(tz1, tz2, datetime) | Converts the timezone of *datetime* from *tz1* to *tz2*
| b | CURRENT_DATETIME([ timeZone ]) | Returns the current time as a TIMESTAMP from *timezone*
| m | DAYNAME(datetime) | Returns the name, in the connection's locale, of the weekday in *datetime*; for example, it returns '星期日' for both DATE '2020-02-10' and TIMESTAMP '2020-02-10 10:10:10'
Expand Down

0 comments on commit 60c6269

Please sign in to comment.