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_OPERATOR == type ) {
1191 // Load left and/or right operands
1192 if( -1 == left_operand_id && -1 == right_operand_id ) {
1193 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1194 "Expression # %d is an operator with no operands", id ));
1199 if( left_operand_id != -1 ) {
1200 left_operand = getExpression( state, left_operand_id );
1201 if( !left_operand ) {
1202 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1203 "Unable to get left operand in expression # %d", id ));
1209 if( right_operand_id != -1 ) {
1210 right_operand = getExpression( state, right_operand_id );
1211 if( !right_operand ) {
1212 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1213 "Unable to get right operand in expression # %d", id ));
1215 expressionFree( left_operand );
1219 } else if( EXP_IN == type ) {
1220 if( -1 == left_operand_id ) {
1221 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1222 "IN condition has no left operand in expression # %d", id ));
1226 left_operand = getExpression( state, left_operand_id );
1227 if( !left_operand ) {
1228 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1229 "Unable to get left operand for IN condition in expression # %d", id ));
1235 if( -1 == subquery_id ) {
1236 // To do: load IN list of subexpressions
1237 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1238 "IN lists not yet supported for expression # %d", id ));
1242 subquery = getStoredQuery( state, subquery_id );
1244 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1245 "Unable to load subquery for IN expression # %d", id ));
1250 } else if( EXP_ISNULL == type ) {
1251 if( -1 == left_operand_id ) {
1252 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1253 "Expression # %d IS NULL has no left operand", id ));
1258 if( left_operand_id != -1 ) {
1259 left_operand = getExpression( state, left_operand_id );
1260 if( !left_operand ) {
1261 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1262 "Unable to get left operand in expression # %d", id ));
1267 } else if( EXP_EXIST == type ) {
1268 if( -1 == subquery_id ) {
1269 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1270 "Internal error: No subquery found for EXIST expression # %d", id ));
1274 subquery = getStoredQuery( state, subquery_id );
1276 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1277 "Unable to load subquery for EXIST expression # %d", id ));
1282 } else if( EXP_SERIES == type ) {
1283 subexp_list = getExpressionList( state, id );
1284 if( state->error ) {
1285 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1286 "Unable to get subexpressions for expression series using operator \"%s\"",
1287 operator ? operator : "," ));
1290 } else if( EXP_SUBQUERY == type ) {
1291 if( -1 == subquery_id ) {
1292 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1293 "Subquery expression # %d has no query id", id ));
1297 // Load a subquery, if there is one
1298 subquery = getStoredQuery( state, subquery_id );
1300 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1301 "Unable to load subquery for expression # %d", id ));
1305 if( subquery->select_list && subquery->select_list->next ) {
1306 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1307 "Subquery # %d as expression returns more than one column", subquery_id ));
1311 PRINT( "\tExpression is subquery %d\n", subquery_id );
1313 } else if( EXP_BIND == type ) {
1314 if( bind_variable ) {
1315 // To do: Build a BindVar
1316 bind = getBindVar( state, bind_variable );
1318 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1319 "Unable to load bind variable \"%s\" for expression # %d",
1320 bind_variable, id ));
1324 PRINT( "\tBind variable is \"%s\"\n", bind_variable );
1326 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1327 "No variable specified for bind variable expression # %d",
1328 bind_variable, id ));
1334 // Allocate an Expression: from the free list if possible, from the heap if necessary
1335 Expression* exp = NULL;
1336 if( free_expression_list ) {
1337 exp = free_expression_list;
1338 free_expression_list = free_expression_list->next;
1340 exp = safe_malloc( sizeof( Expression ) );
1342 // Populate the Expression
1346 exp->parenthesize = parenthesize;
1347 exp->parent_expr_id = parent_expr_id;
1348 exp->seq_no = seq_no;
1349 exp->literal = literal ? strdup( literal ) : NULL;
1350 exp->table_alias = table_alias ? strdup( table_alias ) : NULL;
1351 exp->column_name = column_name ? strdup( column_name ) : NULL;
1352 exp->left_operand = left_operand;
1353 exp->op = operator ? strdup( operator ) : NULL;
1354 exp->right_operand = right_operand;
1355 exp->function_id = function_id;
1356 exp->subquery_id = subquery_id;
1357 exp->subquery = subquery;
1358 exp->cast_type_id = subquery_id;
1359 exp->negate = negate;
1361 exp->subexp_list = subexp_list;
1367 @brief Free all the Expressions in a linked list of Expressions.
1368 @param exp Pointer to the first Expression in the list.
1370 static void expressionListFree( Expression* exp ) {
1372 Expression* next = exp->next;
1373 expressionFree( exp );
1379 @brief Deallocate an Expression.
1380 @param exp Pointer to the Expression to be deallocated.
1382 Free the strings owned by the Expression. Put the Expression itself, and any
1383 subexpressions that it owns, into a free list.
1385 static void expressionFree( Expression* exp ) {
1387 free( exp->literal );
1388 exp->literal = NULL;
1389 free( exp->table_alias );
1390 exp->table_alias = NULL;
1391 free( exp->column_name );
1392 exp->column_name = NULL;
1393 if( exp->left_operand ) {
1394 expressionFree( exp->left_operand );
1395 exp->left_operand = NULL;
1399 if( exp->right_operand ) {
1400 expressionFree( exp->right_operand );
1401 exp->right_operand = NULL;
1403 if( exp->subquery ) {
1404 storedQFree( exp->subquery );
1405 exp->subquery = NULL;
1408 // We don't free the bind member here because the Expression doesn't own it;
1409 // the bindvar_list hash owns it, so that multiple Expressions can reference it.
1411 if( exp->subexp_list ) {
1412 // Free the linked list of subexpressions
1413 expressionListFree( exp->subexp_list );
1414 exp->subexp_list = NULL;
1417 // Prepend to the free list
1418 exp->next = free_expression_list;
1419 free_expression_list = exp;
1424 @brief Build a list of subexpressions.
1425 @param state Pointer to the query-building context.
1426 @param id ID of the parent Expression.
1427 @return A pointer to the first in a linked list of Expressions, if there are any; or
1428 NULL if there aren't any, or in case of an error.
1430 static Expression* getExpressionList( BuildSQLState* state, int id ) {
1431 Expression* exp_list = NULL;
1433 // The ORDER BY is in descending order so that we can build the list by adding to
1434 // the head, and it will wind up in the right order.
1435 dbi_result result = dbi_conn_queryf( state->dbhandle,
1436 "SELECT id, type, parenthesize, parent_expr, seq_no, literal, table_alias, column_name, "
1437 "left_operand, operator, right_operand, function_id, subquery, cast_type, negate, "
1439 "FROM query.expression WHERE parent_expr = %d "
1440 "ORDER BY seq_no desc;", id );
1443 if( dbi_result_first_row( result ) ) {
1445 Expression* exp = constructExpression( state, result );
1447 PRINT( "Found a subexpression\n" );
1448 exp->next = exp_list;
1451 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1452 "Unable to build subexpression list for expression id #%d", id ));
1453 expressionListFree( exp_list );
1457 if( !dbi_result_next_row( result ) )
1463 int errnum = dbi_conn_error( state->dbhandle, &msg );
1464 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1465 "Unable to query query.expression table for expression list: #%d %s",
1466 errnum, msg ? msg : "No description available" ));
1474 @brief Build a list of ORDER BY items as a linked list of OrderItems.
1475 @param state Pointer to the query-building context.
1476 @param query_id ID for the query to which the ORDER BY belongs.
1477 @return Pointer to the first node in a linked list of OrderItems.
1479 The calling code is responsible for freeing the list by calling orderItemListFree().
1481 static OrderItem* getOrderByList( BuildSQLState* state, int query_id ) {
1482 OrderItem* ord_list = NULL;
1484 // The ORDER BY is in descending order so that we can build the list by adding to
1485 // the head, and it will wind up in the right order.
1486 dbi_result result = dbi_conn_queryf( state->dbhandle,
1487 "SELECT id, stored_query, seq_no, expression "
1488 "FROM query.order_by_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
1490 if( dbi_result_first_row( result ) ) {
1492 OrderItem* item = constructOrderItem( state, result );
1494 PRINT( "Found an ORDER BY item\n" );
1496 item->next = ord_list;
1499 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1500 "Unable to build ORDER BY item for query id #%d", query_id ));
1501 orderItemListFree( ord_list );
1505 if( !dbi_result_next_row( result ) )
1511 int errnum = dbi_conn_error( state->dbhandle, &msg );
1512 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1513 "Unable to query query.order_by_list table: #%d %s",
1514 errnum, msg ? msg : "No description available" ));
1522 @brief Construct an OrderItem.
1523 @param Pointer to the query-building context.
1524 @param result Database cursor positioned at a row in query.order_by_item.
1525 @return Pointer to a newly constructed OrderItem, if successful, or NULL if not.
1527 The calling code is responsible for freeing the OrderItems by calling orderItemListFree().
1529 static OrderItem* constructOrderItem( BuildSQLState* state, dbi_result result ) {
1530 int id = dbi_result_get_int_idx( result, 1 );
1531 int stored_query_id = dbi_result_get_int_idx( result, 2 );
1532 int seq_no = dbi_result_get_int_idx( result, 3 );
1533 int expression_id = dbi_result_get_int_idx( result, 4 );
1534 // Allocate a SelectItem: from the free list if possible, from the heap if necessary
1536 // Construct an Expression
1537 Expression* expression = getExpression( state, expression_id );
1539 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1540 "Unable to fetch ORDER BY expression for id = %d", expression_id ));
1545 // Allocate an OrderItem; from the free list if possible, or from the heap if necessary.
1547 if( free_order_item_list ) {
1548 ord = free_order_item_list;
1549 free_order_item_list = free_order_item_list->next;
1551 ord = safe_malloc( sizeof( OrderItem ));
1555 ord->stored_query_id = stored_query_id;
1556 ord->seq_no = seq_no;
1557 ord->expression = expression;
1563 @brief Deallocate a linked list of OrderItems.
1564 @param exp Pointer to the first OrderItem in the list to be deallocated.
1566 Deallocate the memory owned by the OrderItems. Put the items themselves into a free list.
1568 static void orderItemListFree( OrderItem* ord ) {
1570 return; // Nothing to free
1572 OrderItem* first = ord;
1574 expressionFree( ord->expression );
1575 ord->expression = NULL;
1577 if( NULL == ord->next ) {
1578 ord->next = free_order_item_list;
1584 // Transfer the entire list to the free list
1585 free_order_item_list = first;
1589 @brief Build a list of column names for a specified query.
1590 @param state Pointer to the query-building context.
1591 @param query Pointer to the specified query.
1592 @return Pointer to a newly-allocated JSON_ARRAY of column names.
1594 In the resulting array, each entry is either a JSON_STRING or (when no column name is
1595 available) a JSON_NULL.
1597 The calling code is responsible for freeing the list by calling jsonObjectFree().
1599 jsonObject* oilsGetColNames( BuildSQLState* state, StoredQ* query ) {
1600 if( !state || !query )
1603 // Save the outermost query id for possible use in an error message
1606 while( query->type != QT_SELECT ) {
1607 // If the query is a UNION, INTERSECT, or EXCEPT, there must be a SELECT in
1608 // there somewhere. Find the first one, and use the SELECT list from that.
1609 QSeq* child_list = query->child_list;
1614 query = child_list->child_query;
1619 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1620 "Unable to find first SELECT in query # %d", id ));
1624 // Get the SELECT list for the first SELECT
1625 SelectItem* col = query->select_list;
1628 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1629 "First SELECT in query # %d has empty SELECT list", id ));
1633 jsonObject* col_list = jsonNewObjectType( JSON_ARRAY );
1635 // Traverse the list, adding an entry for each
1637 const char* alias = NULL;
1638 if( col->column_alias )
1639 alias = col->column_alias;
1641 Expression* expression = col->expression;
1642 if( expression && EXP_COLUMN == expression->type && expression->column_name )
1643 alias = expression->column_name;
1646 jsonObjectPush( col_list, jsonNewObject( alias ) );
1654 @brief Push an IdNode onto a stack of IdNodes.
1655 @param stack Pointer to the stack.
1656 @param id Id of the new node.
1657 @param alias Alias, if any, of the new node.
1659 static void push_id( IdNode** stack, int id, const char* alias ) {
1662 // Allocate a node; from the free list if possible, from the heap if necessary.
1663 IdNode* node = NULL;
1664 if( free_id_node_list ) {
1665 node = free_id_node_list;
1666 free_id_node_list = free_id_node_list->next;
1668 node = safe_malloc( sizeof( IdNode ));
1671 node->next = *stack;
1674 node->alias = strdup( alias );
1684 @brief Remove the node at the top of an IdNode stack.
1685 @param stack Pointer to the IdNode stack.
1687 void pop_id( IdNode** stack ) {
1689 IdNode* node = *stack;
1690 *stack = node->next;
1693 free( node->alias );
1697 node->next = free_id_node_list;
1698 free_id_node_list = node;
1703 @brief Search a stack of IDs for a match by either ID or, optionally, by alias.
1704 @param stack Pointer to the stack.
1705 @param id The id to search for.
1706 @param alias (Optional) the alias to search for.
1707 @return A pointer to the matching node if one is found, or NULL if not.
1709 This search is used to detect cases where a query, expression, or FROM clause is nested
1710 inside itself, in order to avoid infinite recursion; or in order to avoid conflicting
1711 table references in a FROM clause.
1713 static const IdNode* searchIdStack( const IdNode* stack, int id, const char* alias ) {
1715 const IdNode* node = stack;
1717 if( node->id == id )
1718 return node; // Matched on id
1719 else if( alias && node->alias && !strcmp( alias, node->alias ))
1720 return node; // Matched on alias
1725 return NULL; // No match found
1729 @brief Free up any resources held by the StoredQ module.
1731 void storedQCleanup( void ) {
1733 // Free all the nodes in the free state list
1734 StoredQ* sq = free_storedq_list;
1736 free_storedq_list = sq->next;
1738 sq = free_storedq_list;
1741 // Free all the nodes in the free from_relation list
1742 FromRelation* fr = free_from_relation_list;
1744 free_from_relation_list = fr->next;
1746 fr = free_from_relation_list;
1749 // Free all the nodes in the free expression list
1750 Expression* exp = free_expression_list;
1752 free_expression_list = exp->next;
1754 exp = free_expression_list;
1757 // Free all the nodes in the free select item list
1758 SelectItem* sel = free_select_item_list;
1760 free_select_item_list = sel->next;
1762 sel = free_select_item_list;
1765 // Free all the nodes in the free select item list
1766 IdNode* node = free_id_node_list;
1768 free_id_node_list = node->next;
1770 node = free_id_node_list;
1773 // Free all the nodes in the free query sequence list
1774 QSeq* seq = free_qseq_list;
1776 free_qseq_list = seq->next;
1778 seq = free_qseq_list;
1781 // Free all the nodes in the free order item list
1782 OrderItem* ord = free_order_item_list;
1784 free_order_item_list = ord->next;
1786 ord = free_order_item_list;
1789 // Free all the nodes in the bind variable free list
1790 BindVar* bind = free_bindvar_list;
1792 free_bindvar_list = bind->next;
1794 bind = free_bindvar_list;
1799 @brief Return a boolean value from a database result.
1800 @param result The database result.
1801 @param i Index of the column in the result, starting with 1 );
1802 @return 1 if true, or 0 for false.
1804 Null values and error conditions are interpreted as FALSE.
1806 static int oils_result_get_bool_idx( dbi_result result, int i ) {
1808 const char* str = dbi_result_get_string_idx( result, i );
1809 return (str && *str == 't' ) ? 1 : 0;
1815 @brief Enable verbose messages.
1817 The messages are written to standard output, which for a server is /dev/null. Hence this
1818 option is useful only for a non-server. It is intended only as a convenience for
1819 development and debugging.
1821 void oilsStoredQSetVerbose( void ) {