1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
|
/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
*
* Copyright (C) 2008 Sun Microsystems
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <drizzled/server_includes.h>
#include CSTDINT_H
#include <drizzled/function/set_user_var.h>
#include <drizzled/field/num.h>
#include <drizzled/session.h>
#include <drizzled/plugin/client.h>
using namespace drizzled;
/*
When a user variable is updated (in a SET command or a query like
SELECT @a:= ).
*/
bool Item_func_set_user_var::fix_fields(Session *session, Item **ref)
{
assert(fixed == 0);
/* fix_fields will call Item_func_set_user_var::fix_length_and_dec */
if (Item_func::fix_fields(session, ref) ||
!(entry= session->getVariable(name, true)))
return true;
/*
Remember the last query which updated it, this way a query can later know
if this variable is a constant item in the query (it is if update_query_id
is different from query_id).
*/
entry->update_query_id= session->query_id;
/*
As it is wrong and confusing to associate any
character set with NULL, @a should be latin2
after this query sequence:
SET @a=_latin2'string';
SET @a=NULL;
I.e. the second query should not change the charset
to the current default value, but should keep the
original value assigned during the first query.
In order to do it, we don't copy charset
from the argument if the argument is NULL
and the variable has previously been initialized.
*/
null_item= (args[0]->type() == NULL_ITEM);
if (!entry->collation.collation || !null_item)
entry->collation.set(args[0]->collation.collation, DERIVATION_IMPLICIT);
collation.set(entry->collation.collation, DERIVATION_IMPLICIT);
cached_result_type= args[0]->result_type();
return false;
}
void
Item_func_set_user_var::fix_length_and_dec()
{
maybe_null=args[0]->maybe_null;
max_length=args[0]->max_length;
decimals=args[0]->decimals;
collation.set(args[0]->collation.collation, DERIVATION_IMPLICIT);
}
/*
Mark field in read_map
NOTES
This is used by filesort to register used fields in a a temporary
column read set or to register used fields in a view
*/
bool Item_func_set_user_var::register_field_in_read_map(unsigned char *arg)
{
if (result_field)
{
Table *table= (Table *) arg;
if (result_field->table == table || !table)
result_field->table->setReadSet(result_field->field_index);
}
return 0;
}
bool
Item_func_set_user_var::update_hash(void *ptr, uint32_t length,
Item_result res_type,
const CHARSET_INFO * const cs, Derivation dv,
bool unsigned_arg)
{
/*
If we set a variable explicitely to NULL then keep the old
result type of the variable
*/
if ((null_value= args[0]->null_value) && null_item)
res_type= entry->type; // Don't change type of item
if (entry->update_hash((null_value= args[0]->null_value),
ptr, length, res_type, cs, dv, unsigned_arg))
{
null_value= 1;
return 1;
}
return 0;
}
/**
This functions is invoked on SET \@variable or
\@variable:= expression.
Evaluate (and check expression), store results.
@note
For now it always return OK. All problem with value evaluating
will be caught by session->is_error() check in sql_set_variables().
@retval
false OK.
*/
bool
Item_func_set_user_var::check(bool use_result_field)
{
if (use_result_field && !result_field)
use_result_field= false;
switch (cached_result_type) {
case REAL_RESULT:
{
save_result.vreal= use_result_field ? result_field->val_real() :
args[0]->val_real();
break;
}
case INT_RESULT:
{
save_result.vint= use_result_field ? result_field->val_int() :
args[0]->val_int();
unsigned_flag= use_result_field ? ((Field_num*)result_field)->unsigned_flag:
args[0]->unsigned_flag;
break;
}
case STRING_RESULT:
{
save_result.vstr= use_result_field ? result_field->val_str(&value) :
args[0]->val_str(&value);
break;
}
case DECIMAL_RESULT:
{
save_result.vdec= use_result_field ?
result_field->val_decimal(&decimal_buff) :
args[0]->val_decimal(&decimal_buff);
break;
}
case ROW_RESULT:
default:
// This case should never be chosen
assert(0);
break;
}
return(false);
}
/**
This functions is invoked on
SET \@variable or \@variable:= expression.
@note
We have to store the expression as such in the variable, independent of
the value method used by the user
@retval
0 OK
@retval
1 EOM Error
*/
bool
Item_func_set_user_var::update()
{
bool res= false;
switch (cached_result_type) {
case REAL_RESULT:
{
res= update_hash((void*) &save_result.vreal,sizeof(save_result.vreal),
REAL_RESULT, &my_charset_bin, DERIVATION_IMPLICIT, 0);
break;
}
case INT_RESULT:
{
res= update_hash((void*) &save_result.vint, sizeof(save_result.vint),
INT_RESULT, &my_charset_bin, DERIVATION_IMPLICIT,
unsigned_flag);
break;
}
case STRING_RESULT:
{
if (!save_result.vstr) // Null value
res= update_hash((void*) 0, 0, STRING_RESULT, &my_charset_bin,
DERIVATION_IMPLICIT, 0);
else
res= update_hash((void*) save_result.vstr->ptr(),
save_result.vstr->length(), STRING_RESULT,
save_result.vstr->charset(),
DERIVATION_IMPLICIT, 0);
break;
}
case DECIMAL_RESULT:
{
if (!save_result.vdec) // Null value
res= update_hash((void*) 0, 0, DECIMAL_RESULT, &my_charset_bin,
DERIVATION_IMPLICIT, 0);
else
res= update_hash((void*) save_result.vdec,
sizeof(my_decimal), DECIMAL_RESULT,
&my_charset_bin, DERIVATION_IMPLICIT, 0);
break;
}
case ROW_RESULT:
default:
// This case should never be chosen
assert(0);
break;
}
return(res);
}
double Item_func_set_user_var::val_real()
{
assert(fixed == 1);
check(0);
update(); // Store expression
return entry->val_real(&null_value);
}
int64_t Item_func_set_user_var::val_int()
{
assert(fixed == 1);
check(0);
update(); // Store expression
return entry->val_int(&null_value);
}
String *Item_func_set_user_var::val_str(String *str)
{
assert(fixed == 1);
check(0);
update(); // Store expression
return entry->val_str(&null_value, str, decimals);
}
my_decimal *Item_func_set_user_var::val_decimal(my_decimal *val)
{
assert(fixed == 1);
check(0);
update(); // Store expression
return entry->val_decimal(&null_value, val);
}
double Item_func_set_user_var::val_result()
{
assert(fixed == 1);
check(true);
update(); // Store expression
return entry->val_real(&null_value);
}
int64_t Item_func_set_user_var::val_int_result()
{
assert(fixed == 1);
check(true);
update(); // Store expression
return entry->val_int(&null_value);
}
String *Item_func_set_user_var::str_result(String *str)
{
assert(fixed == 1);
check(true);
update(); // Store expression
return entry->val_str(&null_value, str, decimals);
}
my_decimal *Item_func_set_user_var::val_decimal_result(my_decimal *val)
{
assert(fixed == 1);
check(true);
update(); // Store expression
return entry->val_decimal(&null_value, val);
}
void Item_func_set_user_var::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("(@"));
str->append(name.str, name.length);
str->append(STRING_WITH_LEN(":="));
args[0]->print(str, query_type);
str->append(')');
}
void Item_func_set_user_var::print_as_stmt(String *str,
enum_query_type query_type)
{
str->append(STRING_WITH_LEN("set @"));
str->append(name.str, name.length);
str->append(STRING_WITH_LEN(":="));
args[0]->print(str, query_type);
str->append(')');
}
bool Item_func_set_user_var::send(plugin::Client *client, String *str_arg)
{
if (result_field)
{
check(1);
update();
return client->store(result_field);
}
return Item::send(client, str_arg);
}
void Item_func_set_user_var::make_field(SendField *tmp_field)
{
if (result_field)
{
result_field->make_field(tmp_field);
assert(tmp_field->table_name != 0);
if (Item::name)
tmp_field->col_name=Item::name; // Use user supplied name
}
else
Item::make_field(tmp_field);
}
/*
Save the value of a user variable into a field
SYNOPSIS
save_in_field()
field target field to save the value to
no_conversion flag indicating whether conversions are allowed
DESCRIPTION
Save the function value into a field and update the user variable
accordingly. If a result field is defined and the target field doesn't
coincide with it then the value from the result field will be used as
the new value of the user variable.
The reason to have this method rather than simply using the result
field in the val_xxx() methods is that the value from the result field
not always can be used when the result field is defined.
Let's consider the following cases:
1) when filling a tmp table the result field is defined but the value of it
is undefined because it has to be produced yet. Thus we can't use it.
2) on execution of an INSERT ... SELECT statement the save_in_field()
function will be called to fill the data in the new record. If the SELECT
part uses a tmp table then the result field is defined and should be
used in order to get the correct result.
The difference between the SET_USER_VAR function and regular functions
like CONCAT is that the Item_func objects for the regular functions are
replaced by Item_field objects after the values of these functions have
been stored in a tmp table. Yet an object of the Item_field class cannot
be used to update a user variable.
Due to this we have to handle the result field in a special way here and
in the Item_func_set_user_var::send() function.
RETURN VALUES
false Ok
true Error
*/
int Item_func_set_user_var::save_in_field(Field *field, bool no_conversions,
bool can_use_result_field)
{
bool use_result_field= (!can_use_result_field ? 0 :
(result_field && result_field != field));
int error;
/* Update the value of the user variable */
check(use_result_field);
update();
if (result_type() == STRING_RESULT ||
(result_type() == REAL_RESULT && field->result_type() == STRING_RESULT))
{
String *result;
const CHARSET_INFO * const cs= collation.collation;
char buff[MAX_FIELD_WIDTH]; // Alloc buffer for small columns
str_value.set_quick(buff, sizeof(buff), cs);
result= entry->val_str(&null_value, &str_value, decimals);
if (null_value)
{
str_value.set_quick(0, 0, cs);
return set_field_to_null_with_conversions(field, no_conversions);
}
/* NOTE: If null_value == false, "result" must be not NULL. */
field->set_notnull();
error=field->store(result->ptr(),result->length(),cs);
str_value.set_quick(0, 0, cs);
}
else if (result_type() == REAL_RESULT)
{
double nr= entry->val_real(&null_value);
if (null_value)
return set_field_to_null(field);
field->set_notnull();
error=field->store(nr);
}
else if (result_type() == DECIMAL_RESULT)
{
my_decimal decimal_value;
my_decimal *val= entry->val_decimal(&null_value, &decimal_value);
if (null_value)
return set_field_to_null(field);
field->set_notnull();
error=field->store_decimal(val);
}
else
{
int64_t nr= entry->val_int(&null_value);
if (null_value)
return set_field_to_null_with_conversions(field, no_conversions);
field->set_notnull();
error=field->store(nr, unsigned_flag);
}
return error;
}
|