Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Group Concat function support for separator #16237

Merged
merged 7 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ __debug_bin
/php/composer.phar
/php/vendor

report*.xml

# vitess.io preview site
/preview-vitess.io/

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
CREATE TABLE `t1`
(
`id` int unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(191) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE InnoDB,
CHARSET utf8mb4,
COLLATE utf8mb4_unicode_ci;

CREATE TABLE `t2`
(
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`t1_id` int unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE InnoDB,
CHARSET utf8mb4,
COLLATE utf8mb4_unicode_ci;

CREATE TABLE `t3`
(
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(191) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE InnoDB,
CHARSET utf8mb4,
COLLATE utf8mb4_unicode_ci;

insert into t1 (id, name)
values (1, 'A'),
(2, 'B'),
(3, 'C'),
(4, 'D');

insert into t2 (id, t1_id)
values (1, 1),
(2, 2),
(3, 3);

insert into t3 (id, name)
values (1, 'A'),
(2, 'B');

-- wait_authoritative t1
-- wait_authoritative t2
-- wait_authoritative t3
select group_concat(t3.name SEPARATOR ', ') as "Group Name"
from t1
join t2 on t1.id = t2.t1_id
left join t3 on t1.id = t3.id
group by t1.id;
3 changes: 3 additions & 0 deletions go/vt/sqlparser/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,9 @@ const (
// KillType strings
ConnectionStr = "connection"
QueryStr = "query"

// GroupConcatDefaultSeparator is the default separator for GroupConcatExpr.
GroupConcatDefaultSeparator = ","
)
GuptaManan100 marked this conversation as resolved.
Show resolved Hide resolved

// Constants for Enum Type - Insert.Action
Expand Down
17 changes: 12 additions & 5 deletions go/vt/vtgate/engine/aggregations.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type AggregateParams struct {
Type evalengine.Type

Alias string `json:",omitempty"`
Expr sqlparser.Expr
Func sqlparser.AggrFunc
Original *sqlparser.AliasedExpr

// This is based on the function passed in the select expression and
Expand Down Expand Up @@ -255,8 +255,9 @@ func (a *aggregatorScalar) reset() {
}

type aggregatorGroupConcat struct {
from int
type_ sqltypes.Type
from int
type_ sqltypes.Type
separator []byte

concat []byte
n int
Expand All @@ -267,7 +268,7 @@ func (a *aggregatorGroupConcat) add(row []sqltypes.Value) error {
return nil
}
if a.n > 0 {
a.concat = append(a.concat, ',')
a.concat = append(a.concat, a.separator...)
}
a.concat = append(a.concat, row[a.from].Raw()...)
a.n++
Expand Down Expand Up @@ -434,7 +435,13 @@ func newAggregation(fields []*querypb.Field, aggregates []*AggregateParams) (agg
ag = &aggregatorScalar{from: aggr.Col}

case AggregateGroupConcat:
ag = &aggregatorGroupConcat{from: aggr.Col, type_: targetType}
gcFunc := aggr.Func.(*sqlparser.GroupConcatExpr)
separator := []byte(gcFunc.Separator)
ag = &aggregatorGroupConcat{
from: aggr.Col,
type_: targetType,
separator: separator,
}

default:
panic("BUG: unexpected Aggregation opcode")
Expand Down
4 changes: 2 additions & 2 deletions go/vt/vtgate/engine/cached_size.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions go/vt/vtgate/engine/ordered_aggregate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"fmt"
"testing"

"vitess.io/vitess/go/vt/sqlparser"
"vitess.io/vitess/go/vt/vtgate/evalengine"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -1058,8 +1059,10 @@ func TestGroupConcatWithAggrOnEngine(t *testing.T) {
for _, tcase := range tcases {
t.Run(tcase.name, func(t *testing.T) {
fp := &fakePrimitive{results: []*sqltypes.Result{tcase.inputResult}}
agp := NewAggregateParam(AggregateGroupConcat, 1, "group_concat(c2)", collations.MySQL8())
agp.Func = &sqlparser.GroupConcatExpr{Separator: ","}
oa := &OrderedAggregate{
Aggregates: []*AggregateParams{NewAggregateParam(AggregateGroupConcat, 1, "group_concat(c2)", collations.MySQL8())},
Aggregates: []*AggregateParams{agp},
GroupByKeys: []*GroupByParams{{KeyCol: 0}},
Input: fp,
}
Expand Down Expand Up @@ -1137,8 +1140,10 @@ func TestGroupConcat(t *testing.T) {
for _, tcase := range tcases {
t.Run(tcase.name, func(t *testing.T) {
fp := &fakePrimitive{results: []*sqltypes.Result{tcase.inputResult}}
agp := NewAggregateParam(AggregateGroupConcat, 1, "", collations.MySQL8())
agp.Func = &sqlparser.GroupConcatExpr{Separator: ","}
oa := &OrderedAggregate{
Aggregates: []*AggregateParams{NewAggregateParam(AggregateGroupConcat, 1, "", collations.MySQL8())},
Aggregates: []*AggregateParams{agp},
GroupByKeys: []*GroupByParams{{KeyCol: 0}},
Input: fp,
}
Expand Down
3 changes: 3 additions & 0 deletions go/vt/vtgate/engine/scalar_aggregation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"vitess.io/vitess/go/mysql/collations"
"vitess.io/vitess/go/sqltypes"
"vitess.io/vitess/go/test/utils"
"vitess.io/vitess/go/vt/sqlparser"
. "vitess.io/vitess/go/vt/vtgate/engine/opcode"
)

Expand Down Expand Up @@ -233,6 +234,7 @@ func TestScalarGroupConcatWithAggrOnEngine(t *testing.T) {
Opcode: AggregateGroupConcat,
Col: 0,
Alias: "group_concat(c2)",
Func: &sqlparser.GroupConcatExpr{Separator: ","},
}},
Input: fp,
}
Expand Down Expand Up @@ -394,6 +396,7 @@ func TestScalarGroupConcat(t *testing.T) {
Aggregates: []*AggregateParams{{
Opcode: AggregateGroupConcat,
Col: 0,
Func: &sqlparser.GroupConcatExpr{Separator: ","},
}},
Input: fp,
}
Expand Down
5 changes: 4 additions & 1 deletion go/vt/vtgate/planbuilder/operator_transformers.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,10 @@ func transformAggregator(ctx *plancontext.PlanningContext, op *operators.Aggrega
return nil, vterrors.VT12001(fmt.Sprintf("in scatter query: aggregation function '%s'", sqlparser.String(aggr.Original)))
}
aggrParam := engine.NewAggregateParam(aggr.OpCode, aggr.ColOffset, aggr.Alias, ctx.VSchema.Environment().CollationEnv())
aggrParam.Expr = aggr.Func
aggrParam.Func = aggr.Func
GuptaManan100 marked this conversation as resolved.
Show resolved Hide resolved
if gcFunc, isGc := aggrParam.Func.(*sqlparser.GroupConcatExpr); isGc && gcFunc.Separator == "" {
gcFunc.Separator = sqlparser.GroupConcatDefaultSeparator
}
aggrParam.Original = aggr.Original
aggrParam.OrigOpcode = aggr.OriginalOpCode
aggrParam.WCol = aggr.WSOffset
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ func (ab *aggBuilder) handleAggr(ctx *plancontext.PlanningContext, aggr Aggr) er
return ab.handlePushThroughAggregation(ctx, aggr)
case opcode.AggregateGroupConcat:
f := aggr.Func.(*sqlparser.GroupConcatExpr)
if f.Distinct || len(f.OrderBy) > 0 || f.Separator != "" {
panic("fail here")
GuptaManan100 marked this conversation as resolved.
Show resolved Hide resolved
if f.Distinct || len(f.OrderBy) > 0 {
panic(vterrors.VT12001("cannot evaluate group concat with distinct or order by"))
}
// this needs special handling, currently aborting the push of function
// and later will try pushing the column instead.
Expand Down
60 changes: 60 additions & 0 deletions go/vt/vtgate/planbuilder/testdata/aggr_cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,66 @@
]
}
},
{
"comment": "group concat with a separator needing evaluation on vtgate",
"query": "select group_concat(music.name SEPARATOR ', ') as `Group Name` from user join user_extra on user.id = user_extra.user_id left join music on user.id = music.id group by user.id;",
"plan": {
"QueryType": "SELECT",
"Original": "select group_concat(music.name SEPARATOR ', ') as `Group Name` from user join user_extra on user.id = user_extra.user_id left join music on user.id = music.id group by user.id;",
"Instructions": {
"OperatorType": "Aggregate",
"Variant": "Ordered",
"Aggregates": "group_concat(0) AS Group Name",
GuptaManan100 marked this conversation as resolved.
Show resolved Hide resolved
"GroupBy": "(1|2)",
"ResultColumns": 1,
"Inputs": [
{
"OperatorType": "Join",
"Variant": "LeftJoin",
"JoinColumnIndexes": "R:0,L:0,L:1",
"JoinVars": {
"user_id": 0
},
"TableName": "`user`, user_extra_music",
"Inputs": [
{
"OperatorType": "Route",
"Variant": "Scatter",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select `user`.id, weight_string(`user`.id) from `user`, user_extra where 1 != 1",
"OrderBy": "(0|1) ASC",
"Query": "select `user`.id, weight_string(`user`.id) from `user`, user_extra where `user`.id = user_extra.user_id order by `user`.id asc",
"Table": "`user`, user_extra"
},
{
"OperatorType": "Route",
"Variant": "EqualUnique",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select music.`name` from music where 1 != 1",
"Query": "select music.`name` from music where music.id = :user_id",
"Table": "music",
"Values": [
":user_id"
],
"Vindex": "music_user_map"
}
]
}
]
},
"TablesUsed": [
"user.music",
"user.user",
"user.user_extra"
]
}
},
{
"comment": "scatter aggregate group by column number",
"query": "select col from user group by 1",
Expand Down
5 changes: 5 additions & 0 deletions go/vt/vtgate/planbuilder/testdata/unsupported_cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@
"query": "select id2 from user uu where id in (select id from user where id = uu.id and user.col in (select col from (select id from user_extra where user_id = 5) uu where uu.user_id = uu.id))",
"plan": "VT12001: unsupported: correlated subquery is only supported for EXISTS"
},
{
"comment": "group concat with order by requiring evaluation at vtgate",
"query": "select group_concat(music.name ORDER BY 1 asc SEPARATOR ', ') as `Group Name` from user join user_extra on user.id = user_extra.user_id left join music on user.id = music.id group by user.id;",
"plan": "VT12001: unsupported: cannot evaluate group concat with distinct or order by"
},
{
"comment": "outer and inner subquery route reference the same \"uu.id\" name\n# but they refer to different things. The first reference is to the outermost query,\n# and the second reference is to the innermost 'from' subquery.\n# changed to project all the columns from the derived tables.",
"query": "select id2 from user uu where id in (select id from user where id = uu.id and user.col in (select col from (select col, id, user_id from user_extra where user_id = 5) uu where uu.user_id = uu.id))",
Expand Down
Loading