1 /*
2  Copyright (c) 2015, 2016, 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 <NdbApi.hpp>
26 #include "NdbQueryBuilder.hpp"
27 #include "NdbQueryOperation.hpp"
28 
29 #include "adapter_global.h"
30 #include "TransactionImpl.h"
31 #include "QueryOperation.h"
32 
33 #include "node_buffer.h"
34 
35 #include "JsWrapper.h"
36 #include "js_wrapper_macros.h"
37 #include "NativeMethodCall.h"
38 #include "NdbWrapperErrors.h"
39 
40 using namespace v8;
41 
42 #define SET_KEY(X,Y) X.Set(isolate, String::NewFromUtf8(isolate, Y))
43 #define GET_KEY(X) X.Get(isolate)
44 
45 #define MAX_KEY_PARTS 8
46 
47 Eternal<String>    /* keys of NdbProjection */
48   K_next,
49   K_root,
50   K_keyFields,
51   K_joinTo,
52   K_serial,
53   K_parent,
54   K_tableHandler,
55   K_rowRecord,
56   K_indexHandler,
57   K_keyRecord,
58   K_isPrimaryKey,
59   K_relatedField,
60   K_dbTable,
61   K_dbIndex,
62   K_level,
63   K_data,
64   K_tag;
65 
66 
67 V8WrapperFn queryPrepareAndExecute,
68             querySetTransactionImpl,
69             queryFetchAllResults,
70             queryGetResult,
71             queryClose;
72 
73 
74 class QueryOperationEnvelopeClass : public Envelope {
75 public:
QueryOperationEnvelopeClass()76   QueryOperationEnvelopeClass() : Envelope("QueryOperation") {
77     addMethod("prepareAndExecute", queryPrepareAndExecute);
78     addMethod("setTransactionImpl", querySetTransactionImpl);
79     addMethod("fetchAllResults", queryFetchAllResults);
80     addMethod("getResult", queryGetResult);
81     addMethod("close", queryClose);
82   }
83 };
84 
85 QueryOperationEnvelopeClass QueryOperationEnvelope;
86 
QueryOperation_Wrapper(QueryOperation * queryOp)87 Local<Value> QueryOperation_Wrapper(QueryOperation *queryOp) {
88   Local<Value> jsobj = QueryOperationEnvelope.wrap(queryOp);
89   QueryOperationEnvelope.freeFromGC(queryOp, jsobj);
90   return jsobj;
91 }
92 
93 
setRowBuffers(Isolate * isolate,QueryOperation * queryOp,Handle<Object> spec,int parentId)94 void setRowBuffers(Isolate * isolate,
95                    QueryOperation *queryOp,
96                    Handle<Object> spec,
97                    int parentId) {
98   DEBUG_ENTER();
99   Record * record = 0;
100   int level = spec->Get(GET_KEY(K_serial))->Int32Value();
101   if(spec->Get(GET_KEY(K_rowRecord))->IsObject()) {
102     record = unwrapPointer<Record *>(spec->Get(GET_KEY(K_rowRecord))->ToObject());
103   }
104   assert(record);
105   queryOp->createRowBuffer(level, record, parentId);
106 
107   if(spec->Get(GET_KEY(K_relatedField))->IsNull()) {
108     queryOp->levelIsJoinTable(level);
109   }
110 }
111 
112 
createTopLevelQuery(Isolate * isolate,QueryOperation * queryOp,Handle<Object> spec,Handle<Object> keyBuffer)113 const NdbQueryOperationDef * createTopLevelQuery(Isolate * isolate,
114                                                  QueryOperation *queryOp,
115                                                  Handle<Object> spec,
116                                                  Handle<Object> keyBuffer) {
117   DEBUG_MARKER(UDEB_DETAIL);
118   NdbQueryBuilder *builder = queryOp->getBuilder();
119 
120   /* Pull values out of the JavaScript object */
121   Local<Value> v;
122   const Record * keyRecord = 0;
123   const NdbDictionary::Table * table = 0;
124   const NdbDictionary::Index * index = 0;
125 
126   v = spec->Get(GET_KEY(K_keyRecord));
127   if(v->IsObject()) {
128     keyRecord = unwrapPointer<const Record *>(v->ToObject());
129   };
130   v = spec->Get(GET_KEY(K_tableHandler));
131   if(v->IsObject()) {
132     v = v->ToObject()->Get(GET_KEY(K_dbTable));
133     if(v->IsObject()) {
134       table = unwrapPointer<const NdbDictionary::Table *>(v->ToObject());
135     }
136   }
137   bool isPrimaryKey = spec->Get(GET_KEY(K_isPrimaryKey))->BooleanValue();
138   const char * key_buffer = node::Buffer::Data(keyBuffer);
139   if(! isPrimaryKey) {
140     v = spec->Get(GET_KEY(K_indexHandler));
141     if(v->IsObject()) {
142       v = v->ToObject()->Get(GET_KEY(K_dbIndex));
143       if(v->IsObject()) {
144         index = unwrapPointer<const NdbDictionary::Index *> (v->ToObject());
145       }
146     }
147     assert(index);
148   }
149 
150   /* Build the key */
151   int nKeyParts = keyRecord->getNoOfColumns();
152   assert(nKeyParts <= MAX_KEY_PARTS);
153   const NdbQueryOperand * key_parts[MAX_KEY_PARTS + 1];
154 
155   DEBUG_PRINT("Creating root QueryOperationDef for table: %s", table->getName());
156   for(int i = 0; i < nKeyParts ; i++) {
157     uint32_t offset = keyRecord->getColumnOffset(i);
158     uint32_t length = keyRecord->getValueLength(i, key_buffer + offset);
159     offset += keyRecord->getValueOffset(i);  // accounts for length bytes
160     key_parts[i] = builder->constValue(key_buffer + offset, length);
161     DEBUG_PRINT_DETAIL("Key part %d: %s", i, keyRecord->getColumn(i)->getName());
162   }
163   key_parts[nKeyParts] = 0;
164 
165   return queryOp->defineOperation(index, table, key_parts);
166 }
167 
createNextLevel(Isolate * isolate,QueryOperation * queryOp,Handle<Object> spec,const NdbQueryOperationDef * parent)168 const NdbQueryOperationDef * createNextLevel(Isolate * isolate,
169                                              QueryOperation *queryOp,
170                                              Handle<Object> spec,
171                                              const NdbQueryOperationDef * parent) {
172   DEBUG_MARKER(UDEB_DEBUG);
173   NdbQueryBuilder *builder = queryOp->getBuilder();
174 
175   /* Pull values out of the JavaScript object */
176   Local<Value> v;
177   const NdbDictionary::Table * table = 0;
178   const NdbDictionary::Index * index = 0;
179   int depth = spec->Get(GET_KEY(K_serial))->Int32Value();
180 
181   v = spec->Get(GET_KEY(K_tableHandler));
182   if(v->IsObject()) {
183     v = v->ToObject()->Get(GET_KEY(K_dbTable));
184     if(v->IsObject()) {
185       table = unwrapPointer<const NdbDictionary::Table *>(v->ToObject());
186     }
187   }
188   bool isPrimaryKey = spec->Get(GET_KEY(K_isPrimaryKey))->BooleanValue();
189 
190   DEBUG_PRINT("Creating QueryOperationDef at level %d for table: %s",
191               depth, table->getName());
192 
193   if(! isPrimaryKey) {
194     v = spec->Get(GET_KEY(K_indexHandler));
195     if(v->IsObject()) {
196       v = v->ToObject()->Get(GET_KEY(K_dbIndex));
197       if(v->IsObject()) {
198         index = unwrapPointer<const NdbDictionary::Index *> (v->ToObject());
199       }
200     }
201     assert(index);
202   }
203 
204   v = spec->Get(GET_KEY(K_joinTo));
205   Array * joinColumns = Array::Cast(*v);
206 
207   /* Build the key */
208   int nKeyParts = joinColumns->Length();
209   assert(nKeyParts <= MAX_KEY_PARTS);
210   const NdbQueryOperand * key_parts[MAX_KEY_PARTS + 1];
211 
212   for(int i = 0 ; i < nKeyParts ; i++) {
213     String::Utf8Value column_name(joinColumns->Get(i));
214     key_parts[i] = builder->linkedValue(parent, *column_name);
215     DEBUG_PRINT_DETAIL("Key part %d: %s", i, *column_name);
216   }
217   key_parts[nKeyParts] = 0;
218 
219   return queryOp->defineOperation(index, table, key_parts);
220 }
221 
222 /* JS QueryOperation.create(ndbRootProjection, keyBuffer, depth)
223 */
createQueryOperation(const Arguments & args)224 void createQueryOperation(const Arguments & args) {
225   DEBUG_MARKER(UDEB_DEBUG);
226   REQUIRE_ARGS_LENGTH(3);
227   Isolate * isolate = args.GetIsolate();
228 
229   int size = args[2]->Int32Value();
230   int currentId = 0;
231   int parentId;
232   const NdbQueryOperationDef * current;
233   const NdbQueryOperationDef ** all = new const NdbQueryOperationDef * [size];
234   QueryOperation * queryOperation = new QueryOperation(size);
235 
236   Local<Value> v;
237   Local<Object> spec = args[0]->ToObject();
238   Local<Object> parentSpec;
239 
240   setRowBuffers(isolate, queryOperation, spec, 0);
241   current = createTopLevelQuery(isolate, queryOperation, spec, args[1]->ToObject());
242 
243   while(! (v = spec->Get(GET_KEY(K_next)))->IsUndefined()) {
244     all[currentId++] = current;
245     spec = v->ToObject();
246     parentSpec = spec->Get(GET_KEY(K_parent))->ToObject();
247     parentId = parentSpec->Get(GET_KEY(K_serial))->Int32Value();
248     current = createNextLevel(isolate, queryOperation, spec, all[parentId]);
249     assert(current->getOpNo() == spec->Get(GET_KEY(K_serial))->Uint32Value());
250     setRowBuffers(isolate, queryOperation, spec, parentId);
251   }
252   queryOperation->prepare(all[0]);
253   delete[] all;
254   args.GetReturnValue().Set(QueryOperation_Wrapper(queryOperation));
255 }
256 
querySetTransactionImpl(const Arguments & args)257 void querySetTransactionImpl(const Arguments &args) {
258   REQUIRE_ARGS_LENGTH(1);
259 
260   typedef NativeVoidMethodCall_1_<QueryOperation, TransactionImpl *> MCALL;
261   MCALL mcall(& QueryOperation::setTransactionImpl, args);
262   mcall.run();
263 
264   args.GetReturnValue().SetUndefined();
265 }
266 
267 // void prepareAndExecute()
268 // ASYNC
queryPrepareAndExecute(const Arguments & args)269 void queryPrepareAndExecute(const Arguments &args) {
270   EscapableHandleScope scope(args.GetIsolate());
271   DEBUG_MARKER(UDEB_DEBUG);
272   REQUIRE_ARGS_LENGTH(1);
273   typedef NativeMethodCall_0_<int, QueryOperation> MCALL;
274   MCALL * mcallptr = new MCALL(& QueryOperation::prepareAndExecute, args);
275   mcallptr->errorHandler = getNdbErrorIfLessThanZero;
276   mcallptr->runAsync();
277   args.GetReturnValue().SetUndefined();
278 }
279 
280 // fetchAllResults()
281 // ASYNC
queryFetchAllResults(const Arguments & args)282 void queryFetchAllResults(const Arguments &args) {
283   EscapableHandleScope scope(args.GetIsolate());
284   REQUIRE_ARGS_LENGTH(1);
285   typedef NativeMethodCall_0_<int, QueryOperation> MCALL;
286   MCALL * mcallptr = new MCALL(& QueryOperation::fetchAllResults, args);
287   mcallptr->errorHandler = getNdbErrorIfLessThanZero;
288   mcallptr->runAsync();
289   args.GetReturnValue().SetUndefined();
290 }
291 
freeQueryResultAtGC(char * data,void * hint)292 void freeQueryResultAtGC(char *data, void *hint) {
293   (void) hint;   // unused
294   free(data);
295 }
296 
doNotFreeQueryResultAtGC(char * data,void * hint)297 void doNotFreeQueryResultAtGC(char *data, void *hint) {
298   (void) hint;
299   (void) data;
300 }
301 
302 // getResult(id, objectWrapper):  IMMEDIATE
queryGetResult(const Arguments & args)303 void queryGetResult(const Arguments & args) {
304   REQUIRE_ARGS_LENGTH(2);
305   v8::Isolate * isolate = args.GetIsolate();
306 
307   QueryOperation * op = unwrapPointer<QueryOperation *>(args.Holder());
308   uint32_t id = args[0]->Uint32Value();
309   Handle<Object> wrapper = args[1]->ToObject();
310 
311   QueryResultHeader * header = op->getResult(id);
312 
313   if(header) {
314     if(header->data) {
315       wrapper->Set(GET_KEY(K_data),
316         LOCAL_BUFFER(node::Buffer::New(isolate, header->data,
317                                        op->getResultRowSize(header->sector),
318                                        doNotFreeQueryResultAtGC, 0)));
319     } else {
320       wrapper->Set(GET_KEY(K_data), Null(isolate));
321     }
322     wrapper->Set(GET_KEY(K_level), v8::Uint32::New(isolate, header->sector));
323     wrapper->Set(GET_KEY(K_tag),   v8::Uint32::New(isolate, header->tag));
324     args.GetReturnValue().Set(true);
325   } else {
326     args.GetReturnValue().Set(false);
327   }
328 }
329 
330 // void close()
331 // ASYNC
queryClose(const Arguments & args)332 void queryClose(const Arguments & args) {
333   typedef NativeVoidMethodCall_0_<QueryOperation> NCALL;
334   NCALL * ncallptr = new NCALL(& QueryOperation::close, args);
335   ncallptr->runAsync();
336   args.GetReturnValue().SetUndefined();
337 }
338 
QueryOperation_initOnLoad(Handle<Object> target)339 void QueryOperation_initOnLoad(Handle<Object> target) {
340   Isolate * isolate = Isolate::GetCurrent();
341   Local<Object> ibObj = Object::New(Isolate::GetCurrent());
342   Local<String> ibKey = NEW_SYMBOL("QueryOperation");
343   target->Set(ibKey, ibObj);
344 
345   DEFINE_JS_FUNCTION(ibObj, "create", createQueryOperation);
346 
347   SET_KEY(K_next, "next");
348   SET_KEY(K_root, "root");
349   SET_KEY(K_keyFields, "keyFields");
350   SET_KEY(K_joinTo, "joinTo");
351   SET_KEY(K_serial, "serial");
352   SET_KEY(K_parent, "parent");
353   SET_KEY(K_tableHandler, "tableHandler");
354   SET_KEY(K_rowRecord, "rowRecord"),
355   SET_KEY(K_indexHandler, "indexHandler");
356   SET_KEY(K_keyRecord, "keyRecord");
357   SET_KEY(K_isPrimaryKey, "isPrimaryKey");
358   SET_KEY(K_relatedField, "relatedField");
359 
360   SET_KEY(K_dbTable, "dbTable");
361   SET_KEY(K_dbIndex, "dbIndex");
362 
363   SET_KEY(K_level, "level");
364   SET_KEY(K_data, "data");
365   SET_KEY(K_tag, "tag");
366 }
367 
368