1 /* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
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 Foundation,
21 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22
23
24 /**
25 @file
26
27 @brief
28 Buffers to save and compare item values
29 */
30
31 #include "sql_class.h" // THD
32 #include "item.h" // Cached_item, Cached_item_field, ...
33 #include "json_dom.h" // Json_wrapper
34
35 using std::min;
36 using std::max;
37
38 /**
39 Create right type of Cached_item for an item.
40 */
41
new_Cached_item(THD * thd,Item * item,bool use_result_field)42 Cached_item *new_Cached_item(THD *thd, Item *item, bool use_result_field)
43 {
44 if (item->real_item()->type() == Item::FIELD_ITEM &&
45 !(((Item_field *) (item->real_item()))->field->flags & BLOB_FLAG))
46 {
47 Item_field *real_item= (Item_field *) item->real_item();
48 Field *cached_field= use_result_field ? real_item->result_field :
49 real_item->field;
50 return new Cached_item_field(cached_field);
51 }
52 switch (item->result_type()) {
53 case STRING_RESULT:
54 if (item->is_temporal())
55 return new Cached_item_temporal((Item_field *) item);
56 if (item->field_type() == MYSQL_TYPE_JSON)
57 return new Cached_item_json(item);
58 return new Cached_item_str(thd, (Item_field *) item);
59 case INT_RESULT:
60 return new Cached_item_int((Item_field *) item);
61 case REAL_RESULT:
62 return new Cached_item_real(item);
63 case DECIMAL_RESULT:
64 return new Cached_item_decimal(item);
65 case ROW_RESULT:
66 default:
67 assert(0);
68 return 0;
69 }
70 }
71
~Cached_item()72 Cached_item::~Cached_item() {}
73
74 /**
75 Compare with old value and replace value with new value.
76
77 @return
78 Return true if values have changed
79 */
80
Cached_item_str(THD * thd,Item * arg)81 Cached_item_str::Cached_item_str(THD *thd, Item *arg)
82 :item(arg),
83 value_max_length(min<uint32>(arg->max_length, thd->variables.max_sort_length)),
84 value(value_max_length)
85 {}
86
cmp(void)87 bool Cached_item_str::cmp(void)
88 {
89 String *res;
90 bool tmp;
91
92 DBUG_ENTER("Cached_item_str::cmp");
93 assert(!item->is_temporal());
94 assert(item->field_type() != MYSQL_TYPE_JSON);
95 if ((res=item->val_str(&tmp_value)))
96 res->length(min(res->length(), static_cast<size_t>(value_max_length)));
97 DBUG_PRINT("info", ("old: %s, new: %s",
98 value.c_ptr_safe(), res ? res->c_ptr_safe() : ""));
99 if (null_value != item->null_value)
100 {
101 if ((null_value= item->null_value))
102 DBUG_RETURN(TRUE); // New value was null
103 tmp=TRUE;
104 }
105 else if (null_value)
106 DBUG_RETURN(0); // new and old value was null
107 else
108 tmp= sortcmp(&value,res,item->collation.collation) != 0;
109 if (tmp)
110 value.copy(*res); // Remember for next cmp
111 DBUG_RETURN(tmp);
112 }
113
~Cached_item_str()114 Cached_item_str::~Cached_item_str()
115 {
116 item=0; // Safety
117 }
118
119
Cached_item_json(Item * item)120 Cached_item_json::Cached_item_json(Item *item)
121 : m_item(item), m_value(new Json_wrapper())
122 {}
123
124
~Cached_item_json()125 Cached_item_json::~Cached_item_json()
126 {
127 delete m_value;
128 }
129
130
131 /**
132 Compare the new JSON value in m_item with the previous value.
133 @retval true if the new value is different from the previous value,
134 or if there is no previously cached value
135 @retval false if the new value is the same as the already cached value
136 */
cmp()137 bool Cached_item_json::cmp()
138 {
139 Json_wrapper wr;
140 if (m_item->val_json(&wr))
141 {
142 null_value= true; /* purecov: inspected */
143 return true; /* purecov: inspected */
144 }
145 if (null_value != m_item->null_value)
146 {
147 null_value= m_item->null_value;
148 if (null_value)
149 return true; // New value is null.
150 }
151 else if (null_value)
152 {
153 return false; // New and old are null.
154 }
155 else if (!m_value->empty() && m_value->compare(wr) == 0)
156 {
157 return false; // New and old are equal.
158 }
159
160 /*
161 Otherwise, old and new are not equal, and new is not null.
162 Remember the current value till the next time we're called.
163 */
164 m_value->steal(&wr);
165
166 /*
167 The row buffer may change, which would garble the JSON binary
168 representation pointed to by m_value. Convert to DOM so that we
169 own the copy.
170 */
171 m_value->to_dom();
172
173 return true;
174 }
175
176
cmp(void)177 bool Cached_item_real::cmp(void)
178 {
179 DBUG_ENTER("Cached_item_real::cmp");
180 double nr= item->val_real();
181 DBUG_PRINT("info", ("old: %f, new: %f", value, nr));
182 if (null_value != item->null_value || nr != value)
183 {
184 null_value= item->null_value;
185 value=nr;
186 DBUG_RETURN(TRUE);
187 }
188 DBUG_RETURN(FALSE);
189 }
190
cmp(void)191 bool Cached_item_int::cmp(void)
192 {
193 DBUG_ENTER("Cached_item_int::cmp");
194 longlong nr=item->val_int();
195 DBUG_PRINT("info", ("old: %lld, new: %lld", value, nr));
196 if (null_value != item->null_value || nr != value)
197 {
198 null_value= item->null_value;
199 value=nr;
200 DBUG_RETURN(TRUE);
201 }
202 DBUG_RETURN(FALSE);
203 }
204
205
cmp(void)206 bool Cached_item_temporal::cmp(void)
207 {
208 DBUG_ENTER("Cached_item_temporal::cmp");
209 longlong nr= item->val_temporal_by_field_type();
210 DBUG_PRINT("info", ("old: %lld, new: %lld", value, nr));
211 if (null_value != item->null_value || nr != value)
212 {
213 null_value= item->null_value;
214 value= nr;
215 DBUG_RETURN(TRUE);
216 }
217 DBUG_RETURN(FALSE);
218 }
219
220
cmp(void)221 bool Cached_item_field::cmp(void)
222 {
223 DBUG_ENTER("Cached_item_field::cmp");
224 DBUG_EXECUTE("info", dbug_print(););
225
226 bool different= false;
227
228 if (field->is_null())
229 {
230 if (!null_value)
231 {
232 different= true;
233 null_value= true;
234 }
235 }
236 else
237 {
238 if (null_value)
239 {
240 different= true;
241 null_value= false;
242 field->get_image(buff, length, field->charset());
243 }
244 else if (field->cmp(buff)) // Not a blob: cmp() is OK
245 {
246 different= true;
247 field->get_image(buff, length, field->charset());
248 }
249 }
250
251 DBUG_RETURN(different);
252 }
253
254
Cached_item_decimal(Item * it)255 Cached_item_decimal::Cached_item_decimal(Item *it)
256 :item(it)
257 {
258 my_decimal_set_zero(&value);
259 }
260
261
cmp()262 bool Cached_item_decimal::cmp()
263 {
264 my_decimal tmp;
265 my_decimal *ptmp= item->val_decimal(&tmp);
266 if (null_value != item->null_value ||
267 (!item->null_value && my_decimal_cmp(&value, ptmp)))
268 {
269 null_value= item->null_value;
270 /* Save only not null values */
271 if (!null_value)
272 {
273 my_decimal2decimal(ptmp, &value);
274 return TRUE;
275 }
276 return FALSE;
277 }
278 return FALSE;
279 }
280