nodejs-db-informix  master
nodejs bindings for Informix
 All Classes Functions Pages
query.cxx
1 #include <iostream>
2 #include "query.h"
3 
4 bool nodejs_db::Query::gmtDeltaLoaded = false;
5 int nodejs_db::Query::gmtDelta;
6 
7 #if NODE_VERSION_AT_LEAST(0, 7, 8)
8 uv_async_t nodejs_db::Query::g_async;
9 #endif
10 
11 void
12 nodejs_db::Query::Init(v8::Handle<v8::Object> target
13  , v8::Persistent<v8::FunctionTemplate> constructorTemplate
14 ) {
15  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "select", Select);
16  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "skip", Skip);
17  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "limit", Limit);
18  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "first", First);
19  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "from", From);
20  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "join", Join);
21  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "where", Where);
22  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "and", And);
23  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "or", Or);
24  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "orderby", OrderBy);
25  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "add", Add);
26  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "insert", Insert);
27  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "into", Into);
28  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "values", Values);
29  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "update", Update);
30  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "set", Set);
31  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "delete", Delete);
32  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "sql", Sql);
33  NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "execute", Execute);
34 }
35 
36 nodejs_db::Query::Query(): nodejs_db::EventEmitter(),
37  connection(NULL),
38  sqlType(Query::NONE),
39  async(true),
40  cast(true),
41  bufferText(false),
42  cbStart(NULL),
43  cbExecute(NULL),
44  cbFinish(NULL)
45 {}
46 
47 nodejs_db::Query::~Query() {
48  for (std::vector< v8::Persistent<v8::Value> >::iterator iterator = this->values.begin(),
49  end = this->values.end();
50  iterator != end; ++iterator
51  ) {
52  iterator->Dispose();
53  }
54 
55  if (this->cbStart != NULL) {
56  node::cb_destroy(this->cbStart);
57  }
58  if (this->cbExecute != NULL) {
59  node::cb_destroy(this->cbExecute);
60  }
61  if (this->cbFinish != NULL) {
62  node::cb_destroy(this->cbFinish);
63  }
64 }
65 
66 void nodejs_db::Query::setConnection(nodejs_db::Connection* c) {
67  this->connection = c;
68 }
69 
70 v8::Handle<v8::Value> nodejs_db::Query::Select(const v8::Arguments& args) {
71  v8::HandleScope scope;
72 
73  if (args.Length() > 0) {
74  if (args[0]->IsArray()) {
75  ARG_CHECK_ARRAY(0, fields);
76  } else if (args[0]->IsObject()) {
77  ARG_CHECK_OBJECT(0, fields);
78  } else {
79  ARG_CHECK_STRING(0, fields);
80  }
81  } else {
82  ARG_CHECK_STRING(0, fields);
83  }
84 
85  nodejs_db::Query *query = node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
86  assert(query);
87 
88  query->sql << "SELECT ";
89  query->sqlType = Query::SELECT;
90 
91  if (args[0]->IsArray()) {
92  v8::Local<v8::Array> fields = v8::Array::Cast(*args[0]);
93  if (fields->Length() == 0) {
94  THROW_EXCEPTION("No fields specified in select")
95  }
96 
97  for (uint32_t i = 0, limiti = fields->Length(); i < limiti; i++) {
98  if (i > 0) {
99  query->sql << ",";
100  }
101 
102  try {
103  query->sql << query->fieldName(fields->Get(i));
104  } catch(const nodejs_db::Exception& exception) {
105  THROW_EXCEPTION(exception.what())
106  }
107  }
108  } else if (args[0]->IsObject()) {
109  try {
110  query->sql << query->fieldName(args[0]);
111  } catch(const nodejs_db::Exception& exception) {
112  THROW_EXCEPTION(exception.what())
113  }
114  } else {
115  v8::String::Utf8Value fields(args[0]->ToString());
116  query->sql << *fields;
117  }
118 
119  return scope.Close(args.This());
120 }
121 
122 v8::Handle<v8::Value> nodejs_db::Query::From(const v8::Arguments& args) {
123  v8::HandleScope scope;
124 
125  if (args.Length() > 0) {
126  if (args[0]->IsArray()) {
127  ARG_CHECK_ARRAY(0, fields);
128  } else if (args[0]->IsObject()) {
129  ARG_CHECK_OBJECT(0, tables);
130  } else {
131  ARG_CHECK_STRING(0, tables);
132  }
133  } else {
134  ARG_CHECK_STRING(0, tables);
135  }
136 
137  ARG_CHECK_OPTIONAL_BOOL(1, escape);
138 
139  nodejs_db::Query *query = node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
140  assert(query);
141 
142  bool escape = true;
143  if (args.Length() > 1) {
144  escape = args[1]->IsTrue();
145  }
146 
147  query->sql << " FROM ";
148 
149  try {
150  query->sql << query->tableName(args[0], escape);
151  } catch(const nodejs_db::Exception& exception) {
152  THROW_EXCEPTION(exception.what());
153  }
154 
155  return scope.Close(args.This());
156 }
157 
158 v8::Handle<v8::Value> nodejs_db::Query::Join(const v8::Arguments& args) {
159  v8::HandleScope scope;
160 
161  ARG_CHECK_OBJECT(0, join);
162  ARG_CHECK_OPTIONAL_ARRAY(1, values);
163 
164  v8::Local<v8::Object> join = args[0]->ToObject();
165 
166  ARG_CHECK_OBJECT_ATTR_OPTIONAL_STRING(join, type);
167  ARG_CHECK_OBJECT_ATTR_STRING(join, table);
168  ARG_CHECK_OBJECT_ATTR_OPTIONAL_STRING(join, alias);
169  ARG_CHECK_OBJECT_ATTR_OPTIONAL_STRING(join, conditions);
170  ARG_CHECK_OBJECT_ATTR_OPTIONAL_BOOL(join, escape);
171 
172  nodejs_db::Query *query = node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
173  assert(query);
174 
175  std::string type = "INNER";
176  bool escape = true;
177 
178  if (join->Has(type_key)) {
179  v8::String::Utf8Value currentType(join->Get(type_key)->ToString());
180  type = *currentType;
181  std::transform(type.begin(), type.end(), type.begin(), toupper);
182  }
183 
184  if (join->Has(escape_key)) {
185  escape = join->Get(escape_key)->IsTrue();
186  }
187 
188  v8::String::Utf8Value table(join->Get(table_key)->ToString());
189 
190  query->sql << " " << type << " JOIN ";
191  query->sql << (escape ? query->connection->escapeName(*table) : *table);
192 
193  if (join->Has(alias_key)) {
194  v8::String::Utf8Value alias(join->Get(alias_key)->ToString());
195  query->sql << " AS ";
196  query->sql << (escape ? query->connection->escapeName(*alias) : *alias);
197  }
198 
199  if (join->Has(conditions_key)) {
200  v8::String::Utf8Value conditions(join->Get(conditions_key)->ToObject());
201  std::string currentConditions = *conditions;
202  if (args.Length() > 1) {
203  v8::Local<v8::Array> currentValues = v8::Array::Cast(*args[1]);
204  for (uint32_t i = 0, limiti = currentValues->Length(); i < limiti; i++) {
205  query->values.push_back(v8::Persistent<v8::Value>::New(currentValues->Get(i)));
206  }
207  }
208 
209  query->sql << " ON (" << currentConditions << ")";
210  }
211 
212  return scope.Close(args.This());
213 }
214 
215 v8::Handle<v8::Value> nodejs_db::Query::Where(const v8::Arguments& args) {
216  v8::HandleScope scope;
217 
218  nodejs_db::Query *query = node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
219  assert(query);
220 
221  return scope.Close(query->addCondition(args, "WHERE"));
222 }
223 
224 v8::Handle<v8::Value> nodejs_db::Query::And(const v8::Arguments& args) {
225  v8::HandleScope scope;
226 
227  nodejs_db::Query *query = node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
228  assert(query);
229 
230  return scope.Close(query->addCondition(args, "AND"));
231 }
232 
233 v8::Handle<v8::Value> nodejs_db::Query::Or(const v8::Arguments& args) {
234  v8::HandleScope scope;
235 
236  nodejs_db::Query *query = node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
237  assert(query);
238 
239  return scope.Close(query->addCondition(args, "OR"));
240 }
241 
242 v8::Handle<v8::Value> nodejs_db::Query::OrderBy(const v8::Arguments& args) {
243  v8::HandleScope scope;
244 
245  if (args.Length() > 0 && args[0]->IsObject()) {
246  ARG_CHECK_OBJECT(0, fields);
247  } else {
248  ARG_CHECK_STRING(0, fields);
249  }
250 
251  ARG_CHECK_OPTIONAL_BOOL(1, escape);
252 
253  nodejs_db::Query *query = node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
254  assert(query);
255 
256  bool escape = true;
257  if (args.Length() > 1) {
258  escape = args[1]->IsTrue();
259  }
260 
261  query->sql << " ORDER BY ";
262 
263  if (args[0]->IsObject()) {
264  v8::Local<v8::Object> fields = args[0]->ToObject();
265  v8::Local<v8::Array> properties = fields->GetPropertyNames();
266  if (properties->Length() == 0) {
267  THROW_EXCEPTION("Non empty objects should be used for fields in order");
268  }
269 
270  for (uint32_t i = 0, limiti = properties->Length(); i < limiti; i++) {
271  v8::Local<v8::Value> propertyName = properties->Get(i);
272  v8::String::Utf8Value fieldName(propertyName);
273  v8::Local<v8::Value> currentValue = fields->Get(propertyName);
274 
275  if (i > 0) {
276  query->sql << ",";
277  }
278 
279  bool innerEscape = escape;
280  v8::Local<v8::Value> order;
281  if (currentValue->IsObject()) {
282  v8::Local<v8::Object> currentObject = currentValue->ToObject();
283  v8::Local<v8::String> escapeKey = v8::String::New("escape");
284  v8::Local<v8::String> orderKey = v8::String::New("order");
285  v8::Local<v8::Value> optionValue;
286 
287  if (!currentObject->Has(orderKey)) {
288  THROW_EXCEPTION("The \"order\" option for the order field object must be specified");
289  }
290 
291  order = currentObject->Get(orderKey);
292 
293  if (currentObject->Has(escapeKey)) {
294  optionValue = currentObject->Get(escapeKey);
295  if (!optionValue->IsBoolean()) {
296  THROW_EXCEPTION("Specify a valid boolean value for the \"escape\" option in the order field object");
297  }
298  innerEscape = optionValue->IsTrue();
299  }
300  } else {
301  order = currentValue;
302  }
303 
304  query->sql << (innerEscape ? query->connection->escapeName(*fieldName) : *fieldName);
305  query->sql << " ";
306 
307  if (order->IsBoolean()) {
308  query->sql << (order->IsTrue() ? "ASC" : "DESC");
309  } else if (order->IsString()) {
310  v8::String::Utf8Value currentOrder(order->ToString());
311  query->sql << *currentOrder;
312  } else {
313  THROW_EXCEPTION("Invalid value specified for \"order\" property in order field");
314  }
315  }
316  } else {
317  v8::String::Utf8Value sql(args[0]->ToString());
318  query->sql << *sql;
319  }
320 
321  return scope.Close(args.This());
322 }
323 
324 
325 
326 /**
327  * \fn nodejs_db::Query::Limit
328  *
329  * \breif insert the LIMIT projection clause in query
330  *
331  * \param[in] args v8::Arguments&
332  */
333 v8::Handle<v8::Value>
334 nodejs_db::Query::Limit(const v8::Arguments& args) {
335  v8::HandleScope scope;
336 
337  if (args.Length() < 1) {
338  THROW_EXCEPTION("LIMIT requires at-least one integer argument");
339  }
340 
341  if (args.Length() >= 2) {
342  ARG_CHECK_UINT32(0, skip);
343  ARG_CHECK_UINT32(1, rows);
344 
345  nodejs_db::Query *query = node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
346  assert(query);
347 
348  query->projection.skip.flag = true;
349  query->projection.skip.arg = args[0]->ToInt32()->Value();
350 
351  query->projection.first.flag = false;
352  query->projection.first.arg = 0;
353 
354  query->projection.limit.flag = true;
355  query->projection.limit.arg = args[1]->ToInt32()->Value();
356  } else {
357  ARG_CHECK_UINT32(0, rows);
358 
359  nodejs_db::Query *query = node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
360  assert(query);
361 
362  query->projection.first.flag = false;
363  query->projection.first.arg = 0;
364 
365  query->projection.limit.flag = true;
366  query->projection.limit.arg = args[0]->ToInt32()->Value();
367  }
368 
369  return scope.Close(args.This());
370 }
371 
372 
373 
374 /**
375  * \fn nodejs_db::Query::First
376  *
377  * \breif insert the FIRST projection clause
378  *
379  * \param[in] args v8::Arguments&
380  */
381 v8::Handle<v8::Value> nodejs_db::Query::First(const v8::Arguments& args) {
382  v8::HandleScope scope;
383 
384  if (args.Length() < 1) {
385  THROW_EXCEPTION("FIRST clause requires at-least one integer argument");
386  }
387 
388  ARG_CHECK_UINT32(0, rows);
389 
390  nodejs_db::Query *query = node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
391  assert(query);
392 
393  query->projection.first.flag = true;
394  query->projection.first.arg = args[0]->ToInt32()->Value();
395 
396  query->projection.limit.flag = false;
397  query->projection.limit.arg = 0;
398 
399  return scope.Close(args.This());
400 }
401 
402 
403 
404 /**
405  * \fn nodejs_db::Query::Skip
406  *
407  * \breif insert SKIP projection clause into query
408  *
409  * \param[in] args v8::Arguments&
410  */
411 v8::Handle<v8::Value> nodejs_db::Query::Skip(const v8::Arguments& args) {
412  v8::HandleScope scope;
413 
414  if (args.Length() < 1) {
415  THROW_EXCEPTION("SKIP clause requires at-least one integer argument");
416  }
417 
418  ARG_CHECK_UINT32(0, rows);
419 
420  nodejs_db::Query *query = node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
421  assert(query);
422 
423  query->projection.skip.flag = true;
424  query->projection.skip.arg = args[0]->ToInt32()->Value();
425 
426  return scope.Close(args.This());
427 }
428 
429 
430 
431 v8::Handle<v8::Value> nodejs_db::Query::Add(const v8::Arguments& args) {
432  v8::HandleScope scope;
433 
434  nodejs_db::Query* innerQuery = NULL;
435 
436  if (args.Length() > 0 && args[0]->IsObject()) {
437  v8::Local<v8::Object> object = args[0]->ToObject();
438  v8::Handle<v8::String> key = v8::String::New("sql");
439  if (!object->Has(key) || !object->Get(key)->IsFunction()) {
440  ARG_CHECK_STRING(0, sql);
441  }
442 
443  innerQuery = node::ObjectWrap::Unwrap<nodejs_db::Query>(object);
444  assert(innerQuery);
445  } else {
446  ARG_CHECK_STRING(0, sql);
447  }
448 
449  nodejs_db::Query *query = node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
450  assert(query);
451 
452  if (innerQuery != NULL) {
453  query->sql << innerQuery->sql.str();
454  } else {
455  v8::String::Utf8Value sql(args[0]->ToString());
456  query->sql << *sql;
457  }
458 
459  return scope.Close(args.This());
460 }
461 
462 
463 
464 v8::Handle<v8::Value> nodejs_db::Query::Delete(const v8::Arguments& args) {
465  v8::HandleScope scope;
466 
467  if (args.Length() > 0) {
468  if (args[0]->IsArray()) {
469  ARG_CHECK_ARRAY(0, tables);
470  } else if (args[0]->IsObject()) {
471  ARG_CHECK_OBJECT(0, tables);
472  } else {
473  ARG_CHECK_STRING(0, tables);
474  }
475  ARG_CHECK_OPTIONAL_BOOL(1, escape);
476  }
477 
478  nodejs_db::Query *query = node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
479  assert(query);
480 
481  bool escape = true;
482  if (args.Length() > 1) {
483  escape = args[1]->IsTrue();
484  }
485 
486  query->sql << "DELETE";
487  query->sqlType = Query::DELETE;
488 
489  if (args.Length() > 0) {
490  try {
491  query->sql << " " << query->tableName(args[0], escape);
492  } catch(const nodejs_db::Exception& exception) {
493  THROW_EXCEPTION(exception.what());
494  }
495  }
496 
497  return scope.Close(args.This());
498 }
499 
500 
501 
502 v8::Handle<v8::Value> nodejs_db::Query::Insert(const v8::Arguments& args) {
503  v8::HandleScope scope;
504  uint32_t argsLength = (uint32_t)args.Length();
505 
506  int fieldsIndex = -1, valuesIndex = -1;
507 
508  if (argsLength > 0) {
509  ARG_CHECK_STRING(0, table);
510 
511  if (argsLength > 2) {
512  if (args[1]->IsArray()) {
513  ARG_CHECK_ARRAY(1, fields);
514  } else if (args[1]->IsObject()) {
515  ARG_CHECK_OBJECT(1, fields);
516  } else if (!args[1]->IsFalse()) {
517  ARG_CHECK_STRING(1, fields);
518  }
519  fieldsIndex = 1;
520 
521  if (!args[2]->IsFalse()) {
522  valuesIndex = 2;
523  ARG_CHECK_ARRAY(2, values);
524  }
525 
526  ARG_CHECK_OPTIONAL_BOOL(3, escape);
527  } else if (argsLength > 1) {
528  ARG_CHECK_ARRAY(1, values);
529  valuesIndex = 1;
530  }
531  } else {
532  ARG_CHECK_STRING(0, table);
533  }
534 
535  nodejs_db::Query *query = node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
536  assert(query);
537 
538  bool escape = true;
539  if (argsLength > 3) {
540  escape = args[3]->IsTrue();
541  }
542 
543  try {
544  query->sql << "INSERT INTO " << query->tableName(args[0], escape);
545  } catch(const nodejs_db::Exception& exception) {
546  THROW_EXCEPTION(exception.what());
547  }
548 
549  query->sqlType = Query::INSERT;
550 
551  if (argsLength > 1) {
552  if (fieldsIndex != -1) {
553  query->sql << "(";
554  if (args[fieldsIndex]->IsArray()) {
555  v8::Local<v8::Array> fields = v8::Array::Cast(*args[fieldsIndex]);
556  if (fields->Length() == 0) {
557  THROW_EXCEPTION("No fields specified in insert")
558  }
559 
560  for (uint32_t i = 0, limiti = fields->Length(); i < limiti; i++) {
561  if (i > 0) {
562  query->sql << ",";
563  }
564 
565  try {
566  query->sql << query->fieldName(fields->Get(i));
567  } catch(const nodejs_db::Exception& exception) {
568  THROW_EXCEPTION(exception.what())
569  }
570  }
571  } else {
572  v8::String::Utf8Value fields(args[fieldsIndex]->ToString());
573  query->sql << *fields;
574  }
575  query->sql << ")";
576  }
577 
578  query->sql << " ";
579 
580  if (valuesIndex != -1) {
581  v8::Local<v8::Array> values = v8::Array::Cast(*args[valuesIndex]);
582  uint32_t valuesLength = values->Length();
583  if (valuesLength > 0) {
584  bool multipleRecords = values->Get(0)->IsArray();
585 
586  query->sql << "VALUES ";
587  if (!multipleRecords) {
588  query->sql << "(";
589  }
590 
591  for (uint32_t i = 0; i < valuesLength; i++) {
592  if (i > 0) {
593  query->sql << ",";
594  }
595  query->sql << query->value(values->Get(i));
596  }
597 
598  if (!multipleRecords) {
599  query->sql << ")";
600  }
601  }
602  }
603  } else {
604  query->sql << " ";
605  }
606 
607  return scope.Close(args.This());
608 }
609 
610 v8::Handle<v8::Value> nodejs_db::Query::Into(const v8::Arguments& args) {
611  v8::HandleScope scope;
612 
613  if (args.Length() < 1) {
614  THROW_EXCEPTION("INTO clause requires at-least one string argument");
615  }
616 
617  ARG_CHECK_STRING(0, table);
618 
619  ARG_CHECK_OPTIONAL_BOOL(1, escape);
620 
621  nodejs_db::Query *query = node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
622  assert(query);
623 
624  return scope.Close(args.This());
625 }
626 
627 v8::Handle<v8::Value> nodejs_db::Query::Values(const v8::Arguments& args) {
628  v8::HandleScope scope;
629  return scope.Close(args.This());
630 }
631 
632 v8::Handle<v8::Value> nodejs_db::Query::Update(const v8::Arguments& args) {
633  v8::HandleScope scope;
634 
635  if (args.Length() > 0) {
636  if (args[0]->IsArray()) {
637  ARG_CHECK_ARRAY(0, tables);
638  } else if (args[0]->IsObject()) {
639  ARG_CHECK_OBJECT(0, tables);
640  } else {
641  ARG_CHECK_STRING(0, tables);
642  }
643  } else {
644  ARG_CHECK_STRING(0, tables);
645  }
646 
647  ARG_CHECK_OPTIONAL_BOOL(1, escape);
648 
649  nodejs_db::Query *query = node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
650  assert(query);
651 
652  bool escape = true;
653  if (args.Length() > 1) {
654  escape = args[1]->IsTrue();
655  }
656 
657  query->sql << "UPDATE ";
658  query->sqlType = Query::UPDATE;
659 
660  try {
661  query->sql << query->tableName(args[0], escape);
662  } catch(const nodejs_db::Exception& exception) {
663  THROW_EXCEPTION(exception.what());
664  }
665 
666  return scope.Close(args.This());
667 }
668 
669 v8::Handle<v8::Value> nodejs_db::Query::Set(const v8::Arguments& args) {
670  v8::HandleScope scope;
671 
672  ARG_CHECK_OBJECT(0, values);
673  ARG_CHECK_OPTIONAL_BOOL(1, escape);
674 
675  nodejs_db::Query *query = node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
676  assert(query);
677 
678  bool escape = true;
679  if (args.Length() > 1) {
680  escape = args[1]->IsTrue();
681  }
682 
683  query->sql << " SET ";
684 
685  v8::Local<v8::Object> values = args[0]->ToObject();
686  v8::Local<v8::Array> valueProperties = values->GetPropertyNames();
687  if (valueProperties->Length() == 0) {
688  THROW_EXCEPTION("Non empty objects should be used for values in set");
689  }
690 
691  for (uint32_t j = 0, max_j = valueProperties->Length(); j < max_j; j++) {
692  v8::Local<v8::Value> propertyName = valueProperties->Get(j);
693  v8::String::Utf8Value fieldName(propertyName);
694  v8::Local<v8::Value> currentValue = values->Get(propertyName);
695 
696  if (j > 0) {
697  query->sql << ",";
698  }
699 
700  query->sql <<
701  (escape
702  ? query->connection->escapeName(*fieldName)
703  : *fieldName);
704  query->sql << "=";
705  query->sql << query->value(currentValue);
706  }
707 
708  return scope.Close(args.This());
709 }
710 
711 v8::Handle<v8::Value> nodejs_db::Query::Sql(const v8::Arguments& args) {
712  v8::HandleScope scope;
713 
714  nodejs_db::Query *query =
715  node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
716  assert(query);
717 
718  return scope.Close(v8::String::New(query->sql.str().c_str()));
719 }
720 
721 
722 /**
723  * \fn nodejs_db::Query::Execute
724  * Execute the query
725  *
726  */
727 v8::Handle<v8::Value>
728 nodejs_db::Query::Execute(const v8::Arguments& args) {
729  DEBUG_LOG_FUNC;
730  v8::HandleScope scope;
731 
732  nodejs_db::Query *query =
733  node::ObjectWrap::Unwrap<nodejs_db::Query>(args.This());
734  assert(query);
735 
736  if (args.Length() > 0) {
737  v8::Handle<v8::Value> set = query->set(args);
738  if (!set.IsEmpty()) {
739  return scope.Close(set);
740  }
741  }
742 
743  try {
744  query->addProjections();
745  } catch(const nodejs_db::Exception& exception) {
746  THROW_EXCEPTION(exception.what());
747  }
748 
749  std::string sql;
750 
751  try {
752  sql = query->parseQuery();
753  } catch(const nodejs_db::Exception& exception) {
754  THROW_EXCEPTION(exception.what())
755  }
756 
757  /* invoke the start callback function */
758  if (query->cbStart != NULL && !query->cbStart->IsEmpty()) {
759  v8::Local<v8::Value> argv[1];
760  argv[0] = v8::String::New(sql.c_str());
761 
762  v8::TryCatch tryCatch;
763  v8::Handle<v8::Value> result =
764  (*(query->cbStart))->Call(
765  v8::Context::GetCurrent()->Global()
766  , 1
767  , argv);
768  if (tryCatch.HasCaught()) {
769  node::FatalException(tryCatch);
770  }
771 
772  if (!result->IsUndefined()) {
773  if (result->IsFalse()) {
774  return scope.Close(v8::Undefined());
775  } else if (result->IsString()) {
776  v8::String::Utf8Value modifiedQuery(result->ToString());
777  sql = *modifiedQuery;
778  }
779  }
780  }
781 
782  if (!query->connection->isAlive(false)) {
783  THROW_EXCEPTION("Can't execute a query without being connected")
784  }
785 
786  execute_request_t *request = new execute_request_t();
787  if (request == NULL) {
788  THROW_EXCEPTION("Could not create request")
789  }
790 
791  query->sql.str("");
792  query->sql.clear();
793  query->sql << sql;
794 
795 #ifdef DEBUG
796  std::cout << "SQL: " << sql << std::endl;
797 #endif
798 
799  request->context = v8::Persistent<v8::Object>::New(args.This());
800  request->query = query;
801  request->buffered = false;
802  request->result = NULL;
803  request->rows = NULL;
804  request->error = NULL;
805 
806  if (query->async) {
807  request->query->Ref();
808  uv_work_t* req = new uv_work_t();
809  req->data = static_cast<void *>(request);
810  /*
811  uv_async_init(
812  uv_default_loop()
813  , &g_async
814  , uvEmitResults);
815  */
816  uv_queue_work(
817  uv_default_loop()
818  , req
819  , uvExecute
820  , (uv_after_work_cb)uvExecuteFinished);
821 
822 #if NODE_VERSION_AT_LEAST(0, 7, 9)
823  uv_ref((uv_handle_t *)&g_async);
824 #else
825  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
826 #endif // NODE_VERSION_AT_LEAST(0, 7, 9)
827 
828  } else {
829  request->query->executeAsync(request);
830  }
831 
832  return scope.Close(v8::Undefined());
833 }
834 
835 
836 /**
837  * uvExecute is responsible for executing the function and creating data. The
838  * data is then passed to uvExecuteFinished callback function for return
839  */
840 void nodejs_db::Query::uvExecute(uv_work_t* uvRequest) {
841  DEBUG_LOG_FUNC;
842 
843  execute_request_t *request =
844  static_cast<execute_request_t *>(uvRequest->data);
845 
846  assert(request);
847 
848  try {
849  if (request == NULL
850  || request->query == NULL
851  || request->query->connection == NULL
852  || !request->query->connection->isAlive()
853  ) {
854  throw nodejs_db::Exception("No open connection");
855  }
856 
857  request->query->connection->lock();
858  request->result = request->query->execute();
859 
860 #ifdef DEBUG
861  std::cout << "Result is ";
862 #endif
863  if ((request->result != NULL) && !request->result->isEmpty()) {
864 #ifdef DEBUG
865  std::cout << "not empty or null ";
866 #endif
867 
868  // allocate a new row_t
869  request->rows = new std::vector<row_t*>();
870  if (request->rows == NULL) {
871  throw nodejs_db::Exception("Could not create buffer for rows");
872  }
873 
874  // reaad vlaues from request
875  request->buffered = request->result->isBuffered();
876  request->columnCount = request->result->columnCount();
877 
878  while (request->result->hasNext()) {
879  unsigned long* columnLengths = request->result->columnLengths();
880  assert(columnLengths);
881 
882  std::vector<std::string> *currentRow = request->result->next();
883  assert(currentRow);
884 
885  if (!currentRow) {
886  break;
887  }
888 
889  row_t* row = new row_t();
890 
891  if (row == NULL) {
892  throw nodejs_db::Exception(
893  "Could not create buffer for row");
894  }
895 
896  if (request->buffered) {
897  row->columns = currentRow;
898  row->columnLengths = columnLengths;
899  } else {
900  row->columnLengths =
901  new unsigned long[request->columnCount];
902 
903  if (row->columnLengths == NULL) {
904  throw nodejs_db::Exception(
905  "Could not create buffer for column lengths");
906  }
907 
908  // pvmagacho - removed size initializer.
909  // amitkr - why?
910  row->columns =
911  new std::vector<std::string>(
912  size_t(request->columnCount));
913 
914  if (row->columns == NULL) {
915  throw nodejs_db::Exception(
916  "Could not create buffer for columns");
917  }
918 
919 #ifdef DEBUG
920  std::cout << std::endl;
921 #endif
922  for (uint16_t i = 0; i < request->columnCount; ++i) {
923 #ifdef DEBUG
924  std::cout
925  << "Column: " << i
926  << " Length: " << columnLengths[i]
927  << " Value: " << currentRow->at(i)
928  << std::endl;
929 #endif
930  row->columnLengths[i] = columnLengths[i];
931  row->columns->push_back(currentRow->at(i));
932  }
933  }
934 
935  request->rows->push_back(row);
936 
937  // current row
938  /*
939  query_async_t* cr = new query_async_t();
940  cr->request = request;
941  cr->row = row;
942  g_async.data = static_cast<void *>(cr);
943  uv_async_send(&g_async);
944  */
945  }
946 
947  if (!request->result->isBuffered()) {
948  request->result->release();
949  }
950 #ifdef DEBUG
951  std::cout << std::endl;
952 #endif
953  }
954 
955  request->query->connection->unlock();
956  } catch(const nodejs_db::Exception& exception) {
957  request->query->connection->unlock();
958  Query::freeRequest(request, false);
959  request->error = new std::string(exception.what());
960  }
961 
962 #if !NODE_VERSION_AT_LEAST(0, 5, 0)
963  return 0
964 #endif
965 }
966 
967 
968 #if NODE_VERSION_AT_LEAST(0, 7, 8)
969 void nodejs_db::Query::uvEmitResults(uv_async_t* uvAsync, int status) {
970  DEBUG_LOG_FUNC;
971  v8::HandleScope scope;
972 
973  query_async_t* cr = static_cast<query_async_t *>(uvAsync->data);
974  assert(cr);
975 
976  execute_request_t* request = cr->request;
977  assert(request);
978 
979  row_t* r = cr->row;
980  assert(r);
981 
982  v8::Local<v8::Object> row = request->query->row(request->result, r);
983  v8::Local<v8::Value> eachArgv[1];
984 
985  eachArgv[0] = row;
986 
987  request->query->Emit("each", 1, eachArgv);
988 
989  delete (cr);
990 
991  scope.Close(v8::Undefined());
992 }
993 #endif
994 
995 
996 void nodejs_db::Query::uvExecuteFinished(uv_work_t* uvRequest, int status) {
997  DEBUG_LOG_FUNC;
998  v8::HandleScope scope;
999 
1000  execute_request_t *request =
1001  static_cast<execute_request_t *>(uvRequest->data);
1002 
1003  assert(request);
1004 
1005 #ifdef DEBUG
1006  std::cout << "result ";
1007 #endif
1008 
1009  if (request->error == NULL && request->result != NULL) {
1010 
1011 #ifdef DEBUG
1012  std::cout << "is not null";
1013 #endif
1014 
1015  v8::Local<v8::Value> argv[3];
1016  argv[0] = v8::Local<v8::Value>::New(v8::Null());
1017 
1018  bool isEmpty = request->result->isEmpty();
1019  if (!isEmpty) {
1020 #ifdef DEBUG
1021  std::cout << ", is not empty";
1022 #endif
1023 
1024  assert(request->rows);
1025 
1026  size_t totalRows = request->rows->size();
1027  v8::Local<v8::Array> rows = v8::Array::New(totalRows);
1028 
1029  uint64_t index = 0;
1030  for (std::vector<row_t*>::iterator iterator =
1031  request->rows->begin(), end = request->rows->end();
1032  iterator != end;
1033  ++iterator, index++)
1034  {
1035  row_t* currentRow = *iterator;
1036  v8::Local<v8::Object> row =
1037  request->query->row(request->result, currentRow);
1038  v8::Local<v8::Value> eachArgv[3];
1039 
1040  eachArgv[0] = row;
1041  eachArgv[1] = v8::Number::New(index);
1042  eachArgv[2] = v8::Local<v8::Value>::New(
1043  (index == totalRows - 1)
1044  ? v8::True()
1045  : v8::False());
1046 
1047  request->query->Emit("each", 3, eachArgv);
1048 
1049  rows->Set(index, row);
1050  }
1051 
1052  v8::Local<v8::Array> columns = v8::Array::New(request->columnCount);
1053  for (uint16_t j = 0; j < request->columnCount; j++) {
1054  nodejs_db::Result::Column *currentColumn = request->result->column(j);
1055 
1056  v8::Local<v8::Object> column = v8::Object::New();
1057  column->Set(v8::String::New("name"), v8::String::New(currentColumn->getName().c_str()));
1058  column->Set(v8::String::New("type"), NODE_CONSTANT(currentColumn->getType()));
1059 
1060  columns->Set(j, column);
1061  }
1062 
1063  argv[1] = rows;
1064  argv[2] = columns;
1065  } else {
1066  v8::Local<v8::Object> result = v8::Object::New();
1067  result->Set(v8::String::New("id"), v8::Number::New(request->result->insertId()));
1068  result->Set(v8::String::New("affected"), v8::Number::New(request->result->affectedCount()));
1069  result->Set(v8::String::New("warning"), v8::Number::New(request->result->warningCount()));
1070  argv[1] = result;
1071  }
1072 
1073  request->query->Emit("success", !isEmpty ? 2 : 1, &argv[1]);
1074 
1075  if (request->query->cbExecute != NULL && !request->query->cbExecute->IsEmpty()) {
1076  v8::TryCatch tryCatch;
1077  (*(request->query->cbExecute))->Call(request->context, !isEmpty ? 3 : 2, argv);
1078  if (tryCatch.HasCaught()) {
1079  node::FatalException(tryCatch);
1080  }
1081  }
1082  } else {
1083  v8::Local<v8::Value> argv[1];
1084  argv[0] = v8::String::New(request->error != NULL ? request->error->c_str() : "(unknown error)");
1085 
1086  request->query->Emit("error", 1, argv);
1087 
1088  if (request->query->cbExecute != NULL && !request->query->cbExecute->IsEmpty()) {
1089  v8::TryCatch tryCatch;
1090  (*(request->query->cbExecute))->Call(request->context, 1, argv);
1091  if (tryCatch.HasCaught()) {
1092  node::FatalException(tryCatch);
1093  }
1094  }
1095  }
1096 
1097 
1098  if (request->query->cbFinish != NULL && !request->query->cbFinish->IsEmpty()) {
1099  v8::TryCatch tryCatch;
1100  (*(request->query->cbFinish))->Call(v8::Context::GetCurrent()->Global(), 0, NULL);
1101  if (tryCatch.HasCaught()) {
1102  node::FatalException(tryCatch);
1103  }
1104  }
1105 
1106 #if NODE_VERSION_AT_LEAST(0, 7, 9)
1107  uv_unref((uv_handle_t *)&g_async);
1108  // uv_close((uv_handle_t*)&g_async, NULL);
1109 #else // NODE_VERSION_AT_LEAST(0, 7, 9)
1110  uv_unref(uv_default_loop());
1111 #endif
1112 
1113  request->query->Unref();
1114 
1115  Query::freeRequest(request, true);
1116 
1117 #ifdef DEBUG
1118  std::cout << std::endl;
1119 #endif
1120 
1121 #if !NODE_VERSION_AT_LEAST(0, 7, 8)
1122  return scope.Close(v8::Undefined());
1123 #else
1124  scope.Close(v8::Undefined());
1125 #endif
1126 }
1127 
1128 
1129 /*
1130  *
1131  * @param[in,out] execute_request_t* request Request object
1132  */
1133 void nodejs_db::Query::executeAsync(execute_request_t* request) {
1134  DEBUG_LOG_FUNC;
1135 
1136  bool freeAll = true;
1137 
1138  try {
1139  this->connection->lock();
1140  request->result = this->execute();
1141  this->connection->unlock();
1142 
1143 #ifdef DEBUG
1144  std::cout << "result: ";
1145 #endif
1146 
1147  if (request->result != NULL) {
1148 #ifdef DEBUG
1149  std::cout << " is not null ";
1150 #endif
1151  v8::Local<v8::Value> argv[3];
1152  argv[0] = v8::Local<v8::Value>::New(v8::Null());
1153 
1154  bool isEmpty = request->result->isEmpty();
1155  if (!isEmpty) {
1156 #ifdef DEBUG
1157  std::cout << ", is not empty ";
1158 #endif
1159  request->columnCount = request->result->columnCount();
1160 
1161  v8::Local<v8::Array> columns =
1162  v8::Array::New(request->columnCount);
1163  v8::Local<v8::Array> rows;
1164 
1165  try {
1166  rows = v8::Array::New(request->result->count());
1167  } catch(const nodejs_db::Exception& exception) {
1168  rows = v8::Array::New();
1169  }
1170 
1171 #ifdef DEBUG
1172  std::cout << ", columnCount "
1173  << request->columnCount << std::endl;
1174 #endif
1175  /* setup the columns */
1176  for (uint16_t i = 0; i < request->columnCount; i++) {
1177  nodejs_db::Result::Column *currentColumn =
1178  request->result->column(i);
1179 
1180 #ifdef DEBUG
1181  std::cout
1182  << " Name: " << currentColumn->getName()
1183  << " Type: " << currentColumn->getType()
1184  << " IsBinary: " << currentColumn->isBinary()
1185  << std::endl;
1186 #endif
1187 
1188  v8::Local<v8::Object> column = v8::Object::New();
1189  column->Set(v8::String::New("name")
1190  , v8::String::New(
1191  currentColumn->getName().c_str()));
1192  column->Set(v8::String::New("type")
1193  , NODE_CONSTANT(currentColumn->getType()));
1194 
1195  columns->Set(i, column);
1196  }
1197 
1198  /* setup rows */
1199  row_t row;
1200  uint64_t index = 0;
1201 
1202 #ifdef DEBUG
1203  std::cout << "Rows" << std::endl;
1204 #endif
1205  while (request->result->hasNext()) {
1206 #ifdef DEBUG
1207  std::cout << "Row: " << index;
1208 #endif
1209 
1210  row.columnLengths =
1211  static_cast<unsigned long*>(
1212  request->result->columnLengths());
1213  row.columns = request->result->next();
1214 
1215 #ifdef DEBUG
1216  if (row.columns) {
1217  std::cout
1218  << ", Columns: " << row.columns->size()
1219  << std::endl;
1220  }
1221 #endif
1222 
1223  v8::Local<v8::Object> jsRow = this->row(request->result, &row);
1224  v8::Local<v8::Value> eachArgv[3];
1225 
1226  eachArgv[0] = jsRow;
1227  eachArgv[1] = v8::Number::New(index);
1228  eachArgv[2] = v8::Local<v8::Value>::New(request->result->hasNext() ? v8::True() : v8::False());
1229 
1230  this->Emit("each", 3, eachArgv);
1231 
1232  rows->Set(index++, jsRow);
1233  }
1234 
1235  if (!request->result->isBuffered()) {
1236  request->result->release();
1237  }
1238 
1239  argv[1] = rows;
1240  argv[2] = columns;
1241  } else {
1242 #ifdef DEBUG
1243  std::cout << ", is empty";
1244 #endif
1245  v8::Local<v8::Object> result = v8::Object::New();
1246  result->Set(v8::String::New("id"), v8::Number::New(request->result->insertId()));
1247  result->Set(v8::String::New("affected"), v8::Number::New(request->result->affectedCount()));
1248  result->Set(v8::String::New("warning"), v8::Number::New(request->result->warningCount()));
1249  argv[1] = result;
1250  }
1251 
1252 #ifdef DEBUG
1253  std::cout << std::endl;
1254 #endif
1255 
1256  this->Emit("success", !isEmpty ? 2 : 1, &argv[1]);
1257 
1258  if (this->cbExecute != NULL && !this->cbExecute->IsEmpty()) {
1259  v8::TryCatch tryCatch;
1260  (*(this->cbExecute))->Call(request->context, !isEmpty ? 3 : 2, argv);
1261  if (tryCatch.HasCaught()) {
1262  node::FatalException(tryCatch);
1263  }
1264  }
1265  }
1266  } catch(const nodejs_db::Exception& exception) {
1267  this->connection->unlock();
1268 
1269  v8::Local<v8::Value> argv[1];
1270  argv[0] = v8::String::New(exception.what());
1271 
1272  this->Emit("error", 1, argv);
1273 
1274  if (this->cbExecute != NULL && !this->cbExecute->IsEmpty()) {
1275  v8::TryCatch tryCatch;
1276  (*(this->cbExecute))->Call(request->context, 1, argv);
1277  if (tryCatch.HasCaught()) {
1278  node::FatalException(tryCatch);
1279  }
1280  }
1281 
1282  freeAll = false;
1283  }
1284 
1285  Query::freeRequest(request, freeAll);
1286 }
1287 
1288 
1289 
1290 /**
1291  * execute the SQL query
1292  */
1294  /*
1295  switch (this->sqlType) {
1296  case Query::NONE:
1297  case Query::SELECT:
1298  return this->connection->query(this->sql.str());
1299  case Query::INSERT:
1300  case Query::UPDATE:
1301  case Query::DELETE:
1302  return this->connection->query_x(this->sql.str());
1303  default:
1304  return this->connection->query(this->sql.str());
1305  }
1306  */
1307 
1308  if (this->sqlType == Query::NONE
1309  || this->sqlType == Query::SELECT
1310  ) {
1311  return this->connection->query(this->sql.str());
1312  }
1313 
1314  if (this->sqlType != Query::SELECT) {
1315  return this->connection->query_x(this->sql.str());
1316  }
1317 
1318  return this->connection->query(this->sql.str());
1319 }
1320 
1321 
1322 
1323 void nodejs_db::Query::freeRequest(execute_request_t* request, bool freeAll) {
1324  DEBUG_LOG_FUNC;
1325 
1326  /*
1327  if (request->rows != NULL) {
1328  for (std::vector<row_t*>::iterator iterator = request->rows->begin()
1329  , end = request->rows->end()
1330  ; iterator != end; ++iterator
1331  ) {
1332  row_t* row = *iterator;
1333  assert(row);
1334 
1335  if (!request->buffered) {
1336  if (row->columns) { delete row->columns; }
1337  if (row->columnLengths) { delete [] row->columnLengths; }
1338  }
1339  delete row;
1340  }
1341  delete request->rows;
1342  }
1343  */
1344 
1345  // delete request->rows;
1346 
1347  if (request->error != NULL) {
1348  delete request->error;
1349  }
1350 
1351  if (freeAll) {
1352  /*
1353  if (request->query != NULL) { delete request->query; }
1354  */
1355 
1356  if (request->result != NULL) { delete request->result; }
1357 
1358  request->context.Dispose();
1359 
1360  delete request;
1361  }
1362 }
1363 
1364 
1365 /**
1366  *
1367  */
1368 v8::Handle<v8::Value>
1369 nodejs_db::Query::set(const v8::Arguments& args) {
1370 
1371  /* don't proceed if connection doesn't exists */
1372  if (!this->connection) {
1373  THROW_EXCEPTION("No connection. Did you attempt to create new query "
1374  "without connection?");
1375  }
1376 
1377  if (args.Length() == 0) {
1378  return v8::Handle<v8::Value>();
1379  }
1380 
1381  int queryIndex = -1, optionsIndex = -1, valuesIndex = -1, callbackIndex = -1;
1382 
1383  if (args.Length() > 3) {
1384  ARG_CHECK_STRING(0, query);
1385  ARG_CHECK_ARRAY(1, values);
1386  ARG_CHECK_FUNCTION(2, callback);
1387  ARG_CHECK_OBJECT(3, options);
1388  queryIndex = 0;
1389  valuesIndex = 1;
1390  callbackIndex = 2;
1391  optionsIndex = 3;
1392  } else if (args.Length() == 3) {
1393  ARG_CHECK_STRING(0, query);
1394  queryIndex = 0;
1395  if (args[2]->IsFunction()) {
1396  ARG_CHECK_FUNCTION(2, callback);
1397  if (args[1]->IsArray()) {
1398  ARG_CHECK_ARRAY(1, values);
1399  valuesIndex = 1;
1400  } else {
1401  ARG_CHECK_OBJECT(1, options);
1402  optionsIndex = 1;
1403  }
1404  callbackIndex = 2;
1405  } else {
1406  ARG_CHECK_ARRAY(1, values);
1407  ARG_CHECK_OBJECT(2, options);
1408  valuesIndex = 1;
1409  optionsIndex = 2;
1410  }
1411  } else if (args.Length() == 2) {
1412  if (args[1]->IsFunction()) {
1413  ARG_CHECK_FUNCTION(1, callback);
1414  callbackIndex = 1;
1415  } else if (args[1]->IsArray()) {
1416  ARG_CHECK_ARRAY(1, values);
1417  valuesIndex = 1;
1418  } else {
1419  ARG_CHECK_OBJECT(1, options);
1420  optionsIndex = 1;
1421  }
1422 
1423  if (args[0]->IsFunction() && callbackIndex == -1) {
1424  ARG_CHECK_FUNCTION(0, callback);
1425  callbackIndex = 0;
1426  } else {
1427  ARG_CHECK_STRING(0, query);
1428  queryIndex = 0;
1429  }
1430  } else if (args.Length() == 1) {
1431  if (args[0]->IsString()) {
1432  ARG_CHECK_STRING(0, query);
1433  queryIndex = 0;
1434  } else if (args[0]->IsFunction()) {
1435  ARG_CHECK_FUNCTION(0, callback);
1436  callbackIndex = 0;
1437  } else if (args[0]->IsArray()) {
1438  ARG_CHECK_ARRAY(0, values);
1439  valuesIndex = 0;
1440  } else {
1441  ARG_CHECK_OBJECT(0, options);
1442  optionsIndex = 0;
1443  }
1444  }
1445 
1446  if (queryIndex >= 0) {
1447  v8::String::Utf8Value initialSql(args[queryIndex]->ToString());
1448  this->sql.str("");
1449  this->sql.clear();
1450  this->sql << *initialSql;
1451  }
1452 
1453  if (optionsIndex >= 0) {
1454  v8::Local<v8::Object> options = args[optionsIndex]->ToObject();
1455 
1456  ARG_CHECK_OBJECT_ATTR_OPTIONAL_BOOL(options, async);
1457  ARG_CHECK_OBJECT_ATTR_OPTIONAL_BOOL(options, cast);
1458  ARG_CHECK_OBJECT_ATTR_OPTIONAL_BOOL(options, bufferText);
1459  ARG_CHECK_OBJECT_ATTR_OPTIONAL_FUNCTION(options, start);
1460  ARG_CHECK_OBJECT_ATTR_OPTIONAL_FUNCTION(options, finish);
1461 
1462  if (options->Has(async_key)) {
1463  this->async = options->Get(async_key)->IsTrue();
1464  }
1465 
1466  if (options->Has(cast_key)) {
1467  this->cast = options->Get(cast_key)->IsTrue();
1468  }
1469 
1470  if (options->Has(bufferText_key)) {
1471  this->bufferText = options->Get(bufferText_key)->IsTrue();
1472  }
1473 
1474  if (options->Has(start_key)) {
1475  if (this->cbStart != NULL) {
1476  node::cb_destroy(this->cbStart);
1477  }
1478  this->cbStart = node::cb_persist(options->Get(start_key));
1479  }
1480 
1481  if (options->Has(finish_key)) {
1482  if (this->cbFinish != NULL) {
1483  node::cb_destroy(this->cbFinish);
1484  }
1485  this->cbFinish = node::cb_persist(options->Get(finish_key));
1486  }
1487  }
1488 
1489  if (valuesIndex >= 0) {
1490  v8::Local<v8::Array> v= v8::Array::Cast(*args[valuesIndex]);
1491  for (uint32_t i = 0, limiti = v->Length(); i < limiti; i++) {
1492  this->values.push_back(v8::Persistent<v8::Value>::New(v->Get(i)));
1493  }
1494  }
1495 
1496  if (callbackIndex >= 0) {
1497  this->cbExecute = node::cb_persist(args[callbackIndex]);
1498  }
1499 
1500  return v8::Handle<v8::Value>();
1501 }
1502 
1503 std::string
1504 nodejs_db::Query::fieldName(v8::Local<v8::Value> v)
1505 const throw(nodejs_db::Exception&) {
1506  DEBUG_LOG_FUNC;
1507  std::string buffer;
1508 
1509  if (v->IsObject()) {
1510  v8::Local<v8::Object> valueObject = v->ToObject();
1511  v8::Local<v8::Array> valueProperties = valueObject->GetPropertyNames();
1512  if (valueProperties->Length() == 0) {
1513  throw nodejs_db::Exception("Non empty objects should be used for value aliasing in select");
1514  }
1515 
1516  for (uint32_t j = 0, max_j = valueProperties->Length(); j < max_j; j++) {
1517  v8::Local<v8::Value> propertyName = valueProperties->Get(j);
1518  v8::String::Utf8Value fn(propertyName); // fieldName
1519 
1520  v8::Local<v8::Value> currentValue = valueObject->Get(propertyName);
1521  if (currentValue->IsObject() && !currentValue->IsArray() && !currentValue->IsFunction() && !currentValue->IsDate()) {
1522  v8::Local<v8::Object> currentObject = currentValue->ToObject();
1523  v8::Local<v8::String> escapeKey = v8::String::New("escape");
1524  v8::Local<v8::String> valueKey = v8::String::New("value");
1525  v8::Local<v8::String> precisionKey = v8::String::New("precision");
1526  v8::Local<v8::Value> optionValue;
1527  bool escape = false;
1528  int precision = -1;
1529 
1530  if (!currentObject->Has(valueKey)) {
1531  throw nodejs_db::Exception("The \"value\" option for the select field object must be specified");
1532  }
1533 
1534  if (currentObject->Has(escapeKey)) {
1535  optionValue = currentObject->Get(escapeKey);
1536  if (!optionValue->IsBoolean()) {
1537  throw nodejs_db::Exception("Specify a valid boolean value for the \"escape\" option in the select field object");
1538  }
1539  escape = optionValue->IsTrue();
1540  }
1541 
1542  if (currentObject->Has(precisionKey)) {
1543  optionValue = currentObject->Get(precisionKey);
1544  if (!optionValue->IsNumber() || optionValue->IntegerValue() < 0) {
1545  throw new nodejs_db::Exception("Specify a number equal or greater than 0 for precision");
1546  }
1547  precision = optionValue->IntegerValue();
1548  }
1549 
1550  if (j > 0) {
1551  buffer += ',';
1552  }
1553 
1554  buffer += this->value(currentObject->Get(valueKey), false, escape, precision);
1555  } else {
1556  if (j > 0) {
1557  buffer += ',';
1558  }
1559 
1560  buffer += this->value(currentValue, false, currentValue->IsString() ? false : true);
1561  }
1562 
1563  buffer += " AS ";
1564  buffer += this->connection->escapeName(*fn);
1565  }
1566  } else if (v->IsString()) {
1567  v8::String::Utf8Value fn(v->ToString());
1568  buffer += *fn;
1569  } else {
1570  throw nodejs_db::Exception("Incorrect value type provided as field for select");
1571  }
1572 
1573  return buffer;
1574 }
1575 
1576 
1577 std::string
1578 nodejs_db::Query::tableName(
1579  v8::Local<v8::Value> value
1580  , bool escape
1581 ) const throw(nodejs_db::Exception&) {
1582  DEBUG_LOG_FUNC;
1583  std::string buffer;
1584 
1585  if (value->IsArray()) {
1586  v8::Local<v8::Array> tables = v8::Array::Cast(*value);
1587  if (tables->Length() == 0) {
1588  throw nodejs_db::Exception("No tables specified");
1589  }
1590 
1591  for (uint32_t i = 0, limiti = tables->Length(); i < limiti; i++) {
1592  if (i > 0) {
1593  buffer += ',';
1594  }
1595 
1596  buffer += this->tableName(tables->Get(i), escape);
1597  }
1598  } else if (value->IsObject()) {
1599  v8::Local<v8::Object> valueObject = value->ToObject();
1600  v8::Local<v8::Array> valueProperties = valueObject->GetPropertyNames();
1601  if (valueProperties->Length() == 0) {
1602  throw nodejs_db::Exception("Non empty objects should be used for aliasing");
1603  }
1604 
1605  v8::Local<v8::Value> propertyName = valueProperties->Get(0);
1606  v8::Local<v8::Value> propertyValue = valueObject->Get(propertyName);
1607 
1608  if (!propertyName->IsString() || !propertyValue->IsString()) {
1609  throw nodejs_db::Exception("Only strings are allowed for table / alias name");
1610  }
1611 
1612  v8::String::Utf8Value table(propertyValue);
1613  v8::String::Utf8Value alias(propertyName);
1614 
1615  buffer += (escape ? this->connection->escapeName(*table) : *table);
1616  buffer += " AS ";
1617  buffer += (escape ? this->connection->escapeName(*alias) : *alias);
1618  } else {
1619  v8::String::Utf8Value tables(value->ToString());
1620 
1621  buffer += (escape ? this->connection->escapeName(*tables) : *tables);
1622  }
1623 
1624  return buffer;
1625 }
1626 
1627 
1628 v8::Handle<v8::Value>
1629 nodejs_db::Query::addCondition(
1630  const v8::Arguments& args
1631  , const char* separator
1632 ) {
1633  DEBUG_LOG_FUNC;
1634  ARG_CHECK_STRING(0, conditions);
1635  ARG_CHECK_OPTIONAL_ARRAY(1, values);
1636 
1637  v8::String::Utf8Value conditions(args[0]->ToString());
1638  std::string currentConditions = *conditions;
1639  if (args.Length() > 1) {
1640  v8::Local<v8::Array> currentValues = v8::Array::Cast(*args[1]);
1641  for (uint32_t i = 0, limiti = currentValues->Length(); i < limiti; i++) {
1642  this->values.push_back(v8::Persistent<v8::Value>::New(currentValues->Get(i)));
1643  }
1644  }
1645 
1646  this->sql << " " << separator << " ";
1647  this->sql << currentConditions;
1648 
1649  return args.This();
1650 }
1651 
1652 
1653 v8::Local<v8::Object>
1654 nodejs_db::Query::row(
1655  nodejs_db::Result* result,
1656  row_t* currentRow
1657 ) const {
1658  DEBUG_LOG_FUNC;
1659 
1660 #ifdef DEBUG
1661  std::cout
1662  << std::endl
1663  ;
1664 #endif
1665 
1666  v8::Local<v8::Object> row = v8::Object::New();
1667 
1668  for (uint16_t j = 0, max_j = result->columnCount(); j < max_j; ++j) {
1669  nodejs_db::Result::Column* currentColumn = result->column(j);
1670  v8::Local<v8::Value> v;
1671 
1672 #ifdef DEBUG
1673  std::cout
1674  << "Column: " << j
1675  << ", Name: " << currentColumn->getName().c_str();
1676 #endif
1677 
1678  if (currentRow->columns->at(j).c_str() != NULL) {
1679  const char* currentValue = currentRow->columns->at(j).c_str();
1680  unsigned long currentLength = currentRow->columnLengths[j];
1681 #ifdef DEBUG
1682  std::cout
1683  << ", Value: " << currentValue
1684  << ", length: " << currentLength
1685  ;
1686 #endif
1687 
1688  if (this->cast) {
1689 
1690  nodejs_db::Result::Column::type_t columnType = currentColumn->getType();
1691 
1692 #ifdef DEBUG
1693  std::cout
1694  << ", columnType: " << columnType;
1695 #endif
1696 
1697  switch (columnType) {
1698  case nodejs_db::Result::Column::BOOL:
1699  v = v8::Local<v8::Value>::New(currentValue == NULL || currentLength == 0 || currentValue[0] != '0' ? v8::True() : v8::False());
1700  break;
1701  case nodejs_db::Result::Column::INT:
1702  v = v8::String::New(currentValue, currentLength)->ToInteger();
1703  break;
1704  case nodejs_db::Result::Column::NUMBER:
1705  v = v8::String::New(currentValue, currentLength)->ToNumber();
1706  break;
1707  case nodejs_db::Result::Column::TIME:
1708  {
1709  int hour, min, sec;
1710  sscanf(currentValue, "%d:%d:%d", &hour, &min, &sec);
1711  v = v8::Date::New(static_cast<uint64_t>((hour*60*60 + min*60 + sec) * 1000));
1712  }
1713  break;
1714  case nodejs_db::Result::Column::DATE:
1715  case nodejs_db::Result::Column::DATETIME:
1716  // Code largely inspired from https://github.com/Sannis/node-mysql-libmysqlclient
1717  try {
1718  int day = 0, month = 0, year = 0, hour = 0, min = 0, sec = 0;
1719  time_t rawtime;
1720  struct tm timeinfo;
1721 
1722  if (columnType == nodejs_db::Result::Column::DATETIME) {
1723  sscanf(currentValue, "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &min, &sec);
1724  } else {
1725  sscanf(currentValue, "%d-%d-%d", &year, &month, &day);
1726  }
1727 
1728  time(&rawtime);
1729  if (!localtime_r(&rawtime, &timeinfo)) {
1730  throw nodejs_db::Exception("Can't get local time");
1731  }
1732 
1733  if (!Query::gmtDeltaLoaded) {
1734  int localHour, gmtHour, localMin, gmtMin;
1735 
1736  localHour = timeinfo.tm_hour - (timeinfo.tm_isdst > 0 ? 1 : 0);
1737  localMin = timeinfo.tm_min;
1738 
1739  if (!gmtime_r(&rawtime, &timeinfo)) {
1740  throw nodejs_db::Exception("Can't get GMT time");
1741  }
1742  gmtHour = timeinfo.tm_hour;
1743  gmtMin = timeinfo.tm_min;
1744 
1745  Query::gmtDelta = ((localHour - gmtHour) * 60 + (localMin - gmtMin)) * 60;
1746  if (Query::gmtDelta <= -(12 * 60 * 60)) {
1747  Query::gmtDelta += 24 * 60 * 60;
1748  } else if (Query::gmtDelta > (12 * 60 * 60)) {
1749  Query::gmtDelta -= 24 * 60 * 60;
1750  }
1751  Query::gmtDeltaLoaded = true;
1752  }
1753 
1754  timeinfo.tm_year = year - 1900;
1755  timeinfo.tm_mon = month - 1;
1756  timeinfo.tm_mday = day;
1757  timeinfo.tm_hour = hour;
1758  timeinfo.tm_min = min;
1759  timeinfo.tm_sec = sec;
1760 
1761  v = v8::Date::New(static_cast<double>(mktime(&timeinfo) + Query::gmtDelta) * 1000);
1762  } catch(const nodejs_db::Exception&) {
1763  v = v8::String::New(currentValue, currentLength);
1764  }
1765  break;
1766  case nodejs_db::Result::Column::SET:
1767  {
1768  v8::Local<v8::Array> vs = v8::Array::New();
1769  std::istringstream stream(currentValue);
1770  std::string item;
1771  uint64_t index = 0;
1772  while (std::getline(stream, item, ',')) {
1773  if (!item.empty()) {
1774  vs->Set(v8::Integer::New(index++), v8::String::New(item.c_str()));
1775  }
1776  }
1777  v = vs;
1778  }
1779  break;
1780  case nodejs_db::Result::Column::STRING:
1781  {
1782  v = v8::String::New(currentValue, currentLength);
1783  }
1784  break;
1785  case nodejs_db::Result::Column::TEXT:
1786  if (this->bufferText || currentColumn->isBinary()) {
1787  v = v8::Local<v8::Value>::New(node::Buffer::New(v8::String::New(currentValue, currentLength)));
1788  } else {
1789  v = v8::String::New(currentValue, currentLength);
1790  }
1791  break;
1792  default:
1793  v = v8::String::New(currentValue, currentLength);
1794  break;
1795  }
1796  } else {
1797  v = v8::String::New(currentValue, currentLength);
1798  }
1799  } else {
1800 #ifdef DEBUG
1801  std::cout << " is Null";
1802 #endif
1803  v = v8::Local<v8::Value>::New(v8::Null());
1804  }
1805 #ifdef DEBUG
1806  std::cout << std::endl;
1807 #endif
1808  row->Set(v8::String::New(currentColumn->getName().c_str()), v);
1809  }
1810 
1811  return row;
1812 }
1813 
1814 /**
1815  * \fn nodejs_db::Query::placeholders
1816  * \breif find the placeholders positions in the query
1817  */
1818 std::vector<std::string::size_type>
1820 const throw(nodejs_db::Exception&) {
1821  DEBUG_LOG_FUNC;
1822 
1823  std::string query = this->sql.str();
1824  std::vector<std::string::size_type> positions;
1825  char quote = 0;
1826  bool escaped = false;
1827  uint32_t delta = 0;
1828 
1829  *parsed = query;
1830  assert(parsed);
1831 
1832  for (std::string::size_type i = 0, limiti = query.length()
1833  ; i < limiti
1834  ; i++
1835  ) {
1836  char currentChar = query[i];
1837  if (escaped) {
1838  if (currentChar == '?') {
1839  parsed->replace(i - 1 - delta, 1, "");
1840  delta++;
1841  }
1842  escaped = false;
1843  } else if (currentChar == '\\') {
1844  escaped = true;
1845  } else if (quote && currentChar == quote) {
1846  quote = 0;
1847  } else if (!quote && (currentChar == this->connection->quoteString)) {
1848  quote = currentChar;
1849  } else if (!quote && currentChar == '?') {
1850  positions.push_back(i - delta);
1851  }
1852  }
1853 
1854  if (positions.size() != this->values.size()) {
1855  throw nodejs_db::Exception("Wrong number of values for placeholders");
1856  }
1857 
1858  return positions;
1859 }
1860 
1861 
1862 
1863 /**
1864  * \fn nodejs_db::Query::addProjections
1865  * \breif Add projection clauses into the query.
1866  * Assuming that this function will be called only once and none of the
1867  * projection clauses are present into the query
1868  */
1869 void
1871 throw(nodejs_db::Exception&) {
1872  DEBUG_LOG_FUNC;
1873 
1874  /* make a copy of sql */
1875  std::string s = this->sql.str();
1876 
1877  std::ostringstream ss;
1878  std::string select = "SELECT";
1879  size_t pos = s.find(select);
1880  if (pos == std::string::npos) {
1881  /* silently ignore perhaps this is not a select query */
1882  return;
1883  }
1884 
1885  if (this->projection.skip.flag) {
1886  /* create the skip clause */
1887  std::string skipStr = " SKIP ";
1888 
1889  ss << skipStr << this->projection.skip.arg;
1890  }
1891 
1892  if (this->projection.limit.flag) {
1893  const std::string limitStr = " LIMIT ";
1894 
1895  ss << limitStr << this->projection.limit.arg;
1896  } else if (this->projection.first.flag) {
1897  const std::string firstStr = " FIRST ";
1898 
1899  ss << firstStr << this->projection.first.arg;
1900  }
1901 
1902  s.insert(pos + select.length(), ss.str());
1903 
1904  this->sql.str(s);
1905  this->sql.seekp(s.length(), std::ios_base::beg);
1906 }
1907 
1908 
1909 
1910 /**
1911  * \fn nodejs_db::Query::parseQuery()
1912  * \breif Parse the sql
1913  */
1914 std::string
1915 nodejs_db::Query::parseQuery() const throw(nodejs_db::Exception&) {
1916  DEBUG_LOG_FUNC;
1917 
1918  std::string parsed;
1919  // p - positions
1920  std::vector<std::string::size_type> p
1921  = this->placeholders(&parsed);
1922 
1923  uint32_t index = 0, delta = 0;
1924  for (std::vector<std::string::size_type>::iterator iterator = p.begin()
1925  , end = p.end()
1926  ; iterator != end
1927  ; ++iterator
1928  , index++)
1929  {
1930  std::string v = this->value(*(this->values[index]));
1931 
1932  if(!v.length()) {
1933  throw nodejs_db::Exception(
1934  "Internal error, attempting to replace with zero length\
1935  value");
1936  }
1937 
1938  parsed.replace(*iterator + delta, 1, v);
1939  delta += (v.length() - 1);
1940  }
1941 
1942  return parsed;
1943 }
1944 
1945 
1946 
1947 std::string
1948 nodejs_db::Query::value(
1949  v8::Local<v8::Value> v,
1950  bool inArray,
1951  bool escape,
1952  int precision
1953 ) const throw(nodejs_db::Exception&) {
1954  DEBUG_LOG_FUNC;
1955  std::ostringstream currentStream;
1956 
1957  if (v->IsNull()) {
1958  currentStream << "NULL";
1959  } else if (v->IsArray()) {
1960  v8::Local<v8::Array> array = v8::Array::Cast(*v);
1961  if (!inArray) {
1962  currentStream << '(';
1963  }
1964  for (uint32_t i = 0, limiti = array->Length(); i < limiti; i++) {
1965  v8::Local<v8::Value> child = array->Get(i);
1966  if (child->IsArray() && i > 0) {
1967  currentStream << "),(";
1968  } else if (i > 0) {
1969  currentStream << ',';
1970  }
1971 
1972  currentStream << this->value(child, true, escape);
1973  }
1974  if (!inArray) {
1975  currentStream << ')';
1976  }
1977  } else if (v->IsDate()) {
1978  currentStream
1979  << this->connection->quoteString
1980  << this->fromDate(v8::Date::Cast(*v)->NumberValue())
1981  << this->connection->quoteString;
1982  } else if (v->IsObject()) {
1983  v8::Local<v8::Object> object = v->ToObject();
1984  v8::Handle<v8::String> valueKey = v8::String::New("value");
1985  v8::Handle<v8::String> escapeKey = v8::String::New("escape");
1986 
1987  if (object->Has(valueKey)) {
1988  v8::Handle<v8::String> precisionKey = v8::String::New("precision");
1989  // precision
1990  int p = -1;
1991 
1992  if (object->Has(precisionKey)) {
1993  v8::Local<v8::Value> optionValue = object->Get(precisionKey);
1994  if (!optionValue->IsNumber()
1995  || optionValue->IntegerValue() < 0
1996  ) {
1997  throw new nodejs_db::Exception(
1998  "Specify a number equal or greater than 0 for\
1999  precision");
2000  }
2001  p = optionValue->IntegerValue();
2002  }
2003 
2004  bool innerEscape = true;
2005  if (object->Has(escapeKey)) {
2006  v8::Local<v8::Value> escapeValue = object->Get(escapeKey);
2007  if (!escapeValue->IsBoolean()) {
2008  throw nodejs_db::Exception(
2009  "Specify a valid boolean value for the \"escape\"\
2010  option in the select field object");
2011  }
2012  innerEscape = escapeValue->IsTrue();
2013  }
2014  currentStream
2015  << this->value(object->Get(valueKey), false, innerEscape, p);
2016  } else {
2017  v8::Handle<v8::String> sqlKey = v8::String::New("sql");
2018  if (!object->Has(sqlKey) || !object->Get(sqlKey)->IsFunction()) {
2019  throw nodejs_db::Exception(
2020  "Objects can't be converted to a SQL value");
2021  }
2022 
2023  nodejs_db::Query *query =
2024  node::ObjectWrap::Unwrap<nodejs_db::Query>(object);
2025  assert(query);
2026  if (escape) {
2027  currentStream << "(";
2028  }
2029  currentStream << query->sql.str();
2030  if (escape) {
2031  currentStream << ")";
2032  }
2033  }
2034  } else if (v->IsBoolean()) {
2035  currentStream << (v->IsTrue() ? '1' : '0');
2036  } else if (
2037  v->IsUint32()
2038  || v->IsInt32()
2039  || (v->IsNumber() && v->NumberValue() == v->IntegerValue())
2040  ) {
2041  currentStream << v->IntegerValue();
2042  } else if (v->IsNumber()) {
2043  if (precision == -1) {
2044  v8::String::Utf8Value currentString(v->ToString());
2045  currentStream << *currentString;
2046  } else {
2047  currentStream
2048  << std::fixed
2049  << std::setprecision(precision)
2050  << v->NumberValue();
2051  }
2052  } else if (v->IsString()) {
2053  v8::String::Utf8Value currentString(v->ToString());
2054  std::string s = *currentString;
2055  if (escape) {
2056  try {
2057  currentStream
2058  << this->connection->quoteString
2059  << this->connection->escape(s)
2060  << this->connection->quoteString;
2061  } catch(nodejs_db::Exception& exception) {
2062  currentStream
2063  << this->connection->quoteString
2064  << s
2065  << this->connection->quoteString;
2066  }
2067  } else {
2068  currentStream << s;
2069  }
2070  } else {
2071  v8::String::Utf8Value currentString(v->ToString());
2072  std::string s = *currentString;
2073  throw nodejs_db::Exception(
2074  "Unknown type for to convert to SQL, converting `" + s + "'");
2075  }
2076 
2077  return currentStream.str();
2078 }
2079 
2080 
2081 
2082 std::string
2083 nodejs_db::Query::fromDate(
2084  const double timeStamp
2085 ) const throw(nodejs_db::Exception&) {
2086  char* buffer = new char[20];
2087  if (buffer == NULL) {
2088  throw nodejs_db::Exception(
2089  "Can\'t create buffer to write parsed date");
2090  }
2091 
2092 
2093  struct tm timeinfo;
2094  time_t rawtime = (time_t) (timeStamp / 1000);
2095  if (!localtime_r(&rawtime, &timeinfo)) {
2096  throw nodejs_db::Exception("Can't get local time");
2097  }
2098 
2099  strftime(buffer, 20, "%Y-%m-%d %H:%M:%S", &timeinfo);
2100 
2101  std::string date(buffer);
2102  delete [] buffer;
2103 
2104  return date;
2105 }
2106