1 /*
2  Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License, version 2.0,
6  as published by the Free Software Foundation.
7 
8  This program is also distributed with certain software (including
9  but not limited to OpenSSL) that is licensed under separate terms,
10  as designated in a particular file or component or in included license
11  documentation.  The authors of MySQL hereby grant you an additional
12  permission to link the program and your derivative works with the
13  separately licensed software that they have included with MySQL.
14 
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License, version 2.0, for more details.
19 
20  You should have received a copy of the GNU General Public License
21  along with this program; if not, write to the Free Software
22  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23  */
24 
25 #include <ctype.h>
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <math.h>
30 
31 #ifdef WIN32
32 #include <float.h>
33 #endif
34 
35 /**
36  * Include NDB file CharsetMap.hpp first to avoid clash between NDB
37  * define Int32 and v8::Int32 in declaration of CharsetMap::recode().
38  */
39 #include "CharsetMap.hpp"
40 
41 #include "adapter_global.h"
42 #include "NdbTypeEncoders.h"
43 #include "js_wrapper_macros.h"
44 #include "JsWrapper.h"
45 
46 #include "node.h"
47 #include "node_buffer.h"
48 
49 #include "decimal_utils.hpp"
50 #include "EncoderCharset.h"
51 
52 using namespace v8;
53 
54 extern void freeBufferContentsFromJs(char *, void *);  // in BlobHandler.cpp
55 
56 Eternal<String>    /* keys of MySQLTime (Adapter/impl/common/MySQLTime.js) */
57   K_sign,
58   K_year,
59   K_month,
60   K_day,
61   K_hour,
62   K_minute,
63   K_second,
64   K_microsec,
65   K_fsp,
66   K_valid;
67 
68 Eternal<Value>   /* SQLState Error Codes */
69   K_22000_DataError,
70   K_22001_StringTooLong,
71   K_22003_OutOfRange,
72   K_22007_InvalidDatetime,
73   K_0F001_Bad_BLOB,
74   K_HY000;
75 
76 #define writerOK Local<Value>::New(isolate, Undefined(isolate))
77 
78 Isolate * isolate;
79 
80 #define ENCODER(A, B, C) NdbTypeEncoder A = { & B, & C, 0 }
81 
82 #define DECLARE_ENCODER(TYPE) \
83   EncoderReader TYPE##Reader; \
84   EncoderWriter TYPE##Writer; \
85   ENCODER(TYPE##Encoder, TYPE##Reader, TYPE##Writer)
86 
87 #define DECLARE_ENCODER_TEMPLATES(TYPE) \
88   template <typename T> Local<Value> TYPE##Reader(const NdbDictionary::Column *,\
89     char *, uint32_t); \
90   template <typename T> Local<Value> TYPE##Writer(const NdbDictionary::Column *, \
91     Handle<Value>, char *, uint32_t);
92 
93 DECLARE_ENCODER(UnsupportedType);
94 
95 DECLARE_ENCODER(Int);
96 DECLARE_ENCODER(UnsignedInt);
97 DECLARE_ENCODER_TEMPLATES(smallint);
98 ENCODER(TinyIntEncoder, smallintReader<int8_t>, smallintWriter<int8_t>);
99 ENCODER(TinyUnsignedEncoder, smallintReader<uint8_t>, smallintWriter<uint8_t>);
100 ENCODER(SmallIntEncoder, smallintReader<int16_t>, smallintWriter<int16_t>);
101 ENCODER(SmallUnsignedEncoder, smallintReader<uint16_t>, smallintWriter<uint16_t>);
102 DECLARE_ENCODER(Medium);
103 DECLARE_ENCODER(MediumUnsigned);
104 DECLARE_ENCODER_TEMPLATES(bigint);
105 ENCODER(BigintEncoder, bigintReader<int64_t>, bigintWriter<int64_t>);
106 ENCODER(BigintUnsignedEncoder, bigintReader<uint64_t>, bigintWriter<uint64_t>);
107 
108 DECLARE_ENCODER_TEMPLATES(fp);
109 ENCODER(FloatEncoder, fpReader<float>, fpWriter<float>);
110 ENCODER(DoubleEncoder, fpReader<double>, fpWriter<double>);
111 
112 DECLARE_ENCODER(Binary);
113 DECLARE_ENCODER_TEMPLATES(varbinary);
114 ENCODER(VarbinaryEncoder, varbinaryReader<uint8_t>, varbinaryWriter<uint8_t>);
115 ENCODER(LongVarbinaryEncoder, varbinaryReader<uint16_t>, varbinaryWriter<uint16_t>);
116 
117 DECLARE_ENCODER(Char);
118 DECLARE_ENCODER_TEMPLATES(varchar);
119 ENCODER(VarcharEncoder, varcharReader<uint8_t>, varcharWriter<uint8_t>);
120 ENCODER(LongVarcharEncoder, varcharReader<uint16_t>, varcharWriter<uint16_t>);
121 
122 DECLARE_ENCODER(Year);
123 DECLARE_ENCODER(Timestamp);
124 DECLARE_ENCODER(Datetime);
125 DECLARE_ENCODER(Timestamp2);
126 DECLARE_ENCODER(Datetime2);
127 DECLARE_ENCODER(Time);
128 DECLARE_ENCODER(Time2);
129 DECLARE_ENCODER(Date);
130 DECLARE_ENCODER(Blob);
131 
132 DECLARE_ENCODER(Decimal);
133 EncoderWriter UnsignedDecimalWriter;
134 ENCODER(UnsignedDecimalEncoder, DecimalReader, UnsignedDecimalWriter);
135 
136 const NdbTypeEncoder * AllEncoders[NDB_TYPE_MAX] = {
137   & UnsupportedTypeEncoder,               // 0
138   & TinyIntEncoder,                       // 1  TINY INT
139   & TinyUnsignedEncoder,                  // 2  TINY UNSIGNED
140   & SmallIntEncoder,                      // 3  SMALL INT
141   & SmallUnsignedEncoder,                 // 4  SMALL UNSIGNED
142   & MediumEncoder,                        // 5  MEDIUM INT
143   & MediumUnsignedEncoder,                // 6  MEDIUM UNSIGNED
144   & IntEncoder,                           // 7  INT
145   & UnsignedIntEncoder,                   // 8  UNSIGNED
146   & BigintEncoder,                        // 9  BIGINT
147   & BigintUnsignedEncoder,                // 10 BIG UNSIGNED
148   & FloatEncoder,                         // 11 FLOAT
149   & DoubleEncoder,                        // 12 DOUBLE
150   & UnsupportedTypeEncoder,               // 13 OLDDECIMAL
151   & CharEncoder,                          // 14 CHAR
152   & VarcharEncoder,                       // 15 VARCHAR
153   & BinaryEncoder,                        // 16 BINARY
154   & VarbinaryEncoder,                     // 17 VARBINARY
155   & DatetimeEncoder,                      // 18 DATETIME
156   & DateEncoder,                          // 19 DATE
157   & BlobEncoder,                          // 20 BLOB
158   & UnsupportedTypeEncoder,               // 21 TEXT
159   & UnsupportedTypeEncoder,               // 22 BIT
160   & LongVarcharEncoder,                   // 23 LONGVARCHAR
161   & LongVarbinaryEncoder,                 // 24 LONGVARBINARY
162   & TimeEncoder,                          // 25 TIME
163   & YearEncoder,                          // 26 YEAR
164   & TimestampEncoder,                     // 27 TIMESTAMP
165   & UnsupportedTypeEncoder,               // 28 OLDDECIMAL UNSIGNED
166   & DecimalEncoder,                       // 29 DECIMAL
167   & UnsignedDecimalEncoder                // 30 DECIMAL UNSIGNED
168 #if NDB_TYPE_MAX > 31
169   ,
170   & Time2Encoder,                         // 31 TIME2
171   & Datetime2Encoder,                     // 32 DATETIME2
172   & Timestamp2Encoder,                    // 33 TIMESTAMP2
173 #endif
174 };
175 
176 
getEncoderForColumn(const NdbDictionary::Column * col)177 const NdbTypeEncoder * getEncoderForColumn(const NdbDictionary::Column *col) {
178   return AllEncoders[col->getType()];
179 }
180 
181 
182 /* read(col, buffer, offset)
183 */
encoderRead(const Arguments & args)184 void encoderRead(const Arguments & args) {
185   isolate = args.GetIsolate();
186   EscapableHandleScope scope(isolate);
187 
188   const NdbDictionary::Column * col =
189     unwrapPointer<const NdbDictionary::Column *>(args[0]->ToObject());
190   const NdbTypeEncoder * encoder = getEncoderForColumn(col);
191   char * buffer = node::Buffer::Data(args[1]->ToObject());
192 
193   args.GetReturnValue().Set(
194     scope.Escape(encoder->read(col, buffer, args[2]->Uint32Value())));
195 }
196 
197 
198 /* write(col, value, buffer, offset)
199 */
encoderWrite(const Arguments & args)200 void encoderWrite(const Arguments & args) {
201   isolate = args.GetIsolate();
202   EscapableHandleScope scope(isolate);
203 
204   const NdbDictionary::Column * col =
205     unwrapPointer<const NdbDictionary::Column *>(args[0]->ToObject());
206   const NdbTypeEncoder * encoder = getEncoderForColumn(col);
207   char * buffer = node::Buffer::Data(args[2]->ToObject());
208   uint32_t offset = args[3]->Uint32Value();
209 
210   args.GetReturnValue().Set(
211     scope.Escape(encoder->write(col, args[1], buffer, offset)));
212 }
213 
214 
215 /* String Encoder Statistics */
216 struct encoder_stats_t {
217   unsigned read_strings_externalized; // JS Strings that reference ASCII or UTF16LE buffers
218   unsigned read_strings_created; // JS Strings created from UTF-8 representation
219   unsigned read_strings_recoded; // Reads recoded from MySQL Charset to UTF-8
220   unsigned externalized_text_writes;  // String reused as TEXT buffer (no copying)
221   unsigned direct_writes;  // ASCII/UTF16LE/UTF8 written directly to DB buffer
222   unsigned recode_writes;  // Writes recoded from UTF8 to MySQL Charset
223 } stats;
224 
225 
226 /* Exports to JavaScript
227 */
GET_read_strings_externalized(Local<String>,const AccessorInfo & info)228 void GET_read_strings_externalized(Local<String>, const AccessorInfo & info) {
229   info.GetReturnValue().Set(stats.read_strings_externalized);
230 }
231 
GET_read_strings_created(Local<String>,const AccessorInfo & info)232 void GET_read_strings_created(Local<String>, const AccessorInfo & info) {
233   info.GetReturnValue().Set(stats.read_strings_created);
234 }
235 
GET_read_strings_recoded(Local<String>,const AccessorInfo & info)236 void GET_read_strings_recoded(Local<String>, const AccessorInfo & info) {
237   info.GetReturnValue().Set(stats.read_strings_recoded);
238 }
239 
GET_externalized_text_writes(Local<String>,const AccessorInfo & info)240 void GET_externalized_text_writes(Local<String>, const AccessorInfo & info){
241   info.GetReturnValue().Set(stats.externalized_text_writes);
242 }
243 
GET_direct_writes(Local<String>,const AccessorInfo & info)244 void GET_direct_writes(Local<String>, const AccessorInfo & info) {
245   info.GetReturnValue().Set(stats.direct_writes);
246 }
247 
GET_recode_writes(Local<String>,const AccessorInfo & info)248 void GET_recode_writes(Local<String>, const AccessorInfo & info) {
249   info.GetReturnValue().Set(stats.recode_writes);
250 }
251 
252 void bufferForText(const Arguments &);
253 void textFromBuffer(const Arguments &);
254 
255 #define SET_KEY(X,Y) X.Set(isolate, String::NewFromUtf8(isolate, Y))
256 
NdbTypeEncoders_initOnLoad(Handle<Object> target)257 void NdbTypeEncoders_initOnLoad(Handle<Object> target) {
258   isolate = Isolate::GetCurrent();
259 
260   DEFINE_JS_FUNCTION(target, "encoderRead", encoderRead);
261   DEFINE_JS_FUNCTION(target, "encoderWrite", encoderWrite);
262   DEFINE_JS_FUNCTION(target, "bufferForText", bufferForText);
263   DEFINE_JS_FUNCTION(target, "textFromBuffer", textFromBuffer);
264   SET_KEY(K_sign, "sign");
265   SET_KEY(K_year, "year");
266   SET_KEY(K_month, "month");
267   SET_KEY(K_day, "day");
268   SET_KEY(K_hour, "hour");
269   SET_KEY(K_minute, "minute");
270   SET_KEY(K_second, "second");
271   SET_KEY(K_microsec, "microsec");
272   SET_KEY(K_fsp, "fsp");
273   SET_KEY(K_valid, "valid");
274   SET_KEY(K_22000_DataError, "22000");
275   SET_KEY(K_22001_StringTooLong, "22001");
276   SET_KEY(K_22003_OutOfRange, "22003");
277   SET_KEY(K_22007_InvalidDatetime, "22007");
278   SET_KEY(K_0F001_Bad_BLOB, "0F001");
279   SET_KEY(K_HY000, "HY000");
280 
281   Local<Object> s = Object::New(isolate);
282   target->Set(NEW_SYMBOL("encoder_stats"), s);
283   DEFINE_JS_ACCESSOR(s, "read_strings_externalized",
284                      GET_read_strings_externalized);
285   DEFINE_JS_ACCESSOR(s, "read_strings_created", GET_read_strings_created);
286   DEFINE_JS_ACCESSOR(s, "read_strings_recoded", GET_read_strings_recoded);
287   DEFINE_JS_ACCESSOR(s, "externalized_text_writes", GET_externalized_text_writes);
288   DEFINE_JS_ACCESSOR(s, "direct_writes", GET_direct_writes);
289   DEFINE_JS_ACCESSOR(s, "recode_writes", GET_recode_writes);
290 }
291 
292 
293 
294 /*************************************************************
295    ******                                              *****
296       *****                                         *****
297          *****              Macros               *****/
298 
299 
300 /* FOR INTEGER TYPES, x86 allows unaligned access, but most other machines do not.
301    FOR FLOATING POINT TYPES: access must be aligned on all architectures.
302    v8 supports ARM and MIPS along with x86 and x86_64.
303    Wherever in the code there is a LOAD_ALIGNED_DATA macro, we assume the record
304    has been laid out with necessary padding for alignment.
305 */
306 
307 /* Assign x := *buf, assuming correct alignment */
308 #define LOAD_ALIGNED_DATA(Type, x, buf) \
309 Type x = *((Type *) (buf));
310 
311 /* Assign *buf := x, assuming correct alignment */
312 #define STORE_ALIGNED_DATA(Type, x, buf) \
313 *((Type *) (buf)) = (Type) x;
314 
315 /* Assign x := *buf */
316 #define ALIGN_AND_LOAD(Type, x, buf) \
317 Type x; \
318 memcpy(&x, buf, sizeof(x));
319 
320 /* Assign *buf := x */
321 #define ALIGN_AND_STORE(Type, x, buf) \
322 Type tmp_value = (Type) x; \
323 memcpy(buf, &tmp_value, sizeof(tmp_value));
324 
325 #define sint3korr(A)  ((int32_t) ((((uint8_t) (A)[2]) & 128) ? \
326                                   (((uint32_t) 255L << 24) | \
327                                   (((uint32_t) (uint8_t) (A)[2]) << 16) |\
328                                   (((uint32_t) (uint8_t) (A)[1]) << 8) | \
329                                    ((uint32_t) (uint8_t) (A)[0])) : \
330                                  (((uint32_t) (uint8_t) (A)[2]) << 16) |\
331                                  (((uint32_t) (uint8_t) (A)[1]) << 8) | \
332                                   ((uint32_t) (uint8_t) (A)[0])))
333 
334 #define uint3korr(A)  (uint32_t) (((uint32_t) ((uint8_t) (A)[0])) +\
335                                   (((uint32_t) ((uint8_t) (A)[1])) << 8) +\
336                                   (((uint32_t) ((uint8_t) (A)[2])) << 16))
337 
338 
339 /*************************************************************
340    ******                                              *****
341       *****                                         *****
342          *****              Utilities            *****/
343 
344 /* File-scope global return from succesful write encoders:
345 */
346 template <typename INTSZ> Local<Value> checkNumber(double);
347 
checkNumber(double d)348 template<> inline Local<Value> checkNumber<int>(double d) {
349   if(isfinite(d)) {
350     return (d >= -2147483648.0 && d <= 2147483648.0) ? writerOK : K_22003_OutOfRange.Get(isolate);
351   }
352   return K_HY000.Get(isolate);
353 }
354 
checkNumber(double d)355 template<> inline Local<Value> checkNumber<uint32_t>(double d) {
356   if(isfinite(d)) {
357     return (d >= 0 && d < 4294967296.0) ? writerOK : K_22003_OutOfRange.Get(isolate);
358   }
359   return K_HY000.Get(isolate);
360 }
361 
362 template <typename INTSZ> bool checkIntValue(int);
363 
getStatusForValue(double d)364 template <typename INTSZ> inline Local<Value> getStatusForValue(double d) {
365   if(isfinite(d)) {
366     return checkIntValue<INTSZ>(static_cast<INTSZ>(d)) ? writerOK : K_22003_OutOfRange.Get(isolate);
367   }
368   return K_HY000.Get(isolate);
369 }
370 
checkIntValue(int r)371 template<> inline bool checkIntValue<int8_t>(int r) {
372   return (r >= -128 && r < 128);
373 }
374 
checkIntValue(int r)375 template<> inline bool checkIntValue<uint8_t>(int r) {
376   return (r >= 0 && r < 256);
377 }
378 
checkIntValue(int r)379 template<> inline bool checkIntValue<int16_t>(int r) {
380   return (r >= -32768 && r < 32768);
381 }
382 
checkIntValue(int r)383 template<> inline bool checkIntValue<uint16_t>(int r) {
384   return (r >= 0 && r < 65536);
385 }
386 
checkMedium(int r)387 inline Local<Value> checkMedium(int r) {
388   return (r >= -8338608 && r < 8338608) ? writerOK :  K_22003_OutOfRange.Get(isolate);
389 }
390 
getStatusForMedium(double dval)391 inline Local<Value> getStatusForMedium(double dval) {
392   if(isfinite(dval)) {
393     return checkMedium(static_cast<int>(dval));
394   }
395   return K_HY000.Get(isolate);
396 }
397 
checkUnsignedMedium(int r)398 inline Local<Value> checkUnsignedMedium(int r) {
399   return (r >= 0 && r < 16277216) ? writerOK :  K_22003_OutOfRange.Get(isolate);
400 }
401 
getStatusForUnsignedMedium(double dval)402 inline Local<Value> getStatusForUnsignedMedium(double dval) {
403   if(isfinite(dval)) {
404     return checkUnsignedMedium(static_cast<int>(dval));
405   }
406   return K_HY000.Get(isolate);
407 }
408 
writeSignedMedium(int8_t * cbuf,int mval)409 inline void writeSignedMedium(int8_t * cbuf, int mval) {
410   cbuf[0] = (int8_t) (mval);
411   cbuf[1] = (int8_t) (mval >> 8);
412   cbuf[2] = (int8_t) (mval >> 16);
413 }
414 
writeUnsignedMedium(uint8_t * cbuf,uint32_t mval)415 inline void writeUnsignedMedium(uint8_t * cbuf, uint32_t mval) {
416   cbuf[0] = (uint8_t) (mval);
417   cbuf[1] = (uint8_t) (mval >> 8);
418   cbuf[2] = (uint8_t) (mval >> 16);
419 }
420 
421 /* bigendian utilities, used with the wl#946 temporal types.
422    Derived from ndb/src/comon/util/NdbSqlUtil.cpp
423 */
unpack_bigendian(const char * buf,unsigned int len)424 static uint64_t unpack_bigendian(const char * buf, unsigned int len) {
425   uint64_t val = 0;
426   unsigned int i = len;
427   int shift = 0;
428   while (i != 0) {
429     i--;
430     uint64_t v = (uint8_t) buf[i];
431     val += (v << shift);
432     shift += 8;
433   }
434   return val;
435 }
436 
pack_bigendian(uint64_t val,char * buf,unsigned int len)437 void pack_bigendian(uint64_t val, char * buf, unsigned int len) {
438   uint8_t b[8];
439   unsigned int i = 0, j = 0;
440   while (i  < len) {
441     b[i] = (uint8_t)(val & 255);
442     val >>= 8;
443     i++;
444   }
445   while (i != 0) {
446     buf[--i] = b[j++];
447   }
448 }
449 
450 
451 /*************************************************************
452    ******                                              *****
453       *****                                         *****
454          *****         Implementations           *****/
455 
456 
457 
458 // UnsupportedType
UnsupportedTypeReader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)459 Local<Value> UnsupportedTypeReader(const NdbDictionary::Column *col,
460                                     char *buffer, uint32_t offset) {
461   //TODO EXCEPTION
462   return writerOK;
463 }
464 
UnsupportedTypeWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)465 Local<Value> UnsupportedTypeWriter(const NdbDictionary::Column * col,
466                                     Handle<Value> value,
467                                     char *buffer, uint32_t offset) {
468   //TODO EXCEPTION
469   return writerOK;
470 }
471 
472 // Int
IntReader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)473 Local<Value> IntReader(const NdbDictionary::Column *col,
474                         char *buffer, uint32_t offset) {
475   LOAD_ALIGNED_DATA(int, i, buffer+offset);
476   return Integer::New(isolate, i);
477 }
478 
IntWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)479 Local<Value> IntWriter(const NdbDictionary::Column * col,
480                         Handle<Value> value,
481                         char *buffer, uint32_t offset) {
482   int *ipos = (int *) (buffer+offset);
483   Local<Value> status;
484 
485   if(value->IsInt32()) {
486     *ipos = value->Int32Value();
487     status = writerOK;
488   }
489   else {
490     double dval = value->ToNumber()->Value();
491     *ipos = static_cast<int>(rint(dval));
492     status = checkNumber<int>(dval);
493   }
494   return status;
495 }
496 
497 
498 // Unsigned Int
UnsignedIntReader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)499 Local<Value> UnsignedIntReader(const NdbDictionary::Column *col,
500                                 char *buffer, uint32_t offset) {
501   LOAD_ALIGNED_DATA(uint32_t, i, buffer+offset);
502   return Integer::NewFromUnsigned(isolate, i);
503 }
504 
UnsignedIntWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)505 Local<Value> UnsignedIntWriter(const NdbDictionary::Column * col,
506                                 Handle<Value> value,
507                                 char *buffer, uint32_t offset) {
508   Local<Value> status;
509   uint32_t *ipos = (uint32_t *) (buffer+offset);
510   if(value->IsUint32()) {
511     *ipos = value->Uint32Value();
512     status = writerOK;
513   } else {
514     double dval = value->ToNumber()->Value();
515     *ipos = static_cast<uint32_t>(rint(dval));
516     status = checkNumber<uint32_t>(dval);
517   }
518   return status;
519 }
520 
521 
522 // Templated encoder for TINY and SMALL int types
523 template <typename INTSZ>
smallintReader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)524 Local<Value> smallintReader(const NdbDictionary::Column *col,
525                              char *buffer, uint32_t offset) {
526   LOAD_ALIGNED_DATA(INTSZ, i, buffer+offset);
527   return Integer::New(isolate, i);
528 }
529 
530 
531 template <typename INTSZ>
smallintWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)532 Local<Value> smallintWriter(const NdbDictionary::Column * col,
533                              Handle<Value> value, char *buffer, uint32_t offset) {
534   INTSZ *ipos = (INTSZ *) (buffer+offset);
535   Handle<Value> status;
536   if(value->IsInt32()) {
537     int32_t ival = value->Int32Value();
538     *ipos = static_cast<INTSZ>(ival);
539     status = checkIntValue<INTSZ>(ival) ? writerOK : K_22003_OutOfRange.Get(isolate);
540   } else {
541     double dval = value->ToNumber()->Value();
542     *ipos = static_cast<INTSZ>(dval);
543     status = getStatusForValue<INTSZ>(dval);
544   }
545   return status;
546 }
547 
548 
549 // Medium signed & unsigned int types
MediumReader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)550 Local<Value> MediumReader(const NdbDictionary::Column *col,
551                            char *buffer, uint32_t offset) {
552   char * cbuf = buffer+offset;
553   int i = sint3korr(cbuf);
554   return Integer::New(isolate, i);
555 }
556 
MediumWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)557 Local<Value> MediumWriter(const NdbDictionary::Column * col,
558                            Handle<Value> value, char *buffer, uint32_t offset) {
559   int8_t *cbuf = (int8_t *) (buffer+offset);
560   Local<Value> status;
561   double dval;
562   int chkv;
563   if(value->IsInt32()) {
564     chkv = value->Int32Value();
565     status = checkMedium(chkv);
566   } else {
567     dval = value->ToNumber()->Value();
568     chkv = static_cast<int>(rint(dval));
569     status = getStatusForMedium(dval);
570   }
571   writeSignedMedium(cbuf, chkv);
572 
573   return status;
574 }
575 
MediumUnsignedReader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)576 Local<Value> MediumUnsignedReader(const NdbDictionary::Column *col,
577                                    char *buffer, uint32_t offset) {
578   char * cbuf = buffer+offset;
579   int i = uint3korr(cbuf);
580   return Integer::New(isolate, i);
581 }
582 
MediumUnsignedWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)583 Local<Value> MediumUnsignedWriter(const NdbDictionary::Column * col,
584                                    Handle<Value> value,
585                                    char *buffer, uint32_t offset) {
586   uint8_t *cbuf = (uint8_t *) (buffer+offset);
587   Local<Value> status;
588   double dval;
589   int chkv;
590   if(value->IsInt32()) {
591     chkv = value->Int32Value();
592     status = checkUnsignedMedium(chkv);
593   } else {
594     dval = value->ToNumber()->Value();
595     chkv = static_cast<int>(rint(dval));
596     status = getStatusForUnsignedMedium(dval);
597   }
598   writeUnsignedMedium(cbuf, chkv);
599   return status;
600 }
601 
602 
603 // Bigint encoders
604 template<typename T> bool stringToBigint(const char *, T *);
605 
stringToBigint(const char * str,int64_t * out)606 template<> bool stringToBigint<int64_t>(const char *str, int64_t *out) {
607   char *endptr;
608   bool rval = false;
609   errno = 0;
610   long long ll = strtoll(str, &endptr, 10);
611   if (errno != ERANGE &&
612       ( (isspace((unsigned char) *endptr) ||
613         (*endptr == '\0' && endptr != str)
614       ))) {
615      *out = ll;
616      rval = true;
617   }
618   return rval;
619 }
620 
stringToBigint(const char * str,uint64_t * out)621 template<> bool stringToBigint<uint64_t>(const char *str, uint64_t *out) {
622   char *endptr;
623   errno = 0;
624   unsigned long long ull = strtoull(str, &endptr, 10);
625   if(errno == ERANGE) return false;
626   if(((long long) ull < 0) && (strchr(str, '-') != NULL)) return false;
627   if(isspace((unsigned char) *endptr) || (*endptr == '\0' && endptr != str)) {
628     *out = ull;
629     return true;
630   }
631   return false;
632 }
633 
634 template<typename T> bool writeBigint(Handle<Value>, T *);
635 
writeBigint(Handle<Value> val,int64_t * ipos)636 template<> inline bool writeBigint<int64_t>(Handle<Value> val, int64_t *ipos) {
637   if(val->IsInt32()) {
638     *ipos = val->Int32Value();
639     return true;
640   }
641   return false;
642 }
643 
writeBigint(Handle<Value> val,uint64_t * ipos)644 template<> inline bool writeBigint<uint64_t>(Handle<Value> val, uint64_t *ipos) {
645   if(val->IsUint32()) {
646     *ipos = val->Uint32Value();
647     return true;
648   }
649   return false;
650 }
651 
652 template<typename T> void bigintToString(char *, T);
653 
bigintToString(char * strbuf,int64_t bigint)654 template<> inline void bigintToString<int64_t>(char * strbuf, int64_t bigint) {
655   sprintf(strbuf, "%lld", (long long int) bigint);
656 }
657 
bigintToString(char * strbuf,uint64_t bigint)658 template<> inline void bigintToString<uint64_t>(char * strbuf, uint64_t bigint) {
659   sprintf(strbuf, "%llu", (long long unsigned int) bigint);
660 }
661 
662 template <typename BIGT>
bigintReader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)663 Local<Value> bigintReader(const NdbDictionary::Column *col,
664                             char *buffer, uint32_t offset) {
665   char strbuf[32];
666   LOAD_ALIGNED_DATA(BIGT, bigint, buffer+offset);
667   bigintToString(strbuf, bigint);
668   return String::NewFromUtf8(isolate, strbuf);
669 }
670 
671 template <typename BIGT>
bigintWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)672 Local<Value> bigintWriter(const NdbDictionary::Column *col,
673                            Handle<Value> value, char *buffer, uint32_t offset) {
674   unsigned char strbuf[32];
675   BIGT *ipos = (BIGT *) (buffer+offset);
676   bool valid = writeBigint(value, ipos);  // try fast track
677   if(! valid) {  // slow track
678     value->ToString()->WriteOneByte(strbuf, 0, 32);
679     valid = stringToBigint((char *)strbuf, ipos);
680   }
681   return valid ? writerOK : K_22003_OutOfRange.Get(isolate);
682 }
683 
684 // Decimal.  JS Value to and from decimal types is treated as a string.
DecimalReader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)685 Local<Value> DecimalReader(const NdbDictionary::Column *col,
686                             char *buffer, uint32_t offset) {
687   char strbuf[96];
688   int scale = col->getScale();
689   int prec  = col->getPrecision();
690   int len = scale + prec + 3;
691   decimal_bin2str(buffer + offset, col->getSizeInBytes(),
692                   prec, scale, strbuf, len);
693   return String::NewFromUtf8(isolate, strbuf);
694 }
695 
DecimalWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)696 Local<Value> DecimalWriter(const NdbDictionary::Column *col,
697                             Handle<Value> value, char *buffer, uint32_t offset) {
698   unsigned char strbuf[96];
699   if(! (isfinite(value->NumberValue()))) {
700     return K_HY000.Get(isolate);
701   }
702   int length = value->ToString()->WriteOneByte(strbuf, 0, 96);
703   int status = decimal_str2bin((const char *) strbuf, length,
704                                col->getPrecision(), col->getScale(),
705                                buffer + offset, col->getSizeInBytes());
706   return status ? K_22003_OutOfRange.Get(isolate) : writerOK;
707 }
708 
709 
710 // Unsigned Decimal.  Writer adds boundary checking.
UnsignedDecimalWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)711 Local<Value> UnsignedDecimalWriter(const NdbDictionary::Column *col,
712                                     Handle<Value> value, char *buffer,
713                                     uint32_t offset) {
714   return value->NumberValue() >= 0 ?
715     DecimalWriter(col, value, buffer, offset) :
716     K_22003_OutOfRange.Get(isolate);
717 }
718 
719 
720 // Templated encoder for float and double
721 template<typename FPT>
fpReader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)722 Local<Value> fpReader(const NdbDictionary::Column *col,
723                        char *buffer, uint32_t offset) {
724   LOAD_ALIGNED_DATA(FPT, value, buffer+offset);
725   return Number::New(isolate, value);
726 }
727 
728 template<typename FPT>
fpWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)729 Local<Value> fpWriter(const NdbDictionary::Column * col,
730                        Handle<Value> value,
731                        char *buffer, uint32_t offset) {
732   double dval = value->ToNumber()->NumberValue();
733   bool valid = isfinite(dval);
734   if(valid) {
735     STORE_ALIGNED_DATA(FPT, dval, buffer+offset);
736   }
737   return valid ? writerOK : K_22003_OutOfRange.Get(isolate);
738 }
739 
740 /****** Binary & Varbinary *******/
741 
BinaryReader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)742 Local<Value> BinaryReader(const NdbDictionary::Column *col,
743                            char *buffer, uint32_t offset) {
744   return LOCAL_BUFFER(node::Buffer::New(isolate, buffer + offset, col->getLength()));
745 }
746 
BinaryWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)747 Local<Value> BinaryWriter(const NdbDictionary::Column * col,
748                            Handle<Value> value, char *buffer, uint32_t offset) {
749   bool valid = node::Buffer::HasInstance(value);
750   if(valid) {
751     Handle<Object> obj = value->ToObject();
752     uint32_t col_len = col->getLength();
753     uint32_t data_len = node::Buffer::Length(obj);
754     uint32_t ncopied = col_len > data_len ? data_len : col_len;
755     memmove(buffer+offset, node::Buffer::Data(obj), ncopied);
756     if(ncopied < col_len) {
757       memset(buffer+offset+ncopied, 0, col_len - ncopied); // padding
758     }
759   }
760   return valid ? writerOK : K_0F001_Bad_BLOB.Get(isolate);
761 }
762 
763 template<typename LENGTHTYPE>
varbinaryReader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)764 Local<Value> varbinaryReader(const NdbDictionary::Column *col,
765                               char *buffer, uint32_t offset) {
766   LOAD_ALIGNED_DATA(LENGTHTYPE, length, buffer+offset);
767   char * data = buffer+offset+sizeof(length);
768   return LOCAL_BUFFER(node::Buffer::New(isolate, data, length));
769 }
770 
771 template<typename LENGTHTYPE>
varbinaryWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)772 Local<Value> varbinaryWriter(const NdbDictionary::Column * col,
773                               Handle<Value> value,
774                               char *buffer, uint32_t offset) {
775   bool valid = node::Buffer::HasInstance(value);
776   if(valid) {
777     LENGTHTYPE col_len = static_cast<LENGTHTYPE>(col->getLength());
778     Handle<Object> obj = value->ToObject();
779     LENGTHTYPE data_len = static_cast<LENGTHTYPE>(node::Buffer::Length(obj));
780     if(data_len > col_len) data_len = col_len;  // truncate
781     STORE_ALIGNED_DATA(LENGTHTYPE, data_len, buffer+offset);
782     char * data = buffer+offset+sizeof(data_len);
783     memmove(data, node::Buffer::Data(obj), data_len);
784   }
785   return valid ? writerOK : K_22000_DataError.Get(isolate);
786 }
787 
788 /****** String types ********/
789 
790 /**
791  * V8 can work with two kinds of external strings: Strict ASCII and UTF-16.
792  * But working with external UTF-16 depends on MySQL's UTF-16-LE charset,
793  * which is available only in MySQL 5.6 and higher.
794  *
795  * (A) For any strict ASCII string, even if its character set is latin1 or
796  *     UTF-8 (i.e. it could have non-ascii characters, but it doesn't), we
797  *     present it to V8 as external ASCII.
798  * (B) If a string is UTF16LE, we present it as external UTF-16.
799  * (C) If a string is UTF-8, we create a new JS string (one copy operation)
800  * (D) All others must be recoded.  There are two possibilities:
801  *   (D.1) Recode to UTF16LE and present as external string (one copy operation)
802  *   (D.2) Recode to UTF8 and create a new JS string (two copy operations)
803  *
804  * For all string operations, we basically have four code paths:
805  * (A), (B), (C), and (D.2).
806  * (D.1) is skipped because Cluster < 7.3 does not have UTF16LE and because
807  * it requires some new interfaces from ColumnProxy to TypeEncoder
808  */
809 
stringIsAscii(const unsigned char * str,uint32_t len)810 inline bool stringIsAscii(const unsigned char *str, uint32_t len) {
811   for(unsigned int i = 0 ; i < len ; i++)
812     if(str[i] & 128)
813       return false;
814   return true;
815 }
816 
817 class ExternalizedAsciiString : public String::ExternalOneByteStringResource {
818 public:
819   char * buffer;
820   uint32_t len;
821   bool isAscii;
822   Persistent<Value> ref;
ExternalizedAsciiString(char * _buffer,uint32_t _len)823   ExternalizedAsciiString(char *_buffer, uint32_t _len) :
824     buffer(_buffer), len(_len), isAscii(true)
825   {
826     ref.Reset();
827   };
data() const828   const char* data() const       { return buffer; }
length() const829   size_t length() const          { return len; }
830 };
831 
832 class ExternalizedUnicodeString : public String::ExternalStringResource {
833 public:
834   uint16_t * buffer;
835   uint32_t len;  /* The number of two-byte characters in the string */
836   bool isAscii;
837   Persistent<Value> ref;
ExternalizedUnicodeString(uint16_t * _buffer,uint32_t _len)838   ExternalizedUnicodeString(uint16_t *_buffer, uint32_t _len) :
839     buffer(_buffer), len(_len), isAscii(false)
840   {
841     ref.Reset();
842   };
data() const843   const uint16_t * data() const  { return buffer; }
length() const844   size_t length() const          { return len; }
845 };
846 
getUtf8BufferSizeForColumn(int columnSizeInBytes,const EncoderCharset * csinfo)847 inline int getUtf8BufferSizeForColumn(int columnSizeInBytes,
848                                       const EncoderCharset * csinfo) {
849   int columnSizeInCharacters = columnSizeInBytes / csinfo->minlen;
850   int utf8MaxChar = csinfo->maxlen < 3 ? csinfo->maxlen + 1 : 4;
851   return (columnSizeInCharacters * utf8MaxChar);
852 }
853 
getRecodeBufferSize(int length,int utf8Length,const EncoderCharset * csinfo)854 inline int getRecodeBufferSize(int length, int utf8Length,
855                                const EncoderCharset * csinfo) {
856   int result = csinfo->minlen * length;
857   result += (utf8Length - length) * (csinfo->maxlen - csinfo->minlen);
858   return result;
859 }
860 
861 
862 typedef int CharsetWriter(const NdbDictionary::Column *,
863                           Handle<String>, char *, bool);
864 
865 int writeUtf16le(const NdbDictionary::Column *, Handle<String>, char *, bool);
866 int writeAscii(const NdbDictionary::Column *, Handle<String>, char *, bool);
867 int writeUtf8(const NdbDictionary::Column *, Handle<String>, char *, bool);
868 int writeGeneric(const NdbDictionary::Column *, Handle<String>, char *, bool);
869 int writeRecode(const NdbDictionary::Column *, Handle<String>, char *, bool);
870 
871 
getWriterForColumn(const NdbDictionary::Column * col)872 inline CharsetWriter * getWriterForColumn(const NdbDictionary::Column *col) {
873   const EncoderCharset * csinfo = getEncoderCharsetForColumn(col);
874   CharsetWriter * writer = & writeGeneric;
875   if(csinfo->isUtf8) writer = & writeUtf8;
876   else if(csinfo->isUtf16le) writer = & writeUtf16le;
877   else if(csinfo->isAscii) writer = & writeAscii;
878   else if(csinfo->isMultibyte) writer = & writeRecode;
879   return writer;
880 }
881 
882 /* String writers.
883    For CHAR, bufsz will be bigger than string size, so value will be padded.
884 */
writeUtf16le(const NdbDictionary::Column * column,Handle<String> strval,char * buffer,bool pad)885 int writeUtf16le(const NdbDictionary::Column * column,
886                  Handle<String> strval, char * buffer, bool pad) {
887   stats.direct_writes++;
888   int bufsz = column->getLength() / 2;  /* Work in 16-byte characters */
889   uint16_t * str = (uint16_t *) buffer;
890   if(pad) for(int i = 0; i < bufsz ; i ++) str[i] = ' ';
891   int charsWritten = strval->Write(str, 0, bufsz, String::NO_NULL_TERMINATION);
892   int sz = charsWritten * 2;
893   return sz;
894 }
895 
writeUtf8(const NdbDictionary::Column * column,Handle<String> strval,char * buffer,bool pad)896 int writeUtf8(const NdbDictionary::Column * column,
897                Handle<String> strval, char * buffer, bool pad) {
898   stats.direct_writes++;
899   const int & bufsz = column->getLength();
900   int sz = strval->WriteUtf8(buffer, bufsz, NULL, String::NO_NULL_TERMINATION);
901   if(pad)
902     while(sz < bufsz) buffer[sz++] = ' ';
903   return sz;
904 }
905 
writeAscii(const NdbDictionary::Column * column,Handle<String> strval,char * buffer,bool pad)906 int writeAscii(const NdbDictionary::Column * column,
907                Handle<String> strval, char * buffer, bool pad) {
908   stats.direct_writes++;
909   const int & bufsz = column->getLength();
910   int sz = strval->WriteOneByte((uint8_t*) buffer, 0, bufsz, String::NO_NULL_TERMINATION);
911   if(pad)
912     while(sz < bufsz) buffer[sz++] = ' ';
913   return sz;
914 }
915 
writeGeneric(const NdbDictionary::Column * col,Handle<String> strval,char * buffer,bool pad)916 int writeGeneric(const NdbDictionary::Column *col,
917                 Handle<String> strval, char * buffer, bool pad) {
918   /* In UTF-8 encoding, only characters less than 0x7F are encoded with
919      one byte.  Length() returns the string length in characters.
920      So: Length() == Utf8Length() implies a strict ASCII string.
921   */
922   return (strval->Utf8Length() == strval->Length()) ?
923     writeAscii(col, strval, buffer, pad) :
924     writeRecode(col, strval, buffer, pad);
925 }
926 
recodeFromUtf8(const char * src,int srcLen,char * dest,int destLen,int destCharsetNumber)927 inline int recodeFromUtf8(const char * src, int srcLen,
928                    char * dest, int destLen, int destCharsetNumber) {
929   CharsetMap csmap;
930   int32_t lengths[2] = { srcLen, destLen };
931   csmap.recode(lengths, csmap.getUTF8CharsetNumber(),
932                destCharsetNumber, src, dest);
933   return lengths[1];
934 }
935 
936 
937 /* We have two versions of writeRecode().
938    One for non-Microsoft that recodes onto the stack.
939    One for Microsoft where "char recode_stack[localInt]" is illegal.
940 */
writeRecode(const NdbDictionary::Column * col,Handle<String> strval,char * buffer,bool pad)941 int writeRecode(const NdbDictionary::Column *col,
942                 Handle<String> strval, char * buffer, bool pad) {
943   stats.recode_writes++;
944   const EncoderCharset * csinfo = getEncoderCharsetForColumn(col);
945   int columnSizeInBytes = col->getLength();
946   int utf8bufferSize = getUtf8BufferSizeForColumn(columnSizeInBytes, csinfo);
947 
948   /* Write to the heap */
949   char * recode_stack = new char[utf8bufferSize];
950   int recodeSz = strval->WriteUtf8(recode_stack, utf8bufferSize,
951                                    NULL, String::NO_NULL_TERMINATION);
952   if(pad) {
953     /* Pad all the way to the end of the recode buffer */
954     while(recodeSz < utf8bufferSize) recode_stack[recodeSz++] = ' ';
955   }
956 
957   int bytesWritten = recodeFromUtf8(recode_stack, recodeSz,
958                                     buffer, columnSizeInBytes,
959                                     col->getCharsetNumber());
960   delete[] recode_stack;
961   return bytesWritten;
962 }
963 
964 /* TEXT column writer: bufferForText(column, value).
965    The CHAR and VARCHAR writers refer to the column length, but this TEXT
966    writer assumes the string will fit into the column and lets Ndb truncate
967    the value if needed.
968 */
bufferForText(const Arguments & args)969 void bufferForText(const Arguments & args) {
970   isolate = args.GetIsolate();
971   EscapableHandleScope scope(isolate);
972   if(! args[1]->IsString()) {
973     args.GetReturnValue().SetNull();
974     return;
975   }
976   const NdbDictionary::Column * col =
977     unwrapPointer<const NdbDictionary::Column *>(args[0]->ToObject());
978   args.GetReturnValue().Set(
979     scope.Escape(getBufferForText(col, args[1]->ToString())));
980 }
981 
getBufferForText(const NdbDictionary::Column * col,Handle<String> str)982 Local<Object> getBufferForText(const NdbDictionary::Column *col,
983                                Handle<String> str) {
984   const EncoderCharset * csinfo = getEncoderCharsetForColumn(col);
985   int length, utf8Length;
986   Local<Object> buffer;
987   char * data;
988 
989   /* Fully Externalized Value; no copying.
990   */
991   if(   (str->IsExternalAscii() && ! csinfo->isMultibyte)
992      || (str->IsExternal() && csinfo->isUtf16le))
993   {
994     DEBUG_PRINT("getBufferForText: fully externalized");
995     stats.externalized_text_writes++;
996     return LOCAL_BUFFER(node::Buffer::New(isolate, str));
997   }
998 
999   length = str->Length();
1000   DEBUG_PRINT("getBufferForText: %s %d", col->getName(), length);
1001   utf8Length = str->Utf8Length();
1002   bool valueIsAscii = (utf8Length == length);
1003 
1004   if(csinfo->isAscii || (valueIsAscii && ! csinfo->isMultibyte)) {
1005     stats.direct_writes++;
1006     buffer = LOCAL_BUFFER(node::Buffer::New(isolate, length));
1007     data = node::Buffer::Data(buffer);
1008     str->WriteOneByte((uint8_t*) data, 0, length);
1009   } else if(csinfo->isUtf16le) {
1010     stats.direct_writes++;
1011     buffer = LOCAL_BUFFER(node::Buffer::New(isolate, length * 2));
1012     uint16_t * mbdata = (uint16_t*) node::Buffer::Data(buffer);
1013     str->Write(mbdata, 0, length);
1014   } else if(csinfo->isUtf8) {
1015     stats.direct_writes++;
1016     buffer = LOCAL_BUFFER(node::Buffer::New(isolate, utf8Length));
1017     data = node::Buffer::Data(buffer);
1018     str->WriteUtf8(data, utf8Length);
1019   } else {
1020     /* Recode */
1021     stats.recode_writes++;
1022     char * recode_buffer = new char[utf8Length];
1023     str->WriteUtf8(recode_buffer, utf8Length, 0, String::NO_NULL_TERMINATION);
1024     int buflen = getRecodeBufferSize(length, utf8Length, csinfo);
1025     data = (char *) malloc(buflen);
1026     int result_len = recodeFromUtf8(recode_buffer, utf8Length,
1027                                     data, buflen, col->getCharsetNumber());
1028     buffer = LOCAL_BUFFER(node::Buffer::New(isolate, data, result_len, freeBufferContentsFromJs, 0));
1029     delete[] recode_buffer;
1030   }
1031 
1032   return buffer;
1033 }
1034 
1035 
1036 // TEXT column reader textFromBuffer(column, buffer)
textFromBuffer(const Arguments & args)1037 void textFromBuffer(const Arguments & args) {
1038   isolate = args.GetIsolate();
1039   EscapableHandleScope scope(isolate);
1040   if(! args[1]->IsObject()) {
1041     args.GetReturnValue().SetNull();
1042     return;
1043   }
1044   const NdbDictionary::Column * col =
1045     unwrapPointer<const NdbDictionary::Column *>(args[0]->ToObject());
1046   args.GetReturnValue().Set(
1047     scope.Escape(getTextFromBuffer(col, args[1]->ToObject())));
1048 }
1049 
1050 
getTextFromBuffer(const NdbDictionary::Column * col,Handle<Object> bufferObj)1051 Local<String> getTextFromBuffer(const NdbDictionary::Column *col,
1052                                  Handle<Object> bufferObj) {
1053   const EncoderCharset * csinfo = getEncoderCharsetForColumn(col);
1054   uint32_t len = node::Buffer::Length(bufferObj);
1055   char * str = node::Buffer::Data(bufferObj);
1056 
1057   Local<String> string;
1058 
1059   // We won't call stringIsAscii() on a whole big TEXT buffer...
1060   if(csinfo->isAscii) {
1061     stats.read_strings_externalized++;
1062     ExternalizedAsciiString *ext = new ExternalizedAsciiString(str, len);
1063     ext->ref.Reset(isolate, bufferObj);
1064     string = String::NewExternal(isolate, ext);
1065   } else if (csinfo->isUtf16le) {
1066     stats.read_strings_externalized++;
1067     uint16_t * buf = (uint16_t *) str;
1068     ExternalizedUnicodeString * ext = new ExternalizedUnicodeString(buf, len/2);
1069     ext->ref.Reset(isolate, bufferObj);
1070     string = String::NewExternal(isolate, ext);
1071   } else {
1072     stats.read_strings_created++;
1073     if (csinfo->isUtf8) {
1074       DEBUG_PRINT("New from UTF8 [%d] %s", len, str);
1075       string = String::NewFromUtf8(isolate, str, String::kNormalString, len);
1076     } else { // Recode
1077       stats.read_strings_recoded++;
1078       CharsetMap csmap;
1079       int32_t lengths[2];
1080       lengths[0] = len;
1081       lengths[1] = getUtf8BufferSizeForColumn(len, csinfo);
1082       DEBUG_PRINT("Recode [%d / %d]", lengths[0], lengths[1]);
1083       char * recode_buffer = new char[lengths[1]];
1084       csmap.recode(lengths,
1085                    col->getCharsetNumber(),
1086                    csmap.getUTF8CharsetNumber(),
1087                    str, recode_buffer);
1088       DEBUG_PRINT("New from Recode [%d] %s", lengths[1], recode_buffer);
1089       string = String::NewFromUtf8(isolate, recode_buffer, String::kNormalString, lengths[1]);
1090       delete[] recode_buffer;
1091     }
1092   }
1093   return string;
1094 }
1095 
1096 // CHAR
1097 
CharReader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)1098 Local<Value> CharReader(const NdbDictionary::Column *col,
1099                          char *buffer, uint32_t offset) {
1100   char * str = buffer+offset;
1101   Local<String> string;
1102   int len = col->getLength();
1103   const EncoderCharset * csinfo = getEncoderCharsetForColumn(col);
1104 
1105   if(csinfo->isAscii ||
1106      (! csinfo->isMultibyte && stringIsAscii((const unsigned char *) str, len))) {
1107     stats.read_strings_externalized++;
1108     while(str[--len] == ' ') ;  // skip past space padding
1109     len++;  // undo 1 place
1110     ExternalizedAsciiString *ext = new ExternalizedAsciiString(str, len);
1111     string = String::NewExternal(isolate, ext);
1112     //DEBUG_PRINT("(A): External ASCII");
1113   }
1114   else if(csinfo->isUtf16le) {
1115     len /= 2;
1116     stats.read_strings_externalized++;
1117     uint16_t * buf = (uint16_t *) str;
1118     while(buf[--len] == ' ') {}; len++;  // skip padding, then undo 1
1119     ExternalizedUnicodeString * ext = new ExternalizedUnicodeString(buf, len);
1120     string = String::NewExternal(isolate, ext);
1121     //DEBUG_PRINT("(B): External UTF-16-LE");
1122   }
1123   else if(csinfo->isUtf8) {
1124     stats.read_strings_created++;
1125     while(str[--len] == ' ') {}; len++; // skip padding, then undo 1
1126     string = String::NewFromUtf8(isolate, str, String::kNormalString, len);
1127     //DEBUG_PRINT("(C): New From UTF-8");
1128   }
1129   else {
1130     stats.read_strings_created++;
1131     stats.read_strings_recoded++;
1132     CharsetMap csmap;
1133     int recode_size = getUtf8BufferSizeForColumn(len, csinfo);
1134     char * recode_buffer = new char[recode_size];
1135 
1136     /* Recode from the buffer into the UTF8 stack */
1137     int32_t lengths[2];
1138     lengths[0] = len;
1139     lengths[1] = recode_size;
1140     csmap.recode(lengths,
1141                  col->getCharsetNumber(),
1142                  csmap.getUTF8CharsetNumber(),
1143                  str, recode_buffer);
1144     len = lengths[1];
1145     while(recode_buffer[--len] == ' ') {}; len++; // skip padding, then undo 1
1146 
1147     /* Create a new JS String from the UTF-8 recode buffer */
1148     string = String::NewFromUtf8(isolate, recode_buffer, String::kNormalString, len);
1149 
1150     delete[] recode_buffer;
1151 
1152     //DEBUG_PRINT("(D.2): Recode to UTF-8 and create new");
1153   }
1154 
1155   return string;
1156 }
1157 
CharWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)1158 Local<Value> CharWriter(const NdbDictionary::Column * col,
1159                             Handle<Value> value,
1160                             char *buffer, uint32_t offset) {
1161   Handle<String> strval = value->ToString();
1162   CharsetWriter * writer = getWriterForColumn(col);
1163   writer(col, strval, buffer+offset, true);
1164   return writerOK;
1165 }
1166 
1167 // Templated encoder for Varchar and LongVarchar
1168 template<typename LENGTHTYPE>
varcharReader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)1169 Local<Value> varcharReader(const NdbDictionary::Column *col,
1170                             char *buffer, uint32_t offset) {
1171   LOAD_ALIGNED_DATA(LENGTHTYPE, length, buffer+offset);
1172   char * str = buffer+offset+sizeof(length);
1173   Local<String> string;
1174   const EncoderCharset * csinfo = getEncoderCharsetForColumn(col);
1175 
1176   if(csinfo->isAscii ||
1177      (! csinfo->isMultibyte && stringIsAscii((const unsigned char *) str, length))) {
1178     stats.read_strings_externalized++;
1179     ExternalizedAsciiString *ext = new ExternalizedAsciiString(str, length);
1180     string = String::NewExternal(isolate, ext);
1181     //DEBUG_PRINT("(A): External ASCII [size %d]", length);
1182   }
1183   else if(csinfo->isUtf16le) {
1184     stats.read_strings_externalized++;
1185     uint16_t * buf = (uint16_t *) str;
1186     ExternalizedUnicodeString * ext = new ExternalizedUnicodeString(buf, length/2);
1187     string = String::NewExternal(isolate, ext);
1188     //DEBUG_PRINT("(B): External UTF-16-LE [size %d]", length);
1189   }
1190   else if(csinfo->isUtf8) {
1191     stats.read_strings_created++;
1192     string = String::NewFromUtf8(isolate, str, String::kNormalString, length);
1193     //DEBUG_PRINT("(C): New From UTF-8 [size %d]", length);
1194   }
1195   else {
1196     stats.read_strings_created++;
1197     stats.read_strings_recoded++;
1198     CharsetMap csmap;
1199     int recode_size = getUtf8BufferSizeForColumn(length, csinfo);
1200     char * recode_buffer = new char[recode_size];
1201     int32_t lengths[2];
1202     lengths[0] = length;
1203     lengths[1] = recode_size;
1204     csmap.recode(lengths,
1205                  col->getCharsetNumber(), csmap.getUTF8CharsetNumber(),
1206                  str, recode_buffer);
1207     string = String::NewFromUtf8(isolate, recode_buffer, String::kNormalString, lengths[1]);
1208     delete[] recode_buffer;
1209     //DEBUG_PRINT("(D.2): Recode to UTF-8 and create new [size %d]", length);
1210   }
1211   return string;
1212 }
1213 
1214 template<typename LENGTHTYPE>
varcharWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,unsigned int offset)1215 Local<Value> varcharWriter(const NdbDictionary::Column * col,
1216                             Handle<Value> value,
1217                             char *buffer, unsigned int offset) {
1218   Handle<String> strval = value->ToString();
1219   CharsetWriter * writer = getWriterForColumn(col);
1220 
1221   LENGTHTYPE len = static_cast<LENGTHTYPE>
1222                     (writer(col, strval, buffer+offset+sizeof(len), false));
1223   STORE_ALIGNED_DATA(LENGTHTYPE, len, buffer+offset);
1224 
1225   return (strval->Length() > col->getLength()) ? K_22001_StringTooLong.Get(isolate) : writerOK;
1226 }
1227 
1228 
1229 /******  Temporal types ********/
1230 
1231 /* TimeHelper defines a C structure for managing parts of a MySQL temporal type
1232    and is able to read and write a JavaScript object that handles that date
1233    with no loss of precision.
1234 */
1235 class TimeHelper {
1236 public:
TimeHelper()1237   TimeHelper() :
1238     sign(+1), valid(true), fsp(0),
1239     year(0), month(0), day(0), hour(0), minute(0), second(0), microsec(0)
1240     {};
1241   TimeHelper(Handle<Value>);
1242 
1243   /* methods */
1244   Local<Value> toJs();
factor_HHMMSS(int int_time)1245   void factor_HHMMSS(int int_time) {
1246     if(int_time < 0) { sign = -1; int_time = - int_time; }
1247     hour   = int_time/10000;
1248     minute = int_time/100 % 100;
1249     second = int_time % 100;
1250   };
factor_YYYYMMDD(int int_date)1251   void factor_YYYYMMDD(int int_date) {
1252     year  = int_date/10000 % 10000;
1253     month = int_date/100 % 100;
1254     day   = int_date % 100;
1255   };
1256 
1257   /* data */
1258   int sign;
1259   bool valid;
1260   unsigned int fsp, year, month, day, hour, minute, second, microsec;
1261 };
1262 
toJs()1263 Local<Value> TimeHelper::toJs() {
1264   Local<Object> obj = Object::New(isolate);
1265   obj->Set(K_sign.Get(isolate),     Integer::New(isolate, sign));
1266   obj->Set(K_year.Get(isolate),     Integer::New(isolate, year));
1267   obj->Set(K_month.Get(isolate),    Integer::New(isolate, month));
1268   obj->Set(K_day.Get(isolate),      Integer::New(isolate, day));
1269   obj->Set(K_hour.Get(isolate),     Integer::New(isolate, hour));
1270   obj->Set(K_minute.Get(isolate),   Integer::New(isolate, minute));
1271   obj->Set(K_second.Get(isolate),   Integer::New(isolate, second));
1272   obj->Set(K_microsec.Get(isolate), Integer::New(isolate, microsec));
1273   obj->Set(K_fsp.Get(isolate),      Integer::New(isolate, fsp));
1274   return obj;
1275 }
1276 
TimeHelper(Handle<Value> mysqlTime)1277 TimeHelper::TimeHelper(Handle<Value> mysqlTime) :
1278   sign(+1), valid(false),
1279   year(0), month(0), day(0), hour(0), minute(0), second(0), microsec(0)
1280 {
1281   int nkeys = 0;
1282 
1283 
1284   if(mysqlTime->IsObject()) {
1285 
1286     Local<Value> _sign = K_sign.Get(isolate),
1287                  _year = K_year.Get(isolate),
1288                  _month = K_month.Get(isolate),
1289                  _day = K_day.Get(isolate),
1290                  _hour = K_hour.Get(isolate),
1291                  _minute = K_minute.Get(isolate),
1292                  _second = K_second.Get(isolate),
1293                  _microsec = K_microsec.Get(isolate),
1294                  _valid = K_valid.Get(isolate);
1295 
1296     Local<Object> obj = mysqlTime->ToObject();
1297     if(obj->Has(_valid) && ! (obj->Get(_valid)->BooleanValue())) {
1298       return; // return with this.valid still set to false.
1299     }
1300     if(obj->Has(_sign))    { sign  = obj->Get(_sign)->Int32Value();   nkeys++; }
1301     if(obj->Has(_year))    { year  = obj->Get(_year)->Int32Value();   nkeys++; }
1302     if(obj->Has(_month))   { month = obj->Get(_month)->Int32Value();  nkeys++; }
1303     if(obj->Has(_day))     { day   = obj->Get(_day)->Int32Value();    nkeys++; }
1304     if(obj->Has(_hour))    { hour  = obj->Get(_hour)->Int32Value();   nkeys++; }
1305     if(obj->Has(_minute))  { minute= obj->Get(_minute)->Int32Value(); nkeys++; }
1306     if(obj->Has(_second))  { second= obj->Get(_second)->Int32Value(); nkeys++; }
1307     if(obj->Has(_microsec)){ microsec = obj->Get(_microsec)->Int32Value(); nkeys++; }
1308   }
1309   valid = (nkeys > 0);
1310 }
1311 
1312 
1313 /* readFraction() returns value in microseconds
1314 */
readFraction(const NdbDictionary::Column * col,char * buf)1315 unsigned int readFraction(const NdbDictionary::Column *col, char *buf) {
1316   int prec  = col->getPrecision();
1317   unsigned int usec = 0;
1318   if(prec > 0) {
1319     int bufsz = (1 + prec) / 2;
1320     usec = (unsigned int) unpack_bigendian(buf, bufsz);
1321     while(prec < 5) usec *= 100, prec += 2;
1322   }
1323   return usec;
1324 }
1325 
writeFraction(const NdbDictionary::Column * col,int usec,char * buf)1326 void writeFraction(const NdbDictionary::Column *col, int usec, char *buf) {
1327   int prec  = col->getPrecision();
1328   if(prec > 0) {
1329     int bufsz = (1 + prec) / 2; // {1,1,2,2,3,3}
1330     while(prec < 5) usec /= 100, prec += 2;
1331     if(prec % 2) usec -= (usec % 10); // forced loss of precision
1332     pack_bigendian(usec, buf, bufsz);
1333   }
1334 }
1335 
1336 
1337 // Timstamp
TimestampReader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)1338 Local<Value> TimestampReader(const NdbDictionary::Column *col,
1339                               char *buffer, uint32_t offset) {
1340   LOAD_ALIGNED_DATA(uint32_t, timestamp, buffer+offset);
1341   double jsdate = timestamp * 1000;  // unix seconds-> js milliseconds
1342   return Date::New(isolate, jsdate);
1343 }
1344 
TimestampWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)1345 Local<Value> TimestampWriter(const NdbDictionary::Column * col,
1346                               Handle<Value> value,
1347                               char *buffer, uint32_t offset) {
1348   uint32_t *tpos = (uint32_t *) (buffer+offset);
1349   double dval;
1350   bool valid = value->IsDate();
1351   if(valid) {
1352     dval = Date::Cast(*value)->NumberValue() / 1000;
1353     valid = (dval >= 0);   // MySQL does not accept dates before 1970
1354     *tpos = static_cast<uint32_t>(dval);
1355   }
1356   return valid ? writerOK : K_22007_InvalidDatetime.Get(isolate);
1357 }
1358 
1359 
1360 // Timestamp2
1361 /* Timestamp2 is implemented to directly read and write Javascript Date.
1362    If col->getPrecision() > 3, some precision is lost.
1363 */
Timestamp2Reader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)1364 Local<Value> Timestamp2Reader(const NdbDictionary::Column *col,
1365                                char *buffer, uint32_t offset) {
1366   uint32_t timeSeconds = (uint32_t) unpack_bigendian(buffer+offset, 4);
1367   int timeMilliseconds = readFraction(col, buffer+offset+4) / 1000;
1368   double jsdate = ((double) timeSeconds * 1000) + timeMilliseconds;
1369   return Date::New(isolate, jsdate);
1370 }
1371 
Timestamp2Writer(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)1372 Local<Value> Timestamp2Writer(const NdbDictionary::Column * col,
1373                                Handle<Value> value,
1374                                char *buffer, uint32_t offset) {
1375   bool valid = value->IsDate();
1376   if(valid) {
1377     double jsdate = Date::Cast(*value)->NumberValue();
1378     int64_t timeMilliseconds = (int64_t) jsdate;
1379     int64_t timeSeconds = timeMilliseconds / 1000;
1380     timeMilliseconds %= 1000;
1381     pack_bigendian(timeSeconds, buffer+offset, 4);
1382     writeFraction(col, static_cast<int>(timeMilliseconds * 1000), buffer+offset+4);
1383     valid = (timeSeconds >= 0);   // MySQL does not accept dates before 1970
1384   }
1385   return valid ? writerOK : K_22007_InvalidDatetime.Get(isolate);
1386 }
1387 
1388 /* Datetime
1389    Interfaces with JavaScript via TimeHelper
1390 */
DatetimeReader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)1391 Local<Value> DatetimeReader(const NdbDictionary::Column *col,
1392                              char *buffer, uint32_t offset) {
1393   TimeHelper tm;
1394   LOAD_ALIGNED_DATA(uint64_t, int_datetime, buffer+offset);
1395   int int_date = static_cast<int>(int_datetime / 1000000);
1396   tm.factor_YYYYMMDD(int_date);
1397   int int_time = static_cast<int>(int_datetime - (int_date * 1000000));
1398   // The time part of a stored datetime is non-negative
1399   assert(int_time >= 0);
1400   tm.factor_HHMMSS(int_time);
1401   return tm.toJs();
1402 }
1403 
DatetimeWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)1404 Local<Value> DatetimeWriter(const NdbDictionary::Column * col,
1405                               Handle<Value> value,
1406                               char *buffer, uint32_t offset) {
1407   TimeHelper tm(value);
1408   uint64_t dtval = 0;
1409   if(tm.valid) {
1410     dtval += tm.year;      dtval *= 100;
1411     dtval += tm.month;     dtval *= 100;
1412     dtval += tm.day;       dtval *= 100;
1413     dtval += tm.hour;      dtval *= 100;
1414     dtval += tm.minute;    dtval *= 100;
1415     dtval += tm.second;
1416     STORE_ALIGNED_DATA(uint64_t, dtval, buffer+offset);
1417   }
1418   return tm.valid ? writerOK : K_22007_InvalidDatetime.Get(isolate);
1419 }
1420 
1421 
1422 /* Datetime2
1423    Interfaces with JavaScript via TimeHelper
1424 
1425   The packed datetime2 integer part is:
1426 
1427   1 bit  sign (1= non-negative, 0= negative)     [ALWAYS POSITIVE IN MYSQL 5.6]
1428  17 bits year*13+month  (year 0-9999, month 1-12)
1429   5 bits day            (0-31)
1430   5 bits hour           (0-23)
1431   6 bits minute         (0-59)
1432   6 bits second         (0-59)
1433   ---------------------------
1434   40 bits = 5 bytes
1435 */
Datetime2Reader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)1436 Local<Value> Datetime2Reader(const NdbDictionary::Column *col,
1437                               char *buffer, uint32_t offset) {
1438   TimeHelper tm;
1439   uint64_t packedValue = unpack_bigendian(buffer+offset, 5);
1440   tm.microsec = readFraction(col, buffer+offset+5);
1441   tm.fsp = col->getPrecision();
1442   tm.second = (packedValue & 0x3F);       packedValue >>= 6;
1443   tm.minute = (packedValue & 0x3F);       packedValue >>= 6;
1444   tm.hour   = (packedValue & 0x1F);       packedValue >>= 5;
1445   tm.day    = (packedValue & 0x1F);       packedValue >>= 5;
1446   int yrMo  = (packedValue & 0x01FFFF);
1447   tm.year = yrMo / 13;
1448   tm.month = yrMo % 13;
1449   return tm.toJs();
1450 }
1451 
Datetime2Writer(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)1452 Local<Value> Datetime2Writer(const NdbDictionary::Column * col,
1453                               Handle<Value> value,
1454                               char *buffer, uint32_t offset) {
1455   TimeHelper tm(value);
1456   uint64_t packedValue = 0;
1457   if(tm.valid) {
1458     packedValue = 1;                            packedValue <<= 17;
1459     packedValue |= (tm.year * 13 + tm.month);   packedValue <<= 5;
1460     packedValue |= tm.day;                      packedValue <<= 5;
1461     packedValue |= tm.hour;                     packedValue <<= 6;
1462     packedValue |= tm.minute;                   packedValue <<= 6;
1463     packedValue |= tm.second;
1464     pack_bigendian(packedValue, buffer+offset, 5);
1465     writeFraction(col, tm.microsec, buffer+offset+5);
1466   }
1467   return tm.valid ? writerOK : K_22007_InvalidDatetime.Get(isolate);
1468 }
1469 
1470 
1471 // Year
YearReader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)1472 Local<Value> YearReader(const NdbDictionary::Column *col,
1473                          char *buffer, uint32_t offset) {
1474   LOAD_ALIGNED_DATA(uint8_t, myr, buffer+offset);
1475   int year = 1900 + myr;
1476   return Number::New(isolate, year);
1477 }
1478 
YearWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)1479 Local<Value> YearWriter(const NdbDictionary::Column * col,
1480                          Handle<Value> value, char *buffer, uint32_t offset) {
1481   int chkv;
1482   if(value->IsInt32()) {
1483     chkv = value->Int32Value();
1484   } else {
1485     double dval = value->ToNumber()->Value();
1486     chkv = static_cast<int>(rint(dval));
1487   }
1488 
1489   chkv -= 1900;
1490 
1491   if(checkIntValue<uint8_t>(chkv)) {
1492     STORE_ALIGNED_DATA(uint8_t, chkv, buffer+offset);
1493     return writerOK;
1494   }
1495   return K_22007_InvalidDatetime.Get(isolate);
1496 }
1497 
1498 
1499 // Time.  Uses TimeHelper.
TimeReader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)1500 Local<Value> TimeReader(const NdbDictionary::Column *col,
1501                          char *buffer, uint32_t offset) {
1502   TimeHelper tm;
1503   char * cbuf = buffer+offset;
1504   int sqlTime = sint3korr(cbuf);
1505   tm.factor_HHMMSS(sqlTime);
1506   return tm.toJs();
1507 }
1508 
TimeWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)1509 Local<Value> TimeWriter(const NdbDictionary::Column * col,
1510                          Handle<Value> value, char *buffer, uint32_t offset) {
1511   TimeHelper tm(value);
1512   int dtval = 0;
1513   if(tm.valid) {
1514     dtval += tm.hour;      dtval *= 100;
1515     dtval += tm.minute;    dtval *= 100;
1516     dtval += tm.second;
1517     dtval *= tm.sign;
1518     writeSignedMedium((int8_t *) buffer+offset, dtval);
1519   }
1520 
1521   return tm.valid ? writerOK : K_22007_InvalidDatetime.Get(isolate);
1522 }
1523 
1524 
1525 /* Time2.  Uses TimeHelper.
1526   1 bit sign   (1= non-negative, 0= negative)
1527   1 bit unused  (reserved for INTERVAL type)
1528  10 bits hour   (0-838)
1529   6 bits minute (0-59)
1530   6 bits second (0-59)
1531   --------------------
1532   24 bits = 3 bytes whole-number part, + fractional part.
1533   If time is negative, then the entire value (including fractional part)
1534   is converted to its two's complement.  readFraction() and writeFraction()
1535   cannot be used.
1536 */
Time2Reader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)1537 Local<Value> Time2Reader(const NdbDictionary::Column *col,
1538                           char *buffer, uint32_t offset) {
1539   TimeHelper tm;
1540   int prec = col->getPrecision();
1541   int fsp_size = (1 + prec) / 2;
1542   int buf_size = 3 + fsp_size;
1543   int fsp_bits = fsp_size * 8;
1544   int sign_pos = fsp_bits + 23;
1545   uint64_t fsp_mask = (1ULL << fsp_bits) - 1;
1546   uint64_t sign_val = 1ULL << sign_pos;
1547   uint64_t packedValue = unpack_bigendian(buffer+offset, buf_size);
1548 
1549   if((packedValue & sign_val) == sign_val) {
1550     tm.sign = 1;
1551   }
1552   else {
1553     tm.sign = -1;
1554     packedValue = sign_val - packedValue;   // two's complement
1555   }
1556   tm.fsp      = prec;
1557   tm.microsec = (int) (packedValue & fsp_mask);   packedValue >>= fsp_bits;
1558   tm.second   =       (packedValue & 0x3F);       packedValue >>= 6;
1559   tm.minute   =       (packedValue & 0x3F);       packedValue >>= 6;
1560   tm.hour     =       (packedValue & 0x03FF);     packedValue >>= 10;
1561 
1562   while(prec < 5) tm.microsec *= 100, prec += 2;
1563 
1564   return tm.toJs();
1565 }
1566 
Time2Writer(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)1567 Local<Value> Time2Writer(const NdbDictionary::Column * col,
1568                           Handle<Value> value, char *buffer, uint32_t offset) {
1569   TimeHelper tm(value);
1570   int prec = col->getPrecision();
1571   int fsp_size = (1 + prec) / 2;
1572   int buf_size = 3 + fsp_size;
1573   int fsp_bits = fsp_size * 8;
1574   uint64_t sign_val = 1ULL << (23 + fsp_bits);
1575   int fsec = tm.microsec;
1576   bool is_neg = (tm.sign < 0);
1577   uint64_t packedValue = 0;
1578 
1579   if(fsec) {
1580     while(prec < 5) fsec /= 100, prec += 2;
1581     if(prec % 2) fsec -= (fsec % 10); // forced loss of precision
1582   }
1583 
1584   if(tm.valid) {
1585     packedValue = (is_neg ? 0 : 1);         packedValue <<= 11;
1586     packedValue |= tm.hour;                 packedValue <<= 6;
1587     packedValue |= tm.minute;               packedValue <<= 6;
1588     packedValue |= tm.second;               packedValue <<= fsp_bits;
1589     packedValue |= fsec;
1590     if (is_neg) {
1591       packedValue = sign_val - packedValue;    // two's complement
1592     }
1593     pack_bigendian(packedValue, buffer+offset, buf_size);
1594   }
1595 
1596   return tm.valid ? writerOK : K_22007_InvalidDatetime.Get(isolate);
1597 }
1598 
1599 
1600 // Date
DateReader(const NdbDictionary::Column * col,char * buffer,uint32_t offset)1601 Local<Value> DateReader(const NdbDictionary::Column *col,
1602                          char *buffer, uint32_t offset) {
1603   TimeHelper tm;
1604   char * cbuf = buffer+offset;
1605   int encodedDate = uint3korr(cbuf);
1606   tm.day   = (encodedDate & 31);  // five bits
1607   tm.month = (encodedDate >> 5 & 15); // four bits
1608   tm.year  = (encodedDate >> 9);
1609   return tm.toJs();
1610 }
1611 
DateWriter(const NdbDictionary::Column * col,Handle<Value> value,char * buffer,uint32_t offset)1612 Local<Value> DateWriter(const NdbDictionary::Column * col,
1613                          Handle<Value> value, char *buffer, uint32_t offset) {
1614   TimeHelper tm(value);
1615   int encodedDate = 0;
1616   if(tm.valid) {
1617     encodedDate = (tm.year << 9) | (tm.month << 5) | tm.day;
1618     writeUnsignedMedium((uint8_t *) buffer+offset, encodedDate);
1619   }
1620 
1621   return tm.valid ? writerOK : K_22007_InvalidDatetime.Get(isolate);
1622 }
1623 
1624 
1625 // BLOB
1626 // BlobReader is a no-op
BlobReader(const NdbDictionary::Column *,char *,uint32_t)1627 Local<Value> BlobReader(const NdbDictionary::Column *, char *, uint32_t) {
1628   return writerOK;
1629 }
1630 
1631 // The BlobWriter does write anything, but it does verify that the
1632 // intended value is a Node Buffer.
BlobWriter(const NdbDictionary::Column *,Handle<Value> value,char *,uint32_t)1633 Local<Value> BlobWriter(const NdbDictionary::Column *, Handle<Value> value,
1634                         char *, uint32_t) {
1635   return node::Buffer::HasInstance(value) ? writerOK : K_0F001_Bad_BLOB.Get(isolate);
1636 }
1637