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;
454 @brief Given an id from query.from_relation, load a FromRelation.
455 @param state Pointer the the query-building context.
456 @param id Id of the FromRelation.
457 @return Pointer to a newly-created FromRelation if successful, or NULL if not.
459 The calling code is responsible for freeing the new FromRelation by calling
462 static FromRelation* getFromRelation( BuildSQLState* state, int id ) {
463 FromRelation* fr = NULL;
464 dbi_result result = dbi_conn_queryf( state->dbhandle,
465 "SELECT id, type, table_name, class_name, subquery, function_call, "
466 "table_alias, parent_relation, seq_no, join_type, on_clause "
467 "FROM query.from_relation WHERE id = %d;", id );
469 if( dbi_result_first_row( result ) ) {
470 fr = constructFromRelation( state, result );
472 PRINT( "Got a from_relation row\n" );
473 PRINT( "\tid: %d\n", fr->id );
474 PRINT( "\ttype: %d\n", (int) fr->type );
475 PRINT( "\ttable_name: %s\n", fr->table_name ? fr->table_name : "(none)" );
476 PRINT( "\tclass_name: %s\n", fr->class_name ? fr->class_name : "(none)" );
477 PRINT( "\tsubquery_id: %d\n", fr->subquery_id );
478 PRINT( "\tfunction_call_id: %d\n", fr->function_call_id );
479 PRINT( "\ttable_alias: %s\n", fr->table_alias ? fr->table_alias : "(none)" );
480 PRINT( "\tparent_relation_id: %d\n", fr->parent_relation_id );
481 PRINT( "\tseq_no: %d\n", fr->seq_no );
482 PRINT( "\tjoin_type = %d\n", fr->join_type );
483 // Check the stack to see if the current from clause is nested inside itself.
484 // If it is, then abort in order to avoid infinite recursion. If it isn't,
485 // then add it to the stack. (Make sure to pop it off the stack before
487 const char* effective_alias = fr->table_alias;
488 if( !effective_alias )
489 effective_alias = fr->class_name;
490 const IdNode* node = searchIdStack( state->from_stack, id, effective_alias );
493 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
494 "Infinite recursion detected; from clause # %d is nested "
495 "within itself", id ));
497 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
498 "Conflicting nested table aliases \"%s\" in from clause # %d",
499 effective_alias, node->id ));
503 push_id( &state->from_stack, id, effective_alias );
505 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
506 "Unable to build a FromRelation for id = %d", id ));
508 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
509 "FROM relation not found for id = %d", id ));
511 dbi_result_free( result );
514 int errnum = dbi_conn_error( state->dbhandle, &msg );
515 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
516 "Unable to query query.from_relation table: #%d %s",
517 errnum, msg ? msg : "No description available" ));
522 pop_id( &state->from_stack );
528 @brief Construct a FromRelation.
529 @param Pointer to the query-building context.
530 @param result Database cursor positioned at a row in query.from_relation.
531 @return Pointer to a newly constructed FromRelation, if successful, or NULL if not.
533 The calling code is responsible for freeing FromRelations by calling joinListFree().
535 static FromRelation* constructFromRelation( BuildSQLState* state, dbi_result result ) {
536 // Get the column values from the result
537 int id = dbi_result_get_int_idx( result, 1 );
538 const char* type_str = dbi_result_get_string_idx( result, 2 );
540 FromRelationType type;
541 if( !strcmp( type_str, "RELATION" ))
543 else if( !strcmp( type_str, "SUBQUERY" ))
545 else if( !strcmp( type_str, "FUNCTION" ))
548 type = FRT_RELATION; // shouldn't happen due to database constraint
550 const char* table_name = dbi_result_get_string_idx( result, 3 );
551 const char* class_name = dbi_result_get_string_idx( result, 4 );
554 if( dbi_result_field_is_null_idx( result, 5 ) )
557 subquery_id = dbi_result_get_int_idx( result, 5 );
559 int function_call_id;
560 if( dbi_result_field_is_null_idx( result, 6 ) )
561 function_call_id = -1;
563 function_call_id = dbi_result_get_int_idx( result, 6 );
565 const char* table_alias = dbi_result_get_string_idx( result, 7 );
567 int parent_relation_id;
568 if( dbi_result_field_is_null_idx( result, 8 ) )
569 parent_relation_id = -1;
571 parent_relation_id = dbi_result_get_int_idx( result, 8 );
573 int seq_no = dbi_result_get_int_idx( result, 9 );
576 const char* join_type_str = dbi_result_get_string_idx( result, 10 );
579 else if( !strcmp( join_type_str, "INNER" ) )
580 join_type = JT_INNER;
581 else if( !strcmp( join_type_str, "LEFT" ) )
583 else if( !strcmp( join_type_str, "RIGHT" ) )
584 join_type = JT_RIGHT;
585 else if( !strcmp( join_type_str, "FULL" ) )
588 join_type = JT_NONE; // shouldn't happen due to database constraint
591 if( dbi_result_field_is_null_idx( result, 11 ) )
594 on_clause_id = dbi_result_get_int_idx( result, 11 );
596 StoredQ* subquery = NULL;
602 if( -1 == subquery_id ) {
603 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
604 "Internal error: no subquery specified for FROM relation # %d", id ));
608 if( ! table_alias ) {
609 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
610 "Subquery needs alias in FROM relation # %d", id ));
614 subquery = getStoredQuery( state, subquery_id );
616 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
617 "Unable to load subquery for FROM relation # %d", id ));
623 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
624 "Functions in FROM clause not yet supported" ));
629 FromRelation* join_list = getJoinList( state, id );
631 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
632 "Unable to load join list for FROM relation # %d", id ));
636 Expression* on_clause = NULL;
637 if( on_clause_id != -1 ) {
638 on_clause = getExpression( state, on_clause_id );
640 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
641 "Unable to load ON condition for FROM relation # %d", id ));
642 joinListFree( join_list );
647 PRINT( "\tGot an ON condition\n" );
650 // Allocate a FromRelation: from the free list if possible, from the heap if necessary
653 if( free_from_relation_list ) {
654 fr = free_from_relation_list;
655 free_from_relation_list = free_from_relation_list->next;
657 fr = safe_malloc( sizeof( FromRelation ) );
659 // Populate the FromRelation
664 fr->table_name = table_name ? strdup( table_name ) : NULL;
665 fr->class_name = class_name ? strdup( class_name ) : NULL;
666 fr->subquery_id = subquery_id;
667 fr->subquery = subquery;
668 fr->function_call_id = function_call_id;
669 fr->table_alias = table_alias ? strdup( table_alias ) : NULL;
670 fr->parent_relation_id = parent_relation_id;
672 fr->join_type = join_type;
673 fr->on_clause = on_clause;
674 fr->join_list = join_list;
680 @brief Build a list of joined relations.
681 @param state Pointer to the query-building context.
682 @param id ID of the parent relation.
683 @return A pointer to the first in a linked list of FromRelations, if there are any; or
684 NULL if there aren't any, or in case of an error.
686 Look for relations joined directly to the parent relation, and make a list of them.
688 static FromRelation* getJoinList( BuildSQLState* state, int id ) {
689 FromRelation* join_list = NULL;
691 // The ORDER BY is in descending order so that we can build the list by adding to
692 // the head, and it will wind up in the right order.
693 dbi_result result = dbi_conn_queryf( state->dbhandle,
694 "SELECT id, type, table_name, class_name, subquery, function_call, "
695 "table_alias, parent_relation, seq_no, join_type, on_clause "
696 "FROM query.from_relation WHERE parent_relation = %d ORDER BY seq_no DESC", id );
699 if( dbi_result_first_row( result ) ) {
701 FromRelation* relation = constructFromRelation( state, result );
703 PRINT( "Found a joined relation\n" );
704 PRINT( "\tjoin_type: %d\n", relation->join_type );
705 PRINT( "\ttable_name: %s\n", relation->table_name );
706 relation->next = join_list;
707 join_list = relation;
709 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
710 "Unable to build join list for from relation id #%d", id ));
711 joinListFree( join_list );
715 if( !dbi_result_next_row( result ) )
721 int errnum = dbi_conn_error( state->dbhandle, &msg );
722 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
723 "Unable to query query.from_relation table for join list: #%d %s",
724 errnum, msg ? msg : "No description available" ));
732 @brief Free a list of FromRelations.
733 @param join_list Pointer to the first FromRelation in the list.
735 static void joinListFree( FromRelation* join_list ) {
737 FromRelation* temp = join_list->next;
738 fromRelationFree( join_list );
744 @brief Deallocate a FromRelation.
745 @param fr Pointer to the FromRelation to be freed.
747 Free the strings that the FromRelation owns. The FromRelation itself goes onto a
748 free list for potential reuse.
750 static void fromRelationFree( FromRelation* fr ) {
752 free( fr->table_name );
753 fr->table_name = NULL;
754 free( fr->class_name );
755 fr->class_name = NULL;
757 storedQFree( fr->subquery );
760 free( fr->table_alias );
761 fr->table_alias = NULL;
762 if( fr->on_clause ) {
763 expressionFree( fr->on_clause );
764 fr->on_clause = NULL;
766 joinListFree( fr->join_list );
767 fr->join_list = NULL;
769 fr->next = free_from_relation_list;
770 free_from_relation_list = fr;
774 static SelectItem* getSelectList( BuildSQLState* state, int query_id ) {
775 SelectItem* select_list = NULL;
777 // The ORDER BY is in descending order so that we can build the list by adding to
778 // the head, and it will wind up in the right order.
779 dbi_result result = dbi_conn_queryf( state->dbhandle,
780 "SELECT id, stored_query, seq_no, expression, column_alias, grouped_by "
781 "FROM query.select_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
783 if( dbi_result_first_row( result ) ) {
785 SelectItem* item = constructSelectItem( state, result );
787 PRINT( "Found a SELECT item\n" );
788 PRINT( "\tid: %d\n", item->id );
789 PRINT( "\tstored_query_id: %d\n", item->stored_query_id );
790 PRINT( "\tseq_no: %d\n", item->seq_no );
791 PRINT( "\tcolumn_alias: %s\n",
792 item->column_alias ? item->column_alias : "(none)" );
793 PRINT( "\tgrouped_by: %d\n", item->grouped_by );
795 item->next = select_list;
798 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
799 "Unable to build select list for query id #%d", query_id ));
800 selectListFree( select_list );
804 if( !dbi_result_next_row( result ) )
810 int errnum = dbi_conn_error( state->dbhandle, &msg );
811 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
812 "Unable to query query.select_list table: #%d %s",
813 errnum, msg ? msg : "No description available" ));
821 @brief Construct a SelectItem.
822 @param Pointer to the query-building context.
823 @param result Database cursor positioned at a row in query.select_item.
824 @return Pointer to a newly constructed SelectItem, if successful, or NULL if not.
826 The calling code is responsible for freeing the SelectItems by calling selectListFree().
828 static SelectItem* constructSelectItem( BuildSQLState* state, dbi_result result ) {
830 // Get the column values
831 int id = dbi_result_get_int_idx( result, 1 );
832 int stored_query_id = dbi_result_get_int_idx( result, 2 );
833 int seq_no = dbi_result_get_int_idx( result, 3 );
834 int expression_id = dbi_result_get_int_idx( result, 4 );
835 const char* column_alias = dbi_result_get_string_idx( result, 5 );
836 int grouped_by = oils_result_get_bool_idx( result, 6 );
838 // Construct an Expression
839 Expression* expression = getExpression( state, expression_id );
841 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
842 "Unable to fetch expression for id = %d", expression_id ));
847 // Allocate a SelectItem: from the free list if possible, from the heap if necessary
850 if( free_select_item_list ) {
851 sel = free_select_item_list;
852 free_select_item_list = free_select_item_list->next;
854 sel = safe_malloc( sizeof( SelectItem ) );
858 sel->stored_query_id = stored_query_id;
859 sel->seq_no = seq_no;
860 sel->expression = expression;
861 sel->column_alias = column_alias ? strdup( column_alias ) : NULL;
862 sel->grouped_by = grouped_by;
868 @brief Free a list of SelectItems.
869 @param sel Pointer to the first item in the list to be freed.
871 Free the column alias and expression owned by each item. Put the entire list into a free
874 static void selectListFree( SelectItem* sel ) {
876 return; // Nothing to free
878 SelectItem* first = sel;
880 free( sel->column_alias );
881 sel->column_alias = NULL;
882 expressionFree( sel->expression );
883 sel->expression = NULL;
885 if( NULL == sel->next ) {
886 sel->next = free_select_item_list;
892 // Transfer the entire list to the free list
893 free_select_item_list = first;
897 @brief Given the name of a bind variable, build a corresponding BindVar.
898 @param state Pointer to the query-building context.
899 @param name Name of the bind variable.
900 @return Pointer to the newly-built BindVar.
902 Since the same bind variable may appear multiple times, we load it only once for the
903 entire query, and reference the one copy wherever needed.
905 static BindVar* getBindVar( BuildSQLState* state, const char* name ) {
906 BindVar* bind = NULL;
907 if( state->bindvar_list ) {
908 bind = osrfHashGet( state->bindvar_list, name );
910 return bind; // Already loaded it...
913 // Load a BindVar from the Database.
914 dbi_result result = dbi_conn_queryf( state->dbhandle,
915 "SELECT name, type, description, default_value, label "
916 "FROM query.bind_variable WHERE name = \'%s\';", name );
918 if( dbi_result_first_row( result ) ) {
919 bind = constructBindVar( state, result );
921 PRINT( "Got a bind variable for %s\n", name );
923 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
924 "Unable to load bind variable \"%s\"", name ));
926 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
927 "No bind variable found with name \"%s\"", name ));
931 int errnum = dbi_conn_error( state->dbhandle, &msg );
932 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
933 "Unable to query query.bind_variable table for \"%s\": #%d %s",
934 name, errnum, msg ? msg : "No description available" ));
939 // Add the new bind variable to the list
940 if( !state->bindvar_list ) {
941 // Don't have a list yet? Start one.
942 state->bindvar_list = osrfNewHash();
943 osrfHashSetCallback( state->bindvar_list, bindVarFree );
945 osrfHashSet( state->bindvar_list, bind, name );
953 @brief Construct a BindVar to represent a bind variable.
954 @param Pointer to the query-building context.
955 @param result Database cursor positioned at a row in query.bind_variable.
956 @return Pointer to a newly constructed BindVar, if successful, or NULL if not.
958 The calling code is responsible for freeing the BindVar by calling bindVarFree().
960 static BindVar* constructBindVar( BuildSQLState* state, dbi_result result ) {
962 const char* name = dbi_result_get_string_idx( result, 1 );
964 const char* type_str = dbi_result_get_string_idx( result, 2 );
966 if( !strcmp( type_str, "string" ))
968 else if( !strcmp( type_str, "number" ))
970 else if( !strcmp( type_str, "string_list" ))
971 type = BIND_STR_LIST;
972 else if( !strcmp( type_str, "number_list" ))
973 type = BIND_NUM_LIST;;
975 const char* description = dbi_result_get_string_idx( result, 3 );
977 // The default value is encoded as JSON. Translate it into a jsonObject.
978 const char* default_value_str = dbi_result_get_string_idx( result, 4 );
979 jsonObject* default_value = NULL;
980 if( default_value_str ) {
981 default_value = jsonParse( default_value_str );
982 if( !default_value ) {
983 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
984 "Unable to parse JSON string for default value of bind variable \"%s\"",
991 const char* label = dbi_result_get_string_idx( result, 5 );
993 // Allocate a BindVar: from the free list if possible, from the heap if necessary
994 BindVar* bind = NULL;
995 if( free_bindvar_list ) {
996 bind = free_bindvar_list;
997 free_bindvar_list = free_bindvar_list->next;
999 bind = safe_malloc( sizeof( BindVar ) );
1002 bind->name = strdup( name );
1003 bind->label = strdup( label );
1005 bind->description = strdup( description );
1006 bind->default_value = default_value;
1007 bind->actual_value = NULL;
1013 @brief Deallocate a BindVar.
1014 @param key Pointer to the bind variable name (not used).
1015 @param p Pointer to the BindVar to be deallocated, cast to a void pointer.
1017 Free the strings and jsonObjects owned by the BindVar, and put the BindVar itself into a
1020 This function is a callback installed in an osrfHash; hence the peculiar signature.
1022 static void bindVarFree( char* key, void* p ) {
1026 free( bind->label );
1027 free( bind->description );
1028 if( bind->default_value ) {
1029 jsonObjectFree( bind->default_value );
1030 bind->default_value = NULL;
1032 if( bind->actual_value ) {
1033 jsonObjectFree( bind->actual_value );
1034 bind->actual_value = NULL;
1037 // Prepend to free list
1038 bind->next = free_bindvar_list;
1039 free_bindvar_list = bind;
1044 @brief Given an id for a row in query.expression, build an Expression struct.
1045 @param Pointer to the query-building context.
1046 @param id ID of a row in query.expression.
1047 @return Pointer to a newly-created Expression if successful, or NULL if not.
1049 static Expression* getExpression( BuildSQLState* state, int id ) {
1051 // Check the stack to see if the current expression is nested inside itself. If it is,
1052 // then abort in order to avoid infinite recursion. If it isn't, then add it to the
1053 // stack. (Make sure to pop it off the stack before returning.)
1054 if( searchIdStack( state->expr_stack, id, NULL )) {
1055 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1056 "Infinite recursion detected; expression # %d is nested within itself", id ));
1060 push_id( &state->expr_stack, id, NULL );
1062 Expression* exp = NULL;
1063 dbi_result result = dbi_conn_queryf( state->dbhandle,
1064 "SELECT exp.id, exp.type, exp.parenthesize, exp.parent_expr, exp.seq_no, "
1065 "exp.literal, exp.table_alias, exp.column_name, exp.left_operand, exp.operator, "
1066 "exp.right_operand, exp.subquery, exp.cast_type, exp.negate, exp.bind_variable, "
1067 "func.function_name, COALESCE(func.is_aggregate, false) "
1068 "FROM query.expression AS exp LEFT JOIN query.function_sig AS func "
1069 "ON (exp.function_id = func.id) "
1070 "WHERE exp.id = %d;", id );
1072 if( dbi_result_first_row( result ) ) {
1073 exp = constructExpression( state, result );
1075 PRINT( "Got an expression\n" );
1076 PRINT( "\tid = %d\n", exp->id );
1077 PRINT( "\ttype = %d\n", exp->type );
1078 PRINT( "\tparenthesize = %d\n", exp->parenthesize );
1079 PRINT( "\tcolumn_name = %s\n", exp->column_name ? exp->column_name : "(none)" );
1081 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1082 "Unable to construct an Expression for id = %d", id ));
1086 int errnum = dbi_conn_error( state->dbhandle, &msg );
1087 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1088 "Unable to query query.expression table: #%d %s",
1089 errnum, msg ? msg : "No description available" ));
1093 pop_id( &state->expr_stack );
1098 @brief Construct an Expression.
1099 @param Pointer to the query-building context.
1100 @param result Database cursor positioned at a row in query.expression.
1101 @return Pointer to a newly constructed Expression, if successful, or NULL if not.
1103 The calling code is responsible for freeing the Expression by calling expressionFree().
1105 static Expression* constructExpression( BuildSQLState* state, dbi_result result ) {
1107 int id = dbi_result_get_int_idx( result, 1 );
1108 const char* type_str = dbi_result_get_string_idx( result, 2 );
1111 if( !strcmp( type_str, "xbet" ))
1113 else if( !strcmp( type_str, "xbind" ))
1115 else if( !strcmp( type_str, "xbool" ))
1117 else if( !strcmp( type_str, "xcase" ))
1119 else if( !strcmp( type_str, "xcast" ))
1121 else if( !strcmp( type_str, "xcol" ))
1123 else if( !strcmp( type_str, "xex" ))
1125 else if( !strcmp( type_str, "xfld" ))
1127 else if( !strcmp( type_str, "xfunc" ))
1128 type = EXP_FUNCTION;
1129 else if( !strcmp( type_str, "xin" ))
1131 else if( !strcmp( type_str, "xisnull" ))
1133 else if( !strcmp( type_str, "xnull" ))
1135 else if( !strcmp( type_str, "xnum" ))
1137 else if( !strcmp( type_str, "xop" ))
1138 type = EXP_OPERATOR;
1139 else if( !strcmp( type_str, "xser" ))
1141 else if( !strcmp( type_str, "xstr" ))
1143 else if( !strcmp( type_str, "xsubq" ))
1144 type = EXP_SUBQUERY;
1146 type = EXP_NULL; // shouldn't happen due to database constraint
1148 int parenthesize = oils_result_get_bool_idx( result, 3 );
1151 if( dbi_result_field_is_null_idx( result, 4 ))
1152 parent_expr_id = -1;
1154 parent_expr_id = dbi_result_get_int_idx( result, 4 );
1156 int seq_no = dbi_result_get_int_idx( result, 5 );
1157 const char* literal = dbi_result_get_string_idx( result, 6 );
1158 const char* table_alias = dbi_result_get_string_idx( result, 7 );
1159 const char* column_name = dbi_result_get_string_idx( result, 8 );
1161 int left_operand_id;
1162 if( dbi_result_field_is_null_idx( result, 9 ))
1163 left_operand_id = -1;
1165 left_operand_id = dbi_result_get_int_idx( result, 9 );
1167 const char* operator = dbi_result_get_string_idx( result, 10 );
1169 int right_operand_id;
1170 if( dbi_result_field_is_null_idx( result, 11 ))
1171 right_operand_id = -1;
1173 right_operand_id = dbi_result_get_int_idx( result, 11 );
1176 if( dbi_result_field_is_null_idx( result, 12 ))
1179 subquery_id = dbi_result_get_int_idx( result, 12 );
1182 if( dbi_result_field_is_null_idx( result, 13 ))
1185 cast_type_id = dbi_result_get_int_idx( result, 13 );
1187 int negate = oils_result_get_bool_idx( result, 14 );
1188 const char* bind_variable = dbi_result_get_string_idx( result, 15 );
1189 const char* function_name = dbi_result_get_string_idx( result, 16 );
1190 int is_aggregate = oils_result_get_bool_idx( result, 17 );
1192 Expression* left_operand = NULL;
1193 Expression* right_operand = NULL;
1194 StoredQ* subquery = NULL;
1195 BindVar* bind = NULL;
1196 Expression* subexp_list = NULL;
1198 if( EXP_BIND == type ) {
1199 if( bind_variable ) {
1200 // To do: Build a BindVar
1201 bind = getBindVar( state, bind_variable );
1203 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1204 "Unable to load bind variable \"%s\" for expression # %d",
1205 bind_variable, id ));
1209 PRINT( "\tBind variable is \"%s\"\n", bind_variable );
1211 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1212 "No variable specified for bind variable expression # %d",
1213 bind_variable, id ));
1217 if( right_operand_id != -1 ) {
1218 right_operand = getExpression( state, right_operand_id );
1219 if( !right_operand ) {
1220 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1221 "Unable to get right operand in expression # %d", id ));
1223 expressionFree( left_operand );
1228 } else if( EXP_FUNCTION == type ) {
1229 if( !function_name ) {
1230 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1231 "Function call expression # %d provides no function name", id ));
1235 subexp_list = getExpressionList( state, id );
1236 if( state->error ) {
1237 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1238 "Unable to get parameter list for function expression # %d", id ));
1243 } else if( EXP_IN == type ) {
1244 if( -1 == left_operand_id ) {
1245 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1246 "IN condition has no left operand in expression # %d", id ));
1250 left_operand = getExpression( state, left_operand_id );
1251 if( !left_operand ) {
1252 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1253 "Unable to get left operand for IN condition in expression # %d", id ));
1259 if( -1 == subquery_id ) {
1260 // Load an IN list of subexpressions
1261 subexp_list = getExpressionList( state, id );
1262 if( state->error ) {
1263 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1264 "Unable to get subexpressions for IN list" ));
1266 } else if( !subexp_list ) {
1267 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1268 "IN list is empty in expression # %d", id ));
1274 subquery = getStoredQuery( state, subquery_id );
1276 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1277 "Unable to load subquery for IN expression # %d", id ));
1283 } else if( EXP_ISNULL == type ) {
1284 if( -1 == left_operand_id ) {
1285 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1286 "Expression # %d IS NULL has no left operand", id ));
1291 if( left_operand_id != -1 ) {
1292 left_operand = getExpression( state, left_operand_id );
1293 if( !left_operand ) {
1294 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1295 "Unable to get left operand in expression # %d", id ));
1300 } else if( EXP_EXIST == type ) {
1301 if( -1 == subquery_id ) {
1302 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1303 "Internal error: No subquery found for EXIST expression # %d", id ));
1307 subquery = getStoredQuery( state, subquery_id );
1309 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1310 "Unable to load subquery for EXIST expression # %d", id ));
1316 } else if( EXP_NUMBER == type ) {
1318 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1319 "Numeric expression # %d provides no numeric value", id ));
1324 } else if( EXP_OPERATOR == type ) {
1325 // Load left and/or right operands
1326 if( -1 == left_operand_id && -1 == right_operand_id ) {
1327 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1328 "Expression # %d is an operator with no operands", id ));
1333 if( left_operand_id != -1 ) {
1334 left_operand = getExpression( state, left_operand_id );
1335 if( !left_operand ) {
1336 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1337 "Unable to get left operand in expression # %d", id ));
1343 if( right_operand_id != -1 ) {
1344 right_operand = getExpression( state, right_operand_id );
1345 if( !right_operand ) {
1346 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1347 "Unable to get right operand in expression # %d", id ));
1353 } else if( EXP_SERIES == type ) {
1354 subexp_list = getExpressionList( state, id );
1355 if( state->error ) {
1356 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1357 "Unable to get subexpressions for expression series using operator \"%s\"",
1358 operator ? operator : "," ));
1360 } else if( !subexp_list ) {
1361 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1362 "Series expression is empty in expression # %d", id ));
1367 } else if( EXP_STRING == type ) {
1369 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1370 "String expression # %d provides no string value", id ));
1375 } else if( EXP_SUBQUERY == type ) {
1376 if( -1 == subquery_id ) {
1377 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1378 "Subquery expression # %d has no query id", id ));
1382 // Load a subquery, if there is one
1383 subquery = getStoredQuery( state, subquery_id );
1385 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1386 "Unable to load subquery for expression # %d", id ));
1390 if( subquery->select_list && subquery->select_list->next ) {
1391 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1392 "Subquery # %d as expression returns more than one column", subquery_id ));
1396 PRINT( "\tExpression is subquery %d\n", subquery_id );
1400 // Allocate an Expression: from the free list if possible, from the heap if necessary
1401 Expression* exp = NULL;
1402 if( free_expression_list ) {
1403 exp = free_expression_list;
1404 free_expression_list = free_expression_list->next;
1406 exp = safe_malloc( sizeof( Expression ) );
1408 // Populate the Expression
1412 exp->parenthesize = parenthesize;
1413 exp->parent_expr_id = parent_expr_id;
1414 exp->seq_no = seq_no;
1415 exp->literal = literal ? strdup( literal ) : NULL;
1416 exp->table_alias = table_alias ? strdup( table_alias ) : NULL;
1417 exp->column_name = column_name ? strdup( column_name ) : NULL;
1418 exp->left_operand = left_operand;
1419 exp->op = operator ? strdup( operator ) : NULL;
1420 exp->right_operand = right_operand;
1421 exp->subquery_id = subquery_id;
1422 exp->subquery = subquery;
1423 exp->cast_type_id = subquery_id;
1424 exp->negate = negate;
1426 exp->subexp_list = subexp_list;
1427 exp->function_name = function_name ? strdup( function_name ) : NULL;
1428 exp->is_aggregate = is_aggregate;
1434 @brief Free all the Expressions in a linked list of Expressions.
1435 @param exp Pointer to the first Expression in the list.
1437 static void expressionListFree( Expression* exp ) {
1439 Expression* next = exp->next;
1440 expressionFree( exp );
1446 @brief Deallocate an Expression.
1447 @param exp Pointer to the Expression to be deallocated.
1449 Free the strings owned by the Expression. Put the Expression itself, and any
1450 subexpressions that it owns, into a free list.
1452 static void expressionFree( Expression* exp ) {
1454 free( exp->literal );
1455 exp->literal = NULL;
1456 free( exp->table_alias );
1457 exp->table_alias = NULL;
1458 free( exp->column_name );
1459 exp->column_name = NULL;
1460 if( exp->left_operand ) {
1461 expressionFree( exp->left_operand );
1462 exp->left_operand = NULL;
1466 if( exp->right_operand ) {
1467 expressionFree( exp->right_operand );
1468 exp->right_operand = NULL;
1470 if( exp->subquery ) {
1471 storedQFree( exp->subquery );
1472 exp->subquery = NULL;
1475 // We don't free the bind member here because the Expression doesn't own it;
1476 // the bindvar_list hash owns it, so that multiple Expressions can reference it.
1478 if( exp->subexp_list ) {
1479 // Free the linked list of subexpressions
1480 expressionListFree( exp->subexp_list );
1481 exp->subexp_list = NULL;
1484 if( exp->function_name ) {
1485 free( exp->function_name );
1486 exp->function_name = NULL;
1489 // Prepend to the free list
1490 exp->next = free_expression_list;
1491 free_expression_list = exp;
1496 @brief Build a list of subexpressions.
1497 @param state Pointer to the query-building context.
1498 @param id ID of the parent Expression.
1499 @return A pointer to the first in a linked list of Expressions, if there are any; or
1500 NULL if there aren't any, or in case of an error.
1502 static Expression* getExpressionList( BuildSQLState* state, int id ) {
1503 Expression* exp_list = NULL;
1505 // The ORDER BY is in descending order so that we can build the list by adding to
1506 // the head, and it will wind up in the right order.
1507 dbi_result result = dbi_conn_queryf( state->dbhandle,
1508 "SELECT exp.id, exp.type, exp.parenthesize, exp.parent_expr, exp.seq_no, "
1509 "exp.literal, exp.table_alias, exp.column_name, exp.left_operand, exp.operator, "
1510 "exp.right_operand, exp.subquery, exp.cast_type, exp.negate, exp.bind_variable, "
1511 "func.function_name, COALESCE(func.is_aggregate, false) "
1512 "FROM query.expression AS exp LEFT JOIN query.function_sig AS func "
1513 "ON (exp.function_id = func.id) "
1514 "WHERE exp.parent_expr = %d "
1515 "ORDER BY exp.seq_no desc;", id );
1518 if( dbi_result_first_row( result ) ) {
1520 Expression* exp = constructExpression( state, result );
1522 PRINT( "Found a subexpression\n" );
1523 exp->next = exp_list;
1526 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1527 "Unable to build subexpression list for expression id #%d", id ));
1528 expressionListFree( exp_list );
1532 if( !dbi_result_next_row( result ) )
1538 int errnum = dbi_conn_error( state->dbhandle, &msg );
1539 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1540 "Unable to query query.expression table for expression list: #%d %s",
1541 errnum, msg ? msg : "No description available" ));
1549 @brief Build a list of ORDER BY items as a linked list of OrderItems.
1550 @param state Pointer to the query-building context.
1551 @param query_id ID for the query to which the ORDER BY belongs.
1552 @return Pointer to the first node in a linked list of OrderItems.
1554 The calling code is responsible for freeing the list by calling orderItemListFree().
1556 static OrderItem* getOrderByList( BuildSQLState* state, int query_id ) {
1557 OrderItem* ord_list = NULL;
1559 // The ORDER BY is in descending order so that we can build the list by adding to
1560 // the head, and it will wind up in the right order.
1561 dbi_result result = dbi_conn_queryf( state->dbhandle,
1562 "SELECT id, stored_query, seq_no, expression "
1563 "FROM query.order_by_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
1565 if( dbi_result_first_row( result ) ) {
1567 OrderItem* item = constructOrderItem( state, result );
1569 PRINT( "Found an ORDER BY item\n" );
1571 item->next = ord_list;
1574 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1575 "Unable to build ORDER BY item for query id #%d", query_id ));
1576 orderItemListFree( ord_list );
1580 if( !dbi_result_next_row( result ) )
1586 int errnum = dbi_conn_error( state->dbhandle, &msg );
1587 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1588 "Unable to query query.order_by_list table: #%d %s",
1589 errnum, msg ? msg : "No description available" ));
1597 @brief Construct an OrderItem.
1598 @param Pointer to the query-building context.
1599 @param result Database cursor positioned at a row in query.order_by_item.
1600 @return Pointer to a newly constructed OrderItem, if successful, or NULL if not.
1602 The calling code is responsible for freeing the OrderItems by calling orderItemListFree().
1604 static OrderItem* constructOrderItem( BuildSQLState* state, dbi_result result ) {
1605 int id = dbi_result_get_int_idx( result, 1 );
1606 int stored_query_id = dbi_result_get_int_idx( result, 2 );
1607 int seq_no = dbi_result_get_int_idx( result, 3 );
1608 int expression_id = dbi_result_get_int_idx( result, 4 );
1609 // Allocate a SelectItem: from the free list if possible, from the heap if necessary
1611 // Construct an Expression
1612 Expression* expression = getExpression( state, expression_id );
1614 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1615 "Unable to fetch ORDER BY expression for id = %d", expression_id ));
1620 // Allocate an OrderItem; from the free list if possible, or from the heap if necessary.
1622 if( free_order_item_list ) {
1623 ord = free_order_item_list;
1624 free_order_item_list = free_order_item_list->next;
1626 ord = safe_malloc( sizeof( OrderItem ));
1630 ord->stored_query_id = stored_query_id;
1631 ord->seq_no = seq_no;
1632 ord->expression = expression;
1638 @brief Deallocate a linked list of OrderItems.
1639 @param exp Pointer to the first OrderItem in the list to be deallocated.
1641 Deallocate the memory owned by the OrderItems. Put the items themselves into a free list.
1643 static void orderItemListFree( OrderItem* ord ) {
1645 return; // Nothing to free
1647 OrderItem* first = ord;
1649 expressionFree( ord->expression );
1650 ord->expression = NULL;
1652 if( NULL == ord->next ) {
1653 ord->next = free_order_item_list;
1659 // Transfer the entire list to the free list
1660 free_order_item_list = first;
1664 @brief Build a list of column names for a specified query.
1665 @param state Pointer to the query-building context.
1666 @param query Pointer to the specified query.
1667 @return Pointer to a newly-allocated JSON_ARRAY of column names.
1669 In the resulting array, each entry is either a JSON_STRING or (when no column name is
1670 available) a JSON_NULL.
1672 The calling code is responsible for freeing the list by calling jsonObjectFree().
1674 jsonObject* oilsGetColNames( BuildSQLState* state, StoredQ* query ) {
1675 if( !state || !query )
1678 // Save the outermost query id for possible use in an error message
1681 while( query->type != QT_SELECT ) {
1682 // If the query is a UNION, INTERSECT, or EXCEPT, there must be a SELECT in
1683 // there somewhere. Find the first one, and use the SELECT list from that.
1684 QSeq* child_list = query->child_list;
1689 query = child_list->child_query;
1694 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1695 "Unable to find first SELECT in query # %d", id ));
1699 // Get the SELECT list for the first SELECT
1700 SelectItem* col = query->select_list;
1703 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1704 "First SELECT in query # %d has empty SELECT list", id ));
1708 jsonObject* col_list = jsonNewObjectType( JSON_ARRAY );
1710 // Traverse the list, adding an entry for each
1712 const char* alias = NULL;
1713 if( col->column_alias )
1714 alias = col->column_alias;
1716 Expression* expression = col->expression;
1717 if( expression && EXP_COLUMN == expression->type && expression->column_name )
1718 alias = expression->column_name;
1721 jsonObjectPush( col_list, jsonNewObject( alias ) );
1729 @brief Push an IdNode onto a stack of IdNodes.
1730 @param stack Pointer to the stack.
1731 @param id Id of the new node.
1732 @param alias Alias, if any, of the new node.
1734 static void push_id( IdNode** stack, int id, const char* alias ) {
1737 // Allocate a node; from the free list if possible, from the heap if necessary.
1738 IdNode* node = NULL;
1739 if( free_id_node_list ) {
1740 node = free_id_node_list;
1741 free_id_node_list = free_id_node_list->next;
1743 node = safe_malloc( sizeof( IdNode ));
1746 node->next = *stack;
1749 node->alias = strdup( alias );
1759 @brief Remove the node at the top of an IdNode stack.
1760 @param stack Pointer to the IdNode stack.
1762 void pop_id( IdNode** stack ) {
1764 IdNode* node = *stack;
1765 *stack = node->next;
1768 free( node->alias );
1772 node->next = free_id_node_list;
1773 free_id_node_list = node;
1778 @brief Search a stack of IDs for a match by either ID or, optionally, by alias.
1779 @param stack Pointer to the stack.
1780 @param id The id to search for.
1781 @param alias (Optional) the alias to search for.
1782 @return A pointer to the matching node if one is found, or NULL if not.
1784 This search is used to detect cases where a query, expression, or FROM clause is nested
1785 inside itself, in order to avoid infinite recursion; or in order to avoid conflicting
1786 table references in a FROM clause.
1788 static const IdNode* searchIdStack( const IdNode* stack, int id, const char* alias ) {
1790 const IdNode* node = stack;
1792 if( node->id == id )
1793 return node; // Matched on id
1794 else if( alias && node->alias && !strcmp( alias, node->alias ))
1795 return node; // Matched on alias
1800 return NULL; // No match found
1804 @brief Free up any resources held by the StoredQ module.
1806 void storedQCleanup( void ) {
1808 // Free all the nodes in the free state list
1809 StoredQ* sq = free_storedq_list;
1811 free_storedq_list = sq->next;
1813 sq = free_storedq_list;
1816 // Free all the nodes in the free from_relation list
1817 FromRelation* fr = free_from_relation_list;
1819 free_from_relation_list = fr->next;
1821 fr = free_from_relation_list;
1824 // Free all the nodes in the free expression list
1825 Expression* exp = free_expression_list;
1827 free_expression_list = exp->next;
1829 exp = free_expression_list;
1832 // Free all the nodes in the free select item list
1833 SelectItem* sel = free_select_item_list;
1835 free_select_item_list = sel->next;
1837 sel = free_select_item_list;
1840 // Free all the nodes in the free select item list
1841 IdNode* node = free_id_node_list;
1843 free_id_node_list = node->next;
1845 node = free_id_node_list;
1848 // Free all the nodes in the free query sequence list
1849 QSeq* seq = free_qseq_list;
1851 free_qseq_list = seq->next;
1853 seq = free_qseq_list;
1856 // Free all the nodes in the free order item list
1857 OrderItem* ord = free_order_item_list;
1859 free_order_item_list = ord->next;
1861 ord = free_order_item_list;
1864 // Free all the nodes in the bind variable free list
1865 BindVar* bind = free_bindvar_list;
1867 free_bindvar_list = bind->next;
1869 bind = free_bindvar_list;
1874 @brief Return a boolean value from a database result.
1875 @param result The database result.
1876 @param i Index of the column in the result, starting with 1 );
1877 @return 1 if true, or 0 for false.
1879 Null values and error conditions are interpreted as FALSE.
1881 static int oils_result_get_bool_idx( dbi_result result, int i ) {
1883 const char* str = dbi_result_get_string_idx( result, i );
1884 return (str && *str == 't' ) ? 1 : 0;
1890 @brief Enable verbose messages.
1892 The messages are written to standard output, which for a server is /dev/null. Hence this
1893 option is useful only for a non-server. It is intended only as a convenience for
1894 development and debugging.
1896 void oilsStoredQSetVerbose( void ) {