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