]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/c-apps/oils_storedq.c
3c90dc12902b1c7db9d7187a2091eb9cedea31f5
[working/Evergreen.git] / Open-ILS / src / c-apps / oils_storedq.c
1 /**
2         @file oils_storedq.c
3         @brief Load an abstract representation of a query from the database.
4 */
5
6 #include <stdlib.h>
7 #include <string.h>
8 #include <dbi/dbi.h>
9 #include "opensrf/utils.h"
10 #include "opensrf/log.h"
11 #include "opensrf/string_array.h"
12 #include "opensrf/osrf_hash.h"
13 #include "openils/oils_buildq.h"
14
15 #define PRINT if( verbose ) printf
16
17 struct IdNode_ {
18         IdNode* next;
19         int id;
20         char* alias;
21 };
22
23 static int oils_result_get_bool_idx( dbi_result result, int i );
24
25 static FromRelation* getFromRelation( BuildSQLState* state, int id );
26 static FromRelation* constructFromRelation( BuildSQLState* state, dbi_result result );
27 static FromRelation* getJoinList( BuildSQLState* state, int id );
28 static void joinListFree( FromRelation* join_list );
29 static void fromRelationFree( FromRelation* fr );
30
31 static QSeq* loadChildQueries( BuildSQLState* state, int parent_id, const char* type_str );
32 static QSeq* constructQSeq( BuildSQLState* state, dbi_result result );
33 static void freeQSeqList( QSeq* seq );
34 static StoredQ* constructStoredQ( BuildSQLState* state, dbi_result result );
35
36 static SelectItem* getSelectList( BuildSQLState* state, int query_id );
37 static SelectItem* constructSelectItem( BuildSQLState* state, dbi_result result );
38 static void selectListFree( SelectItem* sel );
39
40 static BindVar* getBindVar( BuildSQLState* state, const char* name );
41 static BindVar* constructBindVar( BuildSQLState* state, dbi_result result );
42 static void bindVarFree( char* name, void* p );
43
44 static Expression* getExpression( BuildSQLState* state, int id );
45 static Expression* constructExpression( BuildSQLState* state, dbi_result result );
46 static void expressionListFree( Expression* exp );
47 static void expressionFree( Expression* exp );
48 static Expression* getExpressionList( BuildSQLState* state, int id );
49
50 static OrderItem* getOrderByList( BuildSQLState* state, int query_id );
51 static OrderItem* constructOrderItem( BuildSQLState* state, dbi_result result );
52 static void orderItemListFree( OrderItem* ord );
53
54 static void push_id( IdNode** stack, int id, const char* alias );
55 static const IdNode* searchIdStack( const IdNode* stack, int id, const char* alias );
56
57 // A series of free lists to store already-allocated objects that are not in use, for
58 // potential reuse.  This is a hack to reduce churning through malloc() and free().
59 static StoredQ* free_storedq_list = NULL;
60 static FromRelation* free_from_relation_list = NULL;
61 static SelectItem* free_select_item_list = NULL;
62 static BindVar* free_bindvar_list = NULL;
63 static Expression* free_expression_list = NULL;
64 static IdNode* free_id_node_list = NULL;
65 static QSeq* free_qseq_list = NULL;
66 static OrderItem* free_order_item_list = NULL;
67
68 // Boolean; settable by call to oilsStoredQSetVerbose(), used by PRINT macro.
69 // The idea is to allow debugging messages from a command line test driver for ease of
70 // testing and development, but not from a real server, where messages to stdout don't
71 // go anywhere.
72 static int verbose = 0;
73
74 /**
75         @brief Load a stored query.
76         @param state Pointer to the query-building context.
77         @param query_id ID of the query in query.stored_query.
78         @return A pointer to the newly loaded StoredQ if successful, or NULL if not.
79
80         The calling code is responsible for freeing the StoredQ by calling storedQFree().
81 */
82 StoredQ* getStoredQuery( BuildSQLState* state, int query_id ) {
83         if( !state )
84                 return NULL;
85
86         // Check the stack to see if the current query is nested inside itself.  If it is, then
87         // abort in order to avoid infinite recursion.  If it isn't, then add it to the stack.
88         // (Make sure to pop it off the stack before returning.)
89         if( searchIdStack( state->query_stack, query_id, NULL )) {
90                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
91                         "Infinite recursion detected; query # %d is nested within itself", query_id ));
92                 state->error = 1;
93                 return NULL;
94         } else
95                 push_id( &state->query_stack, query_id, NULL );
96
97         StoredQ* sq = NULL;
98         dbi_result result = dbi_conn_queryf( state->dbhandle,
99                 "SELECT id, type, use_all, use_distinct, from_clause, where_clause, having_clause "
100                 "FROM query.stored_query WHERE id = %d;", query_id );
101         if( result ) {
102                 if( dbi_result_first_row( result ) ) {
103                         sq = constructStoredQ( state, result );
104                         if( sq ) {
105                                 PRINT( "Got a query row\n" );
106                                 PRINT( "\tid: %d\n", sq->id );
107                                 PRINT( "\ttype: %d\n", (int) sq->type );
108                                 PRINT( "\tuse_all: %s\n", sq->use_all ? "true" : "false" );
109                                 PRINT( "\tuse_distinct: %s\n", sq->use_distinct ? "true" : "false" );
110                         } else
111                                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
112                                         "Unable to build a query for id = %d", query_id ));
113                 } else {
114                         sqlAddMsg( state, "Stored query not found for id %d", query_id );
115                 }
116
117                 dbi_result_free( result );
118         } else {
119                 const char* msg;
120                 int errnum = dbi_conn_error( state->dbhandle, &msg );
121                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, 
122                         "Unable to query query.stored_query table: #%d %s",
123                         errnum, msg ? msg : "No description available" ));
124                 state->error = 1;
125         }
126
127         pop_id( &state->query_stack );
128         return sq;
129 }
130
131 /**
132         @brief Construct a StoredQ.
133         @param Pointer to the query-building context.
134         @param result Database cursor positioned at a row in query.stored_query.
135         @return Pointer to a newly constructed StoredQ, if successful, or NULL if not.
136
137         The calling code is responsible for freeing the StoredQ by calling storedQFree().
138 */
139 static StoredQ* constructStoredQ( BuildSQLState* state, dbi_result result ) {
140
141         // Get the column values from the result
142         int id               = dbi_result_get_int_idx( result, 1 );
143         const char* type_str = dbi_result_get_string_idx( result, 2 );
144
145         QueryType type;
146         if( !strcmp( type_str, "SELECT" ))
147                 type = QT_SELECT;
148         else if( !strcmp( type_str, "UNION" ))
149                 type = QT_UNION;
150         else if( !strcmp( type_str, "INTERSECT" ))
151                 type = QT_INTERSECT;
152         else if( !strcmp( type_str, "EXCEPT" ))
153                 type = QT_EXCEPT;
154         else {
155                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
156                         "Invalid query type \"%s\"", type_str ));
157                 return NULL;
158         }
159
160         int use_all             = oils_result_get_bool_idx( result, 3 );
161         int use_distinct        = oils_result_get_bool_idx( result, 4 );
162
163         int from_clause_id;
164         if( dbi_result_field_is_null_idx( result, 5 ) )
165                 from_clause_id = -1;
166         else
167                 from_clause_id = dbi_result_get_int_idx( result, 5 );
168
169         int where_clause_id;
170         if( dbi_result_field_is_null_idx( result, 6 ) )
171                 where_clause_id = -1;
172         else
173                 where_clause_id = dbi_result_get_int_idx( result, 6 );
174
175         int having_clause_id;
176         if( dbi_result_field_is_null_idx( result, 7 ) )
177                 having_clause_id = -1;
178         else
179                 having_clause_id = dbi_result_get_int_idx( result, 7 );
180
181         FromRelation* from_clause = NULL;
182         if( QT_SELECT == type ) {
183                 // A SELECT query needs a FROM clause; go get it
184                 if( from_clause_id != -1 ) {
185                         from_clause = getFromRelation( state, from_clause_id );
186                         if( !from_clause ) {
187                                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
188                                         "Unable to construct FROM clause for id = %d", from_clause_id ));
189                                 return NULL;
190                         }
191                 }
192         } else {
193                 // Must be one of UNION, INTERSECT, or EXCEPT
194                 if( from_clause_id != -1 )
195                         osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
196                                 "FROM clause found and ignored for %s query in query #%d", type_str, id ));
197         }
198
199         // If this is a SELECT query, we need a SELECT list.  Go get one.
200         SelectItem* select_list = NULL;
201         QSeq* child_list = NULL;
202         if( QT_SELECT == type ) {
203                 select_list = getSelectList( state, id );
204                 if( !select_list ) {
205                         osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
206                                 "No SELECT list found for query id = %d", id ));
207                         fromRelationFree( from_clause );
208                         return NULL;
209                 }
210         } else {
211                 // Construct child queries of UNION, INTERSECT, or EXCEPT query
212                 child_list = loadChildQueries( state, id, type_str );
213                 if( !child_list ) {
214                         osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
215                                 "Unable to load child queries for %s query # %d", type_str, id ));
216                         state->error = 1;
217                         fromRelationFree( from_clause );
218                         return NULL;
219                 }
220         }
221
222         // Get the WHERE clause, if there is one
223         Expression* where_clause = NULL;
224         if( where_clause_id != -1 ) {
225                 where_clause = getExpression( state, where_clause_id );
226                 if( ! where_clause ) {
227                         // shouldn't happen due to foreign key constraint
228                         osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
229                                 "Unable to fetch WHERE expression for query id = %d", id ));
230                         freeQSeqList( child_list );
231                         fromRelationFree( from_clause );
232                         selectListFree( select_list );
233                         state->error = 1;
234                         return NULL;
235                 }
236         }
237
238         Expression* having_clause = NULL;
239         if( having_clause_id != -1 ) {
240                 having_clause = getExpression( state, having_clause_id );
241                 if( ! having_clause ) {
242                         // shouldn't happen due to foreign key constraint
243                         osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
244                                 "Unable to fetch HAVING expression for query id = %d", id ));
245                         expressionFree( where_clause );
246                         freeQSeqList( child_list );
247                         fromRelationFree( from_clause );
248                         selectListFree( select_list );
249                         state->error = 1;
250                         return NULL;
251                 }
252         }
253
254         // Get the ORDER BY clause, if there is one
255         OrderItem* order_by_list = getOrderByList( state, id );
256         if( state->error ) {
257                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
258                         "Unable to load ORDER BY clause for query %d", id ));
259                 expressionFree( having_clause );
260                 expressionFree( where_clause );
261                 freeQSeqList( child_list );
262                 fromRelationFree( from_clause );
263                 selectListFree( select_list );
264                 return NULL;
265         }
266
267         // Allocate a StoredQ: from the free list if possible, from the heap if necessary
268
269         StoredQ* sq;
270         if( free_storedq_list ) {
271                 sq = free_storedq_list;
272                 free_storedq_list = free_storedq_list->next;
273         } else
274                 sq = safe_malloc( sizeof( StoredQ ) );
275
276         // Populate the StoredQ
277         sq->next = NULL;
278         sq->id = id;
279
280         sq->type = type;
281         sq->use_all = use_all;
282         sq->use_distinct = use_distinct;
283         sq->from_clause = from_clause;
284         sq->where_clause = where_clause;
285         sq->select_list = select_list;
286         sq->child_list = child_list;
287         sq->having_clause = having_clause;
288         sq->order_by_list = order_by_list;
289
290         return sq;
291 }
292
293 /**
294         @brief Load the child queries subordinate to a UNION, INTERSECT, or EXCEPT query.
295         @param state Pointer to the query-building context.
296         @param parent ID of the UNION, INTERSECT, or EXCEPT query.
297         @param type_str The type of the query ("UNION", "INTERSECT", or "EXCEPT").
298         @return If successful, a pointer to a linked list of QSeq, each bearing a pointer to a
299                 StoredQ; otherwise NULL.
300
301         The @a type_str parameter is used only for building error messages.
302 */
303 static QSeq* loadChildQueries( BuildSQLState* state, int parent_id, const char* type_str ) {
304         QSeq* child_list = NULL;
305         
306         // The ORDER BY is in descending order so that we can build the list by adding to
307         // the head, and it will wind up in the right order.
308         dbi_result result = dbi_conn_queryf( state->dbhandle,
309                 "SELECT id, parent_query, seq_no, child_query "
310                 "FROM query.query_sequence WHERE parent_query = %d ORDER BY seq_no DESC", parent_id );
311         if( result ) {
312                 if( dbi_result_first_row( result ) ) {
313                         int count = 0;
314                         while( 1 ) {
315                                 ++count;
316                                 QSeq* seq = constructQSeq( state, result );
317                                 if( seq ) {
318                                         PRINT( "Found a child query\n" );
319                                         PRINT( "\tid: %d\n", seq->id );
320                                         PRINT( "\tparent id: %d\n", seq->parent_query_id );
321                                         PRINT( "\tseq_no: %d\n", seq->seq_no );
322                                         // Add to the head of the list
323                                         seq->next = child_list;
324                                         child_list = seq;
325                                 } else{
326                                         freeQSeqList( child_list );
327                                         return NULL;
328                                 }
329                                 if( !dbi_result_next_row( result ))
330                                         break;
331                         }
332                         if( count < 2 ) {
333                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
334                                         "%s query # %d has only one child query", type_str, parent_id ));
335                                 state->error = 1;
336                                 freeQSeqList( child_list );
337                                 return NULL;
338                         }
339                 } else {
340                         osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
341                                 "%s query # %d has no child queries within it", type_str, parent_id ));
342                         state->error = 1;
343                         return NULL;
344                 }
345         } else {
346                 const char* msg;
347                 int errnum = dbi_conn_error( state->dbhandle, &msg );
348                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
349                         "Unable to query query.query_sequence table: # %d %s",
350                         errnum, msg ? msg : "No description available" ));
351                 state->error = 1;
352                 return NULL;
353         }
354
355         return child_list;
356 }
357
358 /**
359         @brief Construct a QSeq.
360         @param Pointer to the query-building context.
361         @param result Database cursor positioned at a row in query.query_sequence.
362         @return Pointer to a newly constructed QSeq, if successful, or NULL if not.
363
364         The calling code is responsible for freeing QSeqs by calling freeQSeqList().
365 */
366 static QSeq* constructQSeq( BuildSQLState* state, dbi_result result ) {
367         int id = dbi_result_get_int_idx( result, 1 );
368         int parent_query_id = dbi_result_get_int_idx( result, 2 );
369         int seq_no = dbi_result_get_int_idx( result, 3 );
370         int child_query_id = dbi_result_get_int_idx( result, 4 );
371
372         StoredQ* child_query = getStoredQuery( state, child_query_id );
373         if( !child_query ) {
374                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
375                         "Unable to load child query # %d for parent query %d",
376                         child_query_id, parent_query_id ));
377                 state->error = 1;
378                 return NULL;
379         }
380
381         // Allocate a QSeq; from the free list if possible, from the heap if necessary
382         QSeq* seq = NULL;
383         if( free_qseq_list ) {
384                 seq = free_qseq_list;
385                 free_qseq_list = free_qseq_list->next;
386         } else
387                 seq = safe_malloc( sizeof( QSeq ));
388
389         seq->next            = NULL;
390         seq->id              = id;
391         seq->parent_query_id = parent_query_id;
392         seq->seq_no          = seq_no;
393         seq->child_query     = child_query;
394
395         return seq;
396 }
397
398 /**
399         @brief Free a list of QSeq's.
400         @param seq Pointer to the first in a linked list of QSeq's to be freed.
401
402         Each QSeq goes onto a free list for potential reuse.
403 */
404 static void freeQSeqList( QSeq* seq ) {
405         if( !seq )
406                 return;
407
408         QSeq* first = seq;
409         while( seq ) {
410                 storedQFree( seq->child_query );
411                 seq->child_query = NULL;
412
413                 if( seq->next )
414                         seq = seq->next;
415                 else {
416                         seq->next = free_qseq_list;
417                         seq = NULL;
418                 }
419         }
420
421         free_qseq_list = first;
422 }
423
424 /**
425         @brief Deallocate the memory owned by a StoredQ.
426         @param sq Pointer to the StoredQ to be deallocated.
427 */
428 void storedQFree( StoredQ* sq ) {
429         if( sq ) {
430                 fromRelationFree( sq->from_clause );
431                 sq->from_clause = NULL;
432                 selectListFree( sq->select_list );
433                 sq->select_list = NULL;
434                 expressionFree( sq->where_clause );
435                 sq->where_clause = NULL;
436                 if( sq->child_list ) {
437                         freeQSeqList( sq->child_list );
438                         sq->child_list = NULL;
439                 }
440                 if( sq->order_by_list ) {
441                         orderItemListFree( sq->order_by_list );
442                         sq->order_by_list = NULL;
443                 }
444                 if( sq->having_clause )
445                         expressionFree( sq->having_clause );
446
447                 // Stick the empty husk on the free list for potential reuse
448                 sq->next = free_storedq_list;
449                 free_storedq_list = sq;
450         }
451 }
452
453 /**
454         @brief Given an id from query.from_relation, load a FromRelation.
455         @param state Pointer the the query-building context.
456         @param id Id of the FromRelation.
457         @return Pointer to a newly-created FromRelation if successful, or NULL if not.
458
459         The calling code is responsible for freeing the new FromRelation by calling
460         fromRelationFree().
461 */
462 static FromRelation* getFromRelation( BuildSQLState* state, int id ) {
463         FromRelation* fr = NULL;
464         dbi_result result = dbi_conn_queryf( state->dbhandle,
465                 "SELECT id, type, table_name, class_name, subquery, function_call, "
466                 "table_alias, parent_relation, seq_no, join_type, on_clause "
467                 "FROM query.from_relation WHERE id = %d;", id );
468         if( result ) {
469                 if( dbi_result_first_row( result ) ) {
470                         fr = constructFromRelation( state, result );
471                         if( fr ) {
472                                 PRINT( "Got a from_relation row\n" );
473                                 PRINT( "\tid: %d\n", fr->id );
474                                 PRINT( "\ttype: %d\n", (int) fr->type );
475                                 PRINT( "\ttable_name: %s\n", fr->table_name ? fr->table_name : "(none)" );
476                                 PRINT( "\tclass_name: %s\n", fr->class_name ? fr->class_name : "(none)" );
477                                 PRINT( "\tsubquery_id: %d\n", fr->subquery_id );
478                                 PRINT( "\tfunction_call_id: %d\n", fr->function_call_id );
479                                 PRINT( "\ttable_alias: %s\n", fr->table_alias ? fr->table_alias : "(none)" );
480                                 PRINT( "\tparent_relation_id: %d\n", fr->parent_relation_id );
481                                 PRINT( "\tseq_no: %d\n", fr->seq_no );
482                                 PRINT( "\tjoin_type = %d\n", fr->join_type );
483                                 // Check the stack to see if the current from clause is nested inside itself.
484                                 // If it is, then abort in order to avoid infinite recursion.  If it isn't,
485                                 // then add it to the stack.  (Make sure to pop it off the stack before
486                                 // returning.)
487                                 const char* effective_alias = fr->table_alias;
488                                 if( !effective_alias )
489                                         effective_alias = fr->class_name;
490                                 const IdNode* node = searchIdStack( state->from_stack, id, effective_alias );
491                                 if( node ) {
492                                         if( node->id == id )
493                                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
494                                                         "Infinite recursion detected; from clause # %d is nested "
495                                                         "within itself", id ));
496                                         else
497                                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
498                                                         "Conflicting nested table aliases \"%s\" in from clause # %d",
499                                                         effective_alias, node->id ));
500                                         state->error = 1;
501                                         return NULL;
502                                 } else
503                                         push_id( &state->from_stack, id, effective_alias );
504                         } else
505                                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
506                                         "Unable to build a FromRelation for id = %d", id ));
507                 } else {
508                         osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
509                                 "FROM relation not found for id = %d", id ));
510                 }
511                 dbi_result_free( result );
512         } else {
513                 const char* msg;
514                 int errnum = dbi_conn_error( state->dbhandle, &msg );
515                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
516                         "Unable to query query.from_relation table: #%d %s",
517                         errnum, msg ? msg : "No description available" ));
518                 state->error = 1;
519         }
520
521         if( fr )
522                 pop_id( &state->from_stack );
523
524         return fr;
525 }
526
527 /**
528         @brief Construct a FromRelation.
529         @param Pointer to the query-building context.
530         @param result Database cursor positioned at a row in query.from_relation.
531         @return Pointer to a newly constructed FromRelation, if successful, or NULL if not.
532
533         The calling code is responsible for freeing FromRelations by calling joinListFree().
534 */
535 static FromRelation* constructFromRelation( BuildSQLState* state, dbi_result result ) {
536         // Get the column values from the result
537         int id                  = dbi_result_get_int_idx( result, 1 );
538         const char* type_str    = dbi_result_get_string_idx( result, 2 );
539
540         FromRelationType type;
541         if( !strcmp( type_str, "RELATION" ))
542                 type = FRT_RELATION;
543         else if( !strcmp( type_str, "SUBQUERY" ))
544                 type = FRT_SUBQUERY;
545         else if( !strcmp( type_str, "FUNCTION" ))
546                 type = FRT_FUNCTION;
547         else
548                 type = FRT_RELATION;     // shouldn't happen due to database constraint
549
550         const char* table_name  = dbi_result_get_string_idx( result, 3 );
551         const char* class_name  = dbi_result_get_string_idx( result, 4 );
552
553         int subquery_id;
554         if( dbi_result_field_is_null_idx( result, 5 ) )
555                 subquery_id          = -1;
556         else
557                 subquery_id          = dbi_result_get_int_idx( result, 5 );
558
559         int function_call_id;
560         if( dbi_result_field_is_null_idx( result, 6 ) )
561                 function_call_id     = -1;
562         else
563                 function_call_id     = dbi_result_get_int_idx( result, 6 );
564
565         const char* table_alias  = dbi_result_get_string_idx( result, 7 );
566
567         int parent_relation_id;
568         if( dbi_result_field_is_null_idx( result, 8 ) )
569                 parent_relation_id   = -1;
570         else
571                 parent_relation_id   = dbi_result_get_int_idx( result, 8 );
572
573         int seq_no               = dbi_result_get_int_idx( result, 9 );
574
575         JoinType join_type;
576         const char* join_type_str = dbi_result_get_string_idx( result, 10 );
577         if( !join_type_str )
578                 join_type = JT_NONE;
579         else if( !strcmp( join_type_str, "INNER" ) )
580                 join_type = JT_INNER;
581         else if( !strcmp( join_type_str, "LEFT" ) )
582                 join_type = JT_LEFT;
583         else if( !strcmp( join_type_str, "RIGHT" ) )
584                 join_type = JT_RIGHT;
585         else if( !strcmp( join_type_str, "FULL" ) )
586                 join_type = JT_FULL;
587         else
588                 join_type = JT_NONE;     // shouldn't happen due to database constraint
589
590         int on_clause_id;
591         if( dbi_result_field_is_null_idx( result, 11 ) )
592                 on_clause_id   = -1;
593         else
594                 on_clause_id   = dbi_result_get_int_idx( result, 11 );
595
596         StoredQ* subquery = NULL;
597
598         switch ( type ) {
599                 case FRT_RELATION :
600                         break;
601                 case FRT_SUBQUERY :
602                         if( -1 == subquery_id ) {
603                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
604                                         "Internal error: no subquery specified for FROM relation # %d", id ));
605                                 state->error = 1;
606                                 return NULL;
607                         }
608                         if( ! table_alias ) {
609                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
610                                         "Subquery needs alias in FROM relation # %d", id ));
611                                 state->error = 1;
612                                 return NULL;
613                         }
614                         subquery = getStoredQuery( state, subquery_id );
615                         if( ! subquery ) {
616                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
617                                         "Unable to load subquery for FROM relation # %d", id ));
618                                 state->error = 1;
619                                 return NULL;
620                         }
621                         break;
622                 case FRT_FUNCTION :
623                         osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
624                                 "Functions in FROM clause not yet supported" ));
625                         state->error = 1;
626                         return NULL;
627         }
628
629         FromRelation* join_list = getJoinList( state, id );
630         if( state->error ) {
631                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
632                         "Unable to load join list for FROM relation # %d", id ));
633                 return NULL;
634         }
635
636         Expression* on_clause = NULL;
637         if( on_clause_id != -1 ) {
638                 on_clause = getExpression( state, on_clause_id );
639                 if( !on_clause ) {
640                         osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
641                                 "Unable to load ON condition for FROM relation # %d", id ));
642                         joinListFree( join_list );
643                         state->error = 1;
644                         return NULL;
645                 }
646                 else
647                         PRINT( "\tGot an ON condition\n" );
648         }
649
650         // Allocate a FromRelation: from the free list if possible, from the heap if necessary
651
652         FromRelation* fr;
653         if( free_from_relation_list ) {
654                 fr = free_from_relation_list;
655                 free_from_relation_list = free_from_relation_list->next;
656         } else
657                 fr = safe_malloc( sizeof( FromRelation ) );
658
659         // Populate the FromRelation
660
661         fr->next = NULL;
662         fr->id = id;
663         fr->type = type;
664         fr->table_name = table_name ? strdup( table_name ) : NULL;
665         fr->class_name = class_name ? strdup( class_name ) : NULL;
666         fr->subquery_id = subquery_id;
667         fr->subquery = subquery;
668         fr->function_call_id = function_call_id;
669         fr->table_alias = table_alias ? strdup( table_alias ) : NULL;
670         fr->parent_relation_id = parent_relation_id;
671         fr->seq_no = seq_no;
672         fr->join_type = join_type;
673         fr->on_clause = on_clause;
674         fr->join_list = join_list;
675
676         return fr;
677 }
678
679 /**
680         @brief Build a list of joined relations.
681         @param state Pointer to the query-building context.
682         @param id ID of the parent relation.
683         @return A pointer to the first in a linked list of FromRelations, if there are any; or
684                 NULL if there aren't any, or in case of an error.
685
686         Look for relations joined directly to the parent relation, and make a list of them.
687 */
688 static FromRelation* getJoinList( BuildSQLState* state, int id ) {
689         FromRelation* join_list = NULL;
690         
691         // The ORDER BY is in descending order so that we can build the list by adding to
692         // the head, and it will wind up in the right order.
693         dbi_result result = dbi_conn_queryf( state->dbhandle,
694                 "SELECT id, type, table_name, class_name, subquery, function_call, "
695                 "table_alias, parent_relation, seq_no, join_type, on_clause "
696                 "FROM query.from_relation WHERE parent_relation = %d ORDER BY seq_no DESC", id );
697
698         if( result ) {
699                 if( dbi_result_first_row( result ) ) {
700                         while( 1 ) {
701                                 FromRelation* relation = constructFromRelation( state, result );
702                                 if( relation ) {
703                                         PRINT( "Found a joined relation\n" );
704                                         PRINT( "\tjoin_type: %d\n", relation->join_type );
705                                         PRINT( "\ttable_name: %s\n", relation->table_name );
706                                         relation->next = join_list;
707                                         join_list = relation;
708                                 } else {
709                                         osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
710                                                 "Unable to build join list for from relation id #%d", id ));
711                                         joinListFree( join_list );
712                                         join_list = NULL;
713                                         break;
714                                 }
715                                 if( !dbi_result_next_row( result ) )
716                                         break;
717                         };
718                 }
719         } else {
720                 const char* msg;
721                 int errnum = dbi_conn_error( state->dbhandle, &msg );
722                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
723                         "Unable to query query.from_relation table for join list: #%d %s",
724                         errnum, msg ? msg : "No description available" ));
725                 state->error = 1;
726         }
727
728         return join_list;
729 }
730
731 /**
732         @brief Free a list of FromRelations.
733         @param join_list Pointer to the first FromRelation in the list.
734 */
735 static void joinListFree( FromRelation* join_list ) {
736         while( join_list ) {
737                 FromRelation* temp = join_list->next;
738                 fromRelationFree( join_list );
739                 join_list = temp;
740         }
741 }
742
743 /**
744         @brief Deallocate a FromRelation.
745         @param fr Pointer to the FromRelation to be freed.
746
747         Free the strings that the FromRelation owns.  The FromRelation itself goes onto a
748         free list for potential reuse.
749 */
750 static void fromRelationFree( FromRelation* fr ) {
751         if( fr ) {
752                 free( fr->table_name );
753                 fr->table_name = NULL;
754                 free( fr->class_name );
755                 fr->class_name = NULL;
756                 if( fr->subquery ) {
757                         storedQFree( fr->subquery );
758                         fr->subquery = NULL;
759                 }
760                 free( fr->table_alias );
761                 fr->table_alias = NULL;
762                 if( fr->on_clause ) {
763                         expressionFree( fr->on_clause );
764                         fr->on_clause = NULL;
765                 }
766                 joinListFree( fr->join_list );
767                 fr->join_list = NULL;
768
769                 fr->next = free_from_relation_list;
770                 free_from_relation_list = fr;
771         }
772 }
773
774 static SelectItem* getSelectList( BuildSQLState* state, int query_id ) {
775         SelectItem* select_list = NULL;
776
777         // The ORDER BY is in descending order so that we can build the list by adding to
778         // the head, and it will wind up in the right order.
779         dbi_result result = dbi_conn_queryf( state->dbhandle,
780                 "SELECT id, stored_query, seq_no, expression, column_alias, grouped_by "
781                 "FROM query.select_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
782         if( result ) {
783                 if( dbi_result_first_row( result ) ) {
784                         while( 1 ) {
785                                 SelectItem* item = constructSelectItem( state, result );
786                                 if( item ) {
787                                         PRINT( "Found a SELECT item\n" );
788                                         PRINT( "\tid: %d\n", item->id );
789                                         PRINT( "\tstored_query_id: %d\n", item->stored_query_id );
790                                         PRINT( "\tseq_no: %d\n", item->seq_no );
791                                         PRINT( "\tcolumn_alias: %s\n",
792                                                         item->column_alias ? item->column_alias : "(none)" );
793                                         PRINT( "\tgrouped_by: %d\n", item->grouped_by );
794
795                                         item->next = select_list;
796                                         select_list = item;
797                                 } else {
798                                         osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
799                                                 "Unable to build select list for query id #%d", query_id ));
800                                         selectListFree( select_list );
801                                         select_list = NULL;
802                                         break;
803                                 }
804                                 if( !dbi_result_next_row( result ) )
805                                         break;
806                         };
807                 }
808         } else {
809                 const char* msg;
810                 int errnum = dbi_conn_error( state->dbhandle, &msg );
811                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
812                         "Unable to query query.select_list table: #%d %s",
813                         errnum, msg ? msg : "No description available" ));
814                 state->error = 1;
815         }
816
817         return select_list;
818 }
819
820 /**
821         @brief Construct a SelectItem.
822         @param Pointer to the query-building context.
823         @param result Database cursor positioned at a row in query.select_item.
824         @return Pointer to a newly constructed SelectItem, if successful, or NULL if not.
825
826         The calling code is responsible for freeing the SelectItems by calling selectListFree().
827 */
828 static SelectItem* constructSelectItem( BuildSQLState* state, dbi_result result ) {
829
830         // Get the column values
831         int id                   = dbi_result_get_int_idx( result, 1 );
832         int stored_query_id      = dbi_result_get_int_idx( result, 2 );
833         int seq_no               = dbi_result_get_int_idx( result, 3 );
834         int expression_id        = dbi_result_get_int_idx( result, 4 );
835         const char* column_alias = dbi_result_get_string_idx( result, 5 );
836         int grouped_by           = oils_result_get_bool_idx( result, 6 );
837         
838         // Construct an Expression
839         Expression* expression = getExpression( state, expression_id );
840         if( !expression ) {
841                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
842                         "Unable to fetch expression for id = %d", expression_id ));
843                 state->error = 1;
844                 return NULL;
845         };
846
847         // Allocate a SelectItem: from the free list if possible, from the heap if necessary
848
849         SelectItem* sel;
850         if( free_select_item_list ) {
851                 sel = free_select_item_list;
852                 free_select_item_list = free_select_item_list->next;
853         } else
854                 sel = safe_malloc( sizeof( SelectItem ) );
855
856         sel->next            = NULL;
857         sel->id              = id;
858         sel->stored_query_id = stored_query_id;
859         sel->seq_no          = seq_no;
860         sel->expression      = expression;
861         sel->column_alias    = column_alias ? strdup( column_alias ) : NULL;
862         sel->grouped_by      = grouped_by;
863
864         return sel;
865 }
866
867 /**
868         @brief Free a list of SelectItems.
869         @param sel Pointer to the first item in the list to be freed.
870
871         Free the column alias and expression owned by each item.  Put the entire list into a free
872         list of SelectItems.
873 */
874 static void selectListFree( SelectItem* sel ) {
875         if( !sel )
876                 return;    // Nothing to free
877
878         SelectItem* first = sel;
879         while( 1 ) {
880                 free( sel->column_alias );
881                 sel->column_alias = NULL;
882                 expressionFree( sel->expression );
883                 sel->expression = NULL;
884
885                 if( NULL == sel->next ) {
886                         sel->next = free_select_item_list;
887                         break;
888                 } else
889                         sel = sel->next;
890         };
891
892         // Transfer the entire list to the free list
893         free_select_item_list = first;
894 }
895
896 /**
897         @brief Given the name of a bind variable, build a corresponding BindVar.
898         @param state Pointer to the query-building context.
899         @param name Name of the bind variable.
900         @return Pointer to the newly-built BindVar.
901
902         Since the same bind variable may appear multiple times, we load it only once for the
903         entire query, and reference the one copy wherever needed.
904 */
905 static BindVar* getBindVar( BuildSQLState* state, const char* name ) {
906         BindVar* bind = NULL;
907         if( state->bindvar_list ) {
908                 bind = osrfHashGet( state->bindvar_list, name );
909                 if( bind )
910                         return bind;   // Already loaded it...
911         }
912
913         // Load a BindVar from the Database.
914         dbi_result result = dbi_conn_queryf( state->dbhandle,
915                 "SELECT name, type, description, default_value, label "
916                 "FROM query.bind_variable WHERE name = \'%s\';", name );
917         if( result ) {
918                 if( dbi_result_first_row( result ) ) {
919                         bind = constructBindVar( state, result );
920                         if( bind ) {
921                                 PRINT( "Got a bind variable for %s\n", name );
922                         } else 
923                                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
924                                         "Unable to load bind variable \"%s\"", name ));
925                 } else {
926                         osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
927                                 "No bind variable found with name \"%s\"", name ));
928                 }
929         } else {
930                 const char* msg;
931                 int errnum = dbi_conn_error( state->dbhandle, &msg );
932                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
933                         "Unable to query query.bind_variable table for \"%s\": #%d %s",
934                         name, errnum, msg ? msg : "No description available" ));
935                 state->error = 1;
936         }
937
938         if( bind ) {
939                 // Add the new bind variable to the list
940                 if( !state->bindvar_list ) {
941                         // Don't have a list yet?  Start one.
942                         state->bindvar_list = osrfNewHash();
943                         osrfHashSetCallback( state->bindvar_list, bindVarFree );
944                 }
945                 osrfHashSet( state->bindvar_list, bind, name );
946         } else
947                 state->error = 1;
948
949         return bind;
950 }
951
952 /**
953         @brief Construct a BindVar to represent a bind variable.
954         @param Pointer to the query-building context.
955         @param result Database cursor positioned at a row in query.bind_variable.
956         @return Pointer to a newly constructed BindVar, if successful, or NULL if not.
957
958         The calling code is responsible for freeing the BindVar by calling bindVarFree().
959 */
960 static BindVar* constructBindVar( BuildSQLState* state, dbi_result result ) {
961
962         const char* name = dbi_result_get_string_idx( result, 1 );
963
964         const char* type_str = dbi_result_get_string_idx( result, 2 );
965         BindVarType type;
966         if( !strcmp( type_str, "string" ))
967                 type = BIND_STR;
968         else if( !strcmp( type_str, "number" ))
969                 type = BIND_NUM;
970         else if( !strcmp( type_str, "string_list" ))
971                 type = BIND_STR_LIST;
972         else if( !strcmp( type_str, "number_list" ))
973                 type = BIND_NUM_LIST;;
974
975         const char* description = dbi_result_get_string_idx( result, 3 );
976
977         // The default value is encoded as JSON.  Translate it into a jsonObject.
978         const char* default_value_str = dbi_result_get_string_idx( result, 4 );
979         jsonObject* default_value = NULL;
980         if( default_value_str ) {
981                 default_value = jsonParse( default_value_str );
982                 if( !default_value ) {
983                         osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
984                                 "Unable to parse JSON string for default value of bind variable \"%s\"",
985                                 name ));
986                         state->error = 1;
987                         return NULL;
988                 }
989         }
990
991         const char* label = dbi_result_get_string_idx( result, 5 );
992
993         // Allocate a BindVar: from the free list if possible, from the heap if necessary
994         BindVar* bind = NULL;
995         if( free_bindvar_list ) {
996                 bind = free_bindvar_list;
997                 free_bindvar_list = free_bindvar_list->next;
998         } else
999                 bind = safe_malloc( sizeof( BindVar ) );
1000
1001         bind->next = NULL;
1002         bind->name = strdup( name );
1003         bind->label = strdup( label );
1004         bind->type = type;
1005         bind->description = strdup( description );
1006         bind->default_value = default_value;
1007         bind->actual_value = NULL;
1008
1009         return bind;
1010 }
1011
1012 /**
1013         @brief Deallocate a BindVar.
1014         @param key Pointer to the bind variable name (not used).
1015         @param p Pointer to the BindVar to be deallocated, cast to a void pointer.
1016
1017         Free the strings and jsonObjects owned by the BindVar, and put the BindVar itself into a
1018         free list.
1019
1020         This function is a callback installed in an osrfHash; hence the peculiar signature.
1021 */
1022 static void bindVarFree( char* key, void* p ) {
1023         if( p ) {
1024                 BindVar* bind = p;
1025                 free( bind->name );
1026                 free( bind->label );
1027                 free( bind->description );
1028                 if( bind->default_value ) {
1029                         jsonObjectFree( bind->default_value );
1030                         bind->default_value = NULL;
1031                 }
1032                 if( bind->actual_value ) {
1033                         jsonObjectFree( bind->actual_value );
1034                         bind->actual_value = NULL;
1035                 }
1036
1037                 // Prepend to free list
1038                 bind->next = free_bindvar_list;
1039                 free_bindvar_list = bind;
1040         }
1041 }
1042
1043 /**
1044         @brief Given an id for a row in query.expression, build an Expression struct.
1045         @param Pointer to the query-building context.
1046         @param id ID of a row in query.expression.
1047         @return Pointer to a newly-created Expression if successful, or NULL if not.
1048 */
1049 static Expression* getExpression( BuildSQLState* state, int id ) {
1050         
1051         // Check the stack to see if the current expression is nested inside itself.  If it is,
1052         // then abort in order to avoid infinite recursion.  If it isn't, then add it to the
1053         // stack.  (Make sure to pop it off the stack before returning.)
1054         if( searchIdStack( state->expr_stack, id, NULL )) {
1055                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1056                         "Infinite recursion detected; expression # %d is nested within itself", id ));
1057                 state->error = 1;
1058                 return NULL;
1059         } else
1060                 push_id( &state->expr_stack, id, NULL );
1061
1062         Expression* exp = NULL;
1063         dbi_result result = dbi_conn_queryf( state->dbhandle,
1064                 "SELECT exp.id, exp.type, exp.parenthesize, exp.parent_expr, exp.seq_no, "
1065                 "exp.literal, exp.table_alias, exp.column_name, exp.left_operand, exp.operator, "
1066                 "exp.right_operand, exp.subquery, exp.cast_type, exp.negate, exp.bind_variable, "
1067                 "func.function_name, COALESCE(func.is_aggregate, false) "
1068                 "FROM query.expression AS exp LEFT JOIN query.function_sig AS func "
1069                 "ON (exp.function_id = func.id) "
1070                 "WHERE exp.id = %d;", id );
1071         if( result ) {
1072                 if( dbi_result_first_row( result ) ) {
1073                         exp = constructExpression( state, result );
1074                         if( exp ) {
1075                                 PRINT( "Got an expression\n" );
1076                                 PRINT( "\tid = %d\n", exp->id );
1077                                 PRINT( "\ttype = %d\n", exp->type );
1078                                 PRINT( "\tparenthesize = %d\n", exp->parenthesize );
1079                                 PRINT( "\tcolumn_name = %s\n", exp->column_name ? exp->column_name : "(none)" );
1080                         } else 
1081                                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1082                                         "Unable to construct an Expression for id = %d", id ));
1083                 }
1084         } else {
1085                 const char* msg;
1086                 int errnum = dbi_conn_error( state->dbhandle, &msg );
1087                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1088                         "Unable to query query.expression table: #%d %s",
1089                         errnum, msg ? msg : "No description available" ));
1090                 state->error = 1;
1091         }
1092
1093         pop_id( &state->expr_stack );
1094         return exp;
1095 }
1096
1097 /**
1098         @brief Construct an Expression.
1099         @param Pointer to the query-building context.
1100         @param result Database cursor positioned at a row in query.expression.
1101         @return Pointer to a newly constructed Expression, if successful, or NULL if not.
1102
1103         The calling code is responsible for freeing the Expression by calling expressionFree().
1104 */
1105 static Expression* constructExpression( BuildSQLState* state, dbi_result result ) {
1106
1107         int id = dbi_result_get_int_idx( result, 1 );
1108         const char* type_str = dbi_result_get_string_idx( result, 2 );
1109         
1110         ExprType type;
1111         if( !strcmp( type_str, "xbet" ))
1112                 type = EXP_BETWEEN;
1113         else if( !strcmp( type_str, "xbind" ))
1114                 type = EXP_BIND;
1115         else if( !strcmp( type_str, "xbool" ))
1116                 type = EXP_BOOL;
1117         else if( !strcmp( type_str, "xcase" ))
1118                 type = EXP_CASE;
1119         else if( !strcmp( type_str, "xcast" ))
1120                 type = EXP_CAST;
1121         else if( !strcmp( type_str, "xcol" ))
1122                 type = EXP_COLUMN;
1123         else if( !strcmp( type_str, "xex" ))
1124                 type = EXP_EXIST;
1125         else if( !strcmp( type_str, "xfld" ))
1126                 type = EXP_FIELD;
1127         else if( !strcmp( type_str, "xfunc" ))
1128                 type = EXP_FUNCTION;
1129         else if( !strcmp( type_str, "xin" ))
1130                 type = EXP_IN;
1131         else if( !strcmp( type_str, "xisnull" ))
1132                 type = EXP_ISNULL;
1133         else if( !strcmp( type_str, "xnull" ))
1134                 type = EXP_NULL;
1135         else if( !strcmp( type_str, "xnum" ))
1136                 type = EXP_NUMBER;
1137         else if( !strcmp( type_str, "xop" ))
1138                 type = EXP_OPERATOR;
1139         else if( !strcmp( type_str, "xser" ))
1140                 type = EXP_SERIES;
1141         else if( !strcmp( type_str, "xstr" ))
1142                 type = EXP_STRING;
1143         else if( !strcmp( type_str, "xsubq" ))
1144                 type = EXP_SUBQUERY;
1145         else
1146                 type = EXP_NULL;     // shouldn't happen due to database constraint
1147
1148         int parenthesize = oils_result_get_bool_idx( result, 3 );
1149
1150         int parent_expr_id;
1151         if( dbi_result_field_is_null_idx( result, 4 ))
1152                 parent_expr_id = -1;
1153         else
1154                 parent_expr_id = dbi_result_get_int_idx( result, 4 );
1155         
1156         int seq_no = dbi_result_get_int_idx( result, 5 );
1157         const char* literal = dbi_result_get_string_idx( result, 6 );
1158         const char* table_alias = dbi_result_get_string_idx( result, 7 );
1159         const char* column_name = dbi_result_get_string_idx( result, 8 );
1160
1161         int left_operand_id;
1162         if( dbi_result_field_is_null_idx( result, 9 ))
1163                 left_operand_id = -1;
1164         else
1165                 left_operand_id = dbi_result_get_int_idx( result, 9 );
1166
1167         const char* operator = dbi_result_get_string_idx( result, 10 );
1168
1169         int right_operand_id;
1170         if( dbi_result_field_is_null_idx( result, 11 ))
1171                 right_operand_id = -1;
1172         else
1173                 right_operand_id = dbi_result_get_int_idx( result, 11 );
1174
1175         int subquery_id;
1176         if( dbi_result_field_is_null_idx( result, 12 ))
1177                 subquery_id = -1;
1178         else
1179                 subquery_id = dbi_result_get_int_idx( result, 12 );
1180
1181         int cast_type_id;
1182         if( dbi_result_field_is_null_idx( result, 13 ))
1183                 cast_type_id = -1;
1184         else
1185                 cast_type_id = dbi_result_get_int_idx( result, 13 );
1186
1187         int negate = oils_result_get_bool_idx( result, 14 );
1188         const char* bind_variable = dbi_result_get_string_idx( result, 15 );
1189         const char* function_name = dbi_result_get_string_idx( result, 16 );
1190         int is_aggregate = oils_result_get_bool_idx( result, 17 );
1191
1192         Expression* left_operand = NULL;
1193         Expression* right_operand = NULL;
1194         StoredQ* subquery = NULL;
1195         BindVar* bind = NULL;
1196         Expression* subexp_list = NULL;
1197
1198         if( EXP_BIND == type ) {
1199                 if( bind_variable ) {
1200                         // To do: Build a BindVar
1201                         bind = getBindVar( state, bind_variable );
1202                         if( ! bind ) {
1203                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1204                                                                 "Unable to load bind variable \"%s\" for expression # %d",
1205                 bind_variable, id ));
1206                                 state->error = 1;
1207                                 return NULL;
1208                         }
1209                         PRINT( "\tBind variable is \"%s\"\n", bind_variable );
1210                 } else {
1211                         osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1212                                                         "No variable specified for bind variable expression # %d",
1213            bind_variable, id ));
1214                         state->error = 1;
1215                         return NULL;
1216                 }
1217                 if( right_operand_id != -1 ) {
1218                         right_operand = getExpression( state, right_operand_id );
1219                         if( !right_operand ) {
1220                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1221                                         "Unable to get right operand in expression # %d", id ));
1222                                 state->error = 1;
1223                                 expressionFree( left_operand );
1224                                 return NULL;
1225                         }
1226                 }
1227
1228         } else if( EXP_FUNCTION == type ) {
1229                 if( !function_name ) {
1230                         osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1231                                 "Function call expression # %d provides no function name", id ));
1232                         state->error = 1;
1233                         return NULL;
1234                 } else {
1235                         subexp_list = getExpressionList( state, id );
1236                         if( state->error ) {
1237                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1238                                         "Unable to get parameter list for function expression # %d", id ));
1239                                 return NULL;
1240                         }
1241                 }
1242
1243         } else if( EXP_IN == type ) {
1244                 if( -1 == left_operand_id ) {
1245                         osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1246                                 "IN condition has no left operand in expression # %d", id ));
1247                         state->error = 1;
1248                         return NULL;
1249                 } else {
1250                         left_operand = getExpression( state, left_operand_id );
1251                         if( !left_operand ) {
1252                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1253                                         "Unable to get left operand for IN condition in expression # %d", id ));
1254                                 state->error = 1;
1255                                 return NULL;
1256                         }
1257                 }
1258
1259                 if( -1 == subquery_id ) {
1260                         // Load an IN list of subexpressions
1261                         subexp_list = getExpressionList( state, id );
1262                         if( state->error ) {
1263                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1264                                         "Unable to get subexpressions for IN list" ));
1265                                 return NULL;
1266                         } else if( !subexp_list ) {
1267                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1268                                         "IN list is empty in expression # %d", id ));
1269                                 state->error = 1;
1270                                 return NULL;
1271                         }
1272                 } else {
1273                         // Load a subquery
1274                         subquery = getStoredQuery( state, subquery_id );
1275                         if( !subquery ) {
1276                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1277                                         "Unable to load subquery for IN expression # %d", id ));
1278                                 state->error = 1;
1279                                 return NULL;
1280                         }
1281                 }
1282
1283         } else if( EXP_ISNULL == type ) {
1284                 if( -1 == left_operand_id ) {
1285                         osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1286                                 "Expression # %d IS NULL has no left operand", id ));
1287                         state->error = 1;
1288                         return NULL;
1289                 }
1290
1291                 if( left_operand_id != -1 ) {
1292                         left_operand = getExpression( state, left_operand_id );
1293                         if( !left_operand ) {
1294                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1295                                         "Unable to get left operand in expression # %d", id ));
1296                                 state->error = 1;
1297                                 return NULL;
1298                         }
1299                 }
1300         } else if( EXP_EXIST == type ) {
1301                 if( -1 == subquery_id ) {
1302                         osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1303                                 "Internal error: No subquery found for EXIST expression # %d", id ));
1304                         state->error = 1;
1305                         return NULL;
1306                 } else {
1307                         subquery = getStoredQuery( state, subquery_id );
1308                         if( !subquery ) {
1309                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1310                                         "Unable to load subquery for EXIST expression # %d", id ));
1311                                 state->error = 1;
1312                                 return NULL;
1313                         }
1314                 }
1315
1316         } else if( EXP_NUMBER == type ) {
1317                 if( !literal ) {
1318                         osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1319                                 "Numeric expression # %d provides no numeric value", id ));
1320                         state->error = 1;
1321                         return NULL;
1322                 }
1323
1324         } else if( EXP_OPERATOR == type ) {
1325                 // Load left and/or right operands
1326                 if( -1 == left_operand_id && -1 == right_operand_id ) {
1327                         osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1328                                 "Expression # %d is an operator with no operands", id ));
1329                         state->error = 1;
1330                         return NULL;
1331                 }
1332
1333                 if( left_operand_id != -1 ) {
1334                         left_operand = getExpression( state, left_operand_id );
1335                         if( !left_operand ) {
1336                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1337                                         "Unable to get left operand in expression # %d", id ));
1338                                 state->error = 1;
1339                                 return NULL;
1340                         }
1341                 }
1342
1343                 if( right_operand_id != -1 ) {
1344                         right_operand = getExpression( state, right_operand_id );
1345                         if( !right_operand ) {
1346                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1347                                                                 "Unable to get right operand in expression # %d", id ));
1348                                 state->error = 1;
1349                                 return NULL;
1350                         }
1351                 }
1352
1353         } else if( EXP_SERIES == type ) {
1354                 subexp_list = getExpressionList( state, id );
1355                 if( state->error ) {
1356                         osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1357                                 "Unable to get subexpressions for expression series using operator \"%s\"",
1358                                         operator ? operator : "," ));
1359                         return NULL;
1360                 } else if( !subexp_list ) {
1361                         osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1362                                 "Series expression is empty in expression # %d", id ));
1363                         state->error = 1;
1364                         return NULL;
1365                 }
1366
1367         } else if( EXP_STRING == type ) {
1368                 if( !literal ) {
1369                         osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1370                                 "String expression # %d provides no string value", id ));
1371                         state->error = 1;
1372                         return NULL;
1373                 }
1374
1375         } else if( EXP_SUBQUERY == type ) {
1376                 if( -1 == subquery_id ) {
1377                         osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1378                                 "Subquery expression # %d has no query id", id ));
1379                         state->error = 1;
1380                         return NULL;
1381                 } else {
1382                         // Load a subquery, if there is one
1383                         subquery = getStoredQuery( state, subquery_id );
1384                         if( !subquery ) {
1385                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1386                                         "Unable to load subquery for expression # %d", id ));
1387                                 state->error = 1;
1388                                 return NULL;
1389                         }
1390                         if( subquery->select_list && subquery->select_list->next ) {
1391                                 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1392                                         "Subquery # %d as expression returns more than one column", subquery_id ));
1393                                 state->error = 1;
1394                                 return NULL;
1395                         }
1396                         PRINT( "\tExpression is subquery %d\n", subquery_id );
1397                 }
1398         }
1399
1400         // Allocate an Expression: from the free list if possible, from the heap if necessary
1401         Expression* exp = NULL;
1402         if( free_expression_list ) {
1403                 exp = free_expression_list;
1404                 free_expression_list = free_expression_list->next;
1405         } else
1406                 exp = safe_malloc( sizeof( Expression ) );
1407
1408         // Populate the Expression
1409         exp->next = NULL;
1410         exp->id = id;
1411         exp->type = type;
1412         exp->parenthesize = parenthesize;
1413         exp->parent_expr_id = parent_expr_id;
1414         exp->seq_no = seq_no;
1415         exp->literal = literal ? strdup( literal ) : NULL;
1416         exp->table_alias = table_alias ? strdup( table_alias ) : NULL;
1417         exp->column_name = column_name ? strdup( column_name ) : NULL;
1418         exp->left_operand = left_operand;
1419         exp->op = operator ? strdup( operator ) : NULL;
1420         exp->right_operand = right_operand;
1421         exp->subquery_id = subquery_id;
1422         exp->subquery = subquery;
1423         exp->cast_type_id = subquery_id;
1424         exp->negate = negate;
1425         exp->bind = bind;
1426         exp->subexp_list = subexp_list;
1427         exp->function_name = function_name ? strdup( function_name ) : NULL;
1428         exp->is_aggregate = is_aggregate;
1429
1430         return exp;
1431 }
1432
1433 /**
1434         @brief Free all the Expressions in a linked list of Expressions.
1435         @param exp Pointer to the first Expression in the list.
1436 */
1437 static void expressionListFree( Expression* exp ) {
1438         while( exp ) {
1439                 Expression* next = exp->next;
1440                 expressionFree( exp );
1441                 exp = next;
1442         }
1443 }
1444
1445 /**
1446         @brief Deallocate an Expression.
1447         @param exp Pointer to the Expression to be deallocated.
1448
1449         Free the strings owned by the Expression.  Put the Expression itself, and any
1450         subexpressions that it owns, into a free list.
1451 */
1452 static void expressionFree( Expression* exp ) {
1453         if( exp ) {
1454                 free( exp->literal );
1455                 exp->literal = NULL;
1456                 free( exp->table_alias );
1457                 exp->table_alias = NULL;
1458                 free( exp->column_name );
1459                 exp->column_name = NULL;
1460                 if( exp->left_operand ) {
1461                         expressionFree( exp->left_operand );
1462                         exp->left_operand = NULL;
1463                 }
1464                 free( exp->op );
1465                 exp->op = NULL;
1466                 if( exp->right_operand ) {
1467                         expressionFree( exp->right_operand );
1468                         exp->right_operand = NULL;
1469                 }
1470                 if( exp->subquery ) {
1471                         storedQFree( exp->subquery );
1472                         exp->subquery = NULL;
1473                 }
1474
1475                 // We don't free the bind member here because the Expression doesn't own it;
1476                 // the bindvar_list hash owns it, so that multiple Expressions can reference it.
1477
1478                 if( exp->subexp_list ) {
1479                         // Free the linked list of subexpressions
1480                         expressionListFree( exp->subexp_list );
1481                         exp->subexp_list = NULL;
1482                 }
1483
1484                 if( exp->function_name ) {
1485                         free( exp->function_name );
1486                         exp->function_name = NULL;
1487                 }
1488
1489                 // Prepend to the free list
1490                 exp->next = free_expression_list;
1491                 free_expression_list = exp;
1492         }
1493 }
1494
1495 /**
1496         @brief Build a list of subexpressions.
1497         @param state Pointer to the query-building context.
1498         @param id ID of the parent Expression.
1499         @return A pointer to the first in a linked list of Expressions, if there are any; or
1500                 NULL if there aren't any, or in case of an error.
1501 */
1502 static Expression* getExpressionList( BuildSQLState* state, int id ) {
1503         Expression* exp_list = NULL;
1504         
1505         // The ORDER BY is in descending order so that we can build the list by adding to
1506         // the head, and it will wind up in the right order.
1507         dbi_result result = dbi_conn_queryf( state->dbhandle,
1508                 "SELECT exp.id, exp.type, exp.parenthesize, exp.parent_expr, exp.seq_no, "
1509                 "exp.literal, exp.table_alias, exp.column_name, exp.left_operand, exp.operator, "
1510                 "exp.right_operand, exp.subquery, exp.cast_type, exp.negate, exp.bind_variable, "
1511                 "func.function_name, COALESCE(func.is_aggregate, false) "
1512                 "FROM query.expression AS exp LEFT JOIN query.function_sig AS func "
1513                 "ON (exp.function_id = func.id) "
1514                 "WHERE exp.parent_expr = %d "
1515                 "ORDER BY exp.seq_no desc;", id );
1516
1517         if( result ) {
1518                 if( dbi_result_first_row( result ) ) {
1519                         while( 1 ) {
1520                                 Expression* exp = constructExpression( state, result );
1521                                 if( exp ) {
1522                                         PRINT( "Found a subexpression\n" );
1523                                         exp->next = exp_list;
1524                                         exp_list  = exp;
1525                                 } else {
1526                                         osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1527                                                 "Unable to build subexpression list for expression id #%d", id ));
1528                                         expressionListFree( exp_list );
1529                                         exp_list = NULL;
1530                                         break;
1531                                 }
1532                                 if( !dbi_result_next_row( result ) )
1533                                         break;
1534                         };
1535                 }
1536         } else {
1537                 const char* msg;
1538                 int errnum = dbi_conn_error( state->dbhandle, &msg );
1539                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1540                         "Unable to query query.expression table for expression list: #%d %s",
1541                         errnum, msg ? msg : "No description available" ));
1542                 state->error = 1;
1543         }
1544
1545         return exp_list;
1546 }
1547
1548 /**
1549         @brief Build a list of ORDER BY items as a linked list of OrderItems.
1550         @param state Pointer to the query-building context.
1551         @param query_id ID for the query to which the ORDER BY belongs.
1552         @return Pointer to the first node in a linked list of OrderItems.
1553
1554         The calling code is responsible for freeing the list by calling orderItemListFree().
1555 */
1556 static OrderItem* getOrderByList( BuildSQLState* state, int query_id ) {
1557         OrderItem* ord_list = NULL;
1558
1559         // The ORDER BY is in descending order so that we can build the list by adding to
1560         // the head, and it will wind up in the right order.
1561         dbi_result result = dbi_conn_queryf( state->dbhandle,
1562                 "SELECT id, stored_query, seq_no, expression "
1563                 "FROM query.order_by_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
1564         if( result ) {
1565                 if( dbi_result_first_row( result ) ) {
1566                         while( 1 ) {
1567                                 OrderItem* item = constructOrderItem( state, result );
1568                                 if( item ) {
1569                                         PRINT( "Found an ORDER BY item\n" );
1570
1571                                         item->next = ord_list;
1572                                         ord_list = item;
1573                                 } else {
1574                                         osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1575                                                 "Unable to build ORDER BY item for query id #%d", query_id ));
1576                                         orderItemListFree( ord_list );
1577                                         ord_list = NULL;
1578                                         break;
1579                                 }
1580                                 if( !dbi_result_next_row( result ) )
1581                                         break;
1582                         };
1583                 }
1584         }  else {
1585                 const char* msg;
1586                 int errnum = dbi_conn_error( state->dbhandle, &msg );
1587                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1588                         "Unable to query query.order_by_list table: #%d %s",
1589                         errnum, msg ? msg : "No description available" ));
1590                 state->error = 1;
1591         }
1592
1593         return ord_list;
1594 }
1595
1596 /**
1597         @brief Construct an OrderItem.
1598         @param Pointer to the query-building context.
1599         @param result Database cursor positioned at a row in query.order_by_item.
1600         @return Pointer to a newly constructed OrderItem, if successful, or NULL if not.
1601
1602         The calling code is responsible for freeing the OrderItems by calling orderItemListFree().
1603 */
1604 static OrderItem* constructOrderItem( BuildSQLState* state, dbi_result result ) {
1605         int id                   = dbi_result_get_int_idx( result, 1 );
1606         int stored_query_id      = dbi_result_get_int_idx( result, 2 );
1607         int seq_no               = dbi_result_get_int_idx( result, 3 );
1608         int expression_id        = dbi_result_get_int_idx( result, 4 );
1609         // Allocate a SelectItem: from the free list if possible, from the heap if necessary
1610
1611         // Construct an Expression
1612         Expression* expression = getExpression( state, expression_id );
1613         if( !expression ) {
1614                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1615                         "Unable to fetch ORDER BY expression for id = %d", expression_id ));
1616                 state->error = 1;
1617                 return NULL;
1618         };
1619
1620         // Allocate an OrderItem; from the free list if possible, or from the heap if necessary.
1621         OrderItem* ord;
1622         if( free_order_item_list ) {
1623                 ord = free_order_item_list;
1624                 free_order_item_list = free_order_item_list->next;
1625         } else
1626                 ord = safe_malloc( sizeof( OrderItem ));
1627
1628         ord->next            = NULL;
1629         ord->id              = id;
1630         ord->stored_query_id = stored_query_id;
1631         ord->seq_no          = seq_no;
1632         ord->expression      = expression;
1633
1634         return ord;
1635 }
1636
1637 /**
1638         @brief Deallocate a linked list of OrderItems.
1639         @param exp Pointer to the first OrderItem in the list to be deallocated.
1640
1641         Deallocate the memory owned by the OrderItems.  Put the items themselves into a free list.
1642 */
1643 static void orderItemListFree( OrderItem* ord ) {
1644         if( !ord )
1645                 return;    // Nothing to free
1646
1647         OrderItem* first = ord;
1648         while( 1 ) {
1649                 expressionFree( ord->expression );
1650                 ord->expression = NULL;
1651
1652                 if( NULL == ord->next ) {
1653                         ord->next = free_order_item_list;
1654                         break;
1655                 } else
1656                         ord = ord->next;
1657         };
1658
1659         // Transfer the entire list to the free list
1660         free_order_item_list = first;
1661 }
1662
1663 /**
1664         @brief Build a list of column names for a specified query.
1665         @param state Pointer to the query-building context.
1666         @param query Pointer to the specified query.
1667         @return Pointer to a newly-allocated JSON_ARRAY of column names.
1668
1669         In the resulting array, each entry is either a JSON_STRING or (when no column name is
1670         available) a JSON_NULL.
1671
1672         The calling code is responsible for freeing the list by calling jsonObjectFree().
1673 */
1674 jsonObject* oilsGetColNames( BuildSQLState* state, StoredQ* query ) {
1675         if( !state || !query )
1676                 return NULL;
1677
1678         // Save the outermost query id for possible use in an error message
1679         int id = query->id;
1680
1681         while( query->type != QT_SELECT ) {
1682                 // If the query is a UNION, INTERSECT, or EXCEPT, there must be a SELECT in
1683                 // there somewhere.  Find the first one, and use the SELECT list from that.
1684                 QSeq* child_list = query->child_list;
1685                 if( !child_list ) {
1686                         query = NULL;
1687                         break;
1688                 } else
1689                         query = child_list->child_query;
1690         }
1691
1692         if( !query ) {
1693                 state->error = 1;
1694                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1695                         "Unable to find first SELECT in query # %d", id ));
1696                 return NULL;
1697         }
1698
1699         // Get the SELECT list for the first SELECT
1700         SelectItem* col = query->select_list;
1701         if( !col ) {
1702                 state->error = 1;
1703                 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1704                         "First SELECT in query # %d has empty SELECT list", id ));
1705                         return NULL;
1706         }
1707
1708         jsonObject* col_list = jsonNewObjectType( JSON_ARRAY );
1709
1710         // Traverse the list, adding an entry for each
1711         do {
1712                 const char* alias = NULL;
1713                 if( col->column_alias )
1714                         alias = col->column_alias;
1715                 else {
1716                         Expression* expression = col->expression;
1717                         if( expression && EXP_COLUMN == expression->type && expression->column_name )
1718                                 alias = expression->column_name;
1719                 }
1720
1721                 jsonObjectPush( col_list, jsonNewObject( alias ) );
1722                 col = col->next;
1723         } while( col );
1724
1725         return col_list;
1726 }
1727
1728 /**
1729         @brief Push an IdNode onto a stack of IdNodes.
1730         @param stack Pointer to the stack.
1731         @param id Id of the new node.
1732         @param alias Alias, if any, of the new node.
1733 */
1734 static void push_id( IdNode** stack, int id, const char* alias ) {
1735
1736         if( stack ) {
1737                 // Allocate a node; from the free list if possible, from the heap if necessary.
1738                 IdNode* node = NULL;
1739                 if( free_id_node_list ) {
1740                         node = free_id_node_list;
1741                         free_id_node_list = free_id_node_list->next;
1742                 } else
1743                         node = safe_malloc( sizeof( IdNode ));
1744
1745                 // Populate it
1746                 node->next = *stack;
1747                 node->id = id;
1748                 if( alias )
1749                         node->alias = strdup( alias );
1750                 else
1751                         node->alias = NULL;
1752                 
1753                 // Reseat the stack
1754                 *stack = node;
1755         }
1756 }
1757
1758 /**
1759         @brief Remove the node at the top of an IdNode stack.
1760         @param stack Pointer to the IdNode stack.
1761 */
1762 void pop_id( IdNode** stack ) {
1763         if( stack ) {
1764                 IdNode* node = *stack;
1765                 *stack = node->next;
1766
1767                 if( node->alias ) {
1768                         free( node->alias );
1769                         node->alias = NULL;
1770                 }
1771
1772                 node->next = free_id_node_list;
1773                 free_id_node_list = node;
1774         }
1775 }
1776
1777 /**
1778         @brief Search a stack of IDs for a match by either ID or, optionally, by alias.
1779         @param stack Pointer to the stack.
1780         @param id The id to search for.
1781         @param alias (Optional) the alias to search for.
1782         @return A pointer to the matching node if one is found, or NULL if not.
1783
1784         This search is used to detect cases where a query, expression, or FROM clause is nested
1785         inside itself, in order to avoid infinite recursion; or in order to avoid conflicting
1786         table references in a FROM clause.
1787 */
1788 static const IdNode* searchIdStack( const IdNode* stack, int id, const char* alias ) {
1789         if( stack ) {
1790                 const IdNode* node = stack;
1791                 while( node ) {
1792                         if( node->id == id )
1793                                 return node;        // Matched on id
1794                         else if( alias && node->alias && !strcmp( alias, node->alias ))
1795                                 return node;        // Matched on alias
1796                         else
1797                                 node = node->next;
1798                 }
1799         }
1800         return NULL;   // No match found
1801 }
1802
1803 /**
1804         @brief Free up any resources held by the StoredQ module.
1805 */
1806 void storedQCleanup( void ) {
1807
1808         // Free all the nodes in the free state list
1809         StoredQ* sq = free_storedq_list;
1810         while( sq ) {
1811                 free_storedq_list = sq->next;
1812                 free( sq );
1813                 sq = free_storedq_list;
1814         }
1815
1816         // Free all the nodes in the free from_relation list
1817         FromRelation* fr = free_from_relation_list;
1818         while( fr ) {
1819                 free_from_relation_list = fr->next;
1820                 free( fr );
1821                 fr = free_from_relation_list;
1822         }
1823
1824         // Free all the nodes in the free expression list
1825         Expression* exp = free_expression_list;
1826         while( exp ) {
1827                 free_expression_list = exp->next;
1828                 free( exp );
1829                 exp = free_expression_list;
1830         }
1831
1832         // Free all the nodes in the free select item list
1833         SelectItem* sel = free_select_item_list;
1834         while( sel ) {
1835                 free_select_item_list = sel->next;
1836                 free( sel );
1837                 sel = free_select_item_list;
1838         }
1839
1840         // Free all the nodes in the free select item list
1841         IdNode* node = free_id_node_list;
1842         while( node ) {
1843                 free_id_node_list = node->next;
1844                 free( node );
1845                 node = free_id_node_list;
1846         }
1847
1848         // Free all the nodes in the free query sequence list
1849         QSeq* seq = free_qseq_list;
1850         while( seq ) {
1851                 free_qseq_list = seq->next;
1852                 free( seq );
1853                 seq = free_qseq_list;
1854         }
1855
1856         // Free all the nodes in the free order item list
1857         OrderItem* ord = free_order_item_list;
1858         while( ord ) {
1859                 free_order_item_list = ord->next;
1860                 free( ord );
1861                 ord = free_order_item_list;
1862         }
1863
1864         // Free all the nodes in the bind variable free list
1865         BindVar* bind = free_bindvar_list;
1866         while( bind ) {
1867                 free_bindvar_list = bind->next;
1868                 free( bind );
1869                 bind = free_bindvar_list;
1870         }
1871 }
1872
1873 /**
1874         @brief Return a boolean value from a database result.
1875         @param result The database result.
1876         @param i Index of the column in the result, starting with 1 );
1877         @return 1 if true, or 0 for false.
1878
1879         Null values and error conditions are interpreted as FALSE.
1880 */
1881 static int oils_result_get_bool_idx( dbi_result result, int i ) {
1882         if( result ) {
1883                 const char* str = dbi_result_get_string_idx( result, i );
1884                 return (str && *str == 't' ) ? 1 : 0;
1885         } else
1886                 return 0;
1887 }
1888
1889 /**
1890         @brief Enable verbose messages.
1891
1892         The messages are written to standard output, which for a server is /dev/null.  Hence this
1893         option is useful only for a non-server.  It is intended only as a convenience for
1894         development and debugging.
1895 */
1896 void oilsStoredQSetVerbose( void ) {
1897         verbose = 1;
1898 }