Skip to content

Commit

Permalink
Implemented INSTR function in SqlLibraryOperators
Browse files Browse the repository at this point in the history
  • Loading branch information
jhugomoore committed Apr 27, 2023
1 parent e5f7729 commit c4e49cf
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 9 deletions.
80 changes: 73 additions & 7 deletions core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
Original file line number Diff line number Diff line change
Expand Up @@ -3104,23 +3104,89 @@ public static int position(ByteString seek, ByteString s) {

/** SQL {@code POSITION(seek IN string FROM integer)} function. */
public static int position(String seek, String s, int from) {
final int from0 = from - 1; // 0-based
if (from0 > s.length() || from0 < 0) {
if (from > 0) {
final int from0 = from - 1; // 0-based
if (from0 > s.length() || from0 < 0) {
return 0;
}

return s.indexOf(seek, from0) + 1;
}
final int rightIndex = from + s.length(); // negative position to positive index
if (rightIndex <= 0) {
return 0;
}

return s.indexOf(seek, from0) + 1;
return s.substring(0, rightIndex).lastIndexOf(seek) + 1;
}

/** SQL {@code POSITION(seek IN string FROM integer)} function for byte
* strings. */
public static int position(ByteString seek, ByteString s, int from) {
final int from0 = from - 1;
if (from0 > s.length() || from0 < 0) {
if (from > 0) {
final int from0 = from - 1; // 0-based
if (from0 > s.length() || from0 < 0) {
return 0;
}

return s.indexOf(seek, from0) + 1;
}
final int rightIndex = from + s.length();
if (rightIndex <= 0) {
return 0;
}
return -1;
//s.lastIndexOf(seek, rightIndex) + 1;
}

/** SQL {@code POSITION(seek, string, from, occurrence)} function. */
public static int position(String seek, String s, int from, int occurrence) {
if (from > 0){
int rollingFrom = from;
for (int i = 0; i< occurrence; i++) {
rollingFrom = position(seek, s, rollingFrom);
if (rollingFrom == 0) {
return 0;
}
}
return rollingFrom;
}
int rollingFromNeg = from;
int rollingFromPos = 0;
for (int i = 0; i< occurrence; i++) {
rollingFromPos = position(seek, s, rollingFromNeg);
if (rollingFromPos == 0) {
return 0;
}
rollingFromNeg = rollingFromPos - s.length();
}
return rollingFromPos;

}

/** SQL {@code POSITION(seek, string, from, occurrence)} function for byte
* strings. */
public static int position(ByteString seek, ByteString s, int from, int occurrence) {
if (from > 0){
int rollingFrom = from;
for (int i = 0; i< occurrence; i++) {
rollingFrom = position(seek, s, rollingFrom);
if (rollingFrom == 0) {
return 0;
}
}
return rollingFrom;
}
int rollingFromNeg = from;
int rollingFromPos = 0;
for (int i = 0; i< occurrence; i++) {
rollingFromPos = position(seek, s, rollingFromNeg);
if (rollingFromPos == 0) {
return 0;
}
rollingFromNeg = rollingFromPos - s.length();
}
return rollingFromPos;

return s.indexOf(seek, from0) + 1;
}

/** Helper for rounding. Truncate(12345, 1000) returns 12000. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ public BigQuerySqlDialect(SqlDialect.Context context) {
final int rightPrec) {
switch (call.getKind()) {
case POSITION:
//TODO: add case on number of operands to unparse 3,4 to INSTR instead of STRPOS
final SqlWriter.Frame frame = writer.startFunCall("STRPOS");
writer.sep(",");
call.operand(1).unparse(writer, leftPrec, rightPrec);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,12 +332,17 @@ static RelDataType deriveTypeSplit(SqlOperatorBinding operatorBinding,
@LibraryOperator(libraries = {BIG_QUERY, POSTGRESQL})
public static final SqlFunction STRPOS = new SqlPositionFunction("STRPOS");

/** The "INSTR(string, substring [, position [, occurrence]])" function. */
@LibraryOperator(libraries = {BIG_QUERY, ORACLE})
public static final SqlFunction INSTR = new SqlPositionFunction("INSTR");

/** Generic "SUBSTR(string, position [, substringLength ])" function. */
private static final SqlBasicFunction SUBSTR =
SqlBasicFunction.create("SUBSTR", ReturnTypes.ARG0_NULLABLE_VARYING,
OperandTypes.STRING_INTEGER_OPTIONAL_INTEGER,
SqlFunctionCategory.STRING);


/** The "ENDS_WITH(value1, value2)" function (BigQuery). */
@LibraryOperator(libraries = {BIG_QUERY})
public static final SqlFunction ENDS_WITH =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
import org.apache.calcite.sql.type.SqlOperandTypeChecker;
import org.apache.calcite.sql.type.SqlTypeFamily;


/**
* The <code>POSITION</code> function.
Expand All @@ -37,8 +39,10 @@ public class SqlPositionFunction extends SqlFunction {
// as part of rtiDyadicStringSumPrecision

private static final SqlOperandTypeChecker OTC_CUSTOM =
OperandTypes.STRING_SAME_SAME
.or(OperandTypes.STRING_SAME_SAME_INTEGER);
// OperandTypes.STRING_SAME_SAME
// .or(OperandTypes.STRING_SAME_SAME_INTEGER)
(OperandTypes.STRING_SAME_SAME_INTEGER_INTEGER);
// .or(OperandTypes.STRING_SAME_SAME_INTEGER_INTEGER);

public SqlPositionFunction(String name) {
super(name, SqlKind.POSITION, ReturnTypes.INTEGER_NULLABLE, null,
Expand All @@ -60,6 +64,10 @@ public SqlPositionFunction(String name) {
writer.sep("FROM");
call.operand(2).unparse(writer, leftPrec, rightPrec);
}
if (4 == call.operandCount()) {
writer.sep(", ");
call.operand(3).unparse(writer, leftPrec, rightPrec);
}
writer.endFunCall(frame);
}

Expand All @@ -69,6 +77,8 @@ public SqlPositionFunction(String name) {
return "{0}({1} IN {2})";
case 3:
return "{0}({1} IN {2} FROM {3})";
case 4:
return "{0}({1}, {2}, {3}, {4})";
default:
throw new AssertionError();
}
Expand All @@ -88,6 +98,9 @@ public SqlPositionFunction(String name) {
return OperandTypes.SAME_SAME_INTEGER.checkOperandTypes(
callBinding, throwOnFailure)
&& super.checkOperandTypes(callBinding, throwOnFailure);
case 4:
return true;
//OperandTypes.and(OperandTypes.SAME_SAME_INTEGER, OperandTypes.INTEGER).checkOperandTypes(callBinding, throwOnFailure) && super.checkOperandTypes(callBinding, throwOnFailure);
default:
throw new AssertionError();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,8 @@ private boolean hasFractionalPart(BigDecimal bd) {
public static final SqlSingleOperandTypeChecker SAME_SAME_INTEGER =
new SameOperandTypeExceptLastOperandChecker(3, "INTEGER");

public static final SqlSingleOperandTypeChecker SAME_SAME_INTEGER_INTEGER =
SAME_SAME_INTEGER.and(family(ImmutableList.of(SqlTypeFamily.INTEGER,SqlTypeFamily.INTEGER,SqlTypeFamily.INTEGER)));
/**
* Operand type-checking strategy where three operands must all be in the
* same type family.
Expand Down Expand Up @@ -716,6 +718,9 @@ public static SqlSingleOperandTypeChecker same(int operandCount,
public static final SqlSingleOperandTypeChecker STRING_SAME_SAME_INTEGER =
STRING_STRING_INTEGER.and(SAME_SAME_INTEGER);

public static final SqlSingleOperandTypeChecker STRING_SAME_SAME_INTEGER_INTEGER =
STRING_STRING_INTEGER_INTEGER.and(SAME_SAME_INTEGER_INTEGER);

public static final SqlSingleOperandTypeChecker STRING_SAME_SAME_OR_ARRAY_SAME_SAME =
or(STRING_SAME_SAME,
and(OperandTypes.SAME_SAME, family(SqlTypeFamily.ARRAY, SqlTypeFamily.ARRAY)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,13 @@ private StandardConvertletTable() {
SqlStdOperatorTable.POSITION.createCall(SqlParserPos.ZERO,
call.operand(1), call.operand(0))));

// "INSTR(string, substring, position, occurrence) is equivalent to
// "POSITION(substring, string, position, occurrence)"
registerOp(SqlLibraryOperators.INSTR,
(cx, call) -> cx.convertExpression(
SqlStdOperatorTable.POSITION.createCall(SqlParserPos.ZERO,
call.operand(1), call.operand(0), call.operand(2), call.operand(3))));

// REVIEW jvs 24-Apr-2006: This only seems to be working from within a
// windowed agg. I have added an optimizer rule
// org.apache.calcite.rel.rules.AggregateReduceFunctionsRule which handles
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2228,6 +2228,15 @@ private SqlDialect nonOrdinalDialect() {
sql(query).withBigQuery().ok(expected);
}

@Test void testBigQueryInstrFunction() {
final String query = "SELECT INSTR('A', 'ABC', 1, 1) from \"product\"";
final String expected = "SELECT INSTR('A', 'ABC', 1, 1)\n"
+ "FROM foodmart.product";
final Sql sql = fixture().withBigQuery().withLibrary(SqlLibrary.BIG_QUERY);
sql.withSql(query).withBigQuery().ok(expected);
}


/** Tests that we escape single-quotes in character literals using back-slash
* in BigQuery. The norm is to escape single-quotes with single-quotes. */
@Test void testCharLiteralForBigQuery() {
Expand Down

0 comments on commit c4e49cf

Please sign in to comment.