Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
udf-builtins.cc
Go to the documentation of this file.
1 // Copyright 2012 Cloudera Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "exprs/udf-builtins.h"
16 
17 #include <ctype.h>
18 #include <gutil/strings/substitute.h>
19 #include <iostream>
20 #include <math.h>
21 #include <sstream>
22 #include <string>
23 
24 #include "gen-cpp/Exprs_types.h"
26 #include "util/bit-util.h"
27 
28 #include "common/names.h"
29 
30 using boost::gregorian::date;
31 using boost::gregorian::date_duration;
32 using boost::posix_time::ptime;
33 using boost::posix_time::time_duration;
34 using namespace impala;
35 using namespace strings;
36 
38  if (v.is_null) return v;
39  return DoubleVal(fabs(v.val));
40 }
41 
43  return DoubleVal(M_PI);
44 }
45 
47  if (v.is_null) return v;
48  StringVal result(context, v.len);
49  for (int i = 0; i < v.len; ++i) {
50  result.ptr[i] = tolower(v.ptr[i]);
51  }
52  return result;
53 }
54 
56  return IntVal(numeric_limits<int32_t>::max());
57 }
58 
60  return TinyIntVal(numeric_limits<int8_t>::max());
61 }
62 
64  return SmallIntVal(numeric_limits<int16_t>::max());
65 }
66 
68  return BigIntVal(numeric_limits<int64_t>::max());
69 }
70 
72  return IntVal(numeric_limits<int32_t>::min());
73 }
74 
76  return TinyIntVal(numeric_limits<int8_t>::min());
77 }
78 
80  return SmallIntVal(numeric_limits<int16_t>::min());
81 }
82 
84  return BigIntVal(numeric_limits<int64_t>::min());
85 }
86 
88  if (val.is_null) return BooleanVal(false);
89  return BooleanVal(isnan(val.val));
90 }
91 
93  if (val.is_null) return BooleanVal(false);
94  return BooleanVal(isinf(val.val));
95 }
96 
97 // The units which can be used when Truncating a Timestamp
98 struct TruncUnit {
99  enum Type {
104  WW,
105  W,
109  MINUTE
110  };
111 };
112 
113 // Maps the user facing name of a unit to a TruncUnit
114 // Returns the TruncUnit for the given string
116  StringVal unit = UdfBuiltins::Lower(ctx, unit_str);
117  if ((unit == "syyyy") || (unit == "yyyy") || (unit == "year") || (unit == "syear") ||
118  (unit == "yyy") || (unit == "yy") || (unit == "y")) {
119  return TruncUnit::YEAR;
120  } else if (unit == "q") {
121  return TruncUnit::QUARTER;
122  } else if ((unit == "month") || (unit == "mon") || (unit == "mm") || (unit == "rm")) {
123  return TruncUnit::MONTH;
124  } else if (unit == "ww") {
125  return TruncUnit::WW;
126  } else if (unit == "w") {
127  return TruncUnit::W;
128  } else if ((unit == "ddd") || (unit == "dd") || (unit == "j")) {
129  return TruncUnit::DAY;
130  } else if ((unit == "day") || (unit == "dy") || (unit == "d")) {
131  return TruncUnit::DAY_OF_WEEK;
132  } else if ((unit == "hh") || (unit == "hh12") || (unit == "hh24")) {
133  return TruncUnit::HOUR;
134  } else if (unit == "mi") {
135  return TruncUnit::MINUTE;
136  } else {
138  }
139 }
140 
141 // Returns the most recent date, no later than orig_date, which is on week_day
142 // week_day: 0==Sunday, 1==Monday, ...
143 date GoBackToWeekday(const date& orig_date, int week_day) {
144  int current_week_day = orig_date.day_of_week();
145  int diff = current_week_day - week_day;
146  if (diff == 0) return orig_date;
147  if (diff > 0) {
148  // ex. Weds(3) shifts to Tues(2), so we go back 1 day
149  return orig_date - date_duration(diff);
150  }
151  // ex. Tues(2) shifts to Weds(3), so we go back 6 days
152  DCHECK_LT(diff, 0);
153  return orig_date - date_duration(7 + diff);
154 }
155 
156 // Truncate to first day of year
157 TimestampValue TruncYear(const date& orig_date) {
158  date new_date(orig_date.year(), 1, 1);
159  time_duration new_time(0, 0, 0, 0);
160  return TimestampValue(new_date, new_time);
161 }
162 
163 // Truncate to first day of quarter
164 TimestampValue TruncQuarter(const date& orig_date) {
165  int first_month_of_quarter = BitUtil::RoundDown(orig_date.month() - 1, 3) + 1;
166  date new_date(orig_date.year(), first_month_of_quarter, 1);
167  time_duration new_time(0, 0, 0, 0);
168  return TimestampValue(new_date, new_time);
169 }
170 
171 // Truncate to first day of month
172 TimestampValue TruncMonth(const date& orig_date) {
173  date new_date(orig_date.year(), orig_date.month(), 1);
174  time_duration new_time(0, 0, 0, 0);
175  return TimestampValue(new_date, new_time);
176 }
177 
178 // Same day of the week as the first day of the year
179 TimestampValue TruncWW(const date& orig_date) {
180  const date& first_day_of_year = TruncYear(orig_date).date();
181  int target_week_day = first_day_of_year.day_of_week();
182  date new_date = GoBackToWeekday(orig_date, target_week_day);
183  time_duration new_time(0, 0, 0, 0);
184  return TimestampValue(new_date, new_time);
185 }
186 
187 // Same day of the week as the first day of the month
188 TimestampValue TruncW(const date& orig_date) {
189  const date& first_day_of_mon = TruncMonth(orig_date).date();
190  const date& new_date = GoBackToWeekday(orig_date, first_day_of_mon.day_of_week());
191  time_duration new_time(0, 0, 0, 0);
192  return TimestampValue(new_date, new_time);
193 }
194 
195 // Truncate to midnight on the given date
196 TimestampValue TruncDay(const date& orig_date) {
197  time_duration new_time(0, 0, 0, 0);
198  return TimestampValue(orig_date, new_time);
199 }
200 
201 // Date of the previous Monday
202 TimestampValue TruncDayOfWeek(const date& orig_date) {
203  const date& new_date = GoBackToWeekday(orig_date, 1);
204  time_duration new_time(0, 0, 0, 0);
205  return TimestampValue(new_date, new_time);
206 }
207 
208 // Truncate minutes, seconds, and parts of seconds
209 TimestampValue TruncHour(const date& orig_date, const time_duration& orig_time) {
210  time_duration new_time(orig_time.hours(), 0, 0, 0);
211  return TimestampValue(orig_date, new_time);
212 }
213 
214 // Truncate seconds and parts of seconds
215 TimestampValue TruncMinute(const date& orig_date, const time_duration& orig_time) {
216  time_duration new_time(orig_time.hours(), orig_time.minutes(), 0, 0);
217  return TimestampValue(orig_date, new_time);
218 }
219 
221  const StringVal &unit_str) {
222  if (tv.is_null) return TimestampVal::null();
224  const date& orig_date = ts.date();
225  const time_duration& orig_time = ts.time();
226 
227  // resolve trunc_unit using the prepared state if possible, o.w. parse now
228  // TruncPrepare() can only parse trunc_unit if user passes it as a string literal
229  TruncUnit::Type trunc_unit;
230  void* state = context->GetFunctionState(FunctionContext::THREAD_LOCAL);
231  if (state != NULL) {
232  trunc_unit = *reinterpret_cast<TruncUnit::Type*>(state);
233  } else {
234  trunc_unit = StrToTruncUnit(context, unit_str);
235  if (trunc_unit == TruncUnit::UNIT_INVALID) {
236  string string_unit(reinterpret_cast<char*>(unit_str.ptr), unit_str.len);
237  context->SetError(Substitute("Invalid Truncate Unit: $0", string_unit).c_str());
238  return TimestampVal::null();
239  }
240  }
241 
242  TimestampValue ret;
243  TimestampVal ret_val;
244 
245  // check for invalid or malformed timestamps
246  switch (trunc_unit) {
247  case TruncUnit::YEAR:
248  case TruncUnit::QUARTER:
249  case TruncUnit::MONTH:
250  case TruncUnit::WW:
251  case TruncUnit::W:
252  case TruncUnit::DAY:
254  if (orig_date.is_special()) return TimestampVal::null();
255  break;
256  case TruncUnit::HOUR:
257  case TruncUnit::MINUTE:
258  if (orig_time.is_special()) return TimestampVal::null();
259  break;
261  DCHECK(false);
262  }
263 
264  switch(trunc_unit) {
265  case TruncUnit::YEAR:
266  ret = TruncYear(orig_date);
267  break;
268  case TruncUnit::QUARTER:
269  ret = TruncQuarter(orig_date);
270  break;
271  case TruncUnit::MONTH:
272  ret = TruncMonth(orig_date);
273  break;
274  case TruncUnit::WW:
275  ret = TruncWW(orig_date);
276  break;
277  case TruncUnit::W:
278  ret = TruncW(orig_date);
279  break;
280  case TruncUnit::DAY:
281  ret = TruncDay(orig_date);
282  break;
284  ret = TruncDayOfWeek(orig_date);
285  break;
286  case TruncUnit::HOUR:
287  ret = TruncHour(orig_date, orig_time);
288  break;
289  case TruncUnit::MINUTE:
290  ret = TruncMinute(orig_date, orig_time);
291  break;
292  default:
293  // internal error: implies StrToTruncUnit out of sync with this switch
294  context->SetError(Substitute("truncate unit $0 not supported", trunc_unit).c_str());
295  return TimestampVal::null();
296  }
297 
298  ret.ToTimestampVal(&ret_val);
299  return ret_val;
300 }
301 
304  // Parse the unit up front if we can, otherwise do it on the fly in Trunc()
305  if (ctx->IsArgConstant(1)) {
306  StringVal* unit_str = reinterpret_cast<StringVal*>(ctx->GetConstantArg(1));
307  TruncUnit::Type trunc_unit = StrToTruncUnit(ctx, *unit_str);
308  if (trunc_unit == TruncUnit::UNIT_INVALID) {
309  string string_unit(reinterpret_cast<char*>(unit_str->ptr), unit_str->len);
310  ctx->SetError(Substitute("Invalid Truncate Unit: $0", string_unit).c_str());
311  } else {
312  TruncUnit::Type* state = reinterpret_cast<TruncUnit::Type*>(
313  ctx->Allocate(sizeof(TruncUnit::Type)));
314  *state = trunc_unit;
315  ctx->SetFunctionState(scope, state);
316  }
317  }
318 }
319 
322  void* state = ctx->GetFunctionState(scope);
323  if (state != NULL) {
324  ctx->Free(reinterpret_cast<uint8_t*>(state));
325  ctx->SetFunctionState(scope, NULL);
326  }
327 }
328 
329 // Maps the user facing name of a unit to a TExtractField
330 // Returns the TExtractField for the given unit
331 TExtractField::type StrToExtractField(FunctionContext* ctx, const StringVal& unit_str) {
332  StringVal unit = UdfBuiltins::Lower(ctx, unit_str);
333  if (unit == "year") return TExtractField::YEAR;
334  if (unit == "month") return TExtractField::MONTH;
335  if (unit == "day") return TExtractField::DAY;
336  if (unit == "hour") return TExtractField::HOUR;
337  if (unit == "minute") return TExtractField::MINUTE;
338  if (unit == "second") return TExtractField::SECOND;
339  if (unit == "millisecond") return TExtractField::MILLISECOND;
340  if (unit == "epoch") return TExtractField::EPOCH;
341  return TExtractField::INVALID_FIELD;
342 }
343 
345  const TimestampVal& tv) {
346  // resolve extract_field using the prepared state if possible, o.w. parse now
347  // ExtractPrepare() can only parse extract_field if user passes it as a string literal
348  if (tv.is_null) return IntVal::null();
349 
350  TExtractField::type field;
351  void* state = context->GetFunctionState(FunctionContext::THREAD_LOCAL);
352  if (state != NULL) {
353  field = *reinterpret_cast<TExtractField::type*>(state);
354  } else {
355  field = StrToExtractField(context, unit_str);
356  if (field == TExtractField::INVALID_FIELD) {
357  string string_unit(reinterpret_cast<char*>(unit_str.ptr), unit_str.len);
358  context->SetError(Substitute("invalid extract field: $0", string_unit).c_str());
359  return IntVal::null();
360  }
361  }
362 
363  const date& orig_date = *reinterpret_cast<const date*>(&tv.date);
364  const time_duration& time = *reinterpret_cast<const time_duration*>(&tv.time_of_day);
365 
366  switch (field) {
367  case TExtractField::YEAR:
368  case TExtractField::MONTH:
369  case TExtractField::DAY:
370  if (orig_date.is_special()) return IntVal::null();
371  break;
372  case TExtractField::HOUR:
373  case TExtractField::MINUTE:
374  case TExtractField::SECOND:
375  case TExtractField::MILLISECOND:
376  if (time.is_special()) return IntVal::null();
377  break;
379  if (time.is_special() || orig_date.is_special()) return IntVal::null();
380  break;
381  case TExtractField::INVALID_FIELD:
382  DCHECK(false);
383  }
384 
385  switch (field) {
386  case TExtractField::YEAR: {
387  return IntVal(orig_date.year());
388  }
389  case TExtractField::MONTH: {
390  return IntVal(orig_date.month());
391  }
392  case TExtractField::DAY: {
393  return IntVal(orig_date.day());
394  }
395  case TExtractField::HOUR: {
396  return IntVal(time.hours());
397  }
398  case TExtractField::MINUTE: {
399  return IntVal(time.minutes());
400  }
401  case TExtractField::SECOND: {
402  return IntVal(time.seconds());
403  }
404  case TExtractField::MILLISECOND: {
405  return IntVal(time.total_milliseconds() - time.total_seconds() * 1000);
406  }
407  case TExtractField::EPOCH: {
408  ptime epoch_date(date(1970, 1, 1), time_duration(0, 0, 0));
409  ptime cur_date(orig_date, time);
410  time_duration diff = cur_date - epoch_date;
411  return IntVal(diff.total_seconds());
412  }
413  default: {
414  DCHECK(false) << field;
415  return IntVal::null();
416  }
417  }
418 }
419 
421  const StringVal& unit_str) {
422  return Extract(context, unit_str, tv);
423 }
424 
426  FunctionContext::FunctionStateScope scope, int unit_idx) {
427  // Parse the unit up front if we can, otherwise do it on the fly in Extract()
428  if (ctx->IsArgConstant(unit_idx)) {
429  StringVal* unit_str = reinterpret_cast<StringVal*>(ctx->GetConstantArg(unit_idx));
430  TExtractField::type field = StrToExtractField(ctx, *unit_str);
431  if (field == TExtractField::INVALID_FIELD) {
432  string string_field(reinterpret_cast<char*>(unit_str->ptr), unit_str->len);
433  ctx->SetError(Substitute("invalid extract field: $0", string_field).c_str());
434  } else {
435  TExtractField::type* state = reinterpret_cast<TExtractField::type*>(
436  ctx->Allocate(sizeof(TExtractField::type)));
437  *state = field;
438  ctx->SetFunctionState(scope, state);
439  }
440  }
441 }
442 
445  ExtractPrepare(ctx, scope, 0);
446 }
447 
450  ExtractPrepare(ctx, scope, 1);
451 }
452 
455  void* state = ctx->GetFunctionState(scope);
456  if (state != NULL) {
457  ctx->Free(reinterpret_cast<uint8_t*>(state));
458  ctx->SetFunctionState(scope, NULL);
459  }
460 }
461 
462 bool ValidateMADlibVector(FunctionContext* context, const StringVal& arr) {
463  if (arr.ptr == NULL) {
464  context->SetError("MADlib vector is null");
465  return false;
466  }
467  if (arr.len % 8 != 0) {
468  context->SetError(Substitute("MADlib vector of incorrect length $0,"
469  " expected multiple of 8", arr.len).c_str());
470  return false;
471  }
472  return true;
473 }
474 
476  StringVal s(context, n * sizeof(double));
477  double* darr = reinterpret_cast<double*>(s.ptr);
478  for (int i = 0; i < n; ++i) {
479  if (vals[i].is_null) {
480  context->SetError(Substitute("madlib vector entry $0 is NULL", i).c_str());
481  return StringVal::null();
482  }
483  darr[i] = vals[i].val;
484  }
485  return s;
486 }
487 
489  if (!ValidateMADlibVector(context, arr)) return StringVal::null();
490  double* darr = reinterpret_cast<double*>(arr.ptr);
491  int len = arr.len / sizeof(double);
492  stringstream ss;
493  ss << "<";
494  for (int i = 0; i < len; ++i) {
495  if (i != 0) ss << ", ";
496  ss << darr[i];
497  }
498  ss << ">";
499  const string& str = ss.str();
500  StringVal result(context, str.size());
501  memcpy(result.ptr, str.c_str(), str.size());
502  return result;
503 }
504 
506  const StringVal& arr) {
507  if (!ValidateMADlibVector(context, arr)) return DoubleVal::null();
508  if (index.is_null) return DoubleVal::null();
509  uint64_t i = index.val;
510  uint64_t len = arr.len / sizeof(double);
511  if (index.val < 0 || len <= i) return DoubleVal::null();
512  double* darr = reinterpret_cast<double*>(arr.ptr);
513  return DoubleVal(darr[i]);
514 }
515 
516 void InplaceDoubleEncode(double* arr, uint64_t len) {
517  for (uint64_t i = 0; i < len; ++i) {
518  char* hex = reinterpret_cast<char*>(&arr[i]);
519  // cast to float so we have 4 bytes to encode but 8 bytes of space
520  float float_val = arr[i];
521  uint32_t float_as_int = *reinterpret_cast<int32_t*>(&float_val);
522  for (int k = 0; k < 8; ++k) {
523  // This is a simple hex encoding, 'a' becomes 0, 'b' is 1, ...
524  // This isn't a complicated encoding, but it is simple to debug and is
525  // a temporary solution until we have nested types
526  hex[k] = 'a' + ((float_as_int >> (4*k)) & 0xF);
527  }
528  }
529 }
530 
531 // Inplace conversion from a printable ascii encoding to a double*
532 void InplaceDoubleDecode(char* arr, uint64_t len) {
533  for (uint64_t i = 0; i < len; i += 8) {
534  double* dub = reinterpret_cast<double*>(&arr[i]);
535  // cast to float so we have 4 bytes to encode but 8 bytes of space
536  int32_t float_as_int = 0;
537  for (int k = 7; k >= 0; --k) {
538  float_as_int = (float_as_int <<4) | ((arr[i+k] - 'a') & 0xF);
539  }
540  float* float_ptr = reinterpret_cast<float*>(&float_as_int);
541  *dub = *float_ptr;
542  }
543 }
544 
546  if (arr.is_null) return StringVal::null();
547  double* darr = reinterpret_cast<double*>(arr.ptr);
548  int len = arr.len / sizeof(double);
549  StringVal result(context, arr.len);
550  memcpy(result.ptr, darr, arr.len);
551  InplaceDoubleEncode(reinterpret_cast<double*>(result.ptr), len);
552  return result;
553 }
554 
556  if (arr.is_null) return StringVal::null();
557  StringVal result(context, arr.len);
558  memcpy(result.ptr, arr.ptr, arr.len);
559  InplaceDoubleDecode(reinterpret_cast<char*>(result.ptr), arr.len);
560  return result;
561 }
static void TruncPrepare(FunctionContext *context, FunctionContext::FunctionStateScope scope)
int64_t time_of_day
Nanoseconds in current day.
Definition: udf.h:499
TimestampValue TruncWW(const date &orig_date)
static StringVal ToVector(FunctionContext *context, int n, const DoubleVal *values)
static void SwappedExtractPrepare(FunctionContext *context, FunctionContext::FunctionStateScope scope)
static TinyIntVal MinTinyInt(FunctionContext *context)
Definition: udf-builtins.cc:75
static BooleanVal IsNan(FunctionContext *context, const DoubleVal &val)
Definition: udf-builtins.cc:87
date GoBackToWeekday(const date &orig_date, int week_day)
static StringVal EncodeVector(FunctionContext *context, const StringVal &arr)
TimestampValue TruncHour(const date &orig_date, const time_duration &orig_time)
TExtractField::type StrToExtractField(FunctionContext *ctx, const StringVal &unit_str)
static StringVal DecodeVector(FunctionContext *context, const StringVal &arr)
Converts a printable ascii encoding of a vector to a double[] stored as a StringVal.
void InplaceDoubleDecode(char *arr, uint64_t len)
static TimestampVal Trunc(FunctionContext *context, const TimestampVal &date, const StringVal &unit_str)
static IntVal MinInt(FunctionContext *context)
Definition: udf-builtins.cc:71
TimestampValue TruncMonth(const date &orig_date)
static SmallIntVal MinSmallInt(FunctionContext *context)
Definition: udf-builtins.cc:79
const boost::posix_time::time_duration & time() const
int32_t date
Gregorian date. This has the same binary format as boost::gregorian::date.
Definition: udf.h:497
TimestampValue TruncDay(const date &orig_date)
This object has a compatible storage format with boost::ptime.
Definition: udf.h:495
uint8_t * ptr
Definition: udf.h:523
static StringVal PrintVector(FunctionContext *context, const StringVal &arr)
Converts a double[] stored as a StringVal into a human readable string.
static BigIntVal MinBigInt(FunctionContext *context)
Definition: udf-builtins.cc:83
static DoubleVal VectorGet(FunctionContext *context, const BigIntVal &n, const StringVal &arr)
Returns the n-th (0-indexed) element of a double[] stored as a StringVal.
static SmallIntVal MaxSmallInt(FunctionContext *context)
Definition: udf-builtins.cc:63
bool ValidateMADlibVector(FunctionContext *context, const StringVal &arr)
void ToTimestampVal(impala_udf::TimestampVal *tv) const
bool is_null
Definition: udf.h:359
static int RoundDown(int value, int factor)
Returns 'value' rounded down to the nearest multiple of 'factor'.
Definition: bit-util.h:42
TimestampValue TruncQuarter(const date &orig_date)
TimestampValue TruncMinute(const date &orig_date, const time_duration &orig_time)
TimestampValue TruncYear(const date &orig_date)
static StringVal Lower(FunctionContext *context, const StringVal &str)
Definition: udf-builtins.cc:46
static DoubleVal Pi(FunctionContext *context)
Definition: udf-builtins.cc:42
const ptime EPOCH
void * GetFunctionState(FunctionStateScope scope) const
Definition: udf-ir.cc:38
void Free(uint8_t *buffer)
Frees a buffer returned from Allocate() or Reallocate()
Definition: udf.cc:291
bool IsArgConstant(int arg_idx) const
Definition: udf-ir.cc:20
void SetFunctionState(FunctionStateScope scope, void *ptr)
Definition: udf.cc:370
TruncUnit::Type StrToTruncUnit(FunctionContext *ctx, const StringVal &unit_str)
static TimestampValue FromTimestampVal(const impala_udf::TimestampVal &udf_value)
static DoubleVal Abs(FunctionContext *context, const DoubleVal &val)
Definition: udf-builtins.cc:37
uint8_t * Allocate(int byte_size)
Definition: udf.cc:262
static BooleanVal IsInf(FunctionContext *context, const DoubleVal &val)
Definition: udf-builtins.cc:92
static IntVal Extract(FunctionContext *context, const StringVal &field_str, const TimestampVal &date)
This is used by the DATE_PART function.
void InplaceDoubleEncode(double *arr, uint64_t len)
TimestampValue TruncDayOfWeek(const date &orig_date)
TimestampValue TruncW(const date &orig_date)
static void TruncClose(FunctionContext *context, FunctionContext::FunctionStateScope scope)
static void ExtractClose(FunctionContext *context, FunctionContext::FunctionStateScope scope)
This is used by both EXTRACT and DATE_PART.
void SetError(const char *error_msg)
Definition: udf.cc:332
const boost::gregorian::date & date() const
AnyVal * GetConstantArg(int arg_idx) const
Definition: udf-ir.cc:25
static BigIntVal MaxBigInt(FunctionContext *context)
Definition: udf-builtins.cc:67
static IntVal MaxInt(FunctionContext *context)
Definition: udf-builtins.cc:55
static void ExtractPrepare(FunctionContext *context, FunctionContext::FunctionStateScope scope)
This is used by the DATE_PART function.
static TinyIntVal MaxTinyInt(FunctionContext *context)
Definition: udf-builtins.cc:59