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