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