Line data Source code
1 : // Copyright (c) 2019 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/snapdatabase
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 :
20 :
21 : /** \file
22 : * \brief Script handling implementation.
23 : *
24 : * In various places we allow scripts to be run. Scripts are used to filter
25 : * the data and generate keys of secondary indexes.
26 : */
27 :
28 : // self
29 : //
30 : #include "snapdatabase/data/script.h"
31 :
32 :
33 : // snapwebsites lib (TODO: remove dependency--especially because we do not want Qt in snapdatabase!)
34 : //
35 : #include <snapwebsites/snap_expr.h>
36 :
37 :
38 : // snaplogger lib
39 : //
40 : #include <snaplogger/message.h>
41 :
42 :
43 : // last include
44 : //
45 : #include <snapdev/poison.h>
46 :
47 :
48 :
49 : namespace snapdatabase
50 : {
51 :
52 :
53 :
54 1 : buffer_t compile_script(std::string const & script)
55 : {
56 2 : snap::snap_expr::expr e;
57 1 : if(!e.compile(QString::fromUtf8(script.c_str())))
58 : {
59 0 : SNAP_LOG_WARNING
60 0 : << "Invalid script \""
61 : << script
62 : << "\". We were not able to compile it."
63 : << SNAP_LOG_SEND;
64 0 : return buffer_t();
65 : }
66 2 : QByteArray buf(e.serialize());
67 :
68 : // convert the QByteArray to a buffer_t
69 : //
70 1 : uint32_t size(buf.size());
71 2 : buffer_t result;
72 1 : result.reserve(size + 8); // header is "SSX1" + uint32_t size in little endian
73 1 : char const * magic = "SSX1";
74 1 : result.insert(result.end(), magic, magic + 4);
75 1 : result.insert(result.end(), reinterpret_cast<uint8_t *>(&size), reinterpret_cast<uint8_t *>(&size + 1));
76 1 : result.insert(result.end(), reinterpret_cast<uint8_t *>(buf.data()), reinterpret_cast<uint8_t *>(buf.data()) + size);
77 :
78 1 : return result;
79 : }
80 :
81 :
82 0 : buffer_t execute_script(buffer_t compiled_script, row::pointer_t row)
83 : {
84 0 : buffer_t result;
85 :
86 0 : int size(compiled_script.size());
87 0 : if(size < 8)
88 : {
89 0 : SNAP_LOG_WARNING
90 0 : << "A script buffer has to be at least 8 bytes."
91 : << SNAP_LOG_SEND;
92 0 : return result;
93 : }
94 0 : size -= 8;
95 :
96 0 : char const * input(reinterpret_cast<char const *>(compiled_script.data()));
97 0 : if(input[0] != 'S'
98 0 : || input[1] != 'S'
99 0 : || input[2] != 'X'
100 0 : || input[3] != '1')
101 : {
102 0 : SNAP_LOG_WARNING
103 0 : << "Script type ('"
104 0 : << input[0]
105 0 : << input[1]
106 0 : << input[2]
107 0 : << input[3]
108 : << "') not currently supported."
109 : << SNAP_LOG_SEND;
110 0 : return result;
111 : }
112 0 : input += 4;
113 :
114 0 : int const expected_size((input[0] << 0)
115 0 : | (input[1] << 8)
116 0 : | (input[2] << 16)
117 0 : | (input[3] << 24));
118 0 : if(expected_size != size)
119 : {
120 0 : SNAP_LOG_WARNING
121 0 : << "Unexpected script size (got "
122 : << size
123 0 : << ", expected "
124 : << expected_size
125 : << "')."
126 : << SNAP_LOG_SEND;
127 0 : return result;
128 : }
129 0 : input += 4;
130 :
131 : try
132 : {
133 0 : QByteArray buf(input, size);
134 :
135 0 : snap::snap_expr::expr e;
136 0 : e.unserialize(buf);
137 :
138 0 : snap::snap_expr::variable_t return_value;
139 0 : snap::snap_expr::functions_t functions;
140 :
141 : // TODO: transform the entire row in script variables
142 0 : snap::snap_expr::variable_t::variable_map_t variables;
143 0 : cell::map_t cells(row->cells());
144 0 : for(auto const & c : cells)
145 : {
146 0 : schema_column::pointer_t schema(c.second->schema());
147 0 : snap::snap_expr::variable_t v(QString::fromUtf8(schema->name().c_str()));
148 0 : switch(schema->type())
149 : {
150 0 : case struct_type_t::STRUCT_TYPE_VOID:
151 0 : v.set_value();
152 0 : break;
153 :
154 0 : case struct_type_t::STRUCT_TYPE_BITS8:
155 : case struct_type_t::STRUCT_TYPE_UINT8:
156 0 : v.set_value(c.second->get_uint8());
157 0 : break;
158 :
159 0 : case struct_type_t::STRUCT_TYPE_INT8:
160 0 : v.set_value(c.second->get_int8());
161 0 : break;
162 :
163 0 : case struct_type_t::STRUCT_TYPE_BITS16:
164 : case struct_type_t::STRUCT_TYPE_UINT16:
165 0 : v.set_value(c.second->get_uint16());
166 0 : break;
167 :
168 0 : case struct_type_t::STRUCT_TYPE_INT16:
169 0 : v.set_value(c.second->get_int16());
170 0 : break;
171 :
172 0 : case struct_type_t::STRUCT_TYPE_BITS32:
173 : case struct_type_t::STRUCT_TYPE_UINT32:
174 : case struct_type_t::STRUCT_TYPE_VERSION:
175 0 : v.set_value(c.second->get_uint32());
176 0 : break;
177 :
178 0 : case struct_type_t::STRUCT_TYPE_INT32:
179 0 : v.set_value(c.second->get_int32());
180 0 : break;
181 :
182 0 : case struct_type_t::STRUCT_TYPE_BITS64:
183 : case struct_type_t::STRUCT_TYPE_UINT64:
184 : case struct_type_t::STRUCT_TYPE_REFERENCE:
185 : case struct_type_t::STRUCT_TYPE_OID:
186 : case struct_type_t::STRUCT_TYPE_TIME:
187 : case struct_type_t::STRUCT_TYPE_MSTIME:
188 : case struct_type_t::STRUCT_TYPE_USTIME:
189 0 : v.set_value(c.second->get_uint64());
190 0 : break;
191 :
192 0 : case struct_type_t::STRUCT_TYPE_INT64:
193 0 : v.set_value(c.second->get_int64());
194 0 : break;
195 :
196 0 : case struct_type_t::STRUCT_TYPE_BITS128:
197 : case struct_type_t::STRUCT_TYPE_UINT128:
198 : {
199 0 : uint512_t value(c.second->get_uint128());
200 0 : QByteArray data(reinterpret_cast<char const *>(value.f_value), sizeof(uint64_t) * 2);
201 0 : v.set_value(data);
202 : }
203 0 : break;
204 :
205 0 : case struct_type_t::STRUCT_TYPE_INT128:
206 : {
207 0 : uint512_t value(c.second->get_int128());
208 0 : QByteArray data(reinterpret_cast<char const *>(value.f_value), sizeof(uint64_t) * 2);
209 0 : v.set_value(data);
210 : }
211 0 : break;
212 :
213 0 : case struct_type_t::STRUCT_TYPE_BITS256:
214 : case struct_type_t::STRUCT_TYPE_UINT256:
215 : {
216 0 : uint512_t value(c.second->get_uint256());
217 0 : QByteArray data(reinterpret_cast<char const *>(value.f_value), sizeof(uint64_t) * 4);
218 0 : v.set_value(data);
219 : }
220 0 : break;
221 :
222 0 : case struct_type_t::STRUCT_TYPE_INT256:
223 : {
224 0 : uint512_t value(c.second->get_int256());
225 0 : QByteArray data(reinterpret_cast<char const *>(value.f_value), sizeof(uint64_t) * 4);
226 0 : v.set_value(data);
227 : }
228 0 : break;
229 :
230 0 : case struct_type_t::STRUCT_TYPE_BITS512:
231 : case struct_type_t::STRUCT_TYPE_UINT512:
232 : {
233 0 : uint512_t value(c.second->get_uint512());
234 0 : QByteArray data(reinterpret_cast<char const *>(value.f_value), sizeof(uint64_t) * 8);
235 0 : v.set_value(data);
236 : }
237 0 : break;
238 :
239 0 : case struct_type_t::STRUCT_TYPE_INT512:
240 : {
241 0 : uint512_t value(c.second->get_int512());
242 0 : QByteArray data(reinterpret_cast<char const *>(value.f_value), sizeof(uint64_t) * 8);
243 0 : v.set_value(data);
244 : }
245 0 : break;
246 :
247 0 : case struct_type_t::STRUCT_TYPE_FLOAT32:
248 0 : v.set_value(c.second->get_float32());
249 0 : break;
250 :
251 0 : case struct_type_t::STRUCT_TYPE_FLOAT64:
252 0 : v.set_value(c.second->get_float64());
253 0 : break;
254 :
255 0 : case struct_type_t::STRUCT_TYPE_FLOAT128:
256 : // TODO: we have to add support for long double in the
257 : // expression, for now use a double
258 : //
259 0 : v.set_value(static_cast<double>(c.second->get_float128()));
260 0 : break;
261 :
262 0 : case struct_type_t::STRUCT_TYPE_P8STRING:
263 : case struct_type_t::STRUCT_TYPE_P16STRING:
264 : case struct_type_t::STRUCT_TYPE_P32STRING:
265 0 : v.set_value(QString::fromUtf8(c.second->get_string().c_str()));
266 0 : break;
267 :
268 0 : case struct_type_t::STRUCT_TYPE_STRUCTURE:
269 : case struct_type_t::STRUCT_TYPE_ARRAY8:
270 : case struct_type_t::STRUCT_TYPE_ARRAY16:
271 : case struct_type_t::STRUCT_TYPE_ARRAY32:
272 : case struct_type_t::STRUCT_TYPE_BUFFER8:
273 : case struct_type_t::STRUCT_TYPE_BUFFER16:
274 : case struct_type_t::STRUCT_TYPE_BUFFER32:
275 : case struct_type_t::STRUCT_TYPE_END:
276 : case struct_type_t::STRUCT_TYPE_RENAMED:
277 : throw type_mismatch(
278 : "Unexpected type ("
279 0 : + std::to_string(static_cast<int>(schema->type()))
280 0 : + ") to convert a cell from binary.");
281 :
282 : }
283 0 : variables[QString::fromUtf8(schema->name().c_str())] = v;
284 : }
285 :
286 0 : e.execute(return_value, variables, functions);
287 :
288 0 : libdbproxy::value const & value(return_value.get_value());
289 0 : switch(return_value.get_type())
290 : {
291 0 : case snap::snap_expr::variable_t::variable_type_t::EXPR_VARIABLE_TYPE_NULL:
292 : // done
293 0 : break;
294 :
295 0 : case snap::snap_expr::variable_t::variable_type_t::EXPR_VARIABLE_TYPE_BOOL:
296 : {
297 0 : auto const v(value.safeBoolValue());
298 0 : result.insert(result.end(), reinterpret_cast<uint8_t const *>(&v), reinterpret_cast<uint8_t const *>(&v + 1));
299 : }
300 0 : break;
301 :
302 0 : case snap::snap_expr::variable_t::variable_type_t::EXPR_VARIABLE_TYPE_INT8:
303 : {
304 0 : auto const v(value.safeSignedCharValue());
305 0 : result.insert(result.end(), reinterpret_cast<uint8_t const *>(&v), reinterpret_cast<uint8_t const *>(&v + 1));
306 : }
307 0 : break;
308 :
309 0 : case snap::snap_expr::variable_t::variable_type_t::EXPR_VARIABLE_TYPE_UINT8:
310 : {
311 0 : auto const v(value.safeUnsignedCharValue());
312 0 : result.insert(result.end(), reinterpret_cast<uint8_t const *>(&v), reinterpret_cast<uint8_t const *>(&v + 1));
313 : }
314 0 : break;
315 :
316 0 : case snap::snap_expr::variable_t::variable_type_t::EXPR_VARIABLE_TYPE_INT16:
317 : {
318 0 : auto const v(value.safeInt16Value());
319 0 : result.insert(result.end(), reinterpret_cast<uint8_t const *>(&v), reinterpret_cast<uint8_t const *>(&v + 1));
320 : }
321 0 : break;
322 :
323 0 : case snap::snap_expr::variable_t::variable_type_t::EXPR_VARIABLE_TYPE_UINT16:
324 : {
325 0 : auto const v(value.safeUInt16Value());
326 0 : result.insert(result.end(), reinterpret_cast<uint8_t const *>(&v), reinterpret_cast<uint8_t const *>(&v + 1));
327 : }
328 0 : break;
329 :
330 0 : case snap::snap_expr::variable_t::variable_type_t::EXPR_VARIABLE_TYPE_INT32:
331 : {
332 0 : auto const v(value.safeInt32Value());
333 0 : result.insert(result.end(), reinterpret_cast<uint8_t const *>(&v), reinterpret_cast<uint8_t const *>(&v + 1));
334 : }
335 0 : break;
336 :
337 0 : case snap::snap_expr::variable_t::variable_type_t::EXPR_VARIABLE_TYPE_UINT32:
338 : {
339 0 : auto const v(value.safeUInt32Value());
340 0 : result.insert(result.end(), reinterpret_cast<uint8_t const *>(&v), reinterpret_cast<uint8_t const *>(&v + 1));
341 : }
342 0 : break;
343 :
344 0 : case snap::snap_expr::variable_t::variable_type_t::EXPR_VARIABLE_TYPE_INT64:
345 : {
346 0 : auto const v(value.safeInt64Value());
347 0 : result.insert(result.end(), reinterpret_cast<uint8_t const *>(&v), reinterpret_cast<uint8_t const *>(&v + 1));
348 : }
349 0 : break;
350 :
351 0 : case snap::snap_expr::variable_t::variable_type_t::EXPR_VARIABLE_TYPE_UINT64:
352 : {
353 0 : auto const v(value.safeUInt64Value());
354 0 : result.insert(result.end(), reinterpret_cast<uint8_t const *>(&v), reinterpret_cast<uint8_t const *>(&v + 1));
355 : }
356 0 : break;
357 :
358 0 : case snap::snap_expr::variable_t::variable_type_t::EXPR_VARIABLE_TYPE_FLOAT:
359 : {
360 0 : auto const v(value.safeFloatValue());
361 0 : result.insert(result.end(), reinterpret_cast<uint8_t const *>(&v), reinterpret_cast<uint8_t const *>(&v + 1));
362 : }
363 0 : break;
364 :
365 0 : case snap::snap_expr::variable_t::variable_type_t::EXPR_VARIABLE_TYPE_DOUBLE:
366 : {
367 0 : auto const v(value.safeDoubleValue());
368 0 : result.insert(result.end(), reinterpret_cast<uint8_t const *>(&v), reinterpret_cast<uint8_t const *>(&v + 1));
369 : }
370 0 : break;
371 :
372 0 : case snap::snap_expr::variable_t::variable_type_t::EXPR_VARIABLE_TYPE_STRING:
373 : {
374 0 : auto const v(value.stringValue());
375 0 : QByteArray data(v.toUtf8());
376 0 : result.insert(result.end(), reinterpret_cast<uint8_t const *>(data.data()), reinterpret_cast<uint8_t const *>(data.data()) + data.size());
377 : }
378 0 : break;
379 :
380 0 : case snap::snap_expr::variable_t::variable_type_t::EXPR_VARIABLE_TYPE_BINARY:
381 : {
382 0 : auto const & v(value.binaryValue());
383 0 : result.insert(result.end(), reinterpret_cast<uint8_t const *>(v.data()), reinterpret_cast<uint8_t const *>(v.data()) + v.size());
384 : }
385 0 : break;
386 :
387 : }
388 :
389 : }
390 0 : catch(snap::snap_expr::snap_expr_exception const & e)
391 : {
392 : // ignore all execution exceptions, but log a warning at least
393 : //
394 0 : SNAP_LOG_WARNING
395 0 : << "An error occurred while exceuting a script: "
396 0 : << e.what()
397 : << SNAP_LOG_SEND;
398 0 : return buffer_t();
399 : }
400 :
401 0 : return result;
402 : }
403 :
404 :
405 :
406 6 : } // namespace snapdatabase
407 : // vim: ts=4 sw=4 et
|