]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/c-apps/oils_cstore.c
adding trimming and id_list support
[Evergreen.git] / Open-ILS / src / c-apps / oils_cstore.c
1 #include "opensrf/osrf_application.h"
2 #include "opensrf/osrf_settings.h"
3 #include "opensrf/utils.h"
4 #include "objson/object.h"
5 #include "opensrf/log.h"
6 #include "oils_utils.h"
7 #include "oils_constants.h"
8 #include "oils_event.h"
9 #include <dbi/dbi.h>
10
11 #include <time.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <libxml/globals.h>
15 #include <libxml/xmlerror.h>
16 #include <libxml/parser.h>
17 #include <libxml/tree.h>
18 #include <libxml/debugXML.h>
19 #include <libxml/xmlmemory.h>
20
21 #define OILS_AUTH_CACHE_PRFX "oils_cstore_"
22 #define MODULENAME "open-ils.cstore"
23 #define PERSIST_NS "http://open-ils.org/spec/opensrf/IDL/persistance/v1"
24 #define OBJECT_NS "http://open-ils.org/spec/opensrf/IDL/objects/v1"
25 #define BASE_NS "http://opensrf.org/spec/IDL/base/v1"
26
27 int osrfAppChildInit();
28 int osrfAppInitialize();
29
30 int verifyObjectClass ( osrfMethodContext*, jsonObject* );
31
32 int beginTransaction ( osrfMethodContext* );
33 int commitTransaction ( osrfMethodContext* );
34 int rollbackTransaction ( osrfMethodContext* );
35
36 int setSavepoint ( osrfMethodContext* );
37 int releaseSavepoint ( osrfMethodContext* );
38 int rollbackSavepoint ( osrfMethodContext* );
39
40 int dispatchCRUDMethod ( osrfMethodContext* );
41 jsonObject* doCreate ( osrfMethodContext*, int* );
42 jsonObject* doRetrieve ( osrfMethodContext*, int* );
43 jsonObject* doUpdate ( osrfMethodContext*, int* );
44 jsonObject* doDelete ( osrfMethodContext*, int* );
45 jsonObject* doSearch ( osrfMethodContext*, osrfHash*, jsonObject*, int* );
46 jsonObject* oilsMakeJSONFromResult( dbi_result, osrfHash* );
47
48 char* searchSimplePredicate ( const char*, osrfHash*, jsonObject* );
49 char* searchFunctionPredicate ( osrfHash*, jsonObjectNode* );
50 char* searchBETWEENPredicate ( osrfHash*, jsonObject* );
51 char* searchINPredicate ( osrfHash*, jsonObject* );
52 char* searchPredicate ( osrfHash*, jsonObject* );
53
54 void userDataFree( void* );
55 void sessionDataFree( char*, void* );
56
57 dbi_conn dbhandle; /* our db connection */
58 xmlDocPtr idlDoc = NULL; // parse and store the IDL here
59 jsonObject* jsonNULL = NULL; // 
60
61
62 /* parse and store the IDL here */
63 osrfHash* idlHash;
64
65 int osrfAppInitialize() {
66
67         // first we register all the transaction and savepoint methods
68         osrfAppRegisterMethod( MODULENAME, "open-ils.cstore.transaction.begin", "beginTransaction", "", 0, 0 );
69         osrfAppRegisterMethod( MODULENAME, "open-ils.cstore.transaction.commit", "commitTransaction", "", 0, 0 );
70         osrfAppRegisterMethod( MODULENAME, "open-ils.cstore.transaction.rollback", "rollbackTransaction", "", 0, 0 );
71
72         osrfAppRegisterMethod( MODULENAME, "open-ils.cstore.savepoint.set", "setSavepoint", "", 1, 0 );
73         osrfAppRegisterMethod( MODULENAME, "open-ils.cstore.savepoint.release", "releaseSavepoint", "", 1, 0 );
74         osrfAppRegisterMethod( MODULENAME, "open-ils.cstore.savepoint.rollback", "rollbackSavepoint", "", 1, 0 );
75
76
77         idlHash = osrfNewHash();
78         osrfHash* usrData = NULL;
79
80         osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
81         osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
82
83         char * idl_filename = osrf_settings_host_value("/apps/%s/app_settings/IDL", MODULENAME);
84         osrfLogInfo(OSRF_LOG_MARK, "Found file:");
85         osrfLogInfo(OSRF_LOG_MARK, idl_filename);
86
87         osrfLogInfo(OSRF_LOG_MARK, "Parsing the IDL XML...");
88         idlDoc = xmlReadFile( idl_filename, NULL, XML_PARSE_XINCLUDE );
89         
90         if (!idlDoc) {
91                 osrfLogError(OSRF_LOG_MARK, "Could not load or parse the IDL XML file!");
92                 exit(1);
93         }
94
95         osrfLogInfo(OSRF_LOG_MARK, "...IDL XML parsed");
96
97         osrfStringArray* global_methods = osrfNewStringArray(1);
98
99         osrfStringArrayAdd( global_methods, "create" );
100         osrfStringArrayAdd( global_methods, "retrieve" );
101         osrfStringArrayAdd( global_methods, "update" );
102         osrfStringArrayAdd( global_methods, "delete" );
103         osrfStringArrayAdd( global_methods, "search" );
104         osrfStringArrayAdd( global_methods, "id_list" );
105
106         xmlNodePtr docRoot = xmlDocGetRootElement(idlDoc);
107         xmlNodePtr kid = docRoot->children;
108         while (kid) {
109                 if (!strcmp( (char*)kid->name, "class" )) {
110
111                         usrData = osrfNewHash();
112                         osrfHashSet( usrData, xmlGetProp(kid, "id"), "classname");
113                         osrfHashSet( usrData, xmlGetNsProp(kid, "tablename", PERSIST_NS), "tablename");
114                         osrfHashSet( usrData, xmlGetNsProp(kid, "fieldmapper", OBJECT_NS), "fieldmapper");
115
116                         osrfHashSet( idlHash, usrData, (char*)osrfHashGet(usrData, "classname") );
117
118                         osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", osrfHashGet(usrData, "fieldmapper") );
119
120                         osrfHash* _tmp;
121                         osrfHash* links = osrfNewHash();
122                         osrfHash* fields = osrfNewHash();
123
124                         osrfHashSet( usrData, fields, "fields" );
125                         osrfHashSet( usrData, links, "links" );
126
127                         xmlNodePtr _cur = kid->children;
128
129                         while (_cur) {
130                                 char* string_tmp = NULL;
131
132                                 if (!strcmp( (char*)_cur->name, "fields" )) {
133
134                                         if( (string_tmp = (char*)xmlGetNsProp(_cur, "primary", PERSIST_NS)) ) {
135                                                 osrfHashSet(
136                                                         usrData,
137                                                         strdup( string_tmp ),
138                                                         "primarykey"
139                                                 );
140                                         }
141                                         string_tmp = NULL;
142
143                                         if( (string_tmp = (char*)xmlGetNsProp(_cur, "sequence", PERSIST_NS)) ) {
144                                                 osrfHashSet(
145                                                         usrData,
146                                                         strdup( string_tmp ),
147                                                         "sequence"
148                                                 );
149                                         }
150                                         string_tmp = NULL;
151
152                                         xmlNodePtr _f = _cur->children;
153
154                                         while(_f) {
155                                                 if (strcmp( (char*)_f->name, "field" )) {
156                                                         _f = _f->next;
157                                                         continue;
158                                                 }
159
160                                                 _tmp = osrfNewHash();
161
162                                                 if( (string_tmp = (char*)xmlGetNsProp(_f, "array_position", OBJECT_NS)) ) {
163                                                         osrfHashSet(
164                                                                 _tmp,
165                                                                 strdup( string_tmp ),
166                                                                 "array_position"
167                                                         );
168                                                 }
169                                                 string_tmp = NULL;
170
171                                                 if( (string_tmp = (char*)xmlGetNsProp(_f, "virtual", PERSIST_NS)) ) {
172                                                         osrfHashSet(
173                                                                 _tmp,
174                                                                 strdup( string_tmp ),
175                                                                 "virtual"
176                                                         );
177                                                 }
178                                                 string_tmp = NULL;
179
180                                                 if( (string_tmp = (char*)xmlGetProp(_f, "name")) ) {
181                                                         osrfHashSet(
182                                                                 _tmp,
183                                                                 strdup( string_tmp ),
184                                                                 "name"
185                                                         );
186                                                 }
187
188                                                 osrfLogInfo(OSRF_LOG_MARK, "Found field %s for class %s", string_tmp, osrfHashGet(usrData, "classname") );
189
190                                                 osrfHashSet(
191                                                         fields,
192                                                         _tmp,
193                                                         strdup( string_tmp )
194                                                 );
195                                                 _f = _f->next;
196                                         }
197                                 }
198
199                                 if (!strcmp( (char*)_cur->name, "links" )) {
200                                         xmlNodePtr _l = _cur->children;
201
202                                         while(_l) {
203                                                 if (strcmp( (char*)_l->name, "link" )) {
204                                                         _l = _l->next;
205                                                         continue;
206                                                 }
207
208                                                 _tmp = osrfNewHash();
209
210                                                 if( (string_tmp = (char*)xmlGetProp(_l, "reltype")) ) {
211                                                         osrfHashSet(
212                                                                 _tmp,
213                                                                 strdup( string_tmp ),
214                                                                 "reltype"
215                                                         );
216                                                 }
217                                                 osrfLogInfo(OSRF_LOG_MARK, "Adding link with reltype %s", string_tmp );
218                                                 string_tmp = NULL;
219
220                                                 if( (string_tmp = (char*)xmlGetProp(_l, "key")) ) {
221                                                         osrfHashSet(
222                                                                 _tmp,
223                                                                 strdup( string_tmp ),
224                                                                 "key"
225                                                         );
226                                                 }
227                                                 osrfLogInfo(OSRF_LOG_MARK, "Link fkey is %s", string_tmp );
228                                                 string_tmp = NULL;
229
230                                                 if( (string_tmp = (char*)xmlGetProp(_l, "class")) ) {
231                                                         osrfHashSet(
232                                                                 _tmp,
233                                                                 strdup( string_tmp ),
234                                                                 "class"
235                                                         );
236                                                 }
237                                                 osrfLogInfo(OSRF_LOG_MARK, "Link fclass is %s", string_tmp );
238                                                 string_tmp = NULL;
239
240                                                 osrfStringArray* map = osrfNewStringArray(0);
241
242                                                 if( (string_tmp = (char*)xmlGetProp(_l, "map") )) {
243                                                         char* map_list = strdup( string_tmp );
244                                                         osrfLogInfo(OSRF_LOG_MARK, "Link mapping list is %s", string_tmp );
245
246                                                         if (strlen( map_list ) > 0) {
247                                                                 char* st_tmp;
248                                                                 char* _map_class = strtok_r(map_list, " ", &st_tmp);
249                                                                 osrfStringArrayAdd(map, strdup(_map_class));
250                                                 
251                                                                 while ((_map_class = strtok_r(NULL, " ", &st_tmp))) {
252                                                                         osrfStringArrayAdd(map, strdup(_map_class));
253                                                                 }
254                                                         }
255                                                 }
256                                                 osrfHashSet( _tmp, map, "map");
257
258                                                 if( (string_tmp = (char*)xmlGetProp(_l, "field")) ) {
259                                                         osrfHashSet(
260                                                                 _tmp,
261                                                                 strdup( string_tmp ),
262                                                                 "field"
263                                                         );
264                                                 }
265
266                                                 osrfHashSet(
267                                                         links,
268                                                         _tmp,
269                                                         strdup( string_tmp )
270                                                 );
271
272                                                 osrfLogInfo(OSRF_LOG_MARK, "Found link %s for class %s", string_tmp, osrfHashGet(usrData, "classname") );
273
274                                                 _l = _l->next;
275                                         }
276                                 }
277
278                                 _cur = _cur->next;
279                         }
280
281                         int i = 0; 
282                         char* method_type;
283                         char* st_tmp;
284                         char* _fm;
285                         char* part;
286                         osrfHash* method_meta;
287                         while ( (method_type = osrfStringArrayGetString(global_methods, i++)) ) {
288
289                                 if (!osrfHashGet(usrData, "fieldmapper")) continue;
290
291                                 method_meta = osrfNewHash();
292                                 osrfHashSet(method_meta, usrData, "class");
293
294                                 _fm = strdup( (char*)osrfHashGet(usrData, "fieldmapper") );
295                                 part = strtok_r(_fm, ":", &st_tmp);
296
297                                 growing_buffer* method_name =  buffer_init(64);
298                                 buffer_fadd(method_name, "%s.direct.%s", MODULENAME, part);
299
300                                 while ((part = strtok_r(NULL, ":", &st_tmp))) {
301                                         buffer_fadd(method_name, ".%s", part);
302                                 }
303                                 buffer_fadd(method_name, ".%s", method_type);
304
305
306                                 char* method = buffer_data(method_name);
307                                 buffer_free(method_name);
308                                 free(_fm);
309
310                                 osrfHashSet( method_meta, method, "methodname" );
311                                 osrfHashSet( method_meta, method_type, "methodtype" );
312
313                                 int flags = 0;
314                                 if (!(strcmp( method_type, "search" )) || !(strcmp( method_type, "id_list" ))) {
315                                         flags = flags | OSRF_METHOD_STREAMING;
316                                 }
317
318                                 osrfAppRegisterExtendedMethod(
319                                         MODULENAME,
320                                         method,
321                                         "dispatchCRUDMethod",
322                                         "",
323                                         1,
324                                         flags,
325                                         (void*)method_meta
326                                 );
327                         }
328                 }
329                 kid = kid->next;
330         }
331
332         return 0;
333 }
334
335 /**
336  * Connects to the database 
337  */
338 int osrfAppChildInit() {
339
340         osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
341         dbi_initialize(NULL);
342         osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
343
344         char* driver    = osrf_settings_host_value("/apps/%s/app_settings/driver", MODULENAME);
345         char* user      = osrf_settings_host_value("/apps/%s/app_settings/database/user", MODULENAME);
346         char* host      = osrf_settings_host_value("/apps/%s/app_settings/database/host", MODULENAME);
347         char* port      = osrf_settings_host_value("/apps/%s/app_settings/database/port", MODULENAME);
348         char* db        = osrf_settings_host_value("/apps/%s/app_settings/database/db", MODULENAME);
349         char* pw        = osrf_settings_host_value("/apps/%s/app_settings/database/pw", MODULENAME);
350
351         osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
352         dbhandle = dbi_conn_new(driver);
353
354         if(!dbhandle) {
355                 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
356                 return -1;
357         }
358         osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
359
360         osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database.  host=%s, "
361                 "port=%s, user=%s, pw=%s, db=%s", MODULENAME, host, port, user, pw, db );
362
363         if(host) dbi_conn_set_option(dbhandle, "host", host );
364         if(port) dbi_conn_set_option_numeric( dbhandle, "port", atoi(port) );
365         if(user) dbi_conn_set_option(dbhandle, "username", user);
366         if(pw) dbi_conn_set_option(dbhandle, "password", pw );
367         if(db) dbi_conn_set_option(dbhandle, "dbname", db );
368
369         free(user);
370         free(host);
371         free(port);
372         free(db);
373         free(pw);
374
375         const char* err;
376         if (dbi_conn_connect(dbhandle) < 0) {
377                 dbi_conn_error(dbhandle, &err);
378                 osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
379                 return -1;
380         }
381
382         osrfLogInfo(OSRF_LOG_MARK, "%s successfully connected to the database", MODULENAME);
383
384         int attr;
385         unsigned short type;
386         int i = 0; 
387         char* classname;
388         osrfStringArray* classes = osrfHashKeys( idlHash );
389         
390         while ( (classname = osrfStringArrayGetString(classes, i++)) ) {
391                 osrfHash* class = osrfHashGet( idlHash, classname );
392                 osrfHash* fields = osrfHashGet( class, "fields" );
393                 
394                 growing_buffer* sql_buf = buffer_init(32);
395                 buffer_fadd( sql_buf, "SELECT * FROM %s WHERE 1=0;", osrfHashGet(class, "tablename") );
396
397                 char* sql = buffer_data(sql_buf);
398                 buffer_free(sql_buf);
399                 osrfLogDebug(OSRF_LOG_MARK, "%s Investigatory SQL = %s", MODULENAME, sql);
400
401                 dbi_result result = dbi_conn_query(dbhandle, sql);
402                 free(sql);
403
404                 if (result) {
405
406                         int columnIndex = 1;
407                         const char* columnName;
408                         osrfHash* _f;
409                         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
410
411                                 osrfLogDebug(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
412
413                                 /* fetch the fieldmapper index */
414                                 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
415
416                                         osrfLogDebug(OSRF_LOG_MARK, "Found [%s] in IDL hash...", (char*)columnName);
417
418                                         /* determine the field type and storage attributes */
419                                         type = dbi_result_get_field_type(result, columnName);
420                                         attr = dbi_result_get_field_attribs(result, columnName);
421
422                                         switch( type ) {
423
424                                                 case DBI_TYPE_INTEGER :
425
426                                                         osrfHashSet(_f,"number", "primitive");
427
428                                                         if( attr & DBI_INTEGER_SIZE8 ) 
429                                                                 osrfHashSet(_f,"INT8", "datatype");
430                                                         else 
431                                                                 osrfHashSet(_f,"INT", "datatype");
432                                                         break;
433
434                                                 case DBI_TYPE_DECIMAL :
435                                                         osrfHashSet(_f,"number", "primitive");
436                                                         osrfHashSet(_f,"NUMERIC", "datatype");
437                                                         break;
438
439                                                 case DBI_TYPE_STRING :
440                                                         osrfHashSet(_f,"string", "primitive");
441                                                         osrfHashSet(_f,"TEXT", "datatype");
442                                                         break;
443
444                                                 case DBI_TYPE_DATETIME :
445                                                         osrfHashSet(_f,"string", "primitive");
446                                                         osrfHashSet(_f,"TIMESTAMP", "datatype");
447                                                         break;
448
449                                                 case DBI_TYPE_BINARY :
450                                                         osrfHashSet(_f,"string", "primitive");
451                                                         osrfHashSet(_f,"BYTEA", "datatype");
452                                         }
453
454                                         osrfLogDebug(
455                                                 OSRF_LOG_MARK,
456                                                 "Setting [%s] to primitive [%s] and datatype [%s]...",
457                                                 (char*)columnName,
458                                                 osrfHashGet(_f, "primitive"),
459                                                 osrfHashGet(_f, "datatype")
460                                         );
461                                 }
462                         }
463                         dbi_result_free(result);
464                 } else {
465                         osrfLogDebug(OSRF_LOG_MARK, "No data found for class [%s]...", (char*)classname);
466                 }
467         }
468
469         osrfStringArrayFree(classes);
470
471         return 0;
472 }
473
474 void userDataFree( void* blob ) {
475         osrfHashFree( (osrfHash*)blob );
476         return;
477 }
478
479 void sessionDataFree( char* key, void* item ) {
480         if (!(strcmp(key,"xact_id")))
481                 free(item);
482
483         return;
484 }
485
486 int beginTransaction ( osrfMethodContext* ctx ) {
487         OSRF_METHOD_VERIFY_CONTEXT(ctx);
488
489         dbi_result result = dbi_conn_query(dbhandle, "START TRANSACTION;");
490         if (!result) {
491                 osrfLogError(OSRF_LOG_MARK, "%s: Error starting transaction", MODULENAME );
492                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error starting transaction" );
493                 return -1;
494         } else {
495                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
496                 osrfAppRespondComplete( ctx, ret );
497                 jsonObjectFree(ret);
498                 
499                 if (!ctx->session->userData) {
500                         ctx->session->userData = osrfNewHash();
501                         ((osrfHash*)ctx->session->userData)->freeItem = &sessionDataFree;
502                 }
503
504                 osrfHashSet( (osrfHash*)ctx->session->userData, strdup( ctx->session->session_id ), "xact_id" );
505                 ctx->session->userDataFree = &userDataFree;
506                 
507         }
508         return 0;
509 }
510
511 int setSavepoint ( osrfMethodContext* ctx ) {
512         OSRF_METHOD_VERIFY_CONTEXT(ctx);
513
514         char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
515
516         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
517                 osrfAppSessionStatus(
518                         ctx->session,
519                         OSRF_STATUS_INTERNALSERVERERROR,
520                         "osrfMethodException",
521                         ctx->request,
522                         "No active transaction -- required for savepoints"
523                 );
524                 return -1;
525         }
526
527         dbi_result result = dbi_conn_queryf(dbhandle, "SAVEPOINT \"%s\";", spName);
528         if (!result) {
529                 osrfLogError(
530                         OSRF_LOG_MARK,
531                         "%s: Error creating savepoint %s in transaction %s",
532                         MODULENAME,
533                         spName,
534                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
535                 );
536                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error creating savepoint" );
537                 return -1;
538         } else {
539                 jsonObject* ret = jsonNewObject(spName);
540                 osrfAppRespondComplete( ctx, ret );
541                 jsonObjectFree(ret);
542         }
543         return 0;
544 }
545
546 int releaseSavepoint ( osrfMethodContext* ctx ) {
547         OSRF_METHOD_VERIFY_CONTEXT(ctx);
548
549         char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
550
551         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
552                 osrfAppSessionStatus(
553                         ctx->session,
554                         OSRF_STATUS_INTERNALSERVERERROR,
555                         "osrfMethodException",
556                         ctx->request,
557                         "No active transaction -- required for savepoints"
558                 );
559                 return -1;
560         }
561
562         dbi_result result = dbi_conn_queryf(dbhandle, "RELEASE SAVEPOINT \"%s\";", spName);
563         if (!result) {
564                 osrfLogError(
565                         OSRF_LOG_MARK,
566                         "%s: Error releasing savepoint %s in transaction %s",
567                         MODULENAME,
568                         spName,
569                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
570                 );
571                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error releasing savepoint" );
572                 return -1;
573         } else {
574                 jsonObject* ret = jsonNewObject(spName);
575                 osrfAppRespondComplete( ctx, ret );
576                 jsonObjectFree(ret);
577         }
578         return 0;
579 }
580
581 int rollbackSavepoint ( osrfMethodContext* ctx ) {
582         OSRF_METHOD_VERIFY_CONTEXT(ctx);
583
584         char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
585
586         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
587                 osrfAppSessionStatus(
588                         ctx->session,
589                         OSRF_STATUS_INTERNALSERVERERROR,
590                         "osrfMethodException",
591                         ctx->request,
592                         "No active transaction -- required for savepoints"
593                 );
594                 return -1;
595         }
596
597         dbi_result result = dbi_conn_queryf(dbhandle, "ROLLBACK TO SAVEPOINT \"%s\";", spName);
598         if (!result) {
599                 osrfLogError(
600                         OSRF_LOG_MARK,
601                         "%s: Error rolling back savepoint %s in transaction %s",
602                         MODULENAME,
603                         spName,
604                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
605                 );
606                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error rolling back savepoint" );
607                 return -1;
608         } else {
609                 jsonObject* ret = jsonNewObject(spName);
610                 osrfAppRespondComplete( ctx, ret );
611                 jsonObjectFree(ret);
612         }
613         return 0;
614 }
615
616 int commitTransaction ( osrfMethodContext* ctx ) {
617         OSRF_METHOD_VERIFY_CONTEXT(ctx);
618
619         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
620                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to commit" );
621                 return -1;
622         }
623
624         dbi_result result = dbi_conn_query(dbhandle, "COMMIT;");
625         if (!result) {
626                 osrfLogError(OSRF_LOG_MARK, "%s: Error committing transaction", MODULENAME );
627                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error committing transaction" );
628                 return -1;
629         } else {
630                 osrfHashRemove(ctx->session->userData, "xact_id");
631                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
632                 osrfAppRespondComplete( ctx, ret );
633                 jsonObjectFree(ret);
634         }
635         return 0;
636 }
637
638 int rollbackTransaction ( osrfMethodContext* ctx ) {
639         OSRF_METHOD_VERIFY_CONTEXT(ctx);
640
641         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
642                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to roll back" );
643                 return -1;
644         }
645
646         dbi_result result = dbi_conn_query(dbhandle, "ROLLBACK;");
647         if (!result) {
648                 osrfLogError(OSRF_LOG_MARK, "%s: Error rolling back transaction", MODULENAME );
649                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error rolling back transaction" );
650                 return -1;
651         } else {
652                 osrfHashRemove(ctx->session->userData, "xact_id");
653                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
654                 osrfAppRespondComplete( ctx, ret );
655                 jsonObjectFree(ret);
656         }
657         return 0;
658 }
659
660 int dispatchCRUDMethod ( osrfMethodContext* ctx ) {
661         OSRF_METHOD_VERIFY_CONTEXT(ctx);
662
663         osrfHash* meta = (osrfHash*) ctx->method->userData;
664         osrfHash* class_obj = osrfHashGet( meta, "class" );
665         
666         int err = 0;
667
668         jsonObject * obj = NULL;
669         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "create"))
670                 obj = doCreate(ctx, &err);
671
672         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "retrieve"))
673                 obj = doRetrieve(ctx, &err);
674
675         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "update"))
676                 obj = doUpdate(ctx, &err);
677
678         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "delete"))
679                 obj = doDelete(ctx, &err);
680
681         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "search")) {
682
683                 obj = doSearch(ctx, class_obj, ctx->params, &err);
684                 if(err) return err;
685
686                 jsonObjectNode* cur;
687                 jsonObjectIterator* itr = jsonNewObjectIterator( obj );
688                 while ((cur = jsonObjectIteratorNext( itr ))) {
689                         osrfAppRespond( ctx, jsonObjectClone(cur->item) );
690                 }
691                 jsonObjectIteratorFree(itr);
692                 osrfAppRespondComplete( ctx, NULL );
693
694         } else if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "id_list")) {
695
696                 jsonObject* _p = jsonObjectClone( ctx->params );
697                 if (jsonObjectGetIndex( _p, 1 )) {
698                         jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "flesh" );
699                         jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "flesh_columns" );
700                 } else {
701                         jsonObjectSetIndex( _p, 1, jsonParseString("{}") );
702                 }
703
704                 growing_buffer* sel_list = buffer_init(16);
705                 buffer_fadd(sel_list, "{ \"%s\":[\"%s\"] }", osrfHashGet( class_obj, "classname" ), osrfHashGet( class_obj, "primarykey" ));
706                 char* _s = buffer_data(sel_list);
707                 buffer_free(sel_list);
708
709                 jsonObjectSetKey( jsonObjectGetIndex( _p, 1 ), "select", jsonParseString(_s) );
710                 osrfLogDebug(OSRF_LOG_MARK, "%s: Select qualifer set to [%s]", MODULENAME, _s);
711                 free(_s);
712
713                 obj = doSearch(ctx, class_obj, _p, &err);
714                 if(err) return err;
715
716                 jsonObjectNode* cur;
717                 jsonObjectIterator* itr = jsonNewObjectIterator( obj );
718                 while ((cur = jsonObjectIteratorNext( itr ))) {
719                         osrfAppRespond(
720                                 ctx,
721                                 jsonObjectClone(
722                                         jsonObjectGetIndex(
723                                                 cur->item,
724                                                 atoi(
725                                                         osrfHashGet(
726                                                                 osrfHashGet(
727                                                                         osrfHashGet( class_obj, "fields" ),
728                                                                         osrfHashGet( class_obj, "primarykey")
729                                                                 ),
730                                                                 "array_position"
731                                                         )
732                                                 )
733                                         )
734                                 )
735                         );
736                 }
737                 jsonObjectIteratorFree(itr);
738                 osrfAppRespondComplete( ctx, NULL );
739                 
740         } else {
741                 osrfAppRespondComplete( ctx, obj );
742         }
743
744         jsonObjectFree(obj);
745
746         return err;
747 }
748
749 int verifyObjectClass ( osrfMethodContext* ctx, jsonObject* param ) {
750         
751         osrfHash* meta = (osrfHash*) ctx->method->userData;
752         osrfHash* class = osrfHashGet( meta, "class" );
753         
754         if ((strcmp( osrfHashGet(class, "classname"), param->classname ))) {
755
756                 growing_buffer* msg = buffer_init(128);
757                 buffer_fadd(
758                         msg,
759                         "%s: %s method for type %s was passed a %s",
760                         MODULENAME,
761                         osrfHashGet(meta, "methodtype"),
762                         osrfHashGet(class, "classname"),
763                         param->classname
764                 );
765
766                 char* m = buffer_data(msg);
767                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException", ctx->request, m );
768
769                 buffer_free(msg);
770                 free(m);
771
772                 return 0;
773         }
774         return 1;
775 }
776
777 jsonObject* doCreate(osrfMethodContext* ctx, int* err ) {
778
779         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
780         jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
781
782         if (!verifyObjectClass(ctx, target)) {
783                 *err = -1;
784                 return jsonNULL;
785         }
786
787         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
788                 osrfAppSessionStatus(
789                         ctx->session,
790                         OSRF_STATUS_BADREQUEST,
791                         "osrfMethodException",
792                         ctx->request,
793                         "No active transaction -- required for CREATE"
794                 );
795                 *err = -1;
796                 return jsonNULL;
797         }
798
799         osrfHash* fields = osrfHashGet(meta, "fields");
800         char* pkey = osrfHashGet(meta, "primarykey");
801         char* seq = osrfHashGet(meta, "sequence");
802
803         growing_buffer* table_buf = buffer_init(128);
804         growing_buffer* col_buf = buffer_init(128);
805         growing_buffer* val_buf = buffer_init(128);
806
807         buffer_fadd(table_buf,"INSERT INTO %s", osrfHashGet(meta, "tablename"));
808         buffer_add(col_buf,"(");
809         buffer_add(val_buf,"VALUES (");
810
811         int i = 0;
812         int first = 1;
813         char* field_name;
814         osrfStringArray* field_list = osrfHashKeys( fields );
815         while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
816
817                 osrfHash* field = osrfHashGet( fields, field_name );
818
819                 if(!( strcmp( osrfHashGet(osrfHashGet(fields,field_name), "virtual"), "true" ) )) continue;
820
821                 int pos = atoi(osrfHashGet(field, "array_position"));
822                 char* value = jsonObjectToSimpleString( jsonObjectGetIndex( target, pos ) );
823
824                 if (first) {
825                         first = 0;
826                 } else {
827                         buffer_add(col_buf, ",");
828                         buffer_add(val_buf, ",");
829                 }
830
831                 buffer_add(col_buf, field_name);
832
833                 if (jsonObjectGetIndex(target, pos)->type == JSON_NULL) {
834                         buffer_add( val_buf, "DEFAULT" );
835                         
836                 } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
837                         if ( !strcmp(osrfHashGet(field, "datatype"), "INT8") ) {
838                                 buffer_fadd( val_buf, "%lld", atol(value) );
839                                 
840                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "INT") ) {
841                                 buffer_fadd( val_buf, "%ld", atoll(value) );
842                                 
843                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
844                                 buffer_fadd( val_buf, "%f", atof(value) );
845                         }
846                 } else {
847                         if ( dbi_conn_quote_string(dbhandle, &value) ) {
848                                 buffer_fadd( val_buf, "%s", value );
849
850                         } else {
851                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
852                                 osrfAppSessionStatus(
853                                         ctx->session,
854                                         OSRF_STATUS_INTERNALSERVERERROR,
855                                         "osrfMethodException",
856                                         ctx->request,
857                                         "Error quoting string -- please see the error log for more details"
858                                 );
859                                 free(value);
860                                 buffer_free(table_buf);
861                                 buffer_free(col_buf);
862                                 buffer_free(val_buf);
863                                 *err = -1;
864                                 return jsonNULL;
865                         }
866                 }
867
868                 free(value);
869                 
870         }
871
872         buffer_add(col_buf,")");
873         buffer_add(val_buf,")");
874
875         growing_buffer* sql = buffer_init(128);
876         buffer_fadd(
877                 sql,
878                 "%s %s %s;",
879                 buffer_data(table_buf),
880                 buffer_data(col_buf),
881                 buffer_data(val_buf)
882         );
883         buffer_free(table_buf);
884         buffer_free(col_buf);
885         buffer_free(val_buf);
886
887         char* query = buffer_data(sql);
888         buffer_free(sql);
889
890         osrfLogDebug(OSRF_LOG_MARK, "%s: Insert SQL [%s]", MODULENAME, query);
891
892         
893         dbi_result result = dbi_conn_query(dbhandle, query);
894
895         jsonObject* obj = NULL;
896
897         if (!result) {
898                 obj = jsonNewObject(NULL);
899                 osrfLogError(
900                         OSRF_LOG_MARK,
901                         "%s ERROR inserting %s object using query [%s]",
902                         MODULENAME,
903                         osrfHashGet(meta, "fieldmapper"),
904                         query
905                 );
906                 osrfAppSessionStatus(
907                         ctx->session,
908                         OSRF_STATUS_INTERNALSERVERERROR,
909                         "osrfMethodException",
910                         ctx->request,
911                         "INSERT error -- please see the error log for more details"
912                 );
913                 *err = -1;
914         } else {
915
916                 int pos = atoi(osrfHashGet( osrfHashGet(fields, pkey), "array_position" ));
917                 char* id = jsonObjectToSimpleString(jsonObjectGetIndex(target, pos));
918                 if (!id) {
919                         unsigned long long new_id = dbi_conn_sequence_last(dbhandle, seq);
920                         growing_buffer* _id = buffer_init(10);
921                         buffer_fadd(_id, "%lld", new_id);
922                         id = buffer_data(_id);
923                         buffer_free(_id);
924                 }
925
926                 jsonObject* fake_params = jsonParseString("[]");
927                 jsonObjectPush(fake_params, jsonParseString("{}"));
928
929                 jsonObjectSetKey(
930                         jsonObjectGetIndex(fake_params, 0),
931                         osrfHashGet(meta, "primarykey"),
932                         jsonNewObject(id)
933                 );
934
935                 jsonObject* list = doSearch( ctx,meta, fake_params, err);
936
937                 if(*err) {
938                         jsonObjectFree( fake_params );
939                         obj = jsonNULL;
940                 } else {
941                         obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
942                 }
943
944                 jsonObjectFree( list );
945                 jsonObjectFree( fake_params );
946
947         }
948
949         free(query);
950
951         return obj;
952
953 }
954
955
956 jsonObject* doRetrieve(osrfMethodContext* ctx, int* err ) {
957
958         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
959
960         jsonObject* obj;
961
962         char* id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
963         jsonObject* order_hash = jsonObjectGetIndex(ctx->params, 1);
964
965         osrfLogDebug(
966                 OSRF_LOG_MARK,
967                 "%s retrieving %s object with id %s",
968                 MODULENAME,
969                 osrfHashGet(meta, "fieldmapper"),
970                 id
971         );
972
973         jsonObject* fake_params = jsonParseString("[]");
974         jsonObjectPush(fake_params, jsonParseString("{}"));
975
976         jsonObjectSetKey(
977                 jsonObjectGetIndex(fake_params, 0),
978                 osrfHashGet(meta, "primarykey"),
979                 jsonParseString(id)
980         );
981
982         if (order_hash) jsonObjectPush(fake_params, jsonObjectClone(order_hash) );
983
984         jsonObject* list = doSearch( ctx,meta, fake_params, err);
985
986         if(*err) {
987                 jsonObjectFree( fake_params );
988                 return jsonNULL;
989         }
990
991         obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
992
993         jsonObjectFree( list );
994         jsonObjectFree( fake_params );
995
996         return obj;
997 }
998
999 char* jsonNumberToDBString ( osrfHash* field, jsonObject* value ) {
1000         growing_buffer* val_buf = buffer_init(32);
1001
1002         if ( !strncmp(osrfHashGet(field, "datatype"), "INT", 3) ) {
1003                 if (value->type == JSON_NUMBER) buffer_fadd( val_buf, "%ld", (long)jsonObjectGetNumber(value) );
1004                 else buffer_fadd( val_buf, "%ld", atol(jsonObjectToSimpleString(value)) );
1005
1006         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
1007                 if (value->type == JSON_NUMBER) buffer_fadd( val_buf, "%f",  jsonObjectGetNumber(value) );
1008                 else buffer_fadd( val_buf, "%f", atof(jsonObjectToSimpleString(value)) );
1009         }
1010
1011         char* pred = buffer_data(val_buf);
1012         buffer_free(val_buf);
1013
1014         return pred;
1015 }
1016
1017 char* searchINPredicate (osrfHash* field, jsonObject* node) {
1018         growing_buffer* sql_buf = buffer_init(32);
1019         
1020         buffer_fadd(
1021                 sql_buf,
1022                 "%s IN (",
1023                 osrfHashGet(field, "name")
1024         );
1025
1026         int in_item_index = 0;
1027         int in_item_first = 1;
1028         jsonObject* in_item;
1029         while ( (in_item = jsonObjectGetIndex(node, in_item_index++)) ) {
1030
1031                 if (in_item_first)
1032                         in_item_first = 0;
1033                 else
1034                         buffer_add(sql_buf, ", ");
1035
1036                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1037                         char* val = jsonNumberToDBString( field, in_item );
1038                         buffer_fadd( sql_buf, "%s", val );
1039                         free(val);
1040
1041                 } else {
1042                         char* key_string = jsonObjectToSimpleString(in_item);
1043                         if ( dbi_conn_quote_string(dbhandle, &key_string) ) {
1044                                 buffer_fadd( sql_buf, "%s", key_string );
1045                                 free(key_string);
1046                         } else {
1047                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string);
1048                                 free(key_string);
1049                                 buffer_free(sql_buf);
1050                                 return NULL;
1051                         }
1052                 }
1053         }
1054
1055         buffer_add(
1056                 sql_buf,
1057                 ")"
1058         );
1059
1060         char* pred = buffer_data(sql_buf);
1061         buffer_free(sql_buf);
1062
1063         return pred;
1064 }
1065
1066 char* searchFunctionPredicate (osrfHash* field, jsonObjectNode* node) {
1067         growing_buffer* sql_buf = buffer_init(32);
1068         
1069         buffer_fadd(
1070                 sql_buf,
1071                 "%s %s ",
1072                 osrfHashGet(field, "name"),
1073                 node->key
1074         );
1075
1076         char* val = NULL;
1077         int func_item_index = 0;
1078         int func_item_first = 2;
1079         jsonObject* func_item;
1080         while ( (func_item = jsonObjectGetIndex(node->item, func_item_index++)) ) {
1081
1082                 val = jsonObjectToSimpleString(func_item);
1083
1084                 if (func_item_first == 2) {
1085                         buffer_fadd(sql_buf, "%s( ", val);
1086                         free(val);
1087                         func_item_first--;
1088                         continue;
1089                 }
1090
1091                 if (func_item_first)
1092                         func_item_first--;
1093                 else
1094                         buffer_add(sql_buf, ", ");
1095
1096                 if ( dbi_conn_quote_string(dbhandle, &val) ) {
1097                         buffer_fadd( sql_buf, "%s", val );
1098                         free(val);
1099                 } else {
1100                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1101                         free(val);
1102                         buffer_free(sql_buf);
1103                         return NULL;
1104                 }
1105         }
1106
1107         buffer_add(
1108                 sql_buf,
1109                 " )"
1110         );
1111
1112         char* pred = buffer_data(sql_buf);
1113         buffer_free(sql_buf);
1114
1115         return pred;
1116 }
1117
1118 char* searchSimplePredicate (const char* orig_op, osrfHash* field, jsonObject* node) {
1119
1120         char* val = NULL;
1121         char* op = NULL;
1122         if (node->type == JSON_NULL) {
1123                 val = strdup("NULL");
1124
1125                 if (strcmp( orig_op, "=" ))
1126                         op = strdup("IS NOT");
1127                 else
1128                         op = strdup("IS");
1129
1130         } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1131                 val = jsonNumberToDBString( field, node );
1132                 op = strdup (orig_op);
1133
1134         } else {
1135                 val = jsonObjectToSimpleString(node);
1136                 op = strdup (orig_op);
1137                 if ( !dbi_conn_quote_string(dbhandle, &val) ) {
1138                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1139                         free(val);
1140                         return NULL;
1141                 }
1142         }
1143
1144         growing_buffer* sql_buf = buffer_init(16);
1145         buffer_fadd( sql_buf, "%s %s %s", osrfHashGet(field, "name"), op, val );
1146         free(val);
1147         free(op);
1148
1149         char* pred = buffer_data(sql_buf);
1150         buffer_free(sql_buf);
1151
1152         return pred;
1153 }
1154
1155 char* searchBETWEENPredicate (osrfHash* field, jsonObject* node) {
1156
1157         char* x_string;
1158         char* y_string;
1159
1160         if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1161                 x_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,0));
1162                 y_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,1));
1163
1164         } else {
1165                 x_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,0));
1166                 y_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,1));
1167                 if ( !(dbi_conn_quote_string(dbhandle, &x_string) && dbi_conn_quote_string(dbhandle, &y_string)) ) {
1168                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key strings [%s] and [%s]", MODULENAME, x_string, y_string);
1169                         free(x_string);
1170                         free(y_string);
1171                         return NULL;
1172                 }
1173         }
1174
1175         growing_buffer* sql_buf = buffer_init(32);
1176         buffer_fadd( sql_buf, "%s BETWEEN %s AND %s", osrfHashGet(field, "name"), x_string, y_string );
1177         free(x_string);
1178         free(y_string);
1179
1180         char* pred = buffer_data(sql_buf);
1181         buffer_free(sql_buf);
1182
1183         return pred;
1184 }
1185
1186 char* searchPredicate ( osrfHash* field, jsonObject* node ) {
1187
1188         char* pred = NULL;
1189         if (node->type == JSON_ARRAY) { // equality IN search
1190                 pred = searchINPredicate( field, node );
1191         } else if (node->type == JSON_HASH) { // non-equality search
1192                 jsonObjectNode* pred_node;
1193                 jsonObjectIterator* pred_itr = jsonNewObjectIterator( node );
1194                 while ( (pred_node = jsonObjectIteratorNext( pred_itr )) ) {
1195                         if ( !(strcasecmp( pred_node->key,"between" )) )
1196                                 pred = searchBETWEENPredicate( field, pred_node->item );
1197                         else if ( !(strcasecmp( pred_node->key,"in" )) )
1198                                 pred = searchINPredicate( field, pred_node->item );
1199                         else if ( pred_node->item->type == JSON_ARRAY )
1200                                 pred = searchFunctionPredicate( field, pred_node );
1201                         else 
1202                                 pred = searchSimplePredicate( pred_node->key, field, pred_node->item );
1203
1204                         break;
1205                 }
1206         } else if (node->type == JSON_NULL) { // IS NULL search
1207                 growing_buffer* _p = buffer_init(16);
1208                 buffer_fadd(
1209                         _p,
1210                         "%s IS NULL",
1211                         osrfHashGet(field, "name")
1212                 );
1213                 pred = buffer_data(_p);
1214                 buffer_free(_p);
1215         } else { // equality search
1216                 pred = searchSimplePredicate( "=", field, node );
1217         }
1218
1219         return pred;
1220
1221 }
1222
1223 jsonObject* doSearch(osrfMethodContext* ctx, osrfHash* meta, jsonObject* params, int* err ) {
1224
1225         osrfHash* links = osrfHashGet(meta, "links");
1226         osrfHash* fields = osrfHashGet(meta, "fields");
1227         char* core_class = osrfHashGet(meta, "classname");
1228
1229         jsonObjectNode* node = NULL;
1230         jsonObjectNode* snode = NULL;
1231         jsonObject* _tmp;
1232         jsonObject* obj;
1233         jsonObject* search_hash = jsonObjectGetIndex(params, 0);
1234         jsonObject* order_hash = jsonObjectGetIndex(params, 1);
1235
1236         growing_buffer* sql_buf = buffer_init(128);
1237         buffer_add(sql_buf, "SELECT");
1238
1239         int first = 1;
1240         if ( (_tmp = jsonObjectGetKey( order_hash, "select" )) ) {
1241
1242                 jsonObjectIterator* class_itr = jsonNewObjectIterator( _tmp );
1243                 while ( (snode = jsonObjectIteratorNext( class_itr )) ) {
1244
1245                         osrfHash* idlClass = osrfHashGet( idlHash, snode->key );
1246                         if (!idlClass) continue;
1247                         char* cname = osrfHashGet(idlClass, "classname");
1248
1249                         jsonObjectIterator* select_itr = jsonNewObjectIterator( snode->item );
1250                         while ( (node = jsonObjectIteratorNext( select_itr )) ) {
1251                                 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), jsonObjectToSimpleString(node->item) );
1252                                 char* fname = osrfHashGet(field, "name");
1253
1254                                 if (!field) continue;
1255
1256                                 if (first) {
1257                                         first = 0;
1258                                 } else {
1259                                         buffer_add(sql_buf, ",");
1260                                 }
1261
1262                                 buffer_fadd(sql_buf, " %s.%s", cname, fname, cname, fname);
1263                         }
1264                 }
1265         } else {
1266                 buffer_add(sql_buf, " *");
1267         }
1268
1269         buffer_fadd(sql_buf, " FROM %s AS %s WHERE ", osrfHashGet(meta, "tablename"), core_class );
1270
1271
1272         char* pred;
1273         first = 1;
1274         jsonObjectIterator* search_itr = jsonNewObjectIterator( search_hash );
1275         while ( (node = jsonObjectIteratorNext( search_itr )) ) {
1276                 osrfHash* field = osrfHashGet( fields, node->key );
1277
1278                 if (!field) continue;
1279
1280                 if (first) {
1281                         first = 0;
1282                 } else {
1283                         buffer_add(sql_buf, " AND ");
1284                 }
1285
1286                 pred = searchPredicate( field, node->item);
1287                 buffer_fadd( sql_buf, "%s", pred );
1288                 free(pred);
1289         }
1290
1291         jsonObjectIteratorFree(search_itr);
1292
1293         if (order_hash) {
1294                 char* string;
1295                 if ( (_tmp = jsonObjectGetKey( order_hash, "order_by" )) ){
1296                         string = jsonObjectToSimpleString(_tmp);
1297                         buffer_fadd(
1298                                 sql_buf,
1299                                 " ORDER BY %s",
1300                                 string
1301                         );
1302                         free(string);
1303                 }
1304
1305                 if ( (_tmp = jsonObjectGetKey( order_hash, "limit" )) ){
1306                         string = jsonObjectToSimpleString(_tmp);
1307                         buffer_fadd(
1308                                 sql_buf,
1309                                 " LIMIT %d",
1310                                 atoi(string)
1311                         );
1312                         free(string);
1313                 }
1314
1315                 _tmp = jsonObjectGetKey( order_hash, "offset" );
1316                 if (_tmp) {
1317                         string = jsonObjectToSimpleString(_tmp);
1318                         buffer_fadd(
1319                                 sql_buf,
1320                                 " OFFSET %d",
1321                                 atoi(string)
1322                         );
1323                         free(string);
1324                 }
1325         }
1326
1327         buffer_add(sql_buf, ";");
1328
1329         char* sql = buffer_data(sql_buf);
1330         buffer_free(sql_buf);
1331         
1332         osrfLogDebug(OSRF_LOG_MARK, "%s SQL =  %s", MODULENAME, sql);
1333         dbi_result result = dbi_conn_query(dbhandle, sql);
1334
1335         jsonObject* res_list = jsonParseString("[]");
1336         if(result) {
1337                 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
1338
1339                 if (dbi_result_first_row(result)) {
1340                         /* JSONify the result */
1341                         osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
1342                         do {
1343                                 obj = oilsMakeJSONFromResult( result, meta );
1344                                 jsonObjectPush(res_list, obj);
1345                         } while (dbi_result_next_row(result));
1346                 } else {
1347                         osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
1348                 }
1349
1350                 /* clean up the query */
1351                 dbi_result_free(result); 
1352
1353         } else {
1354                 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]", MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
1355                 osrfAppSessionStatus(
1356                         ctx->session,
1357                         OSRF_STATUS_INTERNALSERVERERROR,
1358                         "osrfMethodException",
1359                         ctx->request,
1360                         "Severe query error -- see error log for more details"
1361                 );
1362                 *err = -1;
1363                 free(sql);
1364                 jsonObjectFree(res_list);
1365                 return jsonNULL;
1366
1367         }
1368
1369         free(sql);
1370
1371         if (order_hash) {
1372                 _tmp = jsonObjectGetKey( order_hash, "flesh" );
1373                 if (_tmp) {
1374                         int x = (int)jsonObjectGetNumber(_tmp);
1375
1376                         if (x > 0) {
1377
1378                                 jsonObjectNode* cur;
1379                                 jsonObjectIterator* itr = jsonNewObjectIterator( res_list );
1380                                 while ((cur = jsonObjectIteratorNext( itr ))) {
1381
1382                                         int i = 0;
1383                                         char* link_field;
1384                                         osrfStringArray* link_fields;
1385                                         
1386                                         jsonObject* flesh_fields = jsonObjectGetKey( order_hash, "flesh_fields" );
1387                                         if (flesh_fields) {
1388                                                 jsonObjectNode* _f;
1389                                                 jsonObjectIterator* _i = jsonNewObjectIterator( flesh_fields );
1390                                                 link_fields = osrfNewStringArray(1);
1391                                                 while ((_f = jsonObjectIteratorNext( _i ))) {
1392                                                         osrfStringArrayAdd( link_fields, jsonObjectToSimpleString( _f->item ) );
1393                                                 }
1394                                         } else {
1395                                                 link_fields = osrfHashKeys( links );
1396                                         }
1397
1398                                         while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
1399
1400                                                 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
1401
1402                                                 osrfHash* kid_link = osrfHashGet(links, link_field);
1403                                                 if (!kid_link) continue;
1404
1405                                                 osrfHash* field = osrfHashGet(fields, link_field);
1406                                                 if (!field) continue;
1407
1408                                                 osrfHash* value_field = field;
1409
1410                                                 osrfHash* kid_idl = osrfHashGet(idlHash, osrfHashGet(kid_link, "class"));
1411                                                 if (!kid_idl) continue;
1412
1413                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
1414                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
1415                                                 }
1416                                                         
1417                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
1418                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
1419                                                 }
1420
1421                                                 osrfLogDebug(
1422                                                         OSRF_LOG_MARK,
1423                                                         "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
1424                                                         osrfHashGet(kid_link, "field"),
1425                                                         osrfHashGet(kid_link, "class"),
1426                                                         osrfHashGet(kid_link, "key"),
1427                                                         osrfHashGet(kid_link, "reltype")
1428                                                 );
1429
1430                                                 jsonObject* fake_params = jsonParseString("[]");
1431                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // search hash
1432                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // order/flesh hash
1433
1434                                                 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
1435
1436                                                 char* search_key =
1437                                                 jsonObjectToSimpleString(
1438                                                         jsonObjectGetIndex(
1439                                                                 cur->item,
1440                                                                 atoi( osrfHashGet(value_field, "array_position") )
1441                                                         )
1442                                                 );
1443
1444                                                 if (!search_key) {
1445                                                         osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
1446                                                         continue;
1447                                                 }
1448                                                         
1449                                                 jsonObjectSetKey(
1450                                                         jsonObjectGetIndex(fake_params, 0),
1451                                                         osrfHashGet(kid_link, "key"),
1452                                                         jsonNewObject( search_key )
1453                                                 );
1454
1455                                                 free(search_key);
1456
1457
1458                                                 jsonObjectSetKey(
1459                                                         jsonObjectGetIndex(fake_params, 1),
1460                                                         "flesh",
1461                                                         jsonNewNumberObject( (double)(x - 1) )
1462                                                 );
1463
1464                                                 if (flesh_fields) {
1465                                                         jsonObjectSetKey(
1466                                                                 jsonObjectGetIndex(fake_params, 1),
1467                                                                 "flesh_fields",
1468                                                                 jsonObjectClone( flesh_fields )
1469                                                         );
1470                                                 }
1471
1472                                                 if (jsonObjectGetKey(order_hash, "order_by")) {
1473                                                         jsonObjectSetKey(
1474                                                                 jsonObjectGetIndex(fake_params, 1),
1475                                                                 "order_by",
1476                                                                 jsonObjectClone(jsonObjectGetKey(order_hash, "order_by"))
1477                                                         );
1478                                                 }
1479
1480                                                 jsonObject* kids = doSearch(ctx, kid_idl, fake_params, err);
1481
1482                                                 if(*err) {
1483                                                         jsonObjectFree( fake_params );
1484                                                         if (flesh_fields) osrfStringArrayFree(link_fields);
1485                                                         jsonObjectIteratorFree(itr);
1486                                                         jsonObjectFree(res_list);
1487                                                         return jsonNULL;
1488                                                 }
1489
1490
1491
1492                                                 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
1493                                                 
1494                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" ))) {
1495                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
1496                                                         jsonObjectSetIndex(
1497                                                                 cur->item,
1498                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
1499                                                                 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
1500                                                         );
1501                                                         jsonObjectFree( kids );
1502                                                 }
1503
1504                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
1505                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
1506                                                         jsonObjectSetIndex(
1507                                                                 cur->item,
1508                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
1509                                                                 kids
1510                                                         );
1511                                                 }
1512
1513                                                 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
1514
1515                                                 jsonObjectFree( fake_params );
1516                                         }
1517                                         if (flesh_fields) osrfStringArrayFree(link_fields);
1518                                 }
1519                                 jsonObjectIteratorFree(itr);
1520                         }
1521                 }
1522         }
1523
1524         return res_list;
1525 }
1526
1527
1528 jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
1529
1530         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
1531         jsonObject* target = jsonObjectGetIndex(ctx->params, 0);
1532
1533         if (!verifyObjectClass(ctx, target)) {
1534                 *err = -1;
1535                 return jsonNULL;
1536         }
1537
1538         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
1539                 osrfAppSessionStatus(
1540                         ctx->session,
1541                         OSRF_STATUS_BADREQUEST,
1542                         "osrfMethodException",
1543                         ctx->request,
1544                         "No active transaction -- required for UPDATE"
1545                 );
1546                 *err = -1;
1547                 return jsonNULL;
1548         }
1549
1550         char* pkey = osrfHashGet(meta, "primarykey");
1551         osrfHash* fields = osrfHashGet(meta, "fields");
1552
1553         char* id =
1554                 jsonObjectToSimpleString(
1555                         jsonObjectGetIndex(
1556                                 target,
1557                                 atoi( osrfHashGet( osrfHashGet( fields, pkey ), "array_position" ) )
1558                         )
1559                 );
1560
1561         osrfLogDebug(
1562                 OSRF_LOG_MARK,
1563                 "%s updating %s object with %s = %s",
1564                 MODULENAME,
1565                 osrfHashGet(meta, "fieldmapper"),
1566                 pkey,
1567                 id
1568         );
1569
1570         growing_buffer* sql = buffer_init(128);
1571         buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
1572
1573         int i = 0;
1574         int first = 1;
1575         char* field_name;
1576         osrfStringArray* field_list = osrfHashKeys( fields );
1577         while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
1578
1579                 osrfHash* field = osrfHashGet( fields, field_name );
1580
1581                 if(!( strcmp( field_name, pkey ) )) continue;
1582                 if(!( strcmp( osrfHashGet(osrfHashGet(fields,field_name), "virtual"), "true" ) )) continue;
1583
1584                 int pos = atoi(osrfHashGet(field, "array_position"));
1585                 char* value = jsonObjectToSimpleString( jsonObjectGetIndex( target, pos ) );
1586
1587                 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
1588
1589                 if (jsonObjectGetIndex(target, pos)->type == JSON_NULL) {
1590                         if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
1591                                 if (first) first = 0;
1592                                 else buffer_add(sql, ",");
1593                                 buffer_fadd( sql, " %s = NULL", field_name );
1594                         }
1595                         
1596                 } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1597                         if (first) first = 0;
1598                         else buffer_add(sql, ",");
1599
1600                         if ( !strncmp(osrfHashGet(field, "datatype"), "INT", (size_t)3) ) {
1601                                 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
1602                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
1603                                 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
1604                         }
1605
1606                         osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, osrfHashGet(field, "datatype"));
1607
1608                 } else {
1609                         if ( dbi_conn_quote_string(dbhandle, &value) ) {
1610                                 if (first) first = 0;
1611                                 else buffer_add(sql, ",");
1612                                 buffer_fadd( sql, " %s = %s", field_name, value );
1613
1614                         } else {
1615                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
1616                                 osrfAppSessionStatus(
1617                                         ctx->session,
1618                                         OSRF_STATUS_INTERNALSERVERERROR,
1619                                         "osrfMethodException",
1620                                         ctx->request,
1621                                         "Error quoting string -- please see the error log for more details"
1622                                 );
1623                                 free(value);
1624                                 free(id);
1625                                 buffer_free(sql);
1626                                 *err = -1;
1627                                 return jsonNULL;
1628                         }
1629                 }
1630
1631                 free(value);
1632                 
1633         }
1634
1635         jsonObject* obj = jsonParseString(id);
1636
1637         if ( strcmp( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "primitive" ), "number" ) )
1638                 dbi_conn_quote_string(dbhandle, &id);
1639
1640         buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
1641
1642         char* query = buffer_data(sql);
1643         buffer_free(sql);
1644
1645         osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
1646
1647         dbi_result result = dbi_conn_query(dbhandle, query);
1648         free(query);
1649
1650         if (!result) {
1651                 jsonObjectFree(obj);
1652                 obj = jsonNewObject(NULL);
1653                 osrfLogError(
1654                         OSRF_LOG_MARK,
1655                         "%s ERROR updating %s object with %s = %s",
1656                         MODULENAME,
1657                         osrfHashGet(meta, "fieldmapper"),
1658                         pkey,
1659                         id
1660                 );
1661         }
1662
1663         free(id);
1664
1665         return obj;
1666 }
1667
1668 jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
1669
1670         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
1671
1672         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
1673                 osrfAppSessionStatus(
1674                         ctx->session,
1675                         OSRF_STATUS_BADREQUEST,
1676                         "osrfMethodException",
1677                         ctx->request,
1678                         "No active transaction -- required for DELETE"
1679                 );
1680                 *err = -1;
1681                 return jsonNULL;
1682         }
1683
1684         jsonObject* obj;
1685
1686         char* pkey = osrfHashGet(meta, "primarykey");
1687
1688         char* id;
1689         if (jsonObjectGetIndex(ctx->params, 0)->classname) {
1690                 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, 0 ))) {
1691                         *err = -1;
1692                         return jsonNULL;
1693                 }
1694
1695                 id = jsonObjectToSimpleString(
1696                         jsonObjectGetIndex(
1697                                 jsonObjectGetIndex(ctx->params, 0),
1698                                 atoi( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "array_position") )
1699                         )
1700                 );
1701         } else {
1702                 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
1703         }
1704
1705         osrfLogDebug(
1706                 OSRF_LOG_MARK,
1707                 "%s deleting %s object with %s = %s",
1708                 MODULENAME,
1709                 osrfHashGet(meta, "fieldmapper"),
1710                 pkey,
1711                 id
1712         );
1713
1714         obj = jsonParseString(id);
1715
1716         if ( strcmp( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "primitive" ), "number" ) )
1717                 dbi_conn_quote_string(dbhandle, &id);
1718
1719         dbi_result result = dbi_conn_queryf(dbhandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
1720
1721         if (!result) {
1722                 jsonObjectFree(obj);
1723                 obj = jsonNewObject(NULL);
1724                 osrfLogError(
1725                         OSRF_LOG_MARK,
1726                         "%s ERROR deleting %s object with %s = %s",
1727                         MODULENAME,
1728                         osrfHashGet(meta, "fieldmapper"),
1729                         pkey,
1730                         id
1731                 );
1732         }
1733
1734         free(id);
1735
1736         return obj;
1737
1738 }
1739
1740
1741 jsonObject* oilsMakeJSONFromResult( dbi_result result, osrfHash* meta) {
1742         if(!(result && meta)) return jsonNULL;
1743
1744         jsonObject* object = jsonParseString("[]");
1745         jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
1746
1747         osrfHash* fields = osrfHashGet(meta, "fields");
1748
1749         osrfLogDebug(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
1750
1751         osrfHash* _f;
1752         time_t _tmp_dt;
1753         char dt_string[256];
1754         struct tm gmdt;
1755
1756         int fmIndex;
1757         int columnIndex = 1;
1758         int attr;
1759         unsigned short type;
1760         const char* columnName;
1761
1762         /* cycle through the column list */
1763         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
1764
1765                 osrfLogDebug(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
1766
1767                 fmIndex = -1; // reset the position
1768                 
1769                 /* determine the field type and storage attributes */
1770                 type = dbi_result_get_field_type(result, columnName);
1771                 attr = dbi_result_get_field_attribs(result, columnName);
1772
1773                 /* fetch the fieldmapper index */
1774                 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
1775                         char* virt = (char*)osrfHashGet(_f, "virtual");
1776                         char* pos = (char*)osrfHashGet(_f, "array_position");
1777
1778                         if ( !virt || !pos || !(strcmp( virt, "true" )) ) continue;
1779
1780                         fmIndex = atoi( pos );
1781                         osrfLogDebug(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
1782                 }
1783
1784                 if (dbi_result_field_is_null(result, columnName)) {
1785                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
1786                 } else {
1787
1788                         switch( type ) {
1789
1790                                 case DBI_TYPE_INTEGER :
1791
1792                                         if( attr & DBI_INTEGER_SIZE8 ) 
1793                                                 jsonObjectSetIndex( object, fmIndex, 
1794                                                         jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
1795                                         else 
1796                                                 jsonObjectSetIndex( object, fmIndex, 
1797                                                         jsonNewNumberObject(dbi_result_get_long(result, columnName)));
1798
1799                                         break;
1800
1801                                 case DBI_TYPE_DECIMAL :
1802                                         jsonObjectSetIndex( object, fmIndex, 
1803                                                         jsonNewNumberObject(dbi_result_get_double(result, columnName)));
1804                                         break;
1805
1806                                 case DBI_TYPE_STRING :
1807
1808
1809                                         jsonObjectSetIndex(
1810                                                 object,
1811                                                 fmIndex,
1812                                                 jsonNewObject( dbi_result_get_string(result, columnName) )
1813                                         );
1814
1815                                         break;
1816
1817                                 case DBI_TYPE_DATETIME :
1818
1819                                         memset(dt_string, '\0', 256);
1820                                         memset(&gmdt, '\0', sizeof(gmdt));
1821                                         memset(&_tmp_dt, '\0', sizeof(_tmp_dt));
1822
1823                                         _tmp_dt = dbi_result_get_datetime(result, columnName);
1824
1825                                         localtime_r( &_tmp_dt, &gmdt );
1826
1827                                         if (!(attr & DBI_DATETIME_DATE)) {
1828                                                 strftime(dt_string, 255, "%T", &gmdt);
1829                                         } else if (!(attr & DBI_DATETIME_TIME)) {
1830                                                 strftime(dt_string, 255, "%F", &gmdt);
1831                                         } else {
1832                                                 /* XXX ARG! My eyes! The goggles, they do nothing! */
1833
1834                                                 strftime(dt_string, 255, "%FT%T%z", &gmdt);
1835                                         }
1836
1837                                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
1838
1839                                         break;
1840
1841                                 case DBI_TYPE_BINARY :
1842                                         osrfLogError( OSRF_LOG_MARK, 
1843                                                 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
1844                         }
1845                 }
1846         }
1847
1848         return object;
1849 }
1850