Skip to content
Snippets Groups Projects
Commit b526f70c authored by Takeshi Yamamuro's avatar Takeshi Yamamuro Committed by Xiao Li
Browse files

[SPARK-19951][SQL] Add string concatenate operator || to Spark SQL

## What changes were proposed in this pull request?
This pr added code to support `||` for string concatenation. This string operation is supported in PostgreSQL and MySQL.

## How was this patch tested?
Added tests in `SparkSqlParserSuite`

Author: Takeshi Yamamuro <yamamuro@apache.org>

Closes #17711 from maropu/SPARK-19951.
parent 92ea7fd7
No related branches found
No related tags found
No related merge requests found
......@@ -549,7 +549,7 @@ valueExpression
: primaryExpression #valueExpressionDefault
| operator=(MINUS | PLUS | TILDE) valueExpression #arithmeticUnary
| left=valueExpression operator=(ASTERISK | SLASH | PERCENT | DIV) right=valueExpression #arithmeticBinary
| left=valueExpression operator=(PLUS | MINUS) right=valueExpression #arithmeticBinary
| left=valueExpression operator=(PLUS | MINUS | CONCAT_PIPE) right=valueExpression #arithmeticBinary
| left=valueExpression operator=AMPERSAND right=valueExpression #arithmeticBinary
| left=valueExpression operator=HAT right=valueExpression #arithmeticBinary
| left=valueExpression operator=PIPE right=valueExpression #arithmeticBinary
......@@ -590,7 +590,7 @@ comparisonOperator
;
arithmeticOperator
: PLUS | MINUS | ASTERISK | SLASH | PERCENT | DIV | TILDE | AMPERSAND | PIPE | HAT
: PLUS | MINUS | ASTERISK | SLASH | PERCENT | DIV | TILDE | AMPERSAND | PIPE | CONCAT_PIPE | HAT
;
predicateOperator
......@@ -869,6 +869,7 @@ DIV: 'DIV';
TILDE: '~';
AMPERSAND: '&';
PIPE: '|';
CONCAT_PIPE: '||';
HAT: '^';
PERCENTLIT: 'PERCENT';
......
......@@ -1010,6 +1010,8 @@ class AstBuilder(conf: SQLConf) extends SqlBaseBaseVisitor[AnyRef] with Logging
Add(left, right)
case SqlBaseParser.MINUS =>
Subtract(left, right)
case SqlBaseParser.CONCAT_PIPE =>
Concat(left :: right :: Nil)
case SqlBaseParser.AMPERSAND =>
BitwiseAnd(left, right)
case SqlBaseParser.HAT =>
......
......@@ -32,3 +32,24 @@ select 1 - 2;
select 2 * 5;
select 5 % 3;
select pmod(-7, 3);
-- check operator precedence.
-- We follow Oracle operator precedence in the table below that lists the levels of precedence
-- among SQL operators from high to low:
------------------------------------------------------------------------------------------
-- Operator Operation
------------------------------------------------------------------------------------------
-- +, - identity, negation
-- *, / multiplication, division
-- +, -, || addition, subtraction, concatenation
-- =, !=, <, >, <=, >=, IS NULL, LIKE, BETWEEN, IN comparison
-- NOT exponentiation, logical negation
-- AND conjunction
-- OR disjunction
------------------------------------------------------------------------------------------
explain select 'a' || 1 + 2;
explain select 1 - 2 || 'b';
explain select 2 * 4 + 3 || 'b';
explain select 3 + 1 || 'a' || 4 / 2;
explain select 1 == 1 OR 'a' || 'b' == 'ab';
explain select 'a' || 'c' == 'ac' AND 2 == 3;
-- Argument number exception
select concat_ws();
select format_string();
-- A pipe operator for string concatenation
select 'a' || 'b' || 'c';
-- Automatically generated by SQLQueryTestSuite
-- Number of queries: 28
-- Number of queries: 34
-- !query 0
......@@ -224,3 +224,63 @@ select pmod(-7, 3)
struct<pmod(-7, 3):int>
-- !query 27 output
2
-- !query 28
explain select 'a' || 1 + 2
-- !query 28 schema
struct<plan:string>
-- !query 28 output
== Physical Plan ==
*Project [null AS (CAST(concat(a, CAST(1 AS STRING)) AS DOUBLE) + CAST(2 AS DOUBLE))#x]
+- Scan OneRowRelation[]
-- !query 29
explain select 1 - 2 || 'b'
-- !query 29 schema
struct<plan:string>
-- !query 29 output
== Physical Plan ==
*Project [-1b AS concat(CAST((1 - 2) AS STRING), b)#x]
+- Scan OneRowRelation[]
-- !query 30
explain select 2 * 4 + 3 || 'b'
-- !query 30 schema
struct<plan:string>
-- !query 30 output
== Physical Plan ==
*Project [11b AS concat(CAST(((2 * 4) + 3) AS STRING), b)#x]
+- Scan OneRowRelation[]
-- !query 31
explain select 3 + 1 || 'a' || 4 / 2
-- !query 31 schema
struct<plan:string>
-- !query 31 output
== Physical Plan ==
*Project [4a2.0 AS concat(concat(CAST((3 + 1) AS STRING), a), CAST((CAST(4 AS DOUBLE) / CAST(2 AS DOUBLE)) AS STRING))#x]
+- Scan OneRowRelation[]
-- !query 32
explain select 1 == 1 OR 'a' || 'b' == 'ab'
-- !query 32 schema
struct<plan:string>
-- !query 32 output
== Physical Plan ==
*Project [true AS ((1 = 1) OR (concat(a, b) = ab))#x]
+- Scan OneRowRelation[]
-- !query 33
explain select 'a' || 'c' == 'ac' AND 2 == 3
-- !query 33 schema
struct<plan:string>
-- !query 33 output
== Physical Plan ==
*Project [false AS ((concat(a, c) = ac) AND (2 = 3))#x]
+- Scan OneRowRelation[]
-- Automatically generated by SQLQueryTestSuite
-- Number of queries: 2
-- Number of queries: 3
-- !query 0
......@@ -18,3 +18,11 @@ struct<>
-- !query 1 output
org.apache.spark.sql.AnalysisException
requirement failed: format_string() should take at least 1 argument; line 1 pos 7
-- !query 2
select 'a' || 'b' || 'c'
-- !query 2 schema
struct<concat(concat(a, b), c):string>
-- !query 2 output
abc
......@@ -19,9 +19,9 @@ package org.apache.spark.sql.execution
import org.apache.spark.sql.SaveMode
import org.apache.spark.sql.catalyst.{FunctionIdentifier, TableIdentifier}
import org.apache.spark.sql.catalyst.analysis.{UnresolvedAttribute, UnresolvedRelation, UnresolvedStar}
import org.apache.spark.sql.catalyst.analysis.{UnresolvedAlias, UnresolvedAttribute, UnresolvedRelation, UnresolvedStar}
import org.apache.spark.sql.catalyst.catalog.{BucketSpec, CatalogStorageFormat, CatalogTable, CatalogTableType}
import org.apache.spark.sql.catalyst.expressions.{Ascending, SortOrder}
import org.apache.spark.sql.catalyst.expressions.{Ascending, Concat, SortOrder}
import org.apache.spark.sql.catalyst.parser.ParseException
import org.apache.spark.sql.catalyst.plans.PlanTest
import org.apache.spark.sql.catalyst.plans.logical.{LogicalPlan, Project, RepartitionByExpression, Sort}
......@@ -290,4 +290,15 @@ class SparkSqlParserSuite extends PlanTest {
basePlan,
numPartitions = newConf.numShufflePartitions)))
}
test("pipeline concatenation") {
val concat = Concat(
Concat(UnresolvedAttribute("a") :: UnresolvedAttribute("b") :: Nil) ::
UnresolvedAttribute("c") ::
Nil
)
assertEqual(
"SELECT a || b || c FROM t",
Project(UnresolvedAlias(concat) :: Nil, UnresolvedRelation(TableIdentifier("t"))))
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment