1 /*
2  * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
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
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23  */
24 
25 #ifndef PLUGIN_X_NGS_INCLUDE_NGS_MYSQLX_GETTER_ANY_H_
26 #define PLUGIN_X_NGS_INCLUDE_NGS_MYSQLX_GETTER_ANY_H_
27 
28 #include <sstream>
29 #include <string>
30 #include <vector>
31 
32 #include "plugin/x/ngs/include/ngs/error_code.h"
33 #include "plugin/x/ngs/include/ngs/protocol/protocol_protobuf.h"
34 #include "plugin/x/src/xpl_error.h"
35 
36 namespace ngs {
37 
38 class Getter_any {
39  public:
40   template <typename Value_type>
get_numeric_value(const::Mysqlx::Datatypes::Any & any)41   static Value_type get_numeric_value(const ::Mysqlx::Datatypes::Any &any) {
42     using ::Mysqlx::Datatypes::Any;
43     using ::Mysqlx::Datatypes::Scalar;
44 
45     if (Any::SCALAR != any.type())
46       throw Error_code(ER_X_INVALID_PROTOCOL_DATA,
47                        "Invalid data, expecting scalar");
48 
49     const Scalar &scalar = any.scalar();
50 
51     switch (scalar.type()) {
52       case Scalar::V_BOOL:
53         return static_cast<Value_type>(scalar.v_bool());
54 
55       case Scalar::V_DOUBLE:
56         return static_cast<Value_type>(scalar.v_double());
57 
58       case Scalar::V_FLOAT:
59         return static_cast<Value_type>(scalar.v_float());
60 
61       case Scalar::V_SINT:
62         return static_cast<Value_type>(scalar.v_signed_int());
63 
64       case Scalar::V_UINT:
65         return static_cast<Value_type>(scalar.v_unsigned_int());
66 
67       default:
68         throw Error_code(ER_X_INVALID_PROTOCOL_DATA,
69                          "Invalid data, expected numeric type");
70     }
71   }
72 
73   template <typename Value_type>
get_numeric_value(const::Mysqlx::Datatypes::Any & any,ngs::Error_code * out_error)74   static Value_type get_numeric_value(const ::Mysqlx::Datatypes::Any &any,
75                                       ngs::Error_code *out_error) {
76     try {
77       return get_numeric_value<Value_type>(any);
78     } catch (const Error_code &e) {
79       if (out_error) *out_error = e;
80     }
81 
82     return {};
83   }
84 
85   static std::string get_string_value(const ::Mysqlx::Datatypes::Any &any,
86                                       ngs::Error_code *out_error = nullptr) {
87     using ::Mysqlx::Datatypes::Any;
88     using ::Mysqlx::Datatypes::Scalar;
89 
90     if (Any::SCALAR != any.type()) {
91       ngs::Error_code error(ER_X_INVALID_PROTOCOL_DATA,
92                             "Invalid data, expecting scalar");
93 
94       if (out_error) {
95         *out_error = error;
96         return {};
97       }
98 
99       throw error;
100     }
101 
102     const Scalar &scalar = any.scalar();
103 
104     switch (scalar.type()) {
105       case Scalar::V_STRING:
106         return scalar.v_string().value();
107 
108       case Scalar::V_OCTETS:
109         return scalar.v_octets().value();
110 
111       default: {
112         ngs::Error_code error(ER_X_INVALID_PROTOCOL_DATA,
113                               "Invalid data, expected string type");
114 
115         if (out_error) {
116           *out_error = error;
117           return {};
118         }
119 
120         throw error;
121       }
122     }
123   }
124 
125   template <typename Value_type>
get_numeric_value_or_default(const::Mysqlx::Datatypes::Any & any,const Value_type & default_value)126   static Value_type get_numeric_value_or_default(
127       const ::Mysqlx::Datatypes::Any &any, const Value_type &default_value) {
128     try {
129       return get_numeric_value<Value_type>(any);
130     } catch (const Error_code &) {
131     }
132 
133     return default_value;
134   }
135 
136   template <typename Functor>
put_scalar_value_to_functor(const::Mysqlx::Datatypes::Any & any,Functor & functor)137   static void put_scalar_value_to_functor(const ::Mysqlx::Datatypes::Any &any,
138                                           Functor &functor) {
139     if (!any.has_type())
140       throw Error_code(ER_X_INVALID_PROTOCOL_DATA,
141                        "Invalid data, expecting type");
142 
143     if (::Mysqlx::Datatypes::Any::SCALAR != any.type())
144       throw Error_code(ER_X_INVALID_PROTOCOL_DATA,
145                        "Invalid data, expecting scalar");
146 
147     using ::Mysqlx::Datatypes::Scalar;
148     const Scalar &scalar = any.scalar();
149 
150     switch (scalar.type()) {
151       case Scalar::V_SINT:
152         throw_invalid_type_if_false(scalar, scalar.has_v_signed_int());
153         functor(scalar.v_signed_int());
154         break;
155 
156       case Scalar::V_UINT:
157         throw_invalid_type_if_false(scalar, scalar.has_v_unsigned_int());
158         functor(scalar.v_unsigned_int());
159         break;
160 
161       case Scalar::V_NULL:
162         functor();
163         break;
164 
165       case Scalar::V_OCTETS:
166         throw_invalid_type_if_false(
167             scalar, scalar.has_v_octets() && scalar.v_octets().has_value());
168         functor(scalar.v_octets().value(), scalar.v_octets().content_type());
169         break;
170 
171       case Scalar::V_DOUBLE:
172         throw_invalid_type_if_false(scalar, scalar.has_v_double());
173         functor(scalar.v_double());
174         break;
175 
176       case Scalar::V_FLOAT:
177         throw_invalid_type_if_false(scalar, scalar.has_v_float());
178         functor(scalar.v_float());
179         break;
180 
181       case Scalar::V_BOOL:
182         throw_invalid_type_if_false(scalar, scalar.has_v_bool());
183         functor(scalar.v_bool());
184         break;
185 
186       case Scalar::V_STRING:
187         // XXX
188         // implement char-set handling
189         const bool is_valid =
190             scalar.has_v_string() && scalar.v_string().has_value();
191 
192         throw_invalid_type_if_false(scalar, is_valid);
193         functor(scalar.v_string().value());
194         break;
195     }
196   }
197 
198  private:
throw_invalid_type_if_false(const::Mysqlx::Datatypes::Scalar & scalar,const bool is_valid)199   static void throw_invalid_type_if_false(
200       const ::Mysqlx::Datatypes::Scalar &scalar, const bool is_valid) {
201     if (!is_valid)
202       throw Error(ER_X_INVALID_PROTOCOL_DATA,
203                   "Missing field required for ScalarType: %d", scalar.type());
204   }
205 };
206 
207 }  // namespace ngs
208 
209 #endif  // PLUGIN_X_NGS_INCLUDE_NGS_MYSQLX_GETTER_ANY_H_
210