1 /* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 #include "sql/dd_sp.h"
24
25 #include <string.h>
26 #include <sys/types.h>
27 #include <memory>
28 #include <ostream>
29 #include <string>
30
31 #include "lex_string.h"
32 #include "m_ctype.h"
33 #include "m_string.h"
34 #include "my_alloc.h"
35 #include "my_dbug.h"
36 #include "my_inttypes.h"
37 #include "my_sys.h"
38 #include "mysql_com.h"
39 #include "sql/dd/collection.h"
40 #include "sql/dd/properties.h" // Properties
41 #include "sql/dd/string_type.h" // dd::Stringstream_type
42 #include "sql/dd/types/column.h"
43 #include "sql/dd/types/parameter.h" // dd::Parameter
44 #include "sql/dd/types/parameter_type_element.h" // dd::Parameter_type_element
45 #include "sql/dd/types/view.h"
46 #include "sql/dd_table_share.h" // dd_get_mysql_charset, dd_get_old_field_type
47 #include "sql/field.h"
48 #include "sql/gis/srid.h"
49 #include "sql/sp.h" // SP_DEFAULT_ACCESS_MAPPING
50 #include "sql/sql_class.h" // THD
51 #include "sql/sql_lex.h"
52 #include "sql/sql_show.h"
53 #include "sql/system_variables.h"
54 #include "sql/table.h"
55 #include "sql_string.h"
56 #include "typelib.h"
57
prepare_sp_chistics_from_dd_routine(const dd::Routine * routine,st_sp_chistics * sp_chistics)58 void prepare_sp_chistics_from_dd_routine(const dd::Routine *routine,
59 st_sp_chistics *sp_chistics) {
60 DBUG_TRACE;
61
62 sp_chistics->detistic = routine->is_deterministic();
63
64 // SQL Data access.
65 switch (routine->sql_data_access()) {
66 case dd::Routine::SDA_NO_SQL:
67 sp_chistics->daccess = SP_NO_SQL;
68 break;
69 case dd::Routine::SDA_CONTAINS_SQL:
70 sp_chistics->daccess = SP_CONTAINS_SQL;
71 break;
72 case dd::Routine::SDA_READS_SQL_DATA:
73 sp_chistics->daccess = SP_READS_SQL_DATA;
74 break;
75 case dd::Routine::SDA_MODIFIES_SQL_DATA:
76 sp_chistics->daccess = SP_MODIFIES_SQL_DATA;
77 break;
78 default:
79 sp_chistics->daccess = SP_DEFAULT_ACCESS_MAPPING; /* purecov: deadcode */
80 }
81
82 // Security type.
83 sp_chistics->suid = (routine->security_type() == dd::View::ST_INVOKER)
84 ? SP_IS_NOT_SUID
85 : SP_IS_SUID;
86
87 // comment string.
88 if (!routine->comment().empty()) {
89 sp_chistics->comment = {routine->comment().c_str(),
90 routine->comment().length()};
91 } else
92 sp_chistics->comment = EMPTY_CSTR;
93 }
94
make_field(const dd::Parameter & param,TABLE_SHARE * share,Field::geometry_type geom_type,TYPELIB * interval)95 static Field *make_field(const dd::Parameter ¶m, TABLE_SHARE *share,
96 Field::geometry_type geom_type, TYPELIB *interval) {
97 // Decimals
98 uint numeric_scale = 0;
99 if (param.data_type() == dd::enum_column_types::DECIMAL ||
100 param.data_type() == dd::enum_column_types::NEWDECIMAL)
101 numeric_scale = param.numeric_scale();
102 else if (param.data_type() == dd::enum_column_types::FLOAT ||
103 param.data_type() == dd::enum_column_types::DOUBLE)
104 numeric_scale = param.is_numeric_scale_null() ? DECIMAL_NOT_SPECIFIED
105 : param.numeric_scale();
106
107 return make_field(*THR_MALLOC, share, nullptr, param.char_length(), nullptr,
108 0, dd_get_old_field_type(param.data_type()),
109 dd_get_mysql_charset(param.collation_id()), geom_type,
110 Field::NONE, interval, "", false, param.is_zerofill(),
111 param.is_unsigned(), numeric_scale, false, 0, {}, false);
112 }
113
114 /**
115 Helper method to prepare type in string format from the dd::Parameter's
116 object.
117 This method is called from the prepare_return_type_string_from_dd_routine()
118 and prepare_params_string_from_dd_routine().
119
120 @param[in] thd Thread handle.
121 @param[in] param dd::Parameter's object.
122 @param[out] type_str SQL type string prepared from the dd::Parameter's
123 object.
124 */
125
prepare_type_string_from_dd_param(THD * thd,const dd::Parameter * param,String * type_str)126 static void prepare_type_string_from_dd_param(THD *thd,
127 const dd::Parameter *param,
128 String *type_str) {
129 DBUG_TRACE;
130
131 // ENUM/SET elements.
132 TYPELIB *interval = nullptr;
133 if (param->data_type() == dd::enum_column_types::ENUM ||
134 param->data_type() == dd::enum_column_types::SET) {
135 // Allocate space for interval.
136 size_t interval_parts = param->elements_count();
137
138 interval = static_cast<TYPELIB *>(thd->mem_root->Alloc(sizeof(TYPELIB)));
139 interval->type_names = static_cast<const char **>(
140 thd->mem_root->Alloc((sizeof(char *) * (interval_parts + 1))));
141 interval->type_names[interval_parts] = nullptr;
142
143 interval->type_lengths = static_cast<uint *>(
144 thd->mem_root->Alloc(sizeof(uint) * interval_parts));
145 interval->count = interval_parts;
146 interval->name = nullptr;
147
148 for (const dd::Parameter_type_element *pe : param->elements()) {
149 // Read the enum/set element name
150 dd::String_type element_name = pe->name();
151
152 uint pos = pe->index() - 1;
153 interval->type_lengths[pos] = static_cast<uint>(element_name.length());
154 interval->type_names[pos] = strmake_root(
155 thd->mem_root, element_name.c_str(), element_name.length());
156 }
157 }
158
159 // Geometry sub type
160 Field::geometry_type geom_type = Field::GEOM_GEOMETRY;
161 if (param->data_type() == dd::enum_column_types::GEOMETRY) {
162 uint32 sub_type = 0;
163 param->options().get("geom_type", &sub_type);
164 geom_type = static_cast<Field::geometry_type>(sub_type);
165 }
166
167 // Get type in string format.
168 TABLE table;
169 TABLE_SHARE share;
170 table.in_use = thd;
171 table.s = &share;
172
173 unique_ptr_destroy_only<Field> field(
174 make_field(*param, table.s, geom_type, interval));
175
176 field->init(&table);
177 field->sql_type(*type_str);
178
179 if (field->has_charset()) {
180 type_str->append(STRING_WITH_LEN(" CHARSET "));
181 type_str->append(field->charset()->csname);
182 if (!(field->charset()->state & MY_CS_PRIMARY)) {
183 type_str->append(STRING_WITH_LEN(" COLLATE "));
184 type_str->append(field->charset()->name);
185 }
186 }
187 }
188
prepare_return_type_string_from_dd_routine(THD * thd,const dd::Routine * routine,dd::String_type * return_type_str)189 void prepare_return_type_string_from_dd_routine(
190 THD *thd, const dd::Routine *routine, dd::String_type *return_type_str) {
191 DBUG_TRACE;
192
193 *return_type_str = "";
194
195 /*
196 Return type of Stored function is stored as the first parameter in the data
197 dictionary table "Parameters".
198 Stored procedures do not have return type so nothing is done for the stored
199 procedures.
200 */
201 if (routine->type() == dd::Routine::RT_FUNCTION) {
202 const dd::Routine::Parameter_collection ¶meters = routine->parameters();
203
204 if (!parameters.empty()) {
205 const dd::Parameter *param = *parameters.begin();
206 DBUG_ASSERT(param->ordinal_position() == 1);
207
208 String type_str(64);
209 type_str.set_charset(system_charset_info);
210
211 prepare_type_string_from_dd_param(thd, param, &type_str);
212 *return_type_str = type_str.ptr();
213 }
214 }
215 }
216
prepare_params_string_from_dd_routine(THD * thd,const dd::Routine * routine,dd::String_type * params_str)217 void prepare_params_string_from_dd_routine(THD *thd, const dd::Routine *routine,
218 dd::String_type *params_str) {
219 DBUG_TRACE;
220
221 *params_str = "";
222
223 dd::Stringstream_type params_ss;
224
225 for (const dd::Parameter *param : routine->parameters()) {
226 /*
227 Return type of stored function is stored as the first parameter. So skip
228 it.
229 */
230 if (routine->type() == dd::Routine::RT_FUNCTION &&
231 param->ordinal_position() == 1)
232 continue;
233
234 if (params_ss.str().length() > 0) params_ss << ", ";
235
236 // PARAMETER MODE
237 if (routine->type() == dd::Routine::RT_PROCEDURE) {
238 switch (param->mode()) {
239 case dd::Parameter::PM_IN:
240 params_ss << "IN ";
241 break;
242 case dd::Parameter::PM_OUT:
243 params_ss << "OUT ";
244 break;
245 case dd::Parameter::PM_INOUT:
246 params_ss << "INOUT ";
247 break;
248 }
249 }
250
251 /*
252 PARAMETER NAME
253 Convert and quote the parameter name if needed.
254 */
255 String param_str(NAME_LEN + 1);
256 sql_mode_t sql_mode = thd->variables.sql_mode;
257 thd->variables.sql_mode = routine->sql_mode();
258 append_identifier(thd, ¶m_str, param->name().c_str(),
259 param->name().length());
260 thd->variables.sql_mode = sql_mode;
261 params_ss << param_str.ptr() << " ";
262
263 // PARAMETER TYPE
264 String type_str(64);
265 type_str.set_charset(system_charset_info);
266 prepare_type_string_from_dd_param(thd, param, &type_str);
267 params_ss << type_str.ptr();
268 }
269
270 if (params_ss.str().length()) *params_str = params_ss.str();
271 }
272