diff --git a/docs/content/querying/multi-value-dimensions.md b/docs/content/querying/multi-value-dimensions.md
index ce29451be2c..34f423302f0 100644
--- a/docs/content/querying/multi-value-dimensions.md
+++ b/docs/content/querying/multi-value-dimensions.md
@@ -25,7 +25,7 @@ title: "Multi-value dimensions"
# Multi-value dimensions
Apache Druid (incubating) supports "multi-value" string dimensions. These are generated when an input field contains an
-array of values instead of a single value (e.e. JSON arrays, or a TSV field containing one or more `listDelimiter`
+array of values instead of a single value (e.g. JSON arrays, or a TSV field containing one or more `listDelimiter`
characters).
This document describes the behavior of groupBy (topN has similar behavior) queries on multi-value dimensions when they
diff --git a/docs/content/querying/sql.md b/docs/content/querying/sql.md
index 3678024e44d..73c5e2d554d 100644
--- a/docs/content/querying/sql.md
+++ b/docs/content/querying/sql.md
@@ -77,7 +77,7 @@ The following table describes how SQL types map onto Druid types during query ru
that have the same Druid runtime type will have no effect, other than exceptions noted in the table. Casts between two
SQL types that have different Druid runtime types will generate a runtime cast in Druid. If a value cannot be properly
cast to another value, as in `CAST('foo' AS BIGINT)`, the runtime will substitute a default value. NULL values cast
-to non-nullable types will also be substitued with a default value (for example, nulls cast to numbers will be
+to non-nullable types will also be substituted with a default value (for example, nulls cast to numbers will be
converted to zeroes).
|SQL type|Druid runtime type|Default value|Notes|
@@ -183,7 +183,7 @@ Only the COUNT aggregation can accept DISTINCT.
|`APPROX_QUANTILE(expr, probability, [resolution])`|Computes approximate quantiles on numeric or [approxHistogram](../development/extensions-core/approximate-histograms.html#approximate-histogram-aggregator) exprs. The "probability" should be between 0 and 1 (exclusive). The "resolution" is the number of centroids to use for the computation. Higher resolutions will give more precise results but also have higher overhead. If not provided, the default resolution is 50. The [approximate histogram extension](../development/extensions-core/approximate-histograms.html) must be loaded to use this function.|
|`APPROX_QUANTILE_DS(expr, probability, [k])`|Computes approximate quantiles on numeric or [Quantiles sketch](../development/extensions-core/datasketches-quantiles.html) exprs. The "probability" should be between 0 and 1 (exclusive). The `k` parameter is described in the Quantiles sketch documentation. The [DataSketches extension](../development/extensions-core/datasketches-extension.html) must be loaded to use this function.|
|`APPROX_QUANTILE_FIXED_BUCKETS(expr, probability, numBuckets, lowerLimit, upperLimit, [outlierHandlingMode])`|Computes approximate quantiles on numeric or [fixed buckets histogram](../development/extensions-core/approximate-histograms.html#fixed-buckets-histogram) exprs. The "probability" should be between 0 and 1 (exclusive). The `numBuckets`, `lowerLimit`, `upperLimit`, and `outlierHandlingMode` parameters are described in the fixed buckets histogram documentation. The [approximate histogram extension](../development/extensions-core/approximate-histograms.html) must be loaded to use this function.|
-|`BLOOM_FILTER(expr, numEntries)`|Computes a bloom filter from values produced by `expr`, with `numEntries` maximum number of distinct values before false positve rate increases. See [bloom filter extension](../development/extensions-core/bloom-filter.html) documentation for additional details.|
+|`BLOOM_FILTER(expr, numEntries)`|Computes a bloom filter from values produced by `expr`, with `numEntries` maximum number of distinct values before false positive rate increases. See [bloom filter extension](../development/extensions-core/bloom-filter.html) documentation for additional details.|
|`VAR_POP(expr)`|Computes variance population of `expr`. See [stats extension](../development/extensions-core/stats.html) documentation for additional details.|
|`VAR_SAMP(expr)`|Computes variance sample of `expr`. See [stats extension](../development/extensions-core/stats.html) documentation for additional details.|
|`VARIANCE(expr)`|Computes variance sample of `expr`. See [stats extension](../development/extensions-core/stats.html) documentation for additional details.|
@@ -233,7 +233,7 @@ String functions accept strings, and return a type appropriate to the function.
|Function|Notes|
|--------|-----|
-|`x || y
`|Concat strings x and y.|
+|x || y
|Concat strings x and y.|
|`CONCAT(expr, expr...)`|Concats a list of expressions.|
|`TEXTCAT(expr, expr)`|Two argument version of CONCAT.|
|`STRING_FORMAT(pattern[, args...])`|Returns a string formatted in the manner of Java's [String.format](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#format-java.lang.String-java.lang.Object...-).|
@@ -252,7 +252,7 @@ String functions accept strings, and return a type appropriate to the function.
|`RIGHT(expr, [length])`|Returns the rightmost length characters from expr.|
|`LEFT(expr, [length])`|Returns the leftmost length characters from expr.|
|`SUBSTR(expr, index, [length])`|Synonym for SUBSTRING.|
-|`TRIM([BOTH | LEADING | TRAILING] [ FROM] expr)`|Returns expr with characters removed from the leading, trailing, or both ends of "expr" if they are in "chars". If "chars" is not provided, it defaults to " " (a space). If the directional argument is not provided, it defaults to "BOTH".|
+|TRIM([BOTH | LEADING | TRAILING] [ FROM] expr)
|Returns expr with characters removed from the leading, trailing, or both ends of "expr" if they are in "chars". If "chars" is not provided, it defaults to " " (a space). If the directional argument is not provided, it defaults to "BOTH".|
|`BTRIM(expr[, chars])`|Alternate form of `TRIM(BOTH FROM `).|
|`LTRIM(expr[, chars])`|Alternate form of `TRIM(LEADING FROM `).|
|`RTRIM(expr[, chars])`|Alternate form of `TRIM(TRAILING FROM `).|
@@ -281,7 +281,7 @@ simplest way to write literal timestamps in other time zones is to use TIME_PARS
|--------|-----|
|`CURRENT_TIMESTAMP`|Current timestamp in the connection's time zone.|
|`CURRENT_DATE`|Current date in the connection's time zone.|
-|`DATE_TRUNC(, )`|Rounds down a timestamp, returning it as a new timestamp. Unit can be 'milliseconds', 'second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year', 'decade', 'century', or 'millenium'.|
+|`DATE_TRUNC(, )`|Rounds down a timestamp, returning it as a new timestamp. Unit can be 'milliseconds', 'second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year', 'decade', 'century', or 'millennium'.|
|`TIME_CEIL(, , [, []])`|Rounds up a timestamp, returning it as a new timestamp. Period can be any ISO8601 period, like P3M (quarters) or PT12H (half-days). The time zone, if provided, should be a time zone name like "America/Los_Angeles" or offset like "-08:00". This function is similar to `CEIL` but is more flexible.|
|`TIME_FLOOR(, , [, []])`|Rounds down a timestamp, returning it as a new timestamp. Period can be any ISO8601 period, like P3M (quarters) or PT12H (half-days). The time zone, if provided, should be a time zone name like "America/Los_Angeles" or offset like "-08:00". This function is similar to `FLOOR` but is more flexible.|
|`TIME_SHIFT(, , , [])`|Shifts a timestamp by a period (step times), returning it as a new timestamp. Period can be any ISO8601 period. Step may be negative. The time zone, if provided, should be a time zone name like "America/Los_Angeles" or offset like "-08:00".|
@@ -295,7 +295,22 @@ simplest way to write literal timestamps in other time zones is to use TIME_PARS
|`CEIL(timestamp_expr TO )`|Rounds up a timestamp, returning it as a new timestamp. Unit can be SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, or YEAR.|
|`TIMESTAMPADD(, , )`|Equivalent to `timestamp + count * INTERVAL '1' UNIT`.|
|`TIMESTAMPDIFF(, , )`|Returns the (signed) number of `unit` between `timestamp1` and `timestamp2`. Unit can be SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, or YEAR.|
-|`timestamp_expr { + | - } `|Add or subtract an amount of time from a timestamp. interval_expr can include interval literals like `INTERVAL '2' HOUR`, and may include interval arithmetic as well. This operator treats days as uniformly 86400 seconds long, and does not take into account daylight savings time. To account for daylight savings time, use TIME_SHIFT instead.|
+|timestamp_expr { + | - } |Add or subtract an amount of time from a timestamp. interval_expr can include interval literals like `INTERVAL '2' HOUR`, and may include interval arithmetic as well. This operator treats days as uniformly 86400 seconds long, and does not take into account daylight savings time. To account for daylight savings time, use TIME_SHIFT instead.|
+
+
+### IP address functions
+
+For the IPv4 address functions, the `address` argument can either be an IPv4 dotted-decimal string
+(e.g., '192.168.0.1') or an IP address represented as an integer (e.g., 3232235521). The `subnet`
+argument should be a string formatted as an IPv4 address subnet in CIDR notation (e.g.,
+'192.168.0.0/16').
+
+|Function|Notes|
+|---|---|
+|`IPV4_MATCH(address, subnet)`|Returns true if the `address` belongs to the `subnet` literal, else false. If `address` is not a valid IPv4 address, then false is returned. This function is more efficient if `address` is an integer instead of a string.|
+|`IPV4_PARSE(address)`|Parses `address` into an IPv4 address stored as an integer . If `address` is an integer that is a valid IPv4 address, then it is passed through. Returns null if `address` cannot be represented as an IPv4 address.|
+|`IPV4_STRINGIFY(address)`|Converts `address` into an IPv4 address dotted-decimal string. If `address` is a string that is a valid IPv4 address, then it is passed through. Returns null if `address` cannot be represented as an IPv4 address.|
+
### Comparison operators
@@ -566,12 +581,12 @@ Connection context can be specified as JDBC connection properties or as a "conte
|---------|-----------|-------------|
|`sqlQueryId`|Unique identifier given to this SQL query. For HTTP client, it will be returned in `X-Druid-SQL-Query-Id` header.|auto-generated|
|`sqlTimeZone`|Sets the time zone for this connection, which will affect how time functions and timestamp literals behave. Should be a time zone name like "America/Los_Angeles" or offset like "-08:00".|druid.sql.planner.sqlTimeZone on the Broker (default: UTC)|
-|`useApproximateCountDistinct`|Whether to use an approximate cardinalty algorithm for `COUNT(DISTINCT foo)`.|druid.sql.planner.useApproximateCountDistinct on the Broker (default: true)|
+|`useApproximateCountDistinct`|Whether to use an approximate cardinality algorithm for `COUNT(DISTINCT foo)`.|druid.sql.planner.useApproximateCountDistinct on the Broker (default: true)|
|`useApproximateTopN`|Whether to use approximate [TopN queries](topnquery.html) when a SQL query could be expressed as such. If false, exact [GroupBy queries](groupbyquery.html) will be used instead.|druid.sql.planner.useApproximateTopN on the Broker (default: true)|
### Retrieving metadata
-Druid Brokers infer table and column metadata for each dataSource from segments loaded in the cluster, and use this to
+Druid Brokers infer table and column metadata for each datasource from segments loaded in the cluster, and use this to
plan SQL queries. This metadata is cached on Broker startup and also updated periodically in the background through
[SegmentMetadata queries](segmentmetadataquery.html). Background metadata refreshing is triggered by
segments entering and exiting the cluster, and can also be throttled through configuration.
@@ -640,7 +655,7 @@ The "sys" schema provides visibility into Druid segments, servers and tasks.
Segments table provides details on all Druid segments, whether they are published yet or not.
#### CAVEAT
-Note that a segment can be served by more than one stream ingestion tasks or Historical processes, in that case it would have multiple replicas. These replicas are weakly consistent with each other when served by multiple ingestion tasks, until a segment is eventually served by a Historical, at that point the segment is immutable. Broker prefers to query a segment from Historical over an ingestion task. But if a segment has multiple realtime replicas, for eg. kafka index tasks, and one task is slower than other, then the sys.segments query results can vary for the duration of the tasks because only one of the ingestion tasks is queried by the Broker and it is not gauranteed that the same task gets picked everytime. The `num_rows` column of segments table can have inconsistent values during this period. There is an open [issue](https://github.com/apache/incubator-druid/issues/5915) about this inconsistency with stream ingestion tasks.
+Note that a segment can be served by more than one stream ingestion tasks or Historical processes, in that case it would have multiple replicas. These replicas are weakly consistent with each other when served by multiple ingestion tasks, until a segment is eventually served by a Historical, at that point the segment is immutable. Broker prefers to query a segment from Historical over an ingestion task. But if a segment has multiple realtime replicas, for eg. kafka index tasks, and one task is slower than other, then the sys.segments query results can vary for the duration of the tasks because only one of the ingestion tasks is queried by the Broker and it is not guaranteed that the same task gets picked every time. The `num_rows` column of segments table can have inconsistent values during this period. There is an open [issue](https://github.com/apache/incubator-druid/issues/5915) about this inconsistency with stream ingestion tasks.
|Column|Type|Notes|
|------|-----|-----|
@@ -652,7 +667,7 @@ Note that a segment can be served by more than one stream ingestion tasks or His
|version|STRING|Version string (generally an ISO8601 timestamp corresponding to when the segment set was first started). Higher version means the more recently created segment. Version comparing is based on string comparison.|
|partition_num|LONG|Partition number (an integer, unique within a datasource+interval+version; may not necessarily be contiguous)|
|num_replicas|LONG|Number of replicas of this segment currently being served|
-|num_rows|LONG|Number of rows in current segment, this value could be null if unkown to Broker at query time|
+|num_rows|LONG|Number of rows in current segment, this value could be null if unknown to Broker at query time|
|is_published|LONG|Boolean is represented as long type where 1 = true, 0 = false. 1 represents this segment has been published to the metadata store with `used=1`|
|is_available|LONG|Boolean is represented as long type where 1 = true, 0 = false. 1 if this segment is currently being served by any process(Historical or realtime)|
|is_realtime|LONG|Boolean is represented as long type where 1 = true, 0 = false. 1 if this segment is _only_ served by realtime tasks, and 0 if any historical process is serving this segment|
@@ -769,9 +784,9 @@ The Druid SQL server is configured through the following properties on the Broke
|`druid.sql.planner.metadataRefreshPeriod`|Throttle for metadata refreshes.|PT1M|
|`druid.sql.planner.useApproximateCountDistinct`|Whether to use an approximate cardinalty algorithm for `COUNT(DISTINCT foo)`.|true|
|`druid.sql.planner.useApproximateTopN`|Whether to use approximate [TopN queries](../querying/topnquery.html) when a SQL query could be expressed as such. If false, exact [GroupBy queries](../querying/groupbyquery.html) will be used instead.|true|
-|`druid.sql.planner.requireTimeCondition`|Whether to require SQL to have filter conditions on __time column so that all generated native queries will have user specified intervals. If true, all queries wihout filter condition on __time column will fail|false|
+|`druid.sql.planner.requireTimeCondition`|Whether to require SQL to have filter conditions on __time column so that all generated native queries will have user specified intervals. If true, all queries without filter condition on __time column will fail|false|
|`druid.sql.planner.sqlTimeZone`|Sets the default time zone for the server, which will affect how time functions and timestamp literals behave. Should be a time zone name like "America/Los_Angeles" or offset like "-08:00".|UTC|
-|`druid.sql.planner.metadataSegmentCacheEnable`|Whether to keep a cache of published segments in broker. If true, broker polls coordinator in background to get segments from metadata store and maintains a local cache. If false, coordinator's REST api will be invoked when broker needs published segments info.|false|
+|`druid.sql.planner.metadataSegmentCacheEnable`|Whether to keep a cache of published segments in broker. If true, broker polls coordinator in background to get segments from metadata store and maintains a local cache. If false, coordinator's REST API will be invoked when broker needs published segments info.|false|
|`druid.sql.planner.metadataSegmentPollPeriod`|How often to poll coordinator for published segments list if `druid.sql.planner.metadataSegmentCacheEnable` is set to true. Poll period is in milliseconds. |60000|
## SQL Metrics
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/IPv4AddressMatchOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/IPv4AddressMatchOperatorConversion.java
new file mode 100644
index 00000000000..b9a39b427b2
--- /dev/null
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/IPv4AddressMatchOperatorConversion.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.sql.calcite.expression.builtin;
+
+import org.apache.calcite.sql.SqlFunction;
+import org.apache.calcite.sql.SqlFunctionCategory;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.type.OperandTypes;
+import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.sql.type.SqlSingleOperandTypeChecker;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.druid.java.util.common.StringUtils;
+import org.apache.druid.query.expression.IPv4AddressMatchExprMacro;
+import org.apache.druid.sql.calcite.expression.DirectOperatorConversion;
+import org.apache.druid.sql.calcite.expression.OperatorConversions;
+
+public class IPv4AddressMatchOperatorConversion extends DirectOperatorConversion
+{
+ private static final SqlSingleOperandTypeChecker ADDRESS_OPERAND = OperandTypes.or(
+ OperandTypes.family(SqlTypeFamily.STRING),
+ OperandTypes.family(SqlTypeFamily.INTEGER)
+ );
+
+ private static final SqlSingleOperandTypeChecker SUBNET_OPERAND = OperandTypes.family(SqlTypeFamily.STRING);
+
+ private static final SqlFunction SQL_FUNCTION = OperatorConversions
+ .operatorBuilder(StringUtils.toUpperCase(IPv4AddressMatchExprMacro.NAME))
+ .operandTypeChecker(OperandTypes.sequence("(expr,string)", ADDRESS_OPERAND, SUBNET_OPERAND))
+ .returnTypeInference(ReturnTypes.BOOLEAN_NULLABLE)
+ .functionCategory(SqlFunctionCategory.USER_DEFINED_FUNCTION)
+ .build();
+
+ public IPv4AddressMatchOperatorConversion()
+ {
+ super(SQL_FUNCTION, IPv4AddressMatchExprMacro.NAME);
+ }
+
+ @Override
+ public SqlOperator calciteOperator()
+ {
+ return SQL_FUNCTION;
+ }
+}
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/IPv4AddressParseOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/IPv4AddressParseOperatorConversion.java
new file mode 100644
index 00000000000..9658bff28af
--- /dev/null
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/IPv4AddressParseOperatorConversion.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.sql.calcite.expression.builtin;
+
+import org.apache.calcite.sql.SqlFunction;
+import org.apache.calcite.sql.SqlFunctionCategory;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.type.OperandTypes;
+import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.druid.java.util.common.StringUtils;
+import org.apache.druid.query.expression.IPv4AddressParseExprMacro;
+import org.apache.druid.sql.calcite.expression.DirectOperatorConversion;
+import org.apache.druid.sql.calcite.expression.OperatorConversions;
+
+public class IPv4AddressParseOperatorConversion extends DirectOperatorConversion
+{
+ private static final SqlFunction SQL_FUNCTION = OperatorConversions
+ .operatorBuilder(StringUtils.toUpperCase(IPv4AddressParseExprMacro.NAME))
+ .operandTypeChecker(
+ OperandTypes.or(
+ OperandTypes.family(SqlTypeFamily.STRING),
+ OperandTypes.family(SqlTypeFamily.INTEGER)
+ ))
+ .returnTypeInference(ReturnTypes.INTEGER_NULLABLE)
+ .functionCategory(SqlFunctionCategory.USER_DEFINED_FUNCTION)
+ .build();
+
+ public IPv4AddressParseOperatorConversion()
+ {
+ super(SQL_FUNCTION, IPv4AddressParseExprMacro.NAME);
+ }
+
+ @Override
+ public SqlOperator calciteOperator()
+ {
+ return SQL_FUNCTION;
+ }
+}
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/IPv4AddressStringifyOperatorConversion.java b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/IPv4AddressStringifyOperatorConversion.java
new file mode 100644
index 00000000000..eb48189b232
--- /dev/null
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/IPv4AddressStringifyOperatorConversion.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.sql.calcite.expression.builtin;
+
+import org.apache.calcite.sql.SqlFunction;
+import org.apache.calcite.sql.SqlFunctionCategory;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.type.OperandTypes;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.druid.java.util.common.StringUtils;
+import org.apache.druid.query.expression.IPv4AddressStringifyExprMacro;
+import org.apache.druid.sql.calcite.expression.DirectOperatorConversion;
+import org.apache.druid.sql.calcite.expression.OperatorConversions;
+
+public class IPv4AddressStringifyOperatorConversion extends DirectOperatorConversion
+{
+ private static final SqlFunction SQL_FUNCTION = OperatorConversions
+ .operatorBuilder(StringUtils.toUpperCase(IPv4AddressStringifyExprMacro.NAME))
+ .operandTypeChecker(
+ OperandTypes.or(
+ OperandTypes.family(SqlTypeFamily.INTEGER),
+ OperandTypes.family(SqlTypeFamily.STRING)
+ ))
+ .nullableReturnType(SqlTypeName.CHAR)
+ .functionCategory(SqlFunctionCategory.USER_DEFINED_FUNCTION)
+ .build();
+
+ public IPv4AddressStringifyOperatorConversion()
+ {
+ super(SQL_FUNCTION, IPv4AddressStringifyExprMacro.NAME);
+ }
+
+
+ @Override
+ public SqlOperator calciteOperator()
+ {
+ return SQL_FUNCTION;
+ }
+}
diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java b/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java
index 4a3ac99c373..a6b7ce842a5 100644
--- a/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java
+++ b/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java
@@ -62,6 +62,9 @@ import org.apache.druid.sql.calcite.expression.builtin.ConcatOperatorConversion;
import org.apache.druid.sql.calcite.expression.builtin.DateTruncOperatorConversion;
import org.apache.druid.sql.calcite.expression.builtin.ExtractOperatorConversion;
import org.apache.druid.sql.calcite.expression.builtin.FloorOperatorConversion;
+import org.apache.druid.sql.calcite.expression.builtin.IPv4AddressMatchOperatorConversion;
+import org.apache.druid.sql.calcite.expression.builtin.IPv4AddressParseOperatorConversion;
+import org.apache.druid.sql.calcite.expression.builtin.IPv4AddressStringifyOperatorConversion;
import org.apache.druid.sql.calcite.expression.builtin.LPadOperatorConversion;
import org.apache.druid.sql.calcite.expression.builtin.LTrimOperatorConversion;
import org.apache.druid.sql.calcite.expression.builtin.LeftOperatorConversion;
@@ -127,6 +130,93 @@ public class DruidOperatorTable implements SqlOperatorTable
"strlen"
);
+ private static final List TIME_OPERATOR_CONVERSIONS =
+ ImmutableList.builder()
+ .add(new CeilOperatorConversion())
+ .add(new DateTruncOperatorConversion())
+ .add(new ExtractOperatorConversion())
+ .add(new FloorOperatorConversion())
+ .add(new MillisToTimestampOperatorConversion())
+ .add(new TimeArithmeticOperatorConversion.TimeMinusIntervalOperatorConversion())
+ .add(new TimeArithmeticOperatorConversion.TimePlusIntervalOperatorConversion())
+ .add(new TimeExtractOperatorConversion())
+ .add(new TimeCeilOperatorConversion())
+ .add(new TimeFloorOperatorConversion())
+ .add(new TimeFormatOperatorConversion())
+ .add(new TimeParseOperatorConversion())
+ .add(new TimeShiftOperatorConversion())
+ .add(new TimestampToMillisOperatorConversion())
+ .build();
+
+ private static final List STRING_OPERATOR_CONVERSIONS =
+ ImmutableList.builder()
+ .add(new BTrimOperatorConversion())
+ .add(new LikeOperatorConversion())
+ .add(new LTrimOperatorConversion())
+ .add(new PositionOperatorConversion())
+ .add(new RegexpExtractOperatorConversion())
+ .add(new RTrimOperatorConversion())
+ .add(new ParseLongOperatorConversion())
+ .add(new StringFormatOperatorConversion())
+ .add(new StrposOperatorConversion())
+ .add(new SubstringOperatorConversion())
+ .add(new RightOperatorConversion())
+ .add(new LeftOperatorConversion())
+ .add(new ReverseOperatorConversion())
+ .add(new RepeatOperatorConversion())
+ .add(new AliasedOperatorConversion(new SubstringOperatorConversion(), "SUBSTR"))
+ .add(new ConcatOperatorConversion())
+ .add(new TextcatOperatorConversion())
+ .add(new TrimOperatorConversion())
+ .add(new TruncateOperatorConversion())
+ .add(new AliasedOperatorConversion(new TruncateOperatorConversion(), "TRUNC"))
+ .add(new LPadOperatorConversion())
+ .add(new RPadOperatorConversion())
+ .build();
+
+ private static final List VALUE_COERCION_OPERATOR_CONVERSIONS =
+ ImmutableList.builder()
+ .add(new CastOperatorConversion())
+ .add(new ReinterpretOperatorConversion())
+ .build();
+
+ private static final List ARRAY_OPERATOR_CONVERSIONS =
+ ImmutableList.builder()
+ .add(new ArrayConstructorOperatorConversion())
+ .add(new ArrayContainsOperatorConversion())
+ .add(new ArrayOverlapOperatorConversion())
+ .add(new AliasedOperatorConversion(new ArrayContainsOperatorConversion(), "MV_CONTAINS"))
+ .add(new AliasedOperatorConversion(new ArrayOverlapOperatorConversion(), "MV_OVERLAP"))
+ .add(new ArrayLengthOperatorConversion())
+ .add(new AliasedOperatorConversion(new ArrayLengthOperatorConversion(), "MV_LENGTH"))
+ .add(new ArrayOffsetOperatorConversion())
+ .add(new AliasedOperatorConversion(new ArrayOffsetOperatorConversion(), "MV_OFFSET"))
+ .add(new ArrayOrdinalOperatorConversion())
+ .add(new AliasedOperatorConversion(new ArrayOrdinalOperatorConversion(), "MV_ORDINAL"))
+ .add(new ArrayOffsetOfOperatorConversion())
+ .add(new AliasedOperatorConversion(new ArrayOffsetOfOperatorConversion(), "MV_OFFSET_OF"))
+ .add(new ArrayOrdinalOfOperatorConversion())
+ .add(new AliasedOperatorConversion(new ArrayOrdinalOfOperatorConversion(), "MV_ORDINAL_OF"))
+ .add(new ArrayToStringOperatorConversion())
+ .add(new AliasedOperatorConversion(new ArrayToStringOperatorConversion(), "MV_TO_STRING"))
+ .build();
+
+ private static final List MULTIVALUE_STRING_OPERATOR_CONVERSIONS =
+ ImmutableList.builder()
+ .add(new MultiValueStringAppendOperatorConversion())
+ .add(new MultiValueStringConcatOperatorConversion())
+ .add(new MultiValueStringPrependOperatorConversion())
+ .add(new MultiValueStringSliceOperatorConversion())
+ .add(new StringToMultiValueStringOperatorConversion())
+ .build();
+
+ private static final List IPV4ADDRESS_OPERATOR_CONVERSIONS =
+ ImmutableList.builder()
+ .add(new IPv4AddressMatchOperatorConversion())
+ .add(new IPv4AddressParseOperatorConversion())
+ .add(new IPv4AddressStringifyOperatorConversion())
+ .build();
+
private static final List STANDARD_OPERATOR_CONVERSIONS =
ImmutableList.builder()
.add(new DirectOperatorConversion(SqlStdOperatorTable.ABS, "abs"))
@@ -178,71 +268,12 @@ public class DruidOperatorTable implements SqlOperatorTable
.add(new BinaryOperatorConversion(SqlStdOperatorTable.AND, "&&"))
.add(new BinaryOperatorConversion(SqlStdOperatorTable.OR, "||"))
.add(new RoundOperatorConversion())
- // time operators
- .add(new CeilOperatorConversion())
- .add(new DateTruncOperatorConversion())
- .add(new ExtractOperatorConversion())
- .add(new FloorOperatorConversion())
- .add(new MillisToTimestampOperatorConversion())
- .add(new TimeArithmeticOperatorConversion.TimeMinusIntervalOperatorConversion())
- .add(new TimeArithmeticOperatorConversion.TimePlusIntervalOperatorConversion())
- .add(new TimeExtractOperatorConversion())
- .add(new TimeCeilOperatorConversion())
- .add(new TimeFloorOperatorConversion())
- .add(new TimeFormatOperatorConversion())
- .add(new TimeParseOperatorConversion())
- .add(new TimeShiftOperatorConversion())
- .add(new TimestampToMillisOperatorConversion())
- // string operators
- .add(new BTrimOperatorConversion())
- .add(new LikeOperatorConversion())
- .add(new LTrimOperatorConversion())
- .add(new PositionOperatorConversion())
- .add(new RegexpExtractOperatorConversion())
- .add(new RTrimOperatorConversion())
- .add(new ParseLongOperatorConversion())
- .add(new StringFormatOperatorConversion())
- .add(new StrposOperatorConversion())
- .add(new SubstringOperatorConversion())
- .add(new RightOperatorConversion())
- .add(new LeftOperatorConversion())
- .add(new ReverseOperatorConversion())
- .add(new RepeatOperatorConversion())
- .add(new AliasedOperatorConversion(new SubstringOperatorConversion(), "SUBSTR"))
- .add(new ConcatOperatorConversion())
- .add(new TextcatOperatorConversion())
- .add(new TrimOperatorConversion())
- .add(new TruncateOperatorConversion())
- .add(new AliasedOperatorConversion(new TruncateOperatorConversion(), "TRUNC"))
- .add(new LPadOperatorConversion())
- .add(new RPadOperatorConversion())
- // value coercion operators
- .add(new CastOperatorConversion())
- .add(new ReinterpretOperatorConversion())
- // array and multi-value string operators
- .add(new ArrayConstructorOperatorConversion())
- .add(new ArrayContainsOperatorConversion())
- .add(new ArrayOverlapOperatorConversion())
- .add(new AliasedOperatorConversion(new ArrayContainsOperatorConversion(), "MV_CONTAINS"))
- .add(new AliasedOperatorConversion(new ArrayOverlapOperatorConversion(), "MV_OVERLAP"))
- .add(new ArrayLengthOperatorConversion())
- .add(new AliasedOperatorConversion(new ArrayLengthOperatorConversion(), "MV_LENGTH"))
- .add(new ArrayOffsetOperatorConversion())
- .add(new AliasedOperatorConversion(new ArrayOffsetOperatorConversion(), "MV_OFFSET"))
- .add(new ArrayOrdinalOperatorConversion())
- .add(new AliasedOperatorConversion(new ArrayOrdinalOperatorConversion(), "MV_ORDINAL"))
- .add(new ArrayOffsetOfOperatorConversion())
- .add(new AliasedOperatorConversion(new ArrayOffsetOfOperatorConversion(), "MV_OFFSET_OF"))
- .add(new ArrayOrdinalOfOperatorConversion())
- .add(new AliasedOperatorConversion(new ArrayOrdinalOfOperatorConversion(), "MV_ORDINAL_OF"))
- .add(new ArrayToStringOperatorConversion())
- .add(new AliasedOperatorConversion(new ArrayToStringOperatorConversion(), "MV_TO_STRING"))
- // multi-value string operators
- .add(new MultiValueStringAppendOperatorConversion())
- .add(new MultiValueStringConcatOperatorConversion())
- .add(new MultiValueStringPrependOperatorConversion())
- .add(new MultiValueStringSliceOperatorConversion())
- .add(new StringToMultiValueStringOperatorConversion())
+ .addAll(TIME_OPERATOR_CONVERSIONS)
+ .addAll(STRING_OPERATOR_CONVERSIONS)
+ .addAll(VALUE_COERCION_OPERATOR_CONVERSIONS)
+ .addAll(ARRAY_OPERATOR_CONVERSIONS)
+ .addAll(MULTIVALUE_STRING_OPERATOR_CONVERSIONS)
+ .addAll(IPV4ADDRESS_OPERATOR_CONVERSIONS)
.build();
// Operators that have no conversion, but are handled in the convertlet table, so they still need to exist.
diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionTestBase.java b/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionTestBase.java
new file mode 100644
index 00000000000..0a03bcc8aa7
--- /dev/null
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionTestBase.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.sql.calcite.expression;
+
+import org.apache.druid.sql.calcite.util.CalciteTestBase;
+import org.junit.Rule;
+import org.junit.rules.ExpectedException;
+
+public abstract class ExpressionTestBase extends CalciteTestBase
+{
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ void expectException(Class extends Throwable> type, String message)
+ {
+ expectedException.expect(type);
+ expectedException.expectMessage(message);
+ }
+}
diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionTestHelper.java b/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionTestHelper.java
new file mode 100644
index 00000000000..254a7b2ee7c
--- /dev/null
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionTestHelper.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.sql.calcite.expression;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.SqlIntervalQualifier;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.druid.math.expr.ExprEval;
+import org.apache.druid.math.expr.Parser;
+import org.apache.druid.sql.calcite.planner.Calcites;
+import org.apache.druid.sql.calcite.planner.PlannerConfig;
+import org.apache.druid.sql.calcite.planner.PlannerContext;
+import org.apache.druid.sql.calcite.table.RowSignature;
+import org.apache.druid.sql.calcite.util.CalciteTests;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.junit.Assert;
+
+import javax.annotation.Nullable;
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+class ExpressionTestHelper
+{
+ private static final PlannerContext PLANNER_CONTEXT = PlannerContext.create(
+ CalciteTests.createOperatorTable(),
+ CalciteTests.createExprMacroTable(),
+ new PlannerConfig(),
+ ImmutableMap.of(),
+ CalciteTests.REGULAR_USER_AUTH_RESULT
+ );
+
+ private final RowSignature rowSignature;
+ private final Map bindings;
+ private final RelDataTypeFactory typeFactory;
+ private final RexBuilder rexBuilder;
+ private final RelDataType relDataType;
+
+ ExpressionTestHelper(RowSignature rowSignature, Map bindings)
+ {
+ this.rowSignature = rowSignature;
+ this.bindings = bindings;
+
+ this.typeFactory = new JavaTypeFactoryImpl();
+ this.rexBuilder = new RexBuilder(typeFactory);
+ this.relDataType = rowSignature.getRelDataType(typeFactory);
+ }
+
+ RelDataType createSqlType(SqlTypeName sqlTypeName)
+ {
+ return typeFactory.createSqlType(sqlTypeName);
+ }
+
+ RexNode makeInputRef(String columnName)
+ {
+ int columnNumber = rowSignature.getRowOrder().indexOf(columnName);
+ return rexBuilder.makeInputRef(relDataType.getFieldList().get(columnNumber).getType(), columnNumber);
+ }
+
+ RexNode getConstantNull()
+ {
+ return rexBuilder.constantNull();
+ }
+
+ RexLiteral makeFlag(Enum flag)
+ {
+ return rexBuilder.makeFlag(flag);
+ }
+
+ RexLiteral makeNullLiteral(SqlTypeName sqlTypeName)
+ {
+ return rexBuilder.makeNullLiteral(createSqlType(sqlTypeName));
+ }
+
+ RexLiteral makeLiteral(String s)
+ {
+ return rexBuilder.makeLiteral(s);
+ }
+
+ RexNode makeLiteral(DateTime timestamp)
+ {
+ return rexBuilder.makeTimestampLiteral(Calcites.jodaToCalciteTimestampString(timestamp, DateTimeZone.UTC), 0);
+ }
+
+ RexNode makeLiteral(Integer integer)
+ {
+ return rexBuilder.makeLiteral(new BigDecimal(integer), createSqlType(SqlTypeName.INTEGER), true);
+ }
+
+ RexNode makeLiteral(Long bigint)
+ {
+ return rexBuilder.makeLiteral(new BigDecimal(bigint), createSqlType(SqlTypeName.BIGINT), true);
+ }
+
+ RexNode makeLiteral(BigDecimal bigDecimal)
+ {
+ return rexBuilder.makeExactLiteral(bigDecimal);
+ }
+
+ RexNode makeLiteral(BigDecimal v, SqlIntervalQualifier intervalQualifier)
+ {
+ return rexBuilder.makeIntervalLiteral(v, intervalQualifier);
+ }
+
+ RexNode makeCall(SqlOperator op, RexNode... exprs)
+ {
+ return rexBuilder.makeCall(op, exprs);
+ }
+
+ RexNode makeAbstractCast(RelDataType type, RexNode exp)
+ {
+ return rexBuilder.makeAbstractCast(type, exp);
+ }
+
+ /**
+ * @return Representation of variable that is bound in an expression. Intended use is as one of
+ * the args to {@link #buildExpectedExpression(String, Object...)}.
+ */
+ Variable makeVariable(String name)
+ {
+ return new Variable(name);
+ }
+
+ private static class Variable
+ {
+ private final String name;
+
+ Variable(String name)
+ {
+ this.name = name;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "\"" + name + "\"";
+ }
+ }
+
+ DruidExpression buildExpectedExpression(String functionName, Object... args)
+ {
+ String noDelimiter = "";
+ String argsString = Arrays.stream(args)
+ .map(ExpressionTestHelper::quoteIfNeeded)
+ .collect(Collectors.joining(","));
+ List elements = Arrays.asList(functionName, "(", argsString, ")");
+ return DruidExpression.fromExpression(String.join(noDelimiter, elements));
+ }
+
+ private static String quoteIfNeeded(@Nullable Object arg)
+ {
+ if (arg == null) {
+ return "null";
+ } else if (arg instanceof String) {
+ return "'" + arg + "'";
+ } else if (arg instanceof Boolean) {
+ return (Boolean) arg ? "1" : "0";
+ } else {
+ return arg.toString();
+ }
+ }
+
+ void testExpression(
+ SqlTypeName sqlTypeName,
+ SqlOperator op,
+ List exprs,
+ DruidExpression expectedExpression,
+ Object expectedResult
+ )
+ {
+ RelDataType returnType = createSqlType(sqlTypeName);
+ testExpression(rexBuilder.makeCall(returnType, op, exprs), expectedExpression, expectedResult);
+ }
+
+ void testExpression(
+ SqlOperator op,
+ RexNode expr,
+ DruidExpression expectedExpression,
+ Object expectedResult
+ )
+ {
+ testExpression(op, Collections.singletonList(expr), expectedExpression, expectedResult);
+ }
+
+ void testExpression(
+ SqlOperator op,
+ List extends RexNode> exprs,
+ DruidExpression expectedExpression,
+ Object expectedResult
+ )
+ {
+ testExpression(rexBuilder.makeCall(op, exprs), expectedExpression, expectedResult);
+ }
+
+ void testExpression(
+ RexNode rexNode,
+ DruidExpression expectedExpression,
+ Object expectedResult
+ )
+ {
+ DruidExpression expression = Expressions.toDruidExpression(PLANNER_CONTEXT, rowSignature, rexNode);
+ Assert.assertEquals("Expression for: " + rexNode, expectedExpression, expression);
+
+ ExprEval result = Parser.parse(expression.getExpression(), PLANNER_CONTEXT.getExprMacroTable())
+ .eval(Parser.withMap(bindings));
+ Assert.assertEquals("Result for: " + rexNode, expectedResult, result.value());
+ }
+}
diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionsTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionsTest.java
index 3a5ba951e3f..35267c843f7 100644
--- a/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionsTest.java
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionsTest.java
@@ -23,11 +23,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.apache.calcite.avatica.util.TimeUnit;
import org.apache.calcite.avatica.util.TimeUnitRange;
-import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rel.type.RelDataTypeFactory;
-import org.apache.calcite.rex.RexBuilder;
-import org.apache.calcite.rex.RexNode;
import org.apache.calcite.sql.SqlFunction;
import org.apache.calcite.sql.SqlIntervalQualifier;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
@@ -37,8 +32,6 @@ import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.druid.common.config.NullHandling;
import org.apache.druid.java.util.common.DateTimes;
import org.apache.druid.java.util.common.IAE;
-import org.apache.druid.math.expr.ExprEval;
-import org.apache.druid.math.expr.Parser;
import org.apache.druid.query.extraction.RegexDimExtractionFn;
import org.apache.druid.segment.column.ValueType;
import org.apache.druid.sql.calcite.expression.builtin.DateTruncOperatorConversion;
@@ -60,37 +53,17 @@ import org.apache.druid.sql.calcite.expression.builtin.TimeFormatOperatorConvers
import org.apache.druid.sql.calcite.expression.builtin.TimeParseOperatorConversion;
import org.apache.druid.sql.calcite.expression.builtin.TimeShiftOperatorConversion;
import org.apache.druid.sql.calcite.expression.builtin.TruncateOperatorConversion;
-import org.apache.druid.sql.calcite.planner.Calcites;
-import org.apache.druid.sql.calcite.planner.PlannerConfig;
-import org.apache.druid.sql.calcite.planner.PlannerContext;
import org.apache.druid.sql.calcite.table.RowSignature;
-import org.apache.druid.sql.calcite.util.CalciteTestBase;
-import org.apache.druid.sql.calcite.util.CalciteTests;
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
import org.joda.time.Period;
-import org.junit.Assert;
-import org.junit.Rule;
+import org.junit.Before;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
import java.math.BigDecimal;
import java.util.Map;
-public class ExpressionsTest extends CalciteTestBase
+public class ExpressionsTest extends ExpressionTestBase
{
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private final PlannerContext plannerContext = PlannerContext.create(
- CalciteTests.createOperatorTable(),
- CalciteTests.createExprMacroTable(),
- new PlannerConfig(),
- ImmutableMap.of(),
- CalciteTests.REGULAR_USER_AUTH_RESULT
- );
- private final RowSignature rowSignature = RowSignature
+ private static final RowSignature ROW_SIGNATURE = RowSignature
.builder()
.add("t", ValueType.LONG)
.add("a", ValueType.LONG)
@@ -105,7 +78,8 @@ public class ExpressionsTest extends CalciteTestBase
.add("tstr", ValueType.STRING)
.add("dstr", ValueType.STRING)
.build();
- private final Map bindings = ImmutableMap.builder()
+
+ private static final Map BINDINGS = ImmutableMap.builder()
.put("t", DateTimes.of("2000-02-03T04:05:06").getMillis())
.put("a", 10)
.put("b", 25)
@@ -119,21 +93,24 @@ public class ExpressionsTest extends CalciteTestBase
.put("tstr", "2000-02-03 04:05:06")
.put("dstr", "2000-02-03")
.build();
- private final RelDataTypeFactory typeFactory = new JavaTypeFactoryImpl();
- private final RexBuilder rexBuilder = new RexBuilder(typeFactory);
- private final RelDataType relDataType = rowSignature.getRelDataType(typeFactory);
+
+ private ExpressionTestHelper testHelper;
+
+ @Before
+ public void setUp()
+ {
+ testHelper = new ExpressionTestHelper(ROW_SIGNATURE, BINDINGS);
+ }
@Test
public void testConcat()
{
- testExpression(
- rexBuilder.makeCall(
- typeFactory.createSqlType(SqlTypeName.VARCHAR),
- SqlStdOperatorTable.CONCAT,
- ImmutableList.of(
- inputRef("s"),
- rexBuilder.makeLiteral("bar")
- )
+ testHelper.testExpression(
+ SqlTypeName.VARCHAR,
+ SqlStdOperatorTable.CONCAT,
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral("bar")
),
DruidExpression.fromExpression("concat(\"s\",'bar')"),
"foobar"
@@ -143,11 +120,9 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testCharacterLength()
{
- testExpression(
- rexBuilder.makeCall(
- SqlStdOperatorTable.CHARACTER_LENGTH,
- inputRef("s")
- ),
+ testHelper.testExpression(
+ SqlStdOperatorTable.CHARACTER_LENGTH,
+ testHelper.makeInputRef("s"),
DruidExpression.fromExpression("strlen(\"s\")"),
3L
);
@@ -156,12 +131,12 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testRegexpExtract()
{
- testExpression(
- rexBuilder.makeCall(
- new RegexpExtractOperatorConversion().calciteOperator(),
- inputRef("s"),
- rexBuilder.makeLiteral("f(.)"),
- integerLiteral(1)
+ testHelper.testExpression(
+ new RegexpExtractOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral("f(.)"),
+ testHelper.makeLiteral(1)
),
DruidExpression.of(
SimpleExtraction.of("s", new RegexDimExtractionFn("f(.)", 1, true, null)),
@@ -170,11 +145,11 @@ public class ExpressionsTest extends CalciteTestBase
"o"
);
- testExpression(
- rexBuilder.makeCall(
- new RegexpExtractOperatorConversion().calciteOperator(),
- inputRef("s"),
- rexBuilder.makeLiteral("f(.)")
+ testHelper.testExpression(
+ new RegexpExtractOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral("f(.)")
),
DruidExpression.of(
SimpleExtraction.of("s", new RegexDimExtractionFn("f(.)", 0, true, null)),
@@ -187,44 +162,44 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testStringFormat()
{
- testExpression(
- rexBuilder.makeCall(
- new StringFormatOperatorConversion().calciteOperator(),
- rexBuilder.makeLiteral("%x"),
- inputRef("b")
+ testHelper.testExpression(
+ new StringFormatOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeLiteral("%x"),
+ testHelper.makeInputRef("b")
),
DruidExpression.fromExpression("format('%x',\"b\")"),
"19"
);
- testExpression(
- rexBuilder.makeCall(
- new StringFormatOperatorConversion().calciteOperator(),
- rexBuilder.makeLiteral("%s %,d"),
- inputRef("s"),
- integerLiteral(1234)
+ testHelper.testExpression(
+ new StringFormatOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeLiteral("%s %,d"),
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(1234)
),
DruidExpression.fromExpression("format('%s %,d',\"s\",1234)"),
"foo 1,234"
);
- testExpression(
- rexBuilder.makeCall(
- new StringFormatOperatorConversion().calciteOperator(),
- rexBuilder.makeLiteral("%s %,d"),
- inputRef("s")
+ testHelper.testExpression(
+ new StringFormatOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeLiteral("%s %,d"),
+ testHelper.makeInputRef("s")
),
DruidExpression.fromExpression("format('%s %,d',\"s\")"),
"%s %,d; foo"
);
- testExpression(
- rexBuilder.makeCall(
- new StringFormatOperatorConversion().calciteOperator(),
- rexBuilder.makeLiteral("%s %,d"),
- inputRef("s"),
- integerLiteral(1234),
- integerLiteral(6789)
+ testHelper.testExpression(
+ new StringFormatOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeLiteral("%s %,d"),
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(1234),
+ testHelper.makeLiteral(6789)
),
DruidExpression.fromExpression("format('%s %,d',\"s\",1234,6789)"),
"foo 1,234"
@@ -234,31 +209,31 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testStrpos()
{
- testExpression(
- rexBuilder.makeCall(
- new StrposOperatorConversion().calciteOperator(),
- inputRef("s"),
- rexBuilder.makeLiteral("oo")
+ testHelper.testExpression(
+ new StrposOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral("oo")
),
DruidExpression.fromExpression("(strpos(\"s\",'oo') + 1)"),
2L
);
- testExpression(
- rexBuilder.makeCall(
- new StrposOperatorConversion().calciteOperator(),
- inputRef("s"),
- rexBuilder.makeLiteral("ax")
+ testHelper.testExpression(
+ new StrposOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral("ax")
),
DruidExpression.fromExpression("(strpos(\"s\",'ax') + 1)"),
0L
);
- testExpression(
- rexBuilder.makeCall(
- new StrposOperatorConversion().calciteOperator(),
- rexBuilder.makeNullLiteral(typeFactory.createSqlType(SqlTypeName.VARCHAR)),
- rexBuilder.makeLiteral("ax")
+ testHelper.testExpression(
+ new StrposOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeNullLiteral(SqlTypeName.VARCHAR),
+ testHelper.makeLiteral("ax")
),
DruidExpression.fromExpression("(strpos(null,'ax') + 1)"),
NullHandling.replaceWithDefault() ? 0L : null
@@ -268,44 +243,40 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testParseLong()
{
- testExpression(
- rexBuilder.makeCall(
- new ParseLongOperatorConversion().calciteOperator(),
- inputRef("intstr")
- ),
+ testHelper.testExpression(
+ new ParseLongOperatorConversion().calciteOperator(),
+ testHelper.makeInputRef("intstr"),
DruidExpression.fromExpression("parse_long(\"intstr\")"),
-100L
);
- testExpression(
- rexBuilder.makeCall(
- new ParseLongOperatorConversion().calciteOperator(),
- inputRef("hexstr"),
- rexBuilder.makeExactLiteral(BigDecimal.valueOf(16))
+ testHelper.testExpression(
+ new ParseLongOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("hexstr"),
+ testHelper.makeLiteral(BigDecimal.valueOf(16))
),
DruidExpression.fromExpression("parse_long(\"hexstr\",16)"),
239L
);
- testExpression(
- rexBuilder.makeCall(
- new ParseLongOperatorConversion().calciteOperator(),
- rexBuilder.makeCall(
+ testHelper.testExpression(
+ new ParseLongOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeCall(
SqlStdOperatorTable.CONCAT,
- rexBuilder.makeLiteral("0x"),
- inputRef("hexstr")
+ testHelper.makeLiteral("0x"),
+ testHelper.makeInputRef("hexstr")
),
- rexBuilder.makeExactLiteral(BigDecimal.valueOf(16))
+ testHelper.makeLiteral(BigDecimal.valueOf(16))
),
DruidExpression.fromExpression("parse_long(concat('0x',\"hexstr\"),16)"),
239L
);
- testExpression(
- rexBuilder.makeCall(
- new ParseLongOperatorConversion().calciteOperator(),
- inputRef("hexstr")
- ),
+ testHelper.testExpression(
+ new ParseLongOperatorConversion().calciteOperator(),
+ testHelper.makeInputRef("hexstr"),
DruidExpression.fromExpression("parse_long(\"hexstr\")"),
NullHandling.sqlCompatible() ? null : 0L
);
@@ -314,33 +285,33 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testPosition()
{
- testExpression(
- rexBuilder.makeCall(
- SqlStdOperatorTable.POSITION,
- rexBuilder.makeLiteral("oo"),
- inputRef("s")
+ testHelper.testExpression(
+ SqlStdOperatorTable.POSITION,
+ ImmutableList.of(
+ testHelper.makeLiteral("oo"),
+ testHelper.makeInputRef("s")
),
DruidExpression.fromExpression("(strpos(\"s\",'oo',0) + 1)"),
2L
);
- testExpression(
- rexBuilder.makeCall(
- SqlStdOperatorTable.POSITION,
- rexBuilder.makeLiteral("oo"),
- inputRef("s"),
- rexBuilder.makeExactLiteral(BigDecimal.valueOf(2))
+ testHelper.testExpression(
+ SqlStdOperatorTable.POSITION,
+ ImmutableList.of(
+ testHelper.makeLiteral("oo"),
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(BigDecimal.valueOf(2))
),
DruidExpression.fromExpression("(strpos(\"s\",'oo',(2 - 1)) + 1)"),
2L
);
- testExpression(
- rexBuilder.makeCall(
- SqlStdOperatorTable.POSITION,
- rexBuilder.makeLiteral("oo"),
- inputRef("s"),
- rexBuilder.makeExactLiteral(BigDecimal.valueOf(3))
+ testHelper.testExpression(
+ SqlStdOperatorTable.POSITION,
+ ImmutableList.of(
+ testHelper.makeLiteral("oo"),
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(BigDecimal.valueOf(3))
),
DruidExpression.fromExpression("(strpos(\"s\",'oo',(3 - 1)) + 1)"),
0L
@@ -350,8 +321,12 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testPower()
{
- testExpression(
- rexBuilder.makeCall(SqlStdOperatorTable.POWER, inputRef("a"), integerLiteral(2)),
+ testHelper.testExpression(
+ SqlStdOperatorTable.POWER,
+ ImmutableList.of(
+ testHelper.makeInputRef("a"),
+ testHelper.makeLiteral(2)
+ ),
DruidExpression.fromExpression("pow(\"a\",2)"),
100.0
);
@@ -360,26 +335,30 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testFloor()
{
- testExpression(
- rexBuilder.makeCall(SqlStdOperatorTable.FLOOR, inputRef("a")),
+ testHelper.testExpression(
+ SqlStdOperatorTable.FLOOR,
+ testHelper.makeInputRef("a"),
DruidExpression.fromExpression("floor(\"a\")"),
10.0
);
- testExpression(
- rexBuilder.makeCall(SqlStdOperatorTable.FLOOR, inputRef("x")),
+ testHelper.testExpression(
+ SqlStdOperatorTable.FLOOR,
+ testHelper.makeInputRef("x"),
DruidExpression.fromExpression("floor(\"x\")"),
2.0
);
- testExpression(
- rexBuilder.makeCall(SqlStdOperatorTable.FLOOR, inputRef("y")),
+ testHelper.testExpression(
+ SqlStdOperatorTable.FLOOR,
+ testHelper.makeInputRef("y"),
DruidExpression.fromExpression("floor(\"y\")"),
3.0
);
- testExpression(
- rexBuilder.makeCall(SqlStdOperatorTable.FLOOR, inputRef("z")),
+ testHelper.testExpression(
+ SqlStdOperatorTable.FLOOR,
+ testHelper.makeInputRef("z"),
DruidExpression.fromExpression("floor(\"z\")"),
-3.0
);
@@ -388,26 +367,30 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testCeil()
{
- testExpression(
- rexBuilder.makeCall(SqlStdOperatorTable.CEIL, inputRef("a")),
+ testHelper.testExpression(
+ SqlStdOperatorTable.CEIL,
+ testHelper.makeInputRef("a"),
DruidExpression.fromExpression("ceil(\"a\")"),
10.0
);
- testExpression(
- rexBuilder.makeCall(SqlStdOperatorTable.CEIL, inputRef("x")),
+ testHelper.testExpression(
+ SqlStdOperatorTable.CEIL,
+ testHelper.makeInputRef("x"),
DruidExpression.fromExpression("ceil(\"x\")"),
3.0
);
- testExpression(
- rexBuilder.makeCall(SqlStdOperatorTable.CEIL, inputRef("y")),
+ testHelper.testExpression(
+ SqlStdOperatorTable.CEIL,
+ testHelper.makeInputRef("y"),
DruidExpression.fromExpression("ceil(\"y\")"),
3.0
);
- testExpression(
- rexBuilder.makeCall(SqlStdOperatorTable.CEIL, inputRef("z")),
+ testHelper.testExpression(
+ SqlStdOperatorTable.CEIL,
+ testHelper.makeInputRef("z"),
DruidExpression.fromExpression("ceil(\"z\")"),
-2.0
);
@@ -418,50 +401,70 @@ public class ExpressionsTest extends CalciteTestBase
{
final SqlFunction truncateFunction = new TruncateOperatorConversion().calciteOperator();
- testExpression(
- rexBuilder.makeCall(truncateFunction, inputRef("a")),
+ testHelper.testExpression(
+ truncateFunction,
+ testHelper.makeInputRef("a"),
DruidExpression.fromExpression("(cast(cast(\"a\" * 1,'long'),'double') / 1)"),
10.0
);
- testExpression(
- rexBuilder.makeCall(truncateFunction, inputRef("x")),
+ testHelper.testExpression(
+ truncateFunction,
+ testHelper.makeInputRef("x"),
DruidExpression.fromExpression("(cast(cast(\"x\" * 1,'long'),'double') / 1)"),
2.0
);
- testExpression(
- rexBuilder.makeCall(truncateFunction, inputRef("y")),
+ testHelper.testExpression(
+ truncateFunction,
+ testHelper.makeInputRef("y"),
DruidExpression.fromExpression("(cast(cast(\"y\" * 1,'long'),'double') / 1)"),
3.0
);
- testExpression(
- rexBuilder.makeCall(truncateFunction, inputRef("z")),
+ testHelper.testExpression(
+ truncateFunction,
+ testHelper.makeInputRef("z"),
DruidExpression.fromExpression("(cast(cast(\"z\" * 1,'long'),'double') / 1)"),
-2.0
);
- testExpression(
- rexBuilder.makeCall(truncateFunction, inputRef("x"), integerLiteral(1)),
+ testHelper.testExpression(
+ truncateFunction,
+ ImmutableList.of(
+ testHelper.makeInputRef("x"),
+ testHelper.makeLiteral(1)
+ ),
DruidExpression.fromExpression("(cast(cast(\"x\" * 10.0,'long'),'double') / 10.0)"),
2.2
);
- testExpression(
- rexBuilder.makeCall(truncateFunction, inputRef("z"), integerLiteral(1)),
+ testHelper.testExpression(
+ truncateFunction,
+ ImmutableList.of(
+ testHelper.makeInputRef("z"),
+ testHelper.makeLiteral(1)
+ ),
DruidExpression.fromExpression("(cast(cast(\"z\" * 10.0,'long'),'double') / 10.0)"),
-2.2
);
- testExpression(
- rexBuilder.makeCall(truncateFunction, inputRef("b"), integerLiteral(-1)),
+ testHelper.testExpression(
+ truncateFunction,
+ ImmutableList.of(
+ testHelper.makeInputRef("b"),
+ testHelper.makeLiteral(-1)
+ ),
DruidExpression.fromExpression("(cast(cast(\"b\" * 0.1,'long'),'double') / 0.1)"),
20.0
);
- testExpression(
- rexBuilder.makeCall(truncateFunction, inputRef("z"), integerLiteral(-1)),
+ testHelper.testExpression(
+ truncateFunction,
+ ImmutableList.of(
+ testHelper.makeInputRef("z"),
+ testHelper.makeLiteral(-1)
+ ),
DruidExpression.fromExpression("(cast(cast(\"z\" * 0.1,'long'),'double') / 0.1)"),
0.0
);
@@ -472,44 +475,57 @@ public class ExpressionsTest extends CalciteTestBase
{
final SqlFunction roundFunction = new RoundOperatorConversion().calciteOperator();
- testExpression(
- rexBuilder.makeCall(roundFunction, inputRef("a")),
+ testHelper.testExpression(
+ roundFunction,
+ testHelper.makeInputRef("a"),
DruidExpression.fromExpression("round(\"a\")"),
10L
);
- testExpression(
- rexBuilder.makeCall(roundFunction, inputRef("b")),
+ testHelper.testExpression(
+ roundFunction,
+ testHelper.makeInputRef("b"),
DruidExpression.fromExpression("round(\"b\")"),
25L
);
- testExpression(
- rexBuilder.makeCall(roundFunction, inputRef("b"), integerLiteral(-1)),
+ testHelper.testExpression(
+ roundFunction,
+ ImmutableList.of(
+ testHelper.makeInputRef("b"),
+ testHelper.makeLiteral(-1)
+ ),
DruidExpression.fromExpression("round(\"b\",-1)"),
30L
);
- testExpression(
- rexBuilder.makeCall(roundFunction, inputRef("x")),
+ testHelper.testExpression(
+ roundFunction,
+ testHelper.makeInputRef("x"),
DruidExpression.fromExpression("round(\"x\")"),
2.0
);
- testExpression(
- rexBuilder.makeCall(roundFunction, inputRef("x"), integerLiteral(1)),
+ testHelper.testExpression(
+ roundFunction,
+ ImmutableList.of(
+ testHelper.makeInputRef("x"),
+ testHelper.makeLiteral(1)
+ ),
DruidExpression.fromExpression("round(\"x\",1)"),
2.3
);
- testExpression(
- rexBuilder.makeCall(roundFunction, inputRef("y")),
+ testHelper.testExpression(
+ roundFunction,
+ testHelper.makeInputRef("y"),
DruidExpression.fromExpression("round(\"y\")"),
3.0
);
- testExpression(
- rexBuilder.makeCall(roundFunction, inputRef("z")),
+ testHelper.testExpression(
+ roundFunction,
+ testHelper.makeInputRef("z"),
DruidExpression.fromExpression("round(\"z\")"),
-2.0
);
@@ -520,11 +536,13 @@ public class ExpressionsTest extends CalciteTestBase
{
final SqlFunction roundFunction = new RoundOperatorConversion().calciteOperator();
- expectedException.expect(IAE.class);
- expectedException.expectMessage(
- "The first argument to the function[round] should be integer or double type but get the STRING type");
- testExpression(
- rexBuilder.makeCall(roundFunction, inputRef("s")),
+ expectException(
+ IAE.class,
+ "The first argument to the function[round] should be integer or double type but get the STRING type"
+ );
+ testHelper.testExpression(
+ roundFunction,
+ testHelper.makeInputRef("s"),
DruidExpression.fromExpression("round(\"s\")"),
"IAE Exception"
);
@@ -535,11 +553,16 @@ public class ExpressionsTest extends CalciteTestBase
{
final SqlFunction roundFunction = new RoundOperatorConversion().calciteOperator();
- expectedException.expect(IAE.class);
- expectedException.expectMessage(
- "The second argument to the function[round] should be integer type but get the STRING type");
- testExpression(
- rexBuilder.makeCall(roundFunction, inputRef("x"), rexBuilder.makeLiteral("foo")),
+ expectException(
+ IAE.class,
+ "The second argument to the function[round] should be integer type but get the STRING type"
+ );
+ testHelper.testExpression(
+ roundFunction,
+ ImmutableList.of(
+ testHelper.makeInputRef("x"),
+ testHelper.makeLiteral("foo")
+ ),
DruidExpression.fromExpression("round(\"x\",'foo')"),
"IAE Exception"
);
@@ -548,21 +571,21 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testDateTrunc()
{
- testExpression(
- rexBuilder.makeCall(
- new DateTruncOperatorConversion().calciteOperator(),
- rexBuilder.makeLiteral("hour"),
- timestampLiteral(DateTimes.of("2000-02-03T04:05:06Z"))
+ testHelper.testExpression(
+ new DateTruncOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeLiteral("hour"),
+ testHelper.makeLiteral(DateTimes.of("2000-02-03T04:05:06Z"))
),
DruidExpression.fromExpression("timestamp_floor(949550706000,'PT1H',null,'UTC')"),
DateTimes.of("2000-02-03T04:00:00").getMillis()
);
- testExpression(
- rexBuilder.makeCall(
- new DateTruncOperatorConversion().calciteOperator(),
- rexBuilder.makeLiteral("DAY"),
- timestampLiteral(DateTimes.of("2000-02-03T04:05:06Z"))
+ testHelper.testExpression(
+ new DateTruncOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeLiteral("DAY"),
+ testHelper.makeLiteral(DateTimes.of("2000-02-03T04:05:06Z"))
),
DruidExpression.fromExpression("timestamp_floor(949550706000,'P1D',null,'UTC')"),
DateTimes.of("2000-02-03T00:00:00").getMillis()
@@ -572,34 +595,34 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testTrim()
{
- testExpression(
- rexBuilder.makeCall(
- SqlStdOperatorTable.TRIM,
- rexBuilder.makeFlag(SqlTrimFunction.Flag.BOTH),
- rexBuilder.makeLiteral(" "),
- inputRef("spacey")
+ testHelper.testExpression(
+ SqlStdOperatorTable.TRIM,
+ ImmutableList.of(
+ testHelper.makeFlag(SqlTrimFunction.Flag.BOTH),
+ testHelper.makeLiteral(" "),
+ testHelper.makeInputRef("spacey")
),
DruidExpression.fromExpression("trim(\"spacey\",' ')"),
"hey there"
);
- testExpression(
- rexBuilder.makeCall(
- SqlStdOperatorTable.TRIM,
- rexBuilder.makeFlag(SqlTrimFunction.Flag.LEADING),
- rexBuilder.makeLiteral(" h"),
- inputRef("spacey")
+ testHelper.testExpression(
+ SqlStdOperatorTable.TRIM,
+ ImmutableList.of(
+ testHelper.makeFlag(SqlTrimFunction.Flag.LEADING),
+ testHelper.makeLiteral(" h"),
+ testHelper.makeInputRef("spacey")
),
DruidExpression.fromExpression("ltrim(\"spacey\",' h')"),
"ey there "
);
- testExpression(
- rexBuilder.makeCall(
- SqlStdOperatorTable.TRIM,
- rexBuilder.makeFlag(SqlTrimFunction.Flag.TRAILING),
- rexBuilder.makeLiteral(" e"),
- inputRef("spacey")
+ testHelper.testExpression(
+ SqlStdOperatorTable.TRIM,
+ ImmutableList.of(
+ testHelper.makeFlag(SqlTrimFunction.Flag.TRAILING),
+ testHelper.makeLiteral(" e"),
+ testHelper.makeInputRef("spacey")
),
DruidExpression.fromExpression("rtrim(\"spacey\",' e')"),
" hey ther"
@@ -609,23 +632,23 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testPad()
{
- testExpression(
- rexBuilder.makeCall(
- new LPadOperatorConversion().calciteOperator(),
- inputRef("s"),
- rexBuilder.makeLiteral(5, typeFactory.createSqlType(SqlTypeName.INTEGER), true),
- rexBuilder.makeLiteral("x")
+ testHelper.testExpression(
+ new LPadOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(5),
+ testHelper.makeLiteral("x")
),
DruidExpression.fromExpression("lpad(\"s\",5,'x')"),
"xxfoo"
);
- testExpression(
- rexBuilder.makeCall(
- new RPadOperatorConversion().calciteOperator(),
- inputRef("s"),
- rexBuilder.makeLiteral(5, typeFactory.createSqlType(SqlTypeName.INTEGER), true),
- rexBuilder.makeLiteral("x")
+ testHelper.testExpression(
+ new RPadOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(5),
+ testHelper.makeLiteral("x")
),
DruidExpression.fromExpression("rpad(\"s\",5,'x')"),
"fooxx"
@@ -636,23 +659,23 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testTimeFloor()
{
- testExpression(
- rexBuilder.makeCall(
- new TimeFloorOperatorConversion().calciteOperator(),
- timestampLiteral(DateTimes.of("2000-02-03T04:05:06Z")),
- rexBuilder.makeLiteral("PT1H")
+ testHelper.testExpression(
+ new TimeFloorOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeLiteral(DateTimes.of("2000-02-03T04:05:06Z")),
+ testHelper.makeLiteral("PT1H")
),
DruidExpression.fromExpression("timestamp_floor(949550706000,'PT1H',null,'UTC')"),
DateTimes.of("2000-02-03T04:00:00").getMillis()
);
- testExpression(
- rexBuilder.makeCall(
- new TimeFloorOperatorConversion().calciteOperator(),
- inputRef("t"),
- rexBuilder.makeLiteral("P1D"),
- rexBuilder.makeNullLiteral(typeFactory.createSqlType(SqlTypeName.TIMESTAMP)),
- rexBuilder.makeLiteral("America/Los_Angeles")
+ testHelper.testExpression(
+ new TimeFloorOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("t"),
+ testHelper.makeLiteral("P1D"),
+ testHelper.makeNullLiteral(SqlTypeName.TIMESTAMP),
+ testHelper.makeLiteral("America/Los_Angeles")
),
DruidExpression.fromExpression("timestamp_floor(\"t\",'P1D',null,'America/Los_Angeles')"),
DateTimes.of("2000-02-02T08:00:00").getMillis()
@@ -664,11 +687,11 @@ public class ExpressionsTest extends CalciteTestBase
{
// FLOOR(__time TO unit)
- testExpression(
- rexBuilder.makeCall(
- SqlStdOperatorTable.FLOOR,
- inputRef("t"),
- rexBuilder.makeFlag(TimeUnitRange.YEAR)
+ testHelper.testExpression(
+ SqlStdOperatorTable.FLOOR,
+ ImmutableList.of(
+ testHelper.makeInputRef("t"),
+ testHelper.makeFlag(TimeUnitRange.YEAR)
),
DruidExpression.fromExpression("timestamp_floor(\"t\",'P1Y',null,'UTC')"),
DateTimes.of("2000").getMillis()
@@ -678,23 +701,23 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testTimeCeil()
{
- testExpression(
- rexBuilder.makeCall(
- new TimeCeilOperatorConversion().calciteOperator(),
- timestampLiteral(DateTimes.of("2000-02-03T04:05:06Z")),
- rexBuilder.makeLiteral("PT1H")
+ testHelper.testExpression(
+ new TimeCeilOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeLiteral(DateTimes.of("2000-02-03T04:05:06Z")),
+ testHelper.makeLiteral("PT1H")
),
DruidExpression.fromExpression("timestamp_ceil(949550706000,'PT1H',null,'UTC')"),
DateTimes.of("2000-02-03T05:00:00").getMillis()
);
- testExpression(
- rexBuilder.makeCall(
- new TimeCeilOperatorConversion().calciteOperator(),
- inputRef("t"),
- rexBuilder.makeLiteral("P1D"),
- rexBuilder.makeNullLiteral(typeFactory.createSqlType(SqlTypeName.TIMESTAMP)),
- rexBuilder.makeLiteral("America/Los_Angeles")
+ testHelper.testExpression(
+ new TimeCeilOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("t"),
+ testHelper.makeLiteral("P1D"),
+ testHelper.makeNullLiteral(SqlTypeName.TIMESTAMP),
+ testHelper.makeLiteral("America/Los_Angeles")
),
DruidExpression.fromExpression("timestamp_ceil(\"t\",'P1D',null,'America/Los_Angeles')"),
DateTimes.of("2000-02-03T08:00:00").getMillis()
@@ -706,11 +729,11 @@ public class ExpressionsTest extends CalciteTestBase
{
// CEIL(__time TO unit)
- testExpression(
- rexBuilder.makeCall(
- SqlStdOperatorTable.CEIL,
- inputRef("t"),
- rexBuilder.makeFlag(TimeUnitRange.YEAR)
+ testHelper.testExpression(
+ SqlStdOperatorTable.CEIL,
+ ImmutableList.of(
+ testHelper.makeInputRef("t"),
+ testHelper.makeFlag(TimeUnitRange.YEAR)
),
DruidExpression.fromExpression("timestamp_ceil(\"t\",'P1Y',null,'UTC')"),
DateTimes.of("2001").getMillis()
@@ -720,24 +743,24 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testTimeShift()
{
- testExpression(
- rexBuilder.makeCall(
- new TimeShiftOperatorConversion().calciteOperator(),
- inputRef("t"),
- rexBuilder.makeLiteral("PT2H"),
- rexBuilder.makeLiteral(-3, typeFactory.createSqlType(SqlTypeName.INTEGER), true)
+ testHelper.testExpression(
+ new TimeShiftOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("t"),
+ testHelper.makeLiteral("PT2H"),
+ testHelper.makeLiteral(-3)
),
DruidExpression.fromExpression("timestamp_shift(\"t\",'PT2H',-3,'UTC')"),
DateTimes.of("2000-02-02T22:05:06").getMillis()
);
- testExpression(
- rexBuilder.makeCall(
- new TimeShiftOperatorConversion().calciteOperator(),
- inputRef("t"),
- rexBuilder.makeLiteral("PT2H"),
- rexBuilder.makeLiteral(-3, typeFactory.createSqlType(SqlTypeName.INTEGER), true),
- rexBuilder.makeLiteral("America/Los_Angeles")
+ testHelper.testExpression(
+ new TimeShiftOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("t"),
+ testHelper.makeLiteral("PT2H"),
+ testHelper.makeLiteral(-3),
+ testHelper.makeLiteral("America/Los_Angeles")
),
DruidExpression.fromExpression("timestamp_shift(\"t\",'PT2H',-3,'America/Los_Angeles')"),
DateTimes.of("2000-02-02T22:05:06").getMillis()
@@ -747,22 +770,22 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testTimeExtract()
{
- testExpression(
- rexBuilder.makeCall(
- new TimeExtractOperatorConversion().calciteOperator(),
- inputRef("t"),
- rexBuilder.makeLiteral("QUARTER")
+ testHelper.testExpression(
+ new TimeExtractOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("t"),
+ testHelper.makeLiteral("QUARTER")
),
DruidExpression.fromExpression("timestamp_extract(\"t\",'QUARTER','UTC')"),
1L
);
- testExpression(
- rexBuilder.makeCall(
- new TimeExtractOperatorConversion().calciteOperator(),
- inputRef("t"),
- rexBuilder.makeLiteral("DAY"),
- rexBuilder.makeLiteral("America/Los_Angeles")
+ testHelper.testExpression(
+ new TimeExtractOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("t"),
+ testHelper.makeLiteral("DAY"),
+ testHelper.makeLiteral("America/Los_Angeles")
),
DruidExpression.fromExpression("timestamp_extract(\"t\",'DAY','America/Los_Angeles')"),
2L
@@ -774,11 +797,11 @@ public class ExpressionsTest extends CalciteTestBase
{
final Period period = new Period("P1DT1H1M");
- testExpression(
- rexBuilder.makeCall(
- SqlStdOperatorTable.DATETIME_PLUS,
- inputRef("t"),
- rexBuilder.makeIntervalLiteral(
+ testHelper.testExpression(
+ SqlStdOperatorTable.DATETIME_PLUS,
+ ImmutableList.of(
+ testHelper.makeInputRef("t"),
+ testHelper.makeLiteral(
new BigDecimal(period.toStandardDuration().getMillis()), // DAY-TIME literals value is millis
new SqlIntervalQualifier(TimeUnit.DAY, TimeUnit.MINUTE, SqlParserPos.ZERO)
)
@@ -796,11 +819,11 @@ public class ExpressionsTest extends CalciteTestBase
{
final Period period = new Period("P1Y1M");
- testExpression(
- rexBuilder.makeCall(
- SqlStdOperatorTable.DATETIME_PLUS,
- inputRef("t"),
- rexBuilder.makeIntervalLiteral(
+ testHelper.testExpression(
+ SqlStdOperatorTable.DATETIME_PLUS,
+ ImmutableList.of(
+ testHelper.makeInputRef("t"),
+ testHelper.makeLiteral(
new BigDecimal(13), // YEAR-MONTH literals value is months
new SqlIntervalQualifier(TimeUnit.YEAR, TimeUnit.MONTH, SqlParserPos.ZERO)
)
@@ -818,16 +841,14 @@ public class ExpressionsTest extends CalciteTestBase
{
final Period period = new Period("P1DT1H1M");
- testExpression(
- rexBuilder.makeCall(
- typeFactory.createSqlType(SqlTypeName.TIMESTAMP),
- SqlStdOperatorTable.MINUS_DATE,
- ImmutableList.of(
- inputRef("t"),
- rexBuilder.makeIntervalLiteral(
- new BigDecimal(period.toStandardDuration().getMillis()), // DAY-TIME literals value is millis
- new SqlIntervalQualifier(TimeUnit.DAY, TimeUnit.MINUTE, SqlParserPos.ZERO)
- )
+ testHelper.testExpression(
+ SqlTypeName.TIMESTAMP,
+ SqlStdOperatorTable.MINUS_DATE,
+ ImmutableList.of(
+ testHelper.makeInputRef("t"),
+ testHelper.makeLiteral(
+ new BigDecimal(period.toStandardDuration().getMillis()), // DAY-TIME literals value is millis
+ new SqlIntervalQualifier(TimeUnit.DAY, TimeUnit.MINUTE, SqlParserPos.ZERO)
)
),
DruidExpression.of(
@@ -843,16 +864,14 @@ public class ExpressionsTest extends CalciteTestBase
{
final Period period = new Period("P1Y1M");
- testExpression(
- rexBuilder.makeCall(
- typeFactory.createSqlType(SqlTypeName.TIMESTAMP),
- SqlStdOperatorTable.MINUS_DATE,
- ImmutableList.of(
- inputRef("t"),
- rexBuilder.makeIntervalLiteral(
- new BigDecimal(13), // YEAR-MONTH literals value is months
- new SqlIntervalQualifier(TimeUnit.YEAR, TimeUnit.MONTH, SqlParserPos.ZERO)
- )
+ testHelper.testExpression(
+ SqlTypeName.TIMESTAMP,
+ SqlStdOperatorTable.MINUS_DATE,
+ ImmutableList.of(
+ testHelper.makeInputRef("t"),
+ testHelper.makeLiteral(
+ new BigDecimal(13), // YEAR-MONTH literals value is months
+ new SqlIntervalQualifier(TimeUnit.YEAR, TimeUnit.MONTH, SqlParserPos.ZERO)
)
),
DruidExpression.of(
@@ -866,22 +885,22 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testTimeParse()
{
- testExpression(
- rexBuilder.makeCall(
- new TimeParseOperatorConversion().calciteOperator(),
- inputRef("tstr"),
- rexBuilder.makeLiteral("yyyy-MM-dd HH:mm:ss")
+ testHelper.testExpression(
+ new TimeParseOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("tstr"),
+ testHelper.makeLiteral("yyyy-MM-dd HH:mm:ss")
),
DruidExpression.fromExpression("timestamp_parse(\"tstr\",'yyyy-MM-dd HH:mm:ss','UTC')"),
DateTimes.of("2000-02-03T04:05:06").getMillis()
);
- testExpression(
- rexBuilder.makeCall(
- new TimeParseOperatorConversion().calciteOperator(),
- inputRef("tstr"),
- rexBuilder.makeLiteral("yyyy-MM-dd HH:mm:ss"),
- rexBuilder.makeLiteral("America/Los_Angeles")
+ testHelper.testExpression(
+ new TimeParseOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("tstr"),
+ testHelper.makeLiteral("yyyy-MM-dd HH:mm:ss"),
+ testHelper.makeLiteral("America/Los_Angeles")
),
DruidExpression.fromExpression("timestamp_parse(\"tstr\",'yyyy-MM-dd HH:mm:ss','America/Los_Angeles')"),
DateTimes.of("2000-02-03T04:05:06-08:00").getMillis()
@@ -891,22 +910,22 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testTimeFormat()
{
- testExpression(
- rexBuilder.makeCall(
- new TimeFormatOperatorConversion().calciteOperator(),
- inputRef("t"),
- rexBuilder.makeLiteral("yyyy-MM-dd HH:mm:ss")
+ testHelper.testExpression(
+ new TimeFormatOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("t"),
+ testHelper.makeLiteral("yyyy-MM-dd HH:mm:ss")
),
DruidExpression.fromExpression("timestamp_format(\"t\",'yyyy-MM-dd HH:mm:ss','UTC')"),
"2000-02-03 04:05:06"
);
- testExpression(
- rexBuilder.makeCall(
- new TimeFormatOperatorConversion().calciteOperator(),
- inputRef("t"),
- rexBuilder.makeLiteral("yyyy-MM-dd HH:mm:ss"),
- rexBuilder.makeLiteral("America/Los_Angeles")
+ testHelper.testExpression(
+ new TimeFormatOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("t"),
+ testHelper.makeLiteral("yyyy-MM-dd HH:mm:ss"),
+ testHelper.makeLiteral("America/Los_Angeles")
),
DruidExpression.fromExpression("timestamp_format(\"t\",'yyyy-MM-dd HH:mm:ss','America/Los_Angeles')"),
"2000-02-02 20:05:06"
@@ -916,21 +935,21 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testExtract()
{
- testExpression(
- rexBuilder.makeCall(
- SqlStdOperatorTable.EXTRACT,
- rexBuilder.makeFlag(TimeUnitRange.QUARTER),
- inputRef("t")
+ testHelper.testExpression(
+ SqlStdOperatorTable.EXTRACT,
+ ImmutableList.of(
+ testHelper.makeFlag(TimeUnitRange.QUARTER),
+ testHelper.makeInputRef("t")
),
DruidExpression.fromExpression("timestamp_extract(\"t\",'QUARTER','UTC')"),
1L
);
- testExpression(
- rexBuilder.makeCall(
- SqlStdOperatorTable.EXTRACT,
- rexBuilder.makeFlag(TimeUnitRange.DAY),
- inputRef("t")
+ testHelper.testExpression(
+ SqlStdOperatorTable.EXTRACT,
+ ImmutableList.of(
+ testHelper.makeFlag(TimeUnitRange.DAY),
+ testHelper.makeInputRef("t")
),
DruidExpression.fromExpression("timestamp_extract(\"t\",'DAY','UTC')"),
3L
@@ -940,10 +959,10 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testCastAsTimestamp()
{
- testExpression(
- rexBuilder.makeAbstractCast(
- typeFactory.createSqlType(SqlTypeName.TIMESTAMP),
- inputRef("t")
+ testHelper.testExpression(
+ testHelper.makeAbstractCast(
+ testHelper.createSqlType(SqlTypeName.TIMESTAMP),
+ testHelper.makeInputRef("t")
),
DruidExpression.of(
SimpleExtraction.of("t", null),
@@ -952,10 +971,10 @@ public class ExpressionsTest extends CalciteTestBase
DateTimes.of("2000-02-03T04:05:06Z").getMillis()
);
- testExpression(
- rexBuilder.makeAbstractCast(
- typeFactory.createSqlType(SqlTypeName.TIMESTAMP),
- inputRef("tstr")
+ testHelper.testExpression(
+ testHelper.makeAbstractCast(
+ testHelper.createSqlType(SqlTypeName.TIMESTAMP),
+ testHelper.makeInputRef("tstr")
),
DruidExpression.of(
null,
@@ -968,12 +987,12 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testCastFromTimestamp()
{
- testExpression(
- rexBuilder.makeAbstractCast(
- typeFactory.createSqlType(SqlTypeName.VARCHAR),
- rexBuilder.makeAbstractCast(
- typeFactory.createSqlType(SqlTypeName.TIMESTAMP),
- inputRef("t")
+ testHelper.testExpression(
+ testHelper.makeAbstractCast(
+ testHelper.createSqlType(SqlTypeName.VARCHAR),
+ testHelper.makeAbstractCast(
+ testHelper.createSqlType(SqlTypeName.TIMESTAMP),
+ testHelper.makeInputRef("t")
)
),
DruidExpression.fromExpression(
@@ -982,12 +1001,12 @@ public class ExpressionsTest extends CalciteTestBase
"2000-02-03 04:05:06"
);
- testExpression(
- rexBuilder.makeAbstractCast(
- typeFactory.createSqlType(SqlTypeName.BIGINT),
- rexBuilder.makeAbstractCast(
- typeFactory.createSqlType(SqlTypeName.TIMESTAMP),
- inputRef("t")
+ testHelper.testExpression(
+ testHelper.makeAbstractCast(
+ testHelper.createSqlType(SqlTypeName.BIGINT),
+ testHelper.makeAbstractCast(
+ testHelper.createSqlType(SqlTypeName.TIMESTAMP),
+ testHelper.makeInputRef("t")
)
),
DruidExpression.of(
@@ -1001,19 +1020,19 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testCastAsDate()
{
- testExpression(
- rexBuilder.makeAbstractCast(
- typeFactory.createSqlType(SqlTypeName.DATE),
- inputRef("t")
+ testHelper.testExpression(
+ testHelper.makeAbstractCast(
+ testHelper.createSqlType(SqlTypeName.DATE),
+ testHelper.makeInputRef("t")
),
DruidExpression.fromExpression("timestamp_floor(\"t\",'P1D',null,'UTC')"),
DateTimes.of("2000-02-03").getMillis()
);
- testExpression(
- rexBuilder.makeAbstractCast(
- typeFactory.createSqlType(SqlTypeName.DATE),
- inputRef("dstr")
+ testHelper.testExpression(
+ testHelper.makeAbstractCast(
+ testHelper.createSqlType(SqlTypeName.DATE),
+ testHelper.makeInputRef("dstr")
),
DruidExpression.fromExpression(
"timestamp_floor(timestamp_parse(\"dstr\",null,'UTC'),'P1D',null,'UTC')"
@@ -1025,12 +1044,12 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testCastFromDate()
{
- testExpression(
- rexBuilder.makeAbstractCast(
- typeFactory.createSqlType(SqlTypeName.VARCHAR),
- rexBuilder.makeAbstractCast(
- typeFactory.createSqlType(SqlTypeName.DATE),
- inputRef("t")
+ testHelper.testExpression(
+ testHelper.makeAbstractCast(
+ testHelper.createSqlType(SqlTypeName.VARCHAR),
+ testHelper.makeAbstractCast(
+ testHelper.createSqlType(SqlTypeName.DATE),
+ testHelper.makeInputRef("t")
)
),
DruidExpression.fromExpression(
@@ -1039,12 +1058,12 @@ public class ExpressionsTest extends CalciteTestBase
"2000-02-03"
);
- testExpression(
- rexBuilder.makeAbstractCast(
- typeFactory.createSqlType(SqlTypeName.BIGINT),
- rexBuilder.makeAbstractCast(
- typeFactory.createSqlType(SqlTypeName.DATE),
- inputRef("t")
+ testHelper.testExpression(
+ testHelper.makeAbstractCast(
+ testHelper.createSqlType(SqlTypeName.BIGINT),
+ testHelper.makeAbstractCast(
+ testHelper.createSqlType(SqlTypeName.DATE),
+ testHelper.makeInputRef("t")
)
),
DruidExpression.fromExpression("timestamp_floor(\"t\",'P1D',null,'UTC')"),
@@ -1055,38 +1074,30 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testReverse()
{
- testExpression(
- rexBuilder.makeCall(
- new ReverseOperatorConversion().calciteOperator(),
- inputRef("s")
- ),
+ testHelper.testExpression(
+ new ReverseOperatorConversion().calciteOperator(),
+ testHelper.makeInputRef("s"),
DruidExpression.fromExpression("reverse(\"s\")"),
"oof"
);
- testExpression(
- rexBuilder.makeCall(
- new ReverseOperatorConversion().calciteOperator(),
- inputRef("spacey")
- ),
+ testHelper.testExpression(
+ new ReverseOperatorConversion().calciteOperator(),
+ testHelper.makeInputRef("spacey"),
DruidExpression.fromExpression("reverse(\"spacey\")"),
" ereht yeh "
);
- testExpression(
- rexBuilder.makeCall(
- new ReverseOperatorConversion().calciteOperator(),
- inputRef("tstr")
- ),
+ testHelper.testExpression(
+ new ReverseOperatorConversion().calciteOperator(),
+ testHelper.makeInputRef("tstr"),
DruidExpression.fromExpression("reverse(\"tstr\")"),
"60:50:40 30-20-0002"
);
- testExpression(
- rexBuilder.makeCall(
- new ReverseOperatorConversion().calciteOperator(),
- inputRef("dstr")
- ),
+ testHelper.testExpression(
+ new ReverseOperatorConversion().calciteOperator(),
+ testHelper.makeInputRef("dstr"),
DruidExpression.fromExpression("reverse(\"dstr\")"),
"30-20-0002"
);
@@ -1095,14 +1106,11 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testAbnormalReverseWithWrongType()
{
- expectedException.expect(IAE.class);
- expectedException.expectMessage("Function[reverse] needs a string argument");
+ expectException(IAE.class, "Function[reverse] needs a string argument");
- testExpression(
- rexBuilder.makeCall(
- new ReverseOperatorConversion().calciteOperator(),
- inputRef("a")
- ),
+ testHelper.testExpression(
+ new ReverseOperatorConversion().calciteOperator(),
+ testHelper.makeInputRef("a"),
DruidExpression.fromExpression("reverse(\"a\")"),
null
);
@@ -1111,51 +1119,51 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testRight()
{
- testExpression(
- rexBuilder.makeCall(
- new RightOperatorConversion().calciteOperator(),
- inputRef("s"),
- integerLiteral(1)
+ testHelper.testExpression(
+ new RightOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(1)
),
DruidExpression.fromExpression("right(\"s\",1)"),
"o"
);
- testExpression(
- rexBuilder.makeCall(
- new RightOperatorConversion().calciteOperator(),
- inputRef("s"),
- integerLiteral(2)
+ testHelper.testExpression(
+ new RightOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(2)
),
DruidExpression.fromExpression("right(\"s\",2)"),
"oo"
);
- testExpression(
- rexBuilder.makeCall(
- new RightOperatorConversion().calciteOperator(),
- inputRef("s"),
- integerLiteral(3)
+ testHelper.testExpression(
+ new RightOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(3)
),
DruidExpression.fromExpression("right(\"s\",3)"),
"foo"
);
- testExpression(
- rexBuilder.makeCall(
- new RightOperatorConversion().calciteOperator(),
- inputRef("s"),
- integerLiteral(4)
+ testHelper.testExpression(
+ new RightOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(4)
),
DruidExpression.fromExpression("right(\"s\",4)"),
"foo"
);
- testExpression(
- rexBuilder.makeCall(
- new RightOperatorConversion().calciteOperator(),
- inputRef("tstr"),
- integerLiteral(5)
+ testHelper.testExpression(
+ new RightOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("tstr"),
+ testHelper.makeLiteral(5)
),
DruidExpression.fromExpression("right(\"tstr\",5)"),
"05:06"
@@ -1165,14 +1173,13 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testAbnormalRightWithNegativeNumber()
{
- expectedException.expect(IAE.class);
- expectedException.expectMessage("Function[right] needs a postive integer as second argument");
+ expectException(IAE.class, "Function[right] needs a postive integer as second argument");
- testExpression(
- rexBuilder.makeCall(
- new RightOperatorConversion().calciteOperator(),
- inputRef("s"),
- integerLiteral(-1)
+ testHelper.testExpression(
+ new RightOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(-1)
),
DruidExpression.fromExpression("right(\"s\",-1)"),
null
@@ -1182,15 +1189,13 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testAbnormalRightWithWrongType()
{
- expectedException.expect(IAE.class);
- expectedException.expectMessage("Function[right] needs a string as first argument "
- + "and an integer as second argument");
+ expectException(IAE.class, "Function[right] needs a string as first argument and an integer as second argument");
- testExpression(
- rexBuilder.makeCall(
- new RightOperatorConversion().calciteOperator(),
- inputRef("s"),
- inputRef("s")
+ testHelper.testExpression(
+ new RightOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeInputRef("s")
),
DruidExpression.fromExpression("right(\"s\",\"s\")"),
null
@@ -1200,51 +1205,51 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testLeft()
{
- testExpression(
- rexBuilder.makeCall(
- new LeftOperatorConversion().calciteOperator(),
- inputRef("s"),
- integerLiteral(1)
+ testHelper.testExpression(
+ new LeftOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(1)
),
DruidExpression.fromExpression("left(\"s\",1)"),
"f"
);
- testExpression(
- rexBuilder.makeCall(
- new LeftOperatorConversion().calciteOperator(),
- inputRef("s"),
- integerLiteral(2)
+ testHelper.testExpression(
+ new LeftOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(2)
),
DruidExpression.fromExpression("left(\"s\",2)"),
"fo"
);
- testExpression(
- rexBuilder.makeCall(
- new LeftOperatorConversion().calciteOperator(),
- inputRef("s"),
- integerLiteral(3)
+ testHelper.testExpression(
+ new LeftOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(3)
),
DruidExpression.fromExpression("left(\"s\",3)"),
"foo"
);
- testExpression(
- rexBuilder.makeCall(
- new LeftOperatorConversion().calciteOperator(),
- inputRef("s"),
- integerLiteral(4)
+ testHelper.testExpression(
+ new LeftOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(4)
),
DruidExpression.fromExpression("left(\"s\",4)"),
"foo"
);
- testExpression(
- rexBuilder.makeCall(
- new LeftOperatorConversion().calciteOperator(),
- inputRef("tstr"),
- integerLiteral(10)
+ testHelper.testExpression(
+ new LeftOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("tstr"),
+ testHelper.makeLiteral(10)
),
DruidExpression.fromExpression("left(\"tstr\",10)"),
"2000-02-03"
@@ -1254,14 +1259,13 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testAbnormalLeftWithNegativeNumber()
{
- expectedException.expect(IAE.class);
- expectedException.expectMessage("Function[left] needs a postive integer as second argument");
+ expectException(IAE.class, "Function[left] needs a postive integer as second argument");
- testExpression(
- rexBuilder.makeCall(
- new LeftOperatorConversion().calciteOperator(),
- inputRef("s"),
- integerLiteral(-1)
+ testHelper.testExpression(
+ new LeftOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(-1)
),
DruidExpression.fromExpression("left(\"s\",-1)"),
null
@@ -1271,15 +1275,13 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testAbnormalLeftWithWrongType()
{
- expectedException.expect(IAE.class);
- expectedException.expectMessage("Function[left] needs a string as first argument "
- + "and an integer as second argument");
+ expectException(IAE.class, "Function[left] needs a string as first argument and an integer as second argument");
- testExpression(
- rexBuilder.makeCall(
- new LeftOperatorConversion().calciteOperator(),
- inputRef("s"),
- inputRef("s")
+ testHelper.testExpression(
+ new LeftOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeInputRef("s")
),
DruidExpression.fromExpression("left(\"s\",\"s\")"),
null
@@ -1289,31 +1291,31 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testRepeat()
{
- testExpression(
- rexBuilder.makeCall(
- new RepeatOperatorConversion().calciteOperator(),
- inputRef("s"),
- integerLiteral(1)
+ testHelper.testExpression(
+ new RepeatOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(1)
),
DruidExpression.fromExpression("repeat(\"s\",1)"),
"foo"
);
- testExpression(
- rexBuilder.makeCall(
- new RepeatOperatorConversion().calciteOperator(),
- inputRef("s"),
- integerLiteral(3)
+ testHelper.testExpression(
+ new RepeatOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(3)
),
DruidExpression.fromExpression("repeat(\"s\",3)"),
"foofoofoo"
);
- testExpression(
- rexBuilder.makeCall(
- new RepeatOperatorConversion().calciteOperator(),
- inputRef("s"),
- integerLiteral(-1)
+ testHelper.testExpression(
+ new RepeatOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeLiteral(-1)
),
DruidExpression.fromExpression("repeat(\"s\",-1)"),
null
@@ -1323,48 +1325,16 @@ public class ExpressionsTest extends CalciteTestBase
@Test
public void testAbnormalRepeatWithWrongType()
{
- expectedException.expect(IAE.class);
- expectedException.expectMessage("Function[repeat] needs a string as first argument "
- + "and an integer as second argument");
+ expectException(IAE.class, "Function[repeat] needs a string as first argument and an integer as second argument");
- testExpression(
- rexBuilder.makeCall(
- new RepeatOperatorConversion().calciteOperator(),
- inputRef("s"),
- inputRef("s")
+ testHelper.testExpression(
+ new RepeatOperatorConversion().calciteOperator(),
+ ImmutableList.of(
+ testHelper.makeInputRef("s"),
+ testHelper.makeInputRef("s")
),
DruidExpression.fromExpression("repeat(\"s\",\"s\")"),
null
);
}
-
- private RexNode inputRef(final String columnName)
- {
- final int columnNumber = rowSignature.getRowOrder().indexOf(columnName);
- return rexBuilder.makeInputRef(relDataType.getFieldList().get(columnNumber).getType(), columnNumber);
- }
-
- private RexNode timestampLiteral(final DateTime timestamp)
- {
- return rexBuilder.makeTimestampLiteral(Calcites.jodaToCalciteTimestampString(timestamp, DateTimeZone.UTC), 0);
- }
-
- private RexNode integerLiteral(final int integer)
- {
- return rexBuilder.makeLiteral(new BigDecimal(integer), typeFactory.createSqlType(SqlTypeName.INTEGER), true);
- }
-
- private void testExpression(
- final RexNode rexNode,
- final DruidExpression expectedExpression,
- final Object expectedResult
- )
- {
- final DruidExpression expression = Expressions.toDruidExpression(plannerContext, rowSignature, rexNode);
- Assert.assertEquals("Expression for: " + rexNode, expectedExpression, expression);
-
- final ExprEval result = Parser.parse(expression.getExpression(), plannerContext.getExprMacroTable())
- .eval(Parser.withMap(bindings));
- Assert.assertEquals("Result for: " + rexNode, expectedResult, result.value());
- }
}
diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/expression/IPv4AddressMatchExpressionTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/expression/IPv4AddressMatchExpressionTest.java
new file mode 100644
index 00000000000..0d0aeb4edad
--- /dev/null
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/expression/IPv4AddressMatchExpressionTest.java
@@ -0,0 +1,269 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.sql.calcite.expression;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.calcite.rex.RexNode;
+import org.apache.druid.segment.column.ValueType;
+import org.apache.druid.sql.calcite.expression.builtin.IPv4AddressMatchOperatorConversion;
+import org.apache.druid.sql.calcite.table.RowSignature;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class IPv4AddressMatchExpressionTest extends ExpressionTestBase
+{
+ private static final String IPV4 = "192.168.0.1";
+ private static final long IPV4_LONG = 3232235521L;
+ private static final String IPV4_UINT = "3232235521";
+ private static final String IPV4_NETWORK = "192.168.0.0";
+ private static final String IPV4_BROADCAST = "192.168.255.255";
+ private static final String IPV6_COMPATIBLE = "::192.168.0.1";
+ private static final String IPV6_MAPPED = "::ffff:192.168.0.1";
+ private static final String SUBNET_192_168 = "192.168.0.0/16";
+ private static final String SUBNET_10 = "10.0.0.0/8";
+ private static final Object IGNORE_EXPECTED_RESULT = null;
+ private static final long MATCH = 1L;
+ private static final long NO_MATCH = 0L;
+
+ private static final String VAR = "s";
+ private static final RowSignature ROW_SIGNATURE = RowSignature.builder().add(VAR, ValueType.STRING).build();
+ private static final Map BINDINGS = ImmutableMap.of(VAR, "foo");
+
+ private IPv4AddressMatchOperatorConversion target;
+ private ExpressionTestHelper testHelper;
+
+ @Before
+ public void setUp()
+ {
+ target = new IPv4AddressMatchOperatorConversion();
+ testHelper = new ExpressionTestHelper(ROW_SIGNATURE, BINDINGS);
+ }
+
+ @Test
+ public void testTooFewArgs()
+ {
+ expectException(IllegalArgumentException.class, "must have 2 arguments");
+
+ testExpression(
+ Collections.emptyList(),
+ buildExpectedExpression(),
+ IGNORE_EXPECTED_RESULT
+ );
+ }
+
+ @Test
+ public void testTooManyArgs()
+ {
+ expectException(IllegalArgumentException.class, "must have 2 arguments");
+
+ String address = IPV4;
+ String subnet = SUBNET_192_168;
+ testExpression(
+ Arrays.asList(
+ testHelper.makeLiteral(address),
+ testHelper.makeLiteral(subnet),
+ testHelper.makeLiteral(address)
+ ),
+ buildExpectedExpression(address, subnet, address),
+ IGNORE_EXPECTED_RESULT
+ );
+ }
+
+ @Test
+ public void testSubnetArgNotLiteral()
+ {
+ expectException(IllegalArgumentException.class, "subnet arg must be a literal");
+
+ String address = IPV4;
+ String variableName = VAR;
+ testExpression(
+ Arrays.asList(
+ testHelper.makeLiteral(address),
+ testHelper.makeInputRef(variableName)
+ ),
+ buildExpectedExpression(address, testHelper.makeVariable(variableName)),
+ IGNORE_EXPECTED_RESULT
+ );
+ }
+
+ @Test
+ public void testSubnetArgInvalid()
+ {
+ expectException(IllegalArgumentException.class, "subnet arg has an invalid format");
+
+ String address = IPV4;
+ String invalidSubnet = "192.168.0.1/invalid";
+ testExpression(
+ Arrays.asList(
+ testHelper.makeLiteral(address),
+ testHelper.makeLiteral(invalidSubnet)
+ ),
+ buildExpectedExpression(address, invalidSubnet),
+ IGNORE_EXPECTED_RESULT
+ );
+ }
+
+ @Test
+ public void testNullArg()
+ {
+ String subnet = SUBNET_192_168;
+ testExpression(
+ Arrays.asList(
+ testHelper.getConstantNull(),
+ testHelper.makeLiteral(subnet)
+ ),
+ buildExpectedExpression(null, subnet),
+ NO_MATCH
+ );
+ }
+
+ @Test
+ public void testInvalidArgType()
+ {
+ String variableNameWithInvalidType = VAR;
+ String subnet = SUBNET_192_168;
+ testExpression(
+ Arrays.asList(
+ testHelper.makeInputRef(variableNameWithInvalidType),
+ testHelper.makeLiteral(subnet)
+ ),
+ buildExpectedExpression(testHelper.makeVariable(variableNameWithInvalidType), subnet),
+ NO_MATCH
+ );
+ }
+
+ @Test
+ public void testMatchingStringArgIPv4()
+ {
+ testExpression(IPV4, SUBNET_192_168, MATCH);
+ }
+
+ @Test
+ public void testNotMatchingStringArgIPv4()
+ {
+ testExpression(IPV4, SUBNET_10, NO_MATCH);
+ }
+
+ @Test
+ public void testMatchingStringArgIPv6Mapped()
+ {
+ testExpression(IPV6_MAPPED, SUBNET_192_168, NO_MATCH);
+ }
+
+ @Test
+ public void testNotMatchingStringArgIPv6Mapped()
+ {
+ testExpression(IPV6_MAPPED, SUBNET_10, NO_MATCH);
+ }
+
+ @Test
+ public void testMatchingStringArgIPv6Compatible()
+ {
+ testExpression(IPV6_COMPATIBLE, SUBNET_192_168, NO_MATCH);
+ }
+
+ @Test
+ public void testNotMatchingStringArgIPv6Compatible()
+ {
+ testExpression(IPV6_COMPATIBLE, SUBNET_10, NO_MATCH);
+ }
+
+ @Test
+ public void testNotIpAddress()
+ {
+ testExpression("druid.apache.org", SUBNET_192_168, NO_MATCH);
+ }
+
+ @Test
+ public void testMatchingLongArg()
+ {
+ testExpression(IPV4_LONG, SUBNET_192_168, MATCH);
+ }
+
+ @Test
+ public void testNotMatchingLongArg()
+ {
+ testExpression(IPV4_LONG, SUBNET_10, NO_MATCH);
+ }
+
+ @Test
+ public void testMatchingStringArgUnsignedInt()
+ {
+ testExpression(IPV4_UINT, SUBNET_192_168, NO_MATCH);
+ }
+
+ @Test
+ public void testNotMatchingStringArgUnsignedInt()
+ {
+ testExpression(IPV4_UINT, SUBNET_10, NO_MATCH);
+ }
+
+ @Test
+ public void testInclusive()
+ {
+ String subnet = SUBNET_192_168;
+ testExpression(IPV4_NETWORK, subnet, MATCH);
+ testExpression(IPV4, subnet, MATCH);
+ testExpression(IPV4_BROADCAST, subnet, MATCH);
+ }
+
+ private void testExpression(String address, String subnet, long match)
+ {
+ testExpression(
+ Arrays.asList(
+ testHelper.makeLiteral(address),
+ testHelper.makeLiteral(subnet)
+ ),
+ buildExpectedExpression(address, subnet),
+ match
+ );
+ }
+
+ private void testExpression(long address, String subnet, long match)
+ {
+ testExpression(
+ Arrays.asList(
+ testHelper.makeLiteral(address),
+ testHelper.makeLiteral(subnet)
+ ),
+ buildExpectedExpression(address, subnet),
+ match
+ );
+ }
+
+ private void testExpression(
+ List extends RexNode> exprs,
+ final DruidExpression expectedExpression,
+ final Object expectedResult
+ )
+ {
+ testHelper.testExpression(target.calciteOperator(), exprs, expectedExpression, expectedResult);
+ }
+
+ private DruidExpression buildExpectedExpression(Object... args)
+ {
+ return testHelper.buildExpectedExpression(target.getDruidFunctionName(), args);
+ }
+}
diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/expression/IPv4AddressParseExpressionTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/expression/IPv4AddressParseExpressionTest.java
new file mode 100644
index 00000000000..c3c7169c213
--- /dev/null
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/expression/IPv4AddressParseExpressionTest.java
@@ -0,0 +1,235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.sql.calcite.expression;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.calcite.rex.RexNode;
+import org.apache.druid.common.config.NullHandling;
+import org.apache.druid.segment.column.ValueType;
+import org.apache.druid.sql.calcite.expression.builtin.IPv4AddressParseOperatorConversion;
+import org.apache.druid.sql.calcite.table.RowSignature;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class IPv4AddressParseExpressionTest extends ExpressionTestBase
+{
+ private static final String VALID = "192.168.0.1";
+ private static final long EXPECTED = 3232235521L;
+ private static final Object IGNORE_EXPECTED_RESULT = null;
+ private static final Long NULL = NullHandling.replaceWithDefault() ? NullHandling.ZERO_LONG : null;
+
+ private static final String VAR = "f";
+ private static final RowSignature ROW_SIGNATURE = RowSignature.builder().add(VAR, ValueType.FLOAT).build();
+ private static final Map BINDINGS = ImmutableMap.of(VAR, 3.14);
+
+ private IPv4AddressParseOperatorConversion target;
+ private ExpressionTestHelper testHelper;
+
+ @Before
+ public void setUp()
+ {
+ target = new IPv4AddressParseOperatorConversion();
+ testHelper = new ExpressionTestHelper(ROW_SIGNATURE, BINDINGS);
+ }
+
+ @Test
+ public void testTooFewArgs()
+ {
+ expectException(IllegalArgumentException.class, "must have 1 argument");
+
+ testExpression(
+ Collections.emptyList(),
+ buildExpectedExpression(),
+ IGNORE_EXPECTED_RESULT
+ );
+ }
+
+ @Test
+ public void testTooManyArgs()
+ {
+ expectException(IllegalArgumentException.class, "must have 1 argument");
+
+ testExpression(
+ Arrays.asList(
+ testHelper.getConstantNull(),
+ testHelper.getConstantNull()
+ ),
+ buildExpectedExpression(null, null),
+ IGNORE_EXPECTED_RESULT
+ );
+ }
+
+ @Test
+ public void testNullArg()
+ {
+ testExpression(
+ testHelper.getConstantNull(),
+ buildExpectedExpression((String) null),
+ NULL
+ );
+ }
+
+ @Test
+ public void testInvalidArgType()
+ {
+ String variableNameWithInvalidType = VAR;
+ testExpression(
+ testHelper.makeInputRef(variableNameWithInvalidType),
+ buildExpectedExpression(testHelper.makeVariable(variableNameWithInvalidType)),
+ NULL
+ );
+ }
+
+ @Test
+ public void testInvalidStringArgNotIPAddress()
+ {
+ String notIpAddress = "druid.apache.org";
+ testExpression(
+ testHelper.makeLiteral(notIpAddress),
+ buildExpectedExpression(notIpAddress),
+ NULL
+ );
+ }
+
+ @Test
+ public void testInvalidStringArgIPv6Compatible()
+ {
+ String ipv6Compatible = "::192.168.0.1";
+ testExpression(
+ testHelper.makeLiteral(ipv6Compatible),
+ buildExpectedExpression(ipv6Compatible),
+ NULL
+ );
+ }
+
+ @Test
+ public void testValidStringArgIPv6Mapped()
+ {
+ String ipv6Mapped = "::ffff:192.168.0.1";
+ testExpression(
+ testHelper.makeLiteral(ipv6Mapped),
+ buildExpectedExpression(ipv6Mapped),
+ NULL
+ );
+ }
+
+ @Test
+ public void testValidStringArgIPv4()
+ {
+ testExpression(
+ testHelper.makeLiteral(VALID),
+ buildExpectedExpression(VALID),
+ EXPECTED
+ );
+ }
+
+ @Test
+ public void testValidStringArgUnsignedInt()
+ {
+ String unsignedInt = "3232235521";
+ testExpression(
+ testHelper.makeLiteral(unsignedInt),
+ buildExpectedExpression(unsignedInt),
+ NULL
+ );
+ }
+
+ @Test
+ public void testInvalidIntegerArgTooLow()
+ {
+ long tooLow = -1L;
+ testExpression(
+ testHelper.makeLiteral(tooLow),
+ buildExpectedExpression(tooLow),
+ NULL
+ );
+ }
+
+ @Test
+ public void testValidIntegerArgLowest()
+ {
+ long lowest = 0L;
+ testExpression(
+ testHelper.makeLiteral(lowest),
+ buildExpectedExpression(lowest),
+ lowest
+ );
+ }
+
+ @Test
+ public void testValidIntegerArg()
+ {
+ testExpression(
+ testHelper.makeLiteral(EXPECTED),
+ buildExpectedExpression(EXPECTED),
+ EXPECTED
+ );
+ }
+
+ @Test
+ public void testValidIntegerArgHighest()
+ {
+ long highest = 0xff_ff_ff_ffL;
+ testExpression(
+ testHelper.makeLiteral(highest),
+ buildExpectedExpression(highest),
+ highest
+ );
+ }
+
+ @Test
+ public void testInvalidIntegerArgTooHigh()
+ {
+ long tooHigh = 0x1_00_00_00_00L;
+ testExpression(
+ testHelper.makeLiteral(tooHigh),
+ buildExpectedExpression(tooHigh),
+ NULL
+ );
+ }
+
+ private void testExpression(
+ RexNode expr,
+ final DruidExpression expectedExpression,
+ final Object expectedResult
+ )
+ {
+ testExpression(Collections.singletonList(expr), expectedExpression, expectedResult);
+ }
+
+ private void testExpression(
+ List extends RexNode> exprs,
+ final DruidExpression expectedExpression,
+ final Object expectedResult
+ )
+ {
+ testHelper.testExpression(target.calciteOperator(), exprs, expectedExpression, expectedResult);
+ }
+
+ private DruidExpression buildExpectedExpression(Object... args)
+ {
+ return testHelper.buildExpectedExpression(target.getDruidFunctionName(), args);
+ }
+}
diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/expression/IPv4AddressStringifyExpressionTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/expression/IPv4AddressStringifyExpressionTest.java
new file mode 100644
index 00000000000..e34a55915a1
--- /dev/null
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/expression/IPv4AddressStringifyExpressionTest.java
@@ -0,0 +1,234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.druid.sql.calcite.expression;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.calcite.rex.RexNode;
+import org.apache.druid.segment.column.ValueType;
+import org.apache.druid.sql.calcite.expression.builtin.IPv4AddressStringifyOperatorConversion;
+import org.apache.druid.sql.calcite.table.RowSignature;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class IPv4AddressStringifyExpressionTest extends ExpressionTestBase
+{
+ private static final long VALID = 3232235521L;
+ private static final String EXPECTED = "192.168.0.1";
+ private static final Object IGNORE_EXPECTED_RESULT = null;
+ private static final String NULL = null;
+
+ private static final String VAR = "f";
+ private static final RowSignature ROW_SIGNATURE = RowSignature.builder().add(VAR, ValueType.FLOAT).build();
+ private static final Map BINDINGS = ImmutableMap.of(VAR, 3.14);
+
+ private IPv4AddressStringifyOperatorConversion target;
+ private ExpressionTestHelper testHelper;
+
+ @Before
+ public void setUp()
+ {
+ target = new IPv4AddressStringifyOperatorConversion();
+ testHelper = new ExpressionTestHelper(ROW_SIGNATURE, BINDINGS);
+ }
+
+ @Test
+ public void testTooFewArgs()
+ {
+ expectException(IllegalArgumentException.class, "must have 1 argument");
+
+ testExpression(
+ Collections.emptyList(),
+ buildExpectedExpression(),
+ IGNORE_EXPECTED_RESULT
+ );
+ }
+
+ @Test
+ public void testTooManyArgs()
+ {
+ expectException(IllegalArgumentException.class, "must have 1 argument");
+
+ testExpression(
+ Arrays.asList(
+ testHelper.makeLiteral(VALID),
+ testHelper.makeLiteral(VALID)
+ ),
+ buildExpectedExpression(VALID, VALID),
+ IGNORE_EXPECTED_RESULT
+ );
+ }
+
+ @Test
+ public void testNullArg()
+ {
+ testExpression(
+ testHelper.getConstantNull(),
+ buildExpectedExpression((String) null),
+ NULL
+ );
+ }
+
+ @Test
+ public void testInvalidArgType()
+ {
+ String variableNameWithInvalidType = VAR;
+ testExpression(
+ testHelper.makeInputRef(variableNameWithInvalidType),
+ buildExpectedExpression(testHelper.makeVariable(variableNameWithInvalidType)),
+ NULL
+ );
+ }
+
+ @Test
+ public void testInvalidIntegerArgTooLow()
+ {
+ long tooLow = -1L;
+ testExpression(
+ testHelper.makeLiteral(tooLow),
+ buildExpectedExpression(tooLow),
+ NULL
+ );
+ }
+
+ @Test
+ public void testValidIntegerArgLowest()
+ {
+ long lowest = 0L;
+ testExpression(
+ testHelper.makeLiteral(lowest),
+ buildExpectedExpression(lowest),
+ "0.0.0.0"
+ );
+ }
+
+ @Test
+ public void testValidIntegerArgHighest()
+ {
+ long highest = 0xff_ff_ff_ffL;
+ testExpression(
+ testHelper.makeLiteral(highest),
+ buildExpectedExpression(highest),
+ "255.255.255.255"
+ );
+ }
+
+ @Test
+ public void testInvalidIntegerArgTooHigh()
+ {
+ long tooHigh = 0x1_00_00_00_00L;
+ testExpression(
+ testHelper.makeLiteral(tooHigh),
+ buildExpectedExpression(tooHigh),
+ NULL
+ );
+ }
+
+ @Test
+ public void testValidIntegerArg()
+ {
+ testExpression(
+ testHelper.makeLiteral(VALID),
+ buildExpectedExpression(VALID),
+ EXPECTED
+ );
+ }
+
+ @Test
+ public void testInvalidStringArgNotIPAddress()
+ {
+ String notIpAddress = "druid.apache.org";
+ testExpression(
+ testHelper.makeLiteral(notIpAddress),
+ buildExpectedExpression(notIpAddress),
+ NULL
+ );
+ }
+
+ @Test
+ public void testInvalidStringArgIPv6Compatible()
+ {
+ String ipv6Compatible = "::192.168.0.1";
+ testExpression(
+ testHelper.makeLiteral(ipv6Compatible),
+ buildExpectedExpression(ipv6Compatible),
+ NULL
+ );
+ }
+
+ @Test
+ public void testValidStringArgIPv6Mapped()
+ {
+ String ipv6Mapped = "::ffff:192.168.0.1";
+ testExpression(
+ testHelper.makeLiteral(ipv6Mapped),
+ buildExpectedExpression(ipv6Mapped),
+ NULL
+ );
+ }
+
+ @Test
+ public void testValidStringArgIPv4()
+ {
+ testExpression(
+ testHelper.makeLiteral(EXPECTED),
+ buildExpectedExpression(EXPECTED),
+ EXPECTED
+ );
+ }
+
+ @Test
+ public void testValidStringArgUnsignedInt()
+ {
+ String unsignedInt = "3232235521";
+ testExpression(
+ testHelper.makeLiteral(unsignedInt),
+ buildExpectedExpression(unsignedInt),
+ NULL
+ );
+ }
+
+ private void testExpression(
+ RexNode expr,
+ final DruidExpression expectedExpression,
+ final Object expectedResult
+ )
+ {
+ testExpression(Collections.singletonList(expr), expectedExpression, expectedResult);
+ }
+
+ private void testExpression(
+ List extends RexNode> exprs,
+ final DruidExpression expectedExpression,
+ final Object expectedResult
+ )
+ {
+ testHelper.testExpression(target.calciteOperator(), exprs, expectedExpression, expectedResult);
+ }
+
+ private DruidExpression buildExpectedExpression(Object... args)
+ {
+ return testHelper.buildExpectedExpression(target.getDruidFunctionName(), args);
+ }
+}