Skip to content

Commit 6a0a7a1

Browse files
author
Grzegorz Szwarc
committed
BUG#22950240 Fix discrepancy in GROUP BY clause for document data model
Description: For ngshell query Crud.Find({ name:$.name, count:count(*) }).GroupBy($.name); an error has been raised: "Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'test.coll.doc' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by" Reviewed-by: Lukasz Kotula <[email protected]> RB:12283
1 parent b20c0a6 commit 6a0a7a1

File tree

6 files changed

+357
-12
lines changed

6 files changed

+357
-12
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
install plugin mysqlx soname "mysqlx.so";
2+
call mtr.add_suppression("Plugin mysqlx reported: .Failed at SSL configuration: .SSL context is not usable without certificate and private key..");
3+
call mtr.add_suppression("Plugin mysqlx reported: .SSL_CTX_load_verify_locations failed.");
4+
DROP SCHEMA IF EXISTS xtest;
5+
Warnings:
6+
Note 1008 Can't drop database 'xtest'; database doesn't exist
7+
CREATE SCHEMA xtest DEFAULT CHARSET 'utf8mb4';
8+
CREATE TABLE xtest.xcoll (doc JSON, _id VARBINARY(16) GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(doc, '$._id'))) STORED PRIMARY KEY);
9+
INSERT INTO xtest.xcoll (doc) VALUES
10+
('{"_id": 1, "age": 1, "name": "foo"}'),
11+
('{"_id": 2, "age": 2, "name": "bar"}'),
12+
('{"_id": 3, "age": 3, "name": "baz", "date": {"day": 20, "month": "Apr"}} '),
13+
('{"_id": 4, "age": 7, "name": "foo"}'),
14+
('{"_id": 5, "age": 17, "name": "buz"}');
15+
doc
16+
{"name": "bar"}
17+
{"name": "baz"}
18+
{"name": "buz"}
19+
{"name": "foo"}
20+
command ok
21+
doc
22+
{"name": "bar", "count": 1}
23+
{"name": "baz", "count": 1}
24+
{"name": "buz", "count": 1}
25+
{"name": "foo", "count": 2}
26+
command ok
27+
doc
28+
{"age": 2, "name": "bar", "count": 1}
29+
{"age": 3, "name": "baz", "count": 1}
30+
{"age": 17, "name": "buz", "count": 1}
31+
{"age": 1, "name": "foo", "count": 1}
32+
{"age": 7, "name": "foo", "count": 1}
33+
command ok
34+
Got expected error: Invalid empty projection list for grouping (code 5114)
35+
Mysqlx.Ok {
36+
msg: "bye!"
37+
}
38+
ok
39+
DROP SCHEMA IF EXISTS xtest;
40+
UNINSTALL PLUGIN mysqlx;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--plugin_dir=$MYSQLXPLUGIN_DIR
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
## Doc: Find with group by clause
2+
## BUG#22950240
3+
4+
--source suite/xplugin/include/xplugin_preamble.inc
5+
6+
DROP SCHEMA IF EXISTS xtest;
7+
CREATE SCHEMA xtest DEFAULT CHARSET 'utf8mb4';
8+
CREATE TABLE xtest.xcoll (doc JSON, _id VARBINARY(16) GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(doc, '$._id'))) STORED PRIMARY KEY);
9+
INSERT INTO xtest.xcoll (doc) VALUES
10+
('{"_id": 1, "age": 1, "name": "foo"}'),
11+
('{"_id": 2, "age": 2, "name": "bar"}'),
12+
('{"_id": 3, "age": 3, "name": "baz", "date": {"day": 20, "month": "Apr"}} '),
13+
('{"_id": 4, "age": 7, "name": "foo"}'),
14+
('{"_id": 5, "age": 17, "name": "buz"}');
15+
16+
17+
## Test starts here
18+
--write_file $MYSQL_TMP_DIR/crud_find_doc_groupby.tmp
19+
-->quiet
20+
21+
# group by name
22+
Mysqlx.Crud.Find {
23+
collection { name: "xcoll" schema: "xtest" }
24+
data_model: DOCUMENT
25+
projection {
26+
alias: "name"
27+
source {
28+
type: IDENT
29+
identifier {
30+
document_path {
31+
type: MEMBER
32+
value: "name"
33+
}
34+
}
35+
}
36+
}
37+
grouping {
38+
type: IDENT
39+
identifier {
40+
document_path {
41+
type: MEMBER
42+
value: "name"
43+
}
44+
}
45+
}
46+
}
47+
-->recvresult
48+
49+
# group by name and count
50+
Mysqlx.Crud.Find {
51+
collection { name: "xcoll" schema: "xtest" }
52+
data_model: DOCUMENT
53+
projection {
54+
alias: "name"
55+
source {
56+
type: IDENT
57+
identifier {
58+
document_path {
59+
type: MEMBER
60+
value: "name"
61+
}
62+
}
63+
}
64+
}
65+
projection {
66+
alias: "count"
67+
source {
68+
type: FUNC_CALL
69+
function_call {
70+
name {
71+
name: "count"
72+
}
73+
param {
74+
type: OPERATOR operator {
75+
name: "*"
76+
}
77+
}
78+
}
79+
}
80+
}
81+
grouping {
82+
type: IDENT
83+
identifier {
84+
document_path {
85+
type: MEMBER
86+
value: "name"
87+
}
88+
}
89+
}
90+
}
91+
-->recvresult
92+
93+
# group by name, age and count
94+
Mysqlx.Crud.Find {
95+
collection { name: "xcoll" schema: "xtest" }
96+
data_model: DOCUMENT
97+
projection {
98+
alias: "name"
99+
source {
100+
type: IDENT
101+
identifier {
102+
document_path {
103+
type: MEMBER
104+
value: "name"
105+
}
106+
}
107+
}
108+
}
109+
projection {
110+
alias: "age"
111+
source {
112+
type: IDENT
113+
identifier {
114+
document_path {
115+
type: MEMBER
116+
value: "age"
117+
}
118+
}
119+
}
120+
}
121+
projection {
122+
alias: "count"
123+
source {
124+
type: FUNC_CALL
125+
function_call {
126+
name {
127+
name: "count"
128+
}
129+
param {
130+
type: OPERATOR operator {
131+
name: "*"
132+
}
133+
}
134+
}
135+
}
136+
}
137+
grouping {
138+
type: IDENT
139+
identifier {
140+
document_path {
141+
type: MEMBER
142+
value: "name"
143+
}
144+
}
145+
}
146+
grouping {
147+
type: IDENT
148+
identifier {
149+
document_path {
150+
type: MEMBER
151+
value: "age"
152+
}
153+
}
154+
}
155+
}
156+
-->recvresult
157+
158+
## no projection - error expected
159+
Mysqlx.Crud.Find {
160+
collection { name: "xcoll" schema: "xtest" }
161+
data_model: DOCUMENT
162+
grouping {
163+
type: IDENT
164+
identifier {
165+
document_path {
166+
type: MEMBER
167+
value: "name"
168+
}
169+
}
170+
}
171+
}
172+
-->expecterror 5114
173+
-->recvresult
174+
EOF
175+
176+
--exec $MYSQLXTEST -uroot --password='' --file=$MYSQL_TMP_DIR/crud_find_doc_groupby.tmp 2>&1
177+
178+
179+
## Postamble
180+
--remove_file $MYSQL_TMP_DIR/crud_find_doc_groupby.tmp
181+
DROP SCHEMA IF EXISTS xtest;
182+
UNINSTALL PLUGIN mysqlx;
183+
184+
185+
186+

rapid/plugin/x/src/find_statement_builder.cc

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,18 @@ xpl::Find_statement_builder::Find_statement_builder(const Find &msg, Query_strin
2727

2828

2929
void xpl::Find_statement_builder::add_statement() const
30+
{
31+
if (!m_is_relational && m_msg.grouping_size() > 0)
32+
add_document_statement_with_grouping();
33+
else
34+
add_statement_common(&Find_statement_builder::add_projection);
35+
}
36+
37+
38+
void xpl::Find_statement_builder::add_statement_common(const Projection_adder &projection_adder) const
3039
{
3140
m_builder.put("SELECT ");
32-
add_projection(m_msg.projection());
41+
(this->*projection_adder)(m_msg.projection());
3342
m_builder.put(" FROM ");
3443
add_table(m_msg.collection());
3544
add_filter(m_msg.criteria());
@@ -39,6 +48,25 @@ void xpl::Find_statement_builder::add_statement() const
3948
}
4049

4150

51+
namespace
52+
{
53+
const char* const DERIVED_TABLE_NAME = "`_DERIVED_TABLE_`";
54+
}
55+
56+
57+
void xpl::Find_statement_builder::add_document_statement_with_grouping() const
58+
{
59+
if (m_msg.projection_size() == 0)
60+
throw ngs::Error_code(ER_X_BAD_PROJECTION, "Invalid empty projection list for grouping");
61+
62+
m_builder.put("SELECT ");
63+
add_document_object(m_msg.projection(), &Find_statement_builder::add_document_primary_projection_item);
64+
m_builder.put(" FROM (");
65+
add_statement_common(&Find_statement_builder::add_table_projection);
66+
m_builder.put(") AS ").put(DERIVED_TABLE_NAME);
67+
}
68+
69+
4270
void xpl::Find_statement_builder::add_projection(const Projection_list &projection) const
4371
{
4472
if (projection.size() == 0)
@@ -78,23 +106,38 @@ void xpl::Find_statement_builder::add_document_projection(const Projection_list
78106
return;
79107
}
80108

81-
m_builder.put("JSON_OBJECT(").
82-
put_list(projection, boost::bind(&Find_statement_builder::add_document_projection_item, this, _1)).
83-
put(") AS doc");
109+
add_document_object(projection, &Find_statement_builder::add_document_projection_item);
110+
}
111+
112+
113+
void xpl::Find_statement_builder::add_document_object(const Projection_list &projection,
114+
const Object_item_adder &adder) const
115+
{
116+
m_builder.put("JSON_OBJECT(")
117+
.put_list(projection, boost::bind(adder, this, _1))
118+
.put(") AS doc");
84119
}
85120

86121

87122
void xpl::Find_statement_builder::add_document_projection_item(const Projection &item) const
88123
{
89-
//TODO: if the source expression contains a *, then the fields in the original doc should be merged with the projected ones
90-
//TODO: when target_alias is nested documents
91124
if (!item.has_alias())
92125
throw ngs::Error_code(ER_X_PROJ_BAD_KEY_NAME, "Invalid projection target name");
93126

94127
m_builder.put_quote(item.alias()).put(", ").gen(item.source());
95128
}
96129

97130

131+
void xpl::Find_statement_builder::add_document_primary_projection_item(const Projection &item) const
132+
{
133+
if (!item.has_alias())
134+
throw ngs::Error_code(ER_X_PROJ_BAD_KEY_NAME, "Invalid projection target name");
135+
136+
m_builder.put_quote(item.alias()).put(", ")
137+
.put(DERIVED_TABLE_NAME).dot().put_identifier(item.alias());
138+
}
139+
140+
98141
void xpl::Find_statement_builder::add_grouping(const Grouping_list &group,
99142
const Having &having) const
100143
{

rapid/plugin/x/src/find_statement_builder.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,12 @@ class Find_statement_builder: public Statement_builder
3535
typedef ::google::protobuf::RepeatedPtrField< Projection > Projection_list;
3636
typedef ::google::protobuf::RepeatedPtrField< ::Mysqlx::Expr::Expr > Grouping_list;
3737
typedef ::Mysqlx::Expr::Expr Having;
38+
typedef void (Find_statement_builder::*Object_item_adder)(const Projection &item) const;
39+
typedef void (Find_statement_builder::*Projection_adder)(const Projection_list &list) const;
3840

3941
virtual void add_statement() const;
42+
void add_statement_common(const Projection_adder &projection_adder) const;
43+
void add_document_statement_with_grouping() const;
4044

4145
void add_projection(const Projection_list &projection) const;
4246
void add_grouping(const Grouping_list &group, const Having &having) const;
@@ -45,6 +49,9 @@ class Find_statement_builder: public Statement_builder
4549
void add_document_projection(const Projection_list &projection) const;
4650
void add_document_projection_item(const Projection &item) const;
4751

52+
void add_document_object(const Projection_list &projection, const Object_item_adder &adder) const;
53+
void add_document_primary_projection_item(const Projection &item) const;
54+
4855
const Find &m_msg;
4956
};
5057

0 commit comments

Comments
 (0)