3 @brief Load an abstract representation of a query from the database.
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"
15 #define PRINT if( verbose ) printf
23 static int oils_result_get_bool_idx( dbi_result result, int i );
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 );
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 );
36 static SelectItem* getSelectList( BuildSQLState* state, int query_id );
37 static SelectItem* constructSelectItem( BuildSQLState* state, dbi_result result );
38 static void selectListFree( SelectItem* sel );
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 );
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 );
50 static OrderItem* getOrderByList( BuildSQLState* state, int query_id );
51 static OrderItem* constructOrderItem( BuildSQLState* state, dbi_result result );
52 static void orderItemListFree( OrderItem* ord );
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 );
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;
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
72 static int verbose = 0;
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.
80 The calling code is responsible for freeing the StoredQ by calling storedQFree().
82 StoredQ* getStoredQuery( BuildSQLState* state, int query_id ) {
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 ));
95 push_id( &state->query_stack, query_id, 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 );
102 if( dbi_result_first_row( result ) ) {
103 sq = constructStoredQ( state, result );
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" );
111 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
112 "Unable to build a query for id = %d", query_id ));
114 sqlAddMsg( state, "Stored query not found for id %d", query_id );
117 dbi_result_free( result );
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" ));
127 pop_id( &state->query_stack );
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.
137 The calling code is responsible for freeing the StoredQ by calling storedQFree().
139 static StoredQ* constructStoredQ( BuildSQLState* state, dbi_result result ) {
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 );
146 if( !strcmp( type_str, "SELECT" ))
148 else if( !strcmp( type_str, "UNION" ))
150 else if( !strcmp( type_str, "INTERSECT" ))
152 else if( !strcmp( type_str, "EXCEPT" ))
155 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
156 "Invalid query type \"%s\"", type_str ));
160 int use_all = oils_result_get_bool_idx( result, 3 );
161 int use_distinct = oils_result_get_bool_idx( result, 4 );
164 if( dbi_result_field_is_null_idx( result, 5 ) )
167 from_clause_id = dbi_result_get_int_idx( result, 5 );
170 if( dbi_result_field_is_null_idx( result, 6 ) )
171 where_clause_id = -1;
173 where_clause_id = dbi_result_get_int_idx( result, 6 );
175 int having_clause_id;
176 if( dbi_result_field_is_null_idx( result, 7 ) )
177 having_clause_id = -1;
179 having_clause_id = dbi_result_get_int_idx( result, 7 );
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 );
187 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
188 "Unable to construct FROM clause for id = %d", from_clause_id ));
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 ));
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 );
205 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
206 "No SELECT list found for query id = %d", id ));
207 fromRelationFree( from_clause );
211 // Construct child queries of UNION, INTERSECT, or EXCEPT query
212 child_list = loadChildQueries( state, id, type_str );
214 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
215 "Unable to load child queries for %s query # %d", type_str, id ));
217 fromRelationFree( from_clause );
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 );
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 );
254 // Get the ORDER BY clause, if there is one
255 OrderItem* order_by_list = getOrderByList( state, id );
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 );
267 // Allocate a StoredQ: from the free list if possible, from the heap if necessary
270 if( free_storedq_list ) {
271 sq = free_storedq_list;
272 free_storedq_list = free_storedq_list->next;
274 sq = safe_malloc( sizeof( StoredQ ) );
276 // Populate the StoredQ
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;
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.
301 The @a type_str parameter is used only for building error messages.
303 static QSeq* loadChildQueries( BuildSQLState* state, int parent_id, const char* type_str ) {
304 QSeq* child_list = NULL;
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 );
312 if( dbi_result_first_row( result ) ) {
316 QSeq* seq = constructQSeq( state, result );
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;
326 freeQSeqList( child_list );
329 if( !dbi_result_next_row( result ))
333 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
334 "%s query # %d has only one child query", type_str, parent_id ));
336 freeQSeqList( child_list );
340 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
341 "%s query # %d has no child queries within it", type_str, parent_id ));
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" ));
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.
364 The calling code is responsible for freeing QSeqs by calling freeQSeqList().
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 );
372 StoredQ* child_query = getStoredQuery( state, child_query_id );
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 ));
381 // Allocate a QSeq; from the free list if possible, from the heap if necessary
383 if( free_qseq_list ) {
384 seq = free_qseq_list;
385 free_qseq_list = free_qseq_list->next;
387 seq = safe_malloc( sizeof( QSeq ));
391 seq->parent_query_id = parent_query_id;
392 seq->seq_no = seq_no;
393 seq->child_query = child_query;
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.
402 Each QSeq goes onto a free list for potential reuse.
404 static void freeQSeqList( QSeq* seq ) {
410 storedQFree( seq->child_query );
411 seq->child_query = NULL;
416 seq->next = free_qseq_list;
421 free_qseq_list = first;
425 @brief Deallocate the memory owned by a StoredQ.
426 @param sq Pointer to the StoredQ to be deallocated.
428 void storedQFree( StoredQ* 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;
440 if( sq->order_by_list ) {
441 orderItemListFree( sq->order_by_list );
442 sq->order_by_list = NULL;
444 if( sq->having_clause )
445 expressionFree( sq->having_clause );
447 // Stick the empty husk on the free list for potential reuse
448 sq->next = free_storedq_list;
449 free_storedq_list = sq;
453 static FromRelation* getFromRelation( BuildSQLState* state, int id ) {
454 FromRelation* fr = NULL;
455 dbi_result result = dbi_conn_queryf( state->dbhandle,
456 "SELECT id, type, table_name, class_name, subquery, function_call, "
457 "table_alias, parent_relation, seq_no, join_type, on_clause "
458 "FROM query.from_relation WHERE id = %d;", id );
460 if( dbi_result_first_row( result ) ) {
461 fr = constructFromRelation( state, result );
463 PRINT( "Got a from_relation row\n" );
464 PRINT( "\tid: %d\n", fr->id );
465 PRINT( "\ttype: %d\n", (int) fr->type );
466 PRINT( "\ttable_name: %s\n", fr->table_name ? fr->table_name : "(none)" );
467 PRINT( "\tclass_name: %s\n", fr->class_name ? fr->class_name : "(none)" );
468 PRINT( "\tsubquery_id: %d\n", fr->subquery_id );
469 PRINT( "\tfunction_call_id: %d\n", fr->function_call_id );
470 PRINT( "\ttable_alias: %s\n", fr->table_alias ? fr->table_alias : "(none)" );
471 PRINT( "\tparent_relation_id: %d\n", fr->parent_relation_id );
472 PRINT( "\tseq_no: %d\n", fr->seq_no );
473 PRINT( "\tjoin_type = %d\n", fr->join_type );
474 // Check the stack to see if the current from clause is nested inside itself.
475 // If it is, then abort in order to avoid infinite recursion. If it isn't,
476 // then add it to the stack. (Make sure to pop it off the stack before
478 const char* effective_alias = fr->table_alias;
479 if( !effective_alias )
480 effective_alias = fr->class_name;
481 const IdNode* node = searchIdStack( state->from_stack, id, effective_alias );
484 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
485 "Infinite recursion detected; from clause # %d is nested "
486 "within itself", id ));
488 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
489 "Conflicting nested table aliases \"%s\" in from clause # %d",
490 effective_alias, node->id ));
494 push_id( &state->from_stack, id, effective_alias );
496 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
497 "Unable to build a FromRelation for id = %d", id ));
499 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
500 "FROM relation not found for id = %d", id ));
502 dbi_result_free( result );
505 int errnum = dbi_conn_error( state->dbhandle, &msg );
506 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
507 "Unable to query query.from_relation table: #%d %s",
508 errnum, msg ? msg : "No description available" ));
513 pop_id( &state->from_stack );
519 @brief Construct a FromRelation.
520 @param Pointer to the query-building context.
521 @param result Database cursor positioned at a row in query.from_relation.
522 @return Pointer to a newly constructed FromRelation, if successful, or NULL if not.
524 The calling code is responsible for freeing FromRelations by calling joinListFree().
526 static FromRelation* constructFromRelation( BuildSQLState* state, dbi_result result ) {
527 // Get the column values from the result
528 int id = dbi_result_get_int_idx( result, 1 );
529 const char* type_str = dbi_result_get_string_idx( result, 2 );
531 FromRelationType type;
532 if( !strcmp( type_str, "RELATION" ))
534 else if( !strcmp( type_str, "SUBQUERY" ))
536 else if( !strcmp( type_str, "FUNCTION" ))
539 type = FRT_RELATION; // shouldn't happen due to database constraint
541 const char* table_name = dbi_result_get_string_idx( result, 3 );
542 const char* class_name = dbi_result_get_string_idx( result, 4 );
545 if( dbi_result_field_is_null_idx( result, 5 ) )
548 subquery_id = dbi_result_get_int_idx( result, 5 );
550 int function_call_id;
551 if( dbi_result_field_is_null_idx( result, 6 ) )
552 function_call_id = -1;
554 function_call_id = dbi_result_get_int_idx( result, 6 );
556 const char* table_alias = dbi_result_get_string_idx( result, 7 );
558 int parent_relation_id;
559 if( dbi_result_field_is_null_idx( result, 8 ) )
560 parent_relation_id = -1;
562 parent_relation_id = dbi_result_get_int_idx( result, 8 );
564 int seq_no = dbi_result_get_int_idx( result, 9 );
567 const char* join_type_str = dbi_result_get_string_idx( result, 10 );
570 else if( !strcmp( join_type_str, "INNER" ) )
571 join_type = JT_INNER;
572 else if( !strcmp( join_type_str, "LEFT" ) )
574 else if( !strcmp( join_type_str, "RIGHT" ) )
575 join_type = JT_RIGHT;
576 else if( !strcmp( join_type_str, "FULL" ) )
579 join_type = JT_NONE; // shouldn't happen due to database constraint
582 if( dbi_result_field_is_null_idx( result, 11 ) )
585 on_clause_id = dbi_result_get_int_idx( result, 11 );
587 StoredQ* subquery = NULL;
593 if( -1 == subquery_id ) {
594 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
595 "Internal error: no subquery specified for FROM relation # %d", id ));
599 if( ! table_alias ) {
600 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
601 "Subquery needs alias in FROM relation # %d", id ));
605 subquery = getStoredQuery( state, subquery_id );
607 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
608 "Unable to load subquery for FROM relation # %d", id ));
614 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
615 "Functions in FROM clause not yet supported" ));
620 FromRelation* join_list = getJoinList( state, id );
622 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
623 "Unable to load join list for FROM relation # %d", id ));
627 Expression* on_clause = NULL;
628 if( on_clause_id != -1 ) {
629 on_clause = getExpression( state, on_clause_id );
631 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
632 "Unable to load ON condition for FROM relation # %d", id ));
633 joinListFree( join_list );
638 PRINT( "\tGot an ON condition\n" );
641 // Allocate a FromRelation: from the free list if possible, from the heap if necessary
644 if( free_from_relation_list ) {
645 fr = free_from_relation_list;
646 free_from_relation_list = free_from_relation_list->next;
648 fr = safe_malloc( sizeof( FromRelation ) );
650 // Populate the FromRelation
655 fr->table_name = table_name ? strdup( table_name ) : NULL;
656 fr->class_name = class_name ? strdup( class_name ) : NULL;
657 fr->subquery_id = subquery_id;
658 fr->subquery = subquery;
659 fr->function_call_id = function_call_id;
660 fr->table_alias = table_alias ? strdup( table_alias ) : NULL;
661 fr->parent_relation_id = parent_relation_id;
663 fr->join_type = join_type;
664 fr->on_clause = on_clause;
665 fr->join_list = join_list;
671 @brief Build a list of joined relations.
672 @param state Pointer to the query-building context.
673 @param id ID of the parent relation.
674 @return A pointer to the first in a linked list of FromRelations, if there are any; or
675 NULL if there aren't any, or in case of an error.
677 Look for relations joined directly to the parent relation, and make a list of them.
679 static FromRelation* getJoinList( BuildSQLState* state, int id ) {
680 FromRelation* join_list = NULL;
682 // The ORDER BY is in descending order so that we can build the list by adding to
683 // the head, and it will wind up in the right order.
684 dbi_result result = dbi_conn_queryf( state->dbhandle,
685 "SELECT id, type, table_name, class_name, subquery, function_call, "
686 "table_alias, parent_relation, seq_no, join_type, on_clause "
687 "FROM query.from_relation WHERE parent_relation = %d ORDER BY seq_no DESC", id );
690 if( dbi_result_first_row( result ) ) {
692 FromRelation* relation = constructFromRelation( state, result );
694 PRINT( "Found a joined relation\n" );
695 PRINT( "\tjoin_type: %d\n", relation->join_type );
696 PRINT( "\ttable_name: %s\n", relation->table_name );
697 relation->next = join_list;
698 join_list = relation;
700 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
701 "Unable to build join list for from relation id #%d", id ));
702 joinListFree( join_list );
706 if( !dbi_result_next_row( result ) )
712 int errnum = dbi_conn_error( state->dbhandle, &msg );
713 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
714 "Unable to query query.from_relation table for join list: #%d %s",
715 errnum, msg ? msg : "No description available" ));
723 @brief Free a list of FromRelations.
724 @param join_list Pointer to the first FromRelation in the list.
726 static void joinListFree( FromRelation* join_list ) {
728 FromRelation* temp = join_list->next;
729 fromRelationFree( join_list );
735 @brief Deallocate a FromRelation.
736 @param fr Pointer to the FromRelation to be freed.
738 Free the strings that the FromRelation owns. The FromRelation itself goes onto a
739 free list for potential reuse.
741 static void fromRelationFree( FromRelation* fr ) {
743 free( fr->table_name );
744 fr->table_name = NULL;
745 free( fr->class_name );
746 fr->class_name = NULL;
748 storedQFree( fr->subquery );
751 free( fr->table_alias );
752 fr->table_alias = NULL;
753 if( fr->on_clause ) {
754 expressionFree( fr->on_clause );
755 fr->on_clause = NULL;
757 joinListFree( fr->join_list );
758 fr->join_list = NULL;
760 fr->next = free_from_relation_list;
761 free_from_relation_list = fr;
765 static SelectItem* getSelectList( BuildSQLState* state, int query_id ) {
766 SelectItem* select_list = NULL;
768 // The ORDER BY is in descending order so that we can build the list by adding to
769 // the head, and it will wind up in the right order.
770 dbi_result result = dbi_conn_queryf( state->dbhandle,
771 "SELECT id, stored_query, seq_no, expression, column_alias, grouped_by "
772 "FROM query.select_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
774 if( dbi_result_first_row( result ) ) {
776 SelectItem* item = constructSelectItem( state, result );
778 PRINT( "Found a SELECT item\n" );
779 PRINT( "\tid: %d\n", item->id );
780 PRINT( "\tstored_query_id: %d\n", item->stored_query_id );
781 PRINT( "\tseq_no: %d\n", item->seq_no );
782 PRINT( "\tcolumn_alias: %s\n",
783 item->column_alias ? item->column_alias : "(none)" );
784 PRINT( "\tgrouped_by: %d\n", item->grouped_by );
786 item->next = select_list;
789 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
790 "Unable to build select list for query id #%d", query_id ));
791 selectListFree( select_list );
795 if( !dbi_result_next_row( result ) )
801 int errnum = dbi_conn_error( state->dbhandle, &msg );
802 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
803 "Unable to query query.select_list table: #%d %s",
804 errnum, msg ? msg : "No description available" ));
812 @brief Construct a SelectItem.
813 @param Pointer to the query-building context.
814 @param result Database cursor positioned at a row in query.select_item.
815 @return Pointer to a newly constructed SelectItem, if successful, or NULL if not.
817 The calling code is responsible for freeing the SelectItems by calling selectListFree().
819 static SelectItem* constructSelectItem( BuildSQLState* state, dbi_result result ) {
821 // Get the column values
822 int id = dbi_result_get_int_idx( result, 1 );
823 int stored_query_id = dbi_result_get_int_idx( result, 2 );
824 int seq_no = dbi_result_get_int_idx( result, 3 );
825 int expression_id = dbi_result_get_int_idx( result, 4 );
826 const char* column_alias = dbi_result_get_string_idx( result, 5 );
827 int grouped_by = oils_result_get_bool_idx( result, 6 );
829 // Construct an Expression
830 Expression* expression = getExpression( state, expression_id );
832 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
833 "Unable to fetch expression for id = %d", expression_id ));
838 // Allocate a SelectItem: from the free list if possible, from the heap if necessary
841 if( free_select_item_list ) {
842 sel = free_select_item_list;
843 free_select_item_list = free_select_item_list->next;
845 sel = safe_malloc( sizeof( SelectItem ) );
849 sel->stored_query_id = stored_query_id;
850 sel->seq_no = seq_no;
851 sel->expression = expression;
852 sel->column_alias = column_alias ? strdup( column_alias ) : NULL;
853 sel->grouped_by = grouped_by;
859 @brief Free a list of SelectItems.
860 @param sel Pointer to the first item in the list to be freed.
862 Free the column alias and expression owned by each item. Put the entire list into a free
865 static void selectListFree( SelectItem* sel ) {
867 return; // Nothing to free
869 SelectItem* first = sel;
871 free( sel->column_alias );
872 sel->column_alias = NULL;
873 expressionFree( sel->expression );
874 sel->expression = NULL;
876 if( NULL == sel->next ) {
877 sel->next = free_select_item_list;
883 // Transfer the entire list to the free list
884 free_select_item_list = first;
888 @brief Given the name of a bind variable, build a corresponding BindVar.
889 @param state Pointer to the query-building context.
890 @param name Name of the bind variable.
891 @return Pointer to the newly-built BindVar.
893 Since the same bind variable may appear multiple times, we load it only once for the
894 entire query, and reference the one copy wherever needed.
896 static BindVar* getBindVar( BuildSQLState* state, const char* name ) {
897 BindVar* bind = NULL;
898 if( state->bindvar_list ) {
899 bind = osrfHashGet( state->bindvar_list, name );
901 return bind; // Already loaded it...
904 // Load a BindVar from the Database.
905 dbi_result result = dbi_conn_queryf( state->dbhandle,
906 "SELECT name, type, description, default_value, label "
907 "FROM query.bind_variable WHERE name = \'%s\';", name );
909 if( dbi_result_first_row( result ) ) {
910 bind = constructBindVar( state, result );
912 PRINT( "Got a bind variable for %s\n", name );
914 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
915 "Unable to load bind variable \"%s\"", name ));
917 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
918 "No bind variable found with name \"%s\"", name ));
922 int errnum = dbi_conn_error( state->dbhandle, &msg );
923 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
924 "Unable to query query.bind_variable table for \"%s\": #%d %s",
925 name, errnum, msg ? msg : "No description available" ));
930 // Add the new bind variable to the list
931 if( !state->bindvar_list ) {
932 // Don't have a list yet? Start one.
933 state->bindvar_list = osrfNewHash();
934 osrfHashSetCallback( state->bindvar_list, bindVarFree );
936 osrfHashSet( state->bindvar_list, bind, name );
944 @brief Construct a BindVar to represent a bind variable.
945 @param Pointer to the query-building context.
946 @param result Database cursor positioned at a row in query.bind_variable.
947 @return Pointer to a newly constructed BindVar, if successful, or NULL if not.
949 The calling code is responsible for freeing the BindVar by calling bindVarFree().
951 static BindVar* constructBindVar( BuildSQLState* state, dbi_result result ) {
953 const char* name = dbi_result_get_string_idx( result, 1 );
955 const char* type_str = dbi_result_get_string_idx( result, 2 );
957 if( !strcmp( type_str, "string" ))
959 else if( !strcmp( type_str, "number" ))
961 else if( !strcmp( type_str, "string_list" ))
962 type = BIND_STR_LIST;
963 else if( !strcmp( type_str, "number_list" ))
964 type = BIND_NUM_LIST;;
966 const char* description = dbi_result_get_string_idx( result, 3 );
968 // The default value is encoded as JSON. Translate it into a jsonObject.
969 const char* default_value_str = dbi_result_get_string_idx( result, 4 );
970 jsonObject* default_value = NULL;
971 if( default_value_str ) {
972 default_value = jsonParse( default_value_str );
973 if( !default_value ) {
974 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
975 "Unable to parse JSON string for default value of bind variable \"%s\"",
982 const char* label = dbi_result_get_string_idx( result, 5 );
984 // Allocate a BindVar: from the free list if possible, from the heap if necessary
985 BindVar* bind = NULL;
986 if( free_bindvar_list ) {
987 bind = free_bindvar_list;
988 free_bindvar_list = free_bindvar_list->next;
990 bind = safe_malloc( sizeof( BindVar ) );
993 bind->name = strdup( name );
994 bind->label = strdup( label );
996 bind->description = strdup( description );
997 bind->default_value = default_value;
998 bind->actual_value = NULL;
1004 @brief Deallocate a BindVar.
1005 @param key Pointer to the bind variable name (not used).
1006 @param p Pointer to the BindVar to be deallocated, cast to a void pointer.
1008 Free the strings and jsonObjects owned by the BindVar, and put the BindVar itself into a
1011 This function is a callback installed in an osrfHash; hence the peculiar signature.
1013 static void bindVarFree( char* key, void* p ) {
1017 free( bind->label );
1018 free( bind->description );
1019 if( bind->default_value ) {
1020 jsonObjectFree( bind->default_value );
1021 bind->default_value = NULL;
1023 if( bind->actual_value ) {
1024 jsonObjectFree( bind->actual_value );
1025 bind->actual_value = NULL;
1028 // Prepend to free list
1029 bind->next = free_bindvar_list;
1030 free_bindvar_list = bind;
1035 @brief Given an id for a row in query.expression, build an Expression struct.
1036 @param Pointer to the query-building context.
1037 @param id ID of a row in query.expression.
1038 @return Pointer to a newly-created Expression if successful, or NULL if not.
1040 static Expression* getExpression( BuildSQLState* state, int id ) {
1042 // Check the stack to see if the current expression is nested inside itself. If it is,
1043 // then abort in order to avoid infinite recursion. If it isn't, then add it to the
1044 // stack. (Make sure to pop it off the stack before returning.)
1045 if( searchIdStack( state->expr_stack, id, NULL )) {
1046 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1047 "Infinite recursion detected; expression # %d is nested within itself", id ));
1051 push_id( &state->expr_stack, id, NULL );
1053 Expression* exp = NULL;
1054 dbi_result result = dbi_conn_queryf( state->dbhandle,
1055 "SELECT id, type, parenthesize, parent_expr, seq_no, literal, table_alias, column_name, "
1056 "left_operand, operator, right_operand, function_id, subquery, cast_type, negate, "
1058 "FROM query.expression WHERE id = %d;", id );
1060 if( dbi_result_first_row( result ) ) {
1061 exp = constructExpression( state, result );
1063 PRINT( "Got an expression\n" );
1064 PRINT( "\tid = %d\n", exp->id );
1065 PRINT( "\ttype = %d\n", exp->type );
1066 PRINT( "\tparenthesize = %d\n", exp->parenthesize );
1067 PRINT( "\tcolumn_name = %s\n", exp->column_name ? exp->column_name : "(none)" );
1069 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1070 "Unable to construct an Expression for id = %d", id ));
1074 int errnum = dbi_conn_error( state->dbhandle, &msg );
1075 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1076 "Unable to query query.expression table: #%d %s",
1077 errnum, msg ? msg : "No description available" ));
1081 pop_id( &state->expr_stack );
1086 @brief Construct an Expression.
1087 @param Pointer to the query-building context.
1088 @param result Database cursor positioned at a row in query.expression.
1089 @return Pointer to a newly constructed Expression, if successful, or NULL if not.
1091 The calling code is responsible for freeing the Expression by calling expressionFree().
1093 static Expression* constructExpression( BuildSQLState* state, dbi_result result ) {
1095 int id = dbi_result_get_int_idx( result, 1 );
1096 const char* type_str = dbi_result_get_string_idx( result, 2 );
1099 if( !strcmp( type_str, "xbet" ))
1101 else if( !strcmp( type_str, "xbind" ))
1103 else if( !strcmp( type_str, "xbool" ))
1105 else if( !strcmp( type_str, "xcase" ))
1107 else if( !strcmp( type_str, "xcast" ))
1109 else if( !strcmp( type_str, "xcol" ))
1111 else if( !strcmp( type_str, "xex" ))
1113 else if( !strcmp( type_str, "xfld" ))
1115 else if( !strcmp( type_str, "xfunc" ))
1116 type = EXP_FUNCTION;
1117 else if( !strcmp( type_str, "xin" ))
1119 else if( !strcmp( type_str, "xisnull" ))
1121 else if( !strcmp( type_str, "xnull" ))
1123 else if( !strcmp( type_str, "xnum" ))
1125 else if( !strcmp( type_str, "xop" ))
1126 type = EXP_OPERATOR;
1127 else if( !strcmp( type_str, "xser" ))
1129 else if( !strcmp( type_str, "xstr" ))
1131 else if( !strcmp( type_str, "xsubq" ))
1132 type = EXP_SUBQUERY;
1134 type = EXP_NULL; // shouldn't happen due to database constraint
1136 int parenthesize = oils_result_get_bool_idx( result, 3 );
1139 if( dbi_result_field_is_null_idx( result, 4 ))
1140 parent_expr_id = -1;
1142 parent_expr_id = dbi_result_get_int_idx( result, 4 );
1144 int seq_no = dbi_result_get_int_idx( result, 5 );
1145 const char* literal = dbi_result_get_string_idx( result, 6 );
1146 const char* table_alias = dbi_result_get_string_idx( result, 7 );
1147 const char* column_name = dbi_result_get_string_idx( result, 8 );
1149 int left_operand_id;
1150 if( dbi_result_field_is_null_idx( result, 9 ))
1151 left_operand_id = -1;
1153 left_operand_id = dbi_result_get_int_idx( result, 9 );
1155 const char* operator = dbi_result_get_string_idx( result, 10 );
1157 int right_operand_id;
1158 if( dbi_result_field_is_null_idx( result, 11 ))
1159 right_operand_id = -1;
1161 right_operand_id = dbi_result_get_int_idx( result, 11 );
1164 if( dbi_result_field_is_null_idx( result, 12 ))
1167 function_id = dbi_result_get_int_idx( result, 12 );
1170 if( dbi_result_field_is_null_idx( result, 13 ))
1173 subquery_id = dbi_result_get_int_idx( result, 13 );
1176 if( dbi_result_field_is_null_idx( result, 14 ))
1179 cast_type_id = dbi_result_get_int_idx( result, 14 );
1181 int negate = oils_result_get_bool_idx( result, 15 );
1182 const char* bind_variable = dbi_result_get_string_idx( result, 16 );
1184 Expression* left_operand = NULL;
1185 Expression* right_operand = NULL;
1186 StoredQ* subquery = NULL;
1187 BindVar* bind = NULL;
1188 Expression* subexp_list = NULL;
1190 if( EXP_BIND == type ) {
1191 if( bind_variable ) {
1192 // To do: Build a BindVar
1193 bind = getBindVar( state, bind_variable );
1195 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1196 "Unable to load bind variable \"%s\" for expression # %d",
1197 bind_variable, id ));
1201 PRINT( "\tBind variable is \"%s\"\n", bind_variable );
1203 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1204 "No variable specified for bind variable expression # %d",
1205 bind_variable, id ));
1209 } else if( EXP_OPERATOR == type ) {
1210 // Load left and/or right operands
1211 if( -1 == left_operand_id && -1 == right_operand_id ) {
1212 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1213 "Expression # %d is an operator with no operands", id ));
1218 if( left_operand_id != -1 ) {
1219 left_operand = getExpression( state, left_operand_id );
1220 if( !left_operand ) {
1221 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1222 "Unable to get left operand in expression # %d", id ));
1228 if( right_operand_id != -1 ) {
1229 right_operand = getExpression( state, right_operand_id );
1230 if( !right_operand ) {
1231 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1232 "Unable to get right operand in expression # %d", id ));
1234 expressionFree( left_operand );
1238 } else if( EXP_IN == type ) {
1239 if( -1 == left_operand_id ) {
1240 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1241 "IN condition has no left operand in expression # %d", id ));
1245 left_operand = getExpression( state, left_operand_id );
1246 if( !left_operand ) {
1247 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1248 "Unable to get left operand for IN condition in expression # %d", id ));
1254 if( -1 == subquery_id ) {
1255 // Load an IN list of subexpressions
1256 subexp_list = getExpressionList( state, id );
1257 if( state->error ) {
1258 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1259 "Unable to get subexpressions for IN list" ));
1261 } else if( !subexp_list ) {
1262 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1263 "IN list is empty in expression # %d", id ));
1269 subquery = getStoredQuery( state, subquery_id );
1271 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1272 "Unable to load subquery for IN expression # %d", id ));
1277 } else if( EXP_ISNULL == type ) {
1278 if( -1 == left_operand_id ) {
1279 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1280 "Expression # %d IS NULL has no left operand", id ));
1285 if( left_operand_id != -1 ) {
1286 left_operand = getExpression( state, left_operand_id );
1287 if( !left_operand ) {
1288 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1289 "Unable to get left operand in expression # %d", id ));
1294 } else if( EXP_EXIST == type ) {
1295 if( -1 == subquery_id ) {
1296 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1297 "Internal error: No subquery found for EXIST expression # %d", id ));
1301 subquery = getStoredQuery( state, subquery_id );
1303 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1304 "Unable to load subquery for EXIST expression # %d", id ));
1309 } else if( EXP_SERIES == type ) {
1310 subexp_list = getExpressionList( state, id );
1311 if( state->error ) {
1312 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1313 "Unable to get subexpressions for expression series using operator \"%s\"",
1314 operator ? operator : "," ));
1316 } else if( !subexp_list ) {
1317 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1318 "Series expression is empty in expression # %d", id ));
1323 } else if( EXP_SUBQUERY == type ) {
1324 if( -1 == subquery_id ) {
1325 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1326 "Subquery expression # %d has no query id", id ));
1330 // Load a subquery, if there is one
1331 subquery = getStoredQuery( state, subquery_id );
1333 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1334 "Unable to load subquery for expression # %d", id ));
1338 if( subquery->select_list && subquery->select_list->next ) {
1339 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1340 "Subquery # %d as expression returns more than one column", subquery_id ));
1344 PRINT( "\tExpression is subquery %d\n", subquery_id );
1348 // Allocate an Expression: from the free list if possible, from the heap if necessary
1349 Expression* exp = NULL;
1350 if( free_expression_list ) {
1351 exp = free_expression_list;
1352 free_expression_list = free_expression_list->next;
1354 exp = safe_malloc( sizeof( Expression ) );
1356 // Populate the Expression
1360 exp->parenthesize = parenthesize;
1361 exp->parent_expr_id = parent_expr_id;
1362 exp->seq_no = seq_no;
1363 exp->literal = literal ? strdup( literal ) : NULL;
1364 exp->table_alias = table_alias ? strdup( table_alias ) : NULL;
1365 exp->column_name = column_name ? strdup( column_name ) : NULL;
1366 exp->left_operand = left_operand;
1367 exp->op = operator ? strdup( operator ) : NULL;
1368 exp->right_operand = right_operand;
1369 exp->function_id = function_id;
1370 exp->subquery_id = subquery_id;
1371 exp->subquery = subquery;
1372 exp->cast_type_id = subquery_id;
1373 exp->negate = negate;
1375 exp->subexp_list = subexp_list;
1381 @brief Free all the Expressions in a linked list of Expressions.
1382 @param exp Pointer to the first Expression in the list.
1384 static void expressionListFree( Expression* exp ) {
1386 Expression* next = exp->next;
1387 expressionFree( exp );
1393 @brief Deallocate an Expression.
1394 @param exp Pointer to the Expression to be deallocated.
1396 Free the strings owned by the Expression. Put the Expression itself, and any
1397 subexpressions that it owns, into a free list.
1399 static void expressionFree( Expression* exp ) {
1401 free( exp->literal );
1402 exp->literal = NULL;
1403 free( exp->table_alias );
1404 exp->table_alias = NULL;
1405 free( exp->column_name );
1406 exp->column_name = NULL;
1407 if( exp->left_operand ) {
1408 expressionFree( exp->left_operand );
1409 exp->left_operand = NULL;
1413 if( exp->right_operand ) {
1414 expressionFree( exp->right_operand );
1415 exp->right_operand = NULL;
1417 if( exp->subquery ) {
1418 storedQFree( exp->subquery );
1419 exp->subquery = NULL;
1422 // We don't free the bind member here because the Expression doesn't own it;
1423 // the bindvar_list hash owns it, so that multiple Expressions can reference it.
1425 if( exp->subexp_list ) {
1426 // Free the linked list of subexpressions
1427 expressionListFree( exp->subexp_list );
1428 exp->subexp_list = NULL;
1431 // Prepend to the free list
1432 exp->next = free_expression_list;
1433 free_expression_list = exp;
1438 @brief Build a list of subexpressions.
1439 @param state Pointer to the query-building context.
1440 @param id ID of the parent Expression.
1441 @return A pointer to the first in a linked list of Expressions, if there are any; or
1442 NULL if there aren't any, or in case of an error.
1444 static Expression* getExpressionList( BuildSQLState* state, int id ) {
1445 Expression* exp_list = NULL;
1447 // The ORDER BY is in descending order so that we can build the list by adding to
1448 // the head, and it will wind up in the right order.
1449 dbi_result result = dbi_conn_queryf( state->dbhandle,
1450 "SELECT id, type, parenthesize, parent_expr, seq_no, literal, table_alias, column_name, "
1451 "left_operand, operator, right_operand, function_id, subquery, cast_type, negate, "
1453 "FROM query.expression WHERE parent_expr = %d "
1454 "ORDER BY seq_no desc;", id );
1457 if( dbi_result_first_row( result ) ) {
1459 Expression* exp = constructExpression( state, result );
1461 PRINT( "Found a subexpression\n" );
1462 exp->next = exp_list;
1465 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1466 "Unable to build subexpression list for expression id #%d", id ));
1467 expressionListFree( exp_list );
1471 if( !dbi_result_next_row( result ) )
1477 int errnum = dbi_conn_error( state->dbhandle, &msg );
1478 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1479 "Unable to query query.expression table for expression list: #%d %s",
1480 errnum, msg ? msg : "No description available" ));
1488 @brief Build a list of ORDER BY items as a linked list of OrderItems.
1489 @param state Pointer to the query-building context.
1490 @param query_id ID for the query to which the ORDER BY belongs.
1491 @return Pointer to the first node in a linked list of OrderItems.
1493 The calling code is responsible for freeing the list by calling orderItemListFree().
1495 static OrderItem* getOrderByList( BuildSQLState* state, int query_id ) {
1496 OrderItem* ord_list = NULL;
1498 // The ORDER BY is in descending order so that we can build the list by adding to
1499 // the head, and it will wind up in the right order.
1500 dbi_result result = dbi_conn_queryf( state->dbhandle,
1501 "SELECT id, stored_query, seq_no, expression "
1502 "FROM query.order_by_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
1504 if( dbi_result_first_row( result ) ) {
1506 OrderItem* item = constructOrderItem( state, result );
1508 PRINT( "Found an ORDER BY item\n" );
1510 item->next = ord_list;
1513 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1514 "Unable to build ORDER BY item for query id #%d", query_id ));
1515 orderItemListFree( ord_list );
1519 if( !dbi_result_next_row( result ) )
1525 int errnum = dbi_conn_error( state->dbhandle, &msg );
1526 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1527 "Unable to query query.order_by_list table: #%d %s",
1528 errnum, msg ? msg : "No description available" ));
1536 @brief Construct an OrderItem.
1537 @param Pointer to the query-building context.
1538 @param result Database cursor positioned at a row in query.order_by_item.
1539 @return Pointer to a newly constructed OrderItem, if successful, or NULL if not.
1541 The calling code is responsible for freeing the OrderItems by calling orderItemListFree().
1543 static OrderItem* constructOrderItem( BuildSQLState* state, dbi_result result ) {
1544 int id = dbi_result_get_int_idx( result, 1 );
1545 int stored_query_id = dbi_result_get_int_idx( result, 2 );
1546 int seq_no = dbi_result_get_int_idx( result, 3 );
1547 int expression_id = dbi_result_get_int_idx( result, 4 );
1548 // Allocate a SelectItem: from the free list if possible, from the heap if necessary
1550 // Construct an Expression
1551 Expression* expression = getExpression( state, expression_id );
1553 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1554 "Unable to fetch ORDER BY expression for id = %d", expression_id ));
1559 // Allocate an OrderItem; from the free list if possible, or from the heap if necessary.
1561 if( free_order_item_list ) {
1562 ord = free_order_item_list;
1563 free_order_item_list = free_order_item_list->next;
1565 ord = safe_malloc( sizeof( OrderItem ));
1569 ord->stored_query_id = stored_query_id;
1570 ord->seq_no = seq_no;
1571 ord->expression = expression;
1577 @brief Deallocate a linked list of OrderItems.
1578 @param exp Pointer to the first OrderItem in the list to be deallocated.
1580 Deallocate the memory owned by the OrderItems. Put the items themselves into a free list.
1582 static void orderItemListFree( OrderItem* ord ) {
1584 return; // Nothing to free
1586 OrderItem* first = ord;
1588 expressionFree( ord->expression );
1589 ord->expression = NULL;
1591 if( NULL == ord->next ) {
1592 ord->next = free_order_item_list;
1598 // Transfer the entire list to the free list
1599 free_order_item_list = first;
1603 @brief Build a list of column names for a specified query.
1604 @param state Pointer to the query-building context.
1605 @param query Pointer to the specified query.
1606 @return Pointer to a newly-allocated JSON_ARRAY of column names.
1608 In the resulting array, each entry is either a JSON_STRING or (when no column name is
1609 available) a JSON_NULL.
1611 The calling code is responsible for freeing the list by calling jsonObjectFree().
1613 jsonObject* oilsGetColNames( BuildSQLState* state, StoredQ* query ) {
1614 if( !state || !query )
1617 // Save the outermost query id for possible use in an error message
1620 while( query->type != QT_SELECT ) {
1621 // If the query is a UNION, INTERSECT, or EXCEPT, there must be a SELECT in
1622 // there somewhere. Find the first one, and use the SELECT list from that.
1623 QSeq* child_list = query->child_list;
1628 query = child_list->child_query;
1633 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1634 "Unable to find first SELECT in query # %d", id ));
1638 // Get the SELECT list for the first SELECT
1639 SelectItem* col = query->select_list;
1642 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1643 "First SELECT in query # %d has empty SELECT list", id ));
1647 jsonObject* col_list = jsonNewObjectType( JSON_ARRAY );
1649 // Traverse the list, adding an entry for each
1651 const char* alias = NULL;
1652 if( col->column_alias )
1653 alias = col->column_alias;
1655 Expression* expression = col->expression;
1656 if( expression && EXP_COLUMN == expression->type && expression->column_name )
1657 alias = expression->column_name;
1660 jsonObjectPush( col_list, jsonNewObject( alias ) );
1668 @brief Push an IdNode onto a stack of IdNodes.
1669 @param stack Pointer to the stack.
1670 @param id Id of the new node.
1671 @param alias Alias, if any, of the new node.
1673 static void push_id( IdNode** stack, int id, const char* alias ) {
1676 // Allocate a node; from the free list if possible, from the heap if necessary.
1677 IdNode* node = NULL;
1678 if( free_id_node_list ) {
1679 node = free_id_node_list;
1680 free_id_node_list = free_id_node_list->next;
1682 node = safe_malloc( sizeof( IdNode ));
1685 node->next = *stack;
1688 node->alias = strdup( alias );
1698 @brief Remove the node at the top of an IdNode stack.
1699 @param stack Pointer to the IdNode stack.
1701 void pop_id( IdNode** stack ) {
1703 IdNode* node = *stack;
1704 *stack = node->next;
1707 free( node->alias );
1711 node->next = free_id_node_list;
1712 free_id_node_list = node;
1717 @brief Search a stack of IDs for a match by either ID or, optionally, by alias.
1718 @param stack Pointer to the stack.
1719 @param id The id to search for.
1720 @param alias (Optional) the alias to search for.
1721 @return A pointer to the matching node if one is found, or NULL if not.
1723 This search is used to detect cases where a query, expression, or FROM clause is nested
1724 inside itself, in order to avoid infinite recursion; or in order to avoid conflicting
1725 table references in a FROM clause.
1727 static const IdNode* searchIdStack( const IdNode* stack, int id, const char* alias ) {
1729 const IdNode* node = stack;
1731 if( node->id == id )
1732 return node; // Matched on id
1733 else if( alias && node->alias && !strcmp( alias, node->alias ))
1734 return node; // Matched on alias
1739 return NULL; // No match found
1743 @brief Free up any resources held by the StoredQ module.
1745 void storedQCleanup( void ) {
1747 // Free all the nodes in the free state list
1748 StoredQ* sq = free_storedq_list;
1750 free_storedq_list = sq->next;
1752 sq = free_storedq_list;
1755 // Free all the nodes in the free from_relation list
1756 FromRelation* fr = free_from_relation_list;
1758 free_from_relation_list = fr->next;
1760 fr = free_from_relation_list;
1763 // Free all the nodes in the free expression list
1764 Expression* exp = free_expression_list;
1766 free_expression_list = exp->next;
1768 exp = free_expression_list;
1771 // Free all the nodes in the free select item list
1772 SelectItem* sel = free_select_item_list;
1774 free_select_item_list = sel->next;
1776 sel = free_select_item_list;
1779 // Free all the nodes in the free select item list
1780 IdNode* node = free_id_node_list;
1782 free_id_node_list = node->next;
1784 node = free_id_node_list;
1787 // Free all the nodes in the free query sequence list
1788 QSeq* seq = free_qseq_list;
1790 free_qseq_list = seq->next;
1792 seq = free_qseq_list;
1795 // Free all the nodes in the free order item list
1796 OrderItem* ord = free_order_item_list;
1798 free_order_item_list = ord->next;
1800 ord = free_order_item_list;
1803 // Free all the nodes in the bind variable free list
1804 BindVar* bind = free_bindvar_list;
1806 free_bindvar_list = bind->next;
1808 bind = free_bindvar_list;
1813 @brief Return a boolean value from a database result.
1814 @param result The database result.
1815 @param i Index of the column in the result, starting with 1 );
1816 @return 1 if true, or 0 for false.
1818 Null values and error conditions are interpreted as FALSE.
1820 static int oils_result_get_bool_idx( dbi_result result, int i ) {
1822 const char* str = dbi_result_get_string_idx( result, i );
1823 return (str && *str == 't' ) ? 1 : 0;
1829 @brief Enable verbose messages.
1831 The messages are written to standard output, which for a server is /dev/null. Hence this
1832 option is useful only for a non-server. It is intended only as a convenience for
1833 development and debugging.
1835 void oilsStoredQSetVerbose( void ) {