1 /*
2 * Copyright (c) 2015, 2021, Oracle and/or its affiliates.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2.0,
6 * as published by the Free Software Foundation.
7 *
8 * This program is also distributed with certain software (including
9 * but not limited to OpenSSL) that is licensed under separate terms,
10 * as designated in a particular file or component or in included license
11 * documentation. The authors of MySQL hereby grant you an additional
12 * permission to link the program and your derivative works with the
13 * separately licensed software that they have included with MySQL.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License, version 2.0, for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include "find_statement_builder.h"
26 #include "ngs_common/protocol_protobuf.h"
27 #include "xpl_error.h"
28 #include <algorithm>
29
30
build(const Find & msg) const31 void xpl::Find_statement_builder::build(const Find &msg) const
32 {
33 if (!is_table_data_model(msg) && msg.grouping_size() > 0)
34 add_document_statement_with_grouping(msg);
35 else
36 add_statement_common(msg);
37 }
38
39
add_statement_common(const Find & msg) const40 void xpl::Find_statement_builder::add_statement_common(const Find &msg) const
41 {
42 m_builder.put("SELECT ");
43 if (is_table_data_model(msg))
44 add_table_projection(msg.projection());
45 else
46 add_document_projection(msg.projection());
47 m_builder.put(" FROM ");
48 add_collection(msg.collection());
49 add_filter(msg.criteria());
50 add_grouping(msg.grouping());
51 add_grouping_criteria(msg.grouping_criteria());
52 add_order(msg.order());
53 add_limit(msg.limit(), false);
54 }
55
56
57 namespace
58 {
59 const char* const DERIVED_TABLE_NAME = "`_DERIVED_TABLE_`";
60 } // namespace
61
62
add_document_statement_with_grouping(const Find & msg) const63 void xpl::Find_statement_builder::add_document_statement_with_grouping(const Find &msg) const
64 {
65 if (msg.projection_size() == 0)
66 throw ngs::Error_code(ER_X_BAD_PROJECTION, "Invalid empty projection list for grouping");
67
68 m_builder.put("SELECT ");
69 add_document_object(msg.projection(), &Find_statement_builder::add_document_primary_projection_item);
70 m_builder.put(" FROM (");
71 m_builder.put("SELECT ");
72 add_table_projection(msg.projection());
73 m_builder.put(" FROM ");
74 add_collection(msg.collection());
75 add_filter(msg.criteria());
76 add_grouping(msg.grouping());
77 add_grouping_criteria(msg.grouping_criteria());
78 add_order(msg.order());
79 add_limit(msg.limit(), false);
80 m_builder.put(") AS ").put(DERIVED_TABLE_NAME);
81 }
82
83
add_table_projection(const Projection_list & projection) const84 void xpl::Find_statement_builder::add_table_projection(const Projection_list &projection) const
85 {
86 if (projection.size() == 0)
87 {
88 m_builder.put("*");
89 return;
90 }
91 m_builder.put_list(projection, ngs::bind(&Find_statement_builder::add_table_projection_item, this, ngs::placeholders::_1));
92 }
93
94
add_table_projection_item(const Projection & item) const95 void xpl::Find_statement_builder::add_table_projection_item(const Projection &item) const
96 {
97 m_builder.put_expr(item.source());
98 add_alias(item);
99 }
100
101
add_document_projection(const Projection_list & projection) const102 void xpl::Find_statement_builder::add_document_projection(const Projection_list &projection) const
103 {
104 if (projection.size() == 0)
105 {
106 m_builder.put("doc");
107 return;
108 }
109
110 if (projection.size() == 1 &&
111 !projection.Get(0).has_alias() &&
112 projection.Get(0).source().type() == Mysqlx::Expr::Expr::OBJECT)
113 {
114 m_builder.put_expr(projection.Get(0).source()).put(" AS doc");
115 return;
116 }
117
118 add_document_object(projection, &Find_statement_builder::add_document_projection_item);
119 }
120
121
add_document_object(const Projection_list & projection,const Object_item_adder & adder) const122 void xpl::Find_statement_builder::add_document_object(const Projection_list &projection,
123 const Object_item_adder &adder) const
124 {
125 m_builder.put("JSON_OBJECT(")
126 .put_list(projection, ngs::bind(adder, this, ngs::placeholders::_1))
127 .put(") AS doc");
128 }
129
130
add_document_projection_item(const Projection & item) const131 void xpl::Find_statement_builder::add_document_projection_item(const Projection &item) const
132 {
133 if (!item.has_alias())
134 throw ngs::Error(ER_X_PROJ_BAD_KEY_NAME,
135 "Invalid projection target name");
136
137 m_builder.put_quote(item.alias()).put(", ").put_expr(item.source());
138 }
139
140
add_document_primary_projection_item(const Projection & item) const141 void xpl::Find_statement_builder::add_document_primary_projection_item(const Projection &item) const
142 {
143 if (!item.has_alias())
144 throw ngs::Error(ER_X_PROJ_BAD_KEY_NAME,
145 "Invalid projection target name");
146
147 m_builder.put_quote(item.alias()).put(", ")
148 .put(DERIVED_TABLE_NAME).dot().put_identifier(item.alias());
149 }
150
151
add_grouping(const Grouping_list & group) const152 void xpl::Find_statement_builder::add_grouping(const Grouping_list &group) const
153 {
154 if (group.size() > 0)
155 m_builder.put(" GROUP BY ").put_list(group, &Generator::put_expr);
156 }
157
158
add_grouping_criteria(const Grouping_criteria & criteria) const159 void xpl::Find_statement_builder::add_grouping_criteria(const Grouping_criteria &criteria) const
160 {
161 if (criteria.IsInitialized())
162 m_builder.put(" HAVING ").put_expr(criteria);
163 }
164