~drizzle-trunk/drizzle/development

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
/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
 *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
 *
 *  Copyright (C) 2008 Sun Microsystems, Inc.
 *
 *  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 <config.h>

#include <drizzled/function/time/to_days.h>
#include <drizzled/error.h>
#include <drizzled/temporal.h>

namespace drizzled
{

/* 
 * We intepret the first argument as a DateTime and then convert
 * it to a Julian Day Number and return it.
 */
int64_t Item_func_to_days::val_int()
{
  assert(fixed);

  /* We return NULL from FROM_DAYS() only when supplied a NULL argument */
  if (args[0]->null_value)
  {
    null_value= true;
    return false;
  }

  /*
   * We attempt to convert the first argument into a
   * temporal value.  If the conversion is successful, 
   * we know that a conversion to a Julian Day Number
   * is always possible.  Upon successful conversion, 
   * we return the Julian Day Number.  If no conversion
   * was possible into a temporal value, we throw an 
   * error and return 0, setting the null_value flag to true.
   */
  /* Grab the first argument as a DateTime object */
  DateTime temporal;
  Item_result arg0_result_type= args[0]->result_type();
  
  switch (arg0_result_type)
  {
    case REAL_RESULT:
    case DECIMAL_RESULT: 
      /* 
       * For doubles supplied, interpret the arg as a string, 
       * so intentionally fall-through here...
       * This allows us to accept double parameters like 
       * 19971231235959.01 and interpret it the way MySQL does:
       * as a TIMESTAMP-like thing with a microsecond component.
       * Ugh, but need to keep backwards-compat.
       */
    case STRING_RESULT:
      {
        char buff[DRIZZLE_MAX_LENGTH_DATETIME_AS_STRING];
        String tmp(buff,sizeof(buff), &my_charset_utf8_bin);
        String *res= args[0]->val_str(&tmp);

        if (! res)
        {
          /* 
           * Likely a nested function issue where the nested
           * function had bad input.  We rely on the nested
           * function my_error() and simply return false here.
           */
          return false;
        }

        if (res != &tmp)
        {
          tmp.copy(*res);
        }

        if (! temporal.from_string(tmp.c_ptr(), tmp.length()))
        {
          /* 
          * Could not interpret the function argument as a temporal value, 
          * so throw an error and return 0
          */
          my_error(ER_INVALID_DATETIME_VALUE, MYF(0), tmp.c_ptr());
          return 0;
        }
      }
      break;
    case INT_RESULT:
      if (temporal.from_int64_t(args[0]->val_int()))
        break;
      /* Intentionally fall-through on invalid conversion from integer */
    default:
      {
        /* 
        * Could not interpret the function argument as a temporal value, 
        * so throw an error and return 0
        */
        null_value= true;
        char buff[DRIZZLE_MAX_LENGTH_DATETIME_AS_STRING];
        String tmp(buff,sizeof(buff), &my_charset_utf8_bin);
        String *res;

        res= args[0]->val_str(&tmp);

        if (! res)
        {
          /* 
           * Likely a nested function issue where the nested
           * function had bad input.  We rely on the nested
           * function my_error() and simply return false here.
           */
          return false;
        }

        if (res != &tmp)
        {
          tmp.copy(*res);
        }

        my_error(ER_INVALID_DATETIME_VALUE, MYF(0), tmp.c_ptr());
        return 0;
      }
  }
  int64_t julian_day_number;
  temporal.to_julian_day_number(&julian_day_number);
  return julian_day_number;
}

int64_t Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp)
{
  assert(fixed);

  /*
   * We attempt to convert the first argument into a
   * temporal value.  If the conversion is successful, 
   * we know that a conversion to a Julian Day Number
   * is always possible. Depending on whether the 
   * first argument is a Date, or a DateTime with no
   * time-portion, we return the Julian Day Number or
   * the appropriate end-point integer.
   */
  /* Grab the first argument as a DateTime object */
  DateTime temporal;
  Item_result arg0_result_type= args[0]->result_type();
  
  switch (arg0_result_type)
  {
    case REAL_RESULT:
    case DECIMAL_RESULT: 
      /* 
       * For doubles supplied, interpret the arg as a string, 
       * so intentionally fall-through here...
       * This allows us to accept double parameters like 
       * 19971231235959.01 and interpret it the way MySQL does:
       * as a TIMESTAMP-like thing with a microsecond component.
       * Ugh, but need to keep backwards-compat.
       */
    case STRING_RESULT:
      {
        char buff[DRIZZLE_MAX_LENGTH_DATETIME_AS_STRING];
        String tmp(buff,sizeof(buff), &my_charset_utf8_bin);
        String *res= args[0]->val_str(&tmp);

        if (! res)
        {
          /* 
           * Likely a nested function issue where the nested
           * function had bad input.  We rely on the nested
           * function my_error() and simply return false here.
           */
          return 0;
        }

        if (res != &tmp)
        {
          tmp.copy(*res);
        }

        if (! temporal.from_string(tmp.c_ptr(), tmp.length()))
        {
          /* 
          * Could not interpret the function argument as a temporal value, 
          * so throw an error and return 0
          */
          my_error(ER_INVALID_DATETIME_VALUE, MYF(0), tmp.c_ptr());
          return 0;
        }
      }
      break;
    case INT_RESULT:
      if (temporal.from_int64_t(args[0]->val_int()))
        break;
      /* Intentionally fall-through on invalid conversion from integer */
    default:
      {
        /* 
        * Could not interpret the function argument as a temporal value, 
        * so throw an error and return 0
        */
        null_value= true;
        char buff[DRIZZLE_MAX_LENGTH_DATETIME_AS_STRING];
        String tmp(buff,sizeof(buff), &my_charset_utf8_bin);
        String *res;

        res= args[0]->val_str(&tmp);

        if (! res)
        {
          /* 
           * Likely a nested function issue where the nested
           * function had bad input.  We rely on the nested
           * function my_error() and simply return false here.
           */
          return 0;
        }

        if (res != &tmp)
        {
          tmp.copy(*res);
        }

        my_error(ER_INVALID_DATETIME_VALUE, MYF(0), tmp.c_ptr());
        return 0;
      }
  }

  if (null_value == true)
  {
    /* got NULL, leave the incl_endp intact */
    return INT64_MIN;
  }

  int64_t julian_day_number;
  temporal.to_julian_day_number(&julian_day_number);

  if (args[0]->field_type() == DRIZZLE_TYPE_DATE)
  {
    /* TO_DAYS() is strictly monotonic for dates, leave incl_endp intact */
    return julian_day_number;
  }

  /*
    Handle the special but practically useful case of datetime values that
    point to day bound ("strictly less" comparison stays intact):

      col < '2007-09-15 00:00:00'  -> TO_DAYS(col) <  TO_DAYS('2007-09-15')

    which is different from the general case ("strictly less" changes to
    "less or equal"):

      col < '2007-09-15 12:34:56'  -> TO_DAYS(col) <= TO_DAYS('2007-09-15')
  */
  if (!left_endp && ! (
                    temporal.hours() 
                    || temporal.minutes()
                    || temporal.seconds() 
                    || temporal.useconds()
                    || temporal.nseconds()
                    )
                    )
    ; /* do nothing */
  else
    *incl_endp= true;
  return julian_day_number;
}

} /* namespace drizzled */