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;
775 @brief Build a SELECT list for a given query ID.
776 @param state Pointer to the query-building context.
777 @param query_id ID of the query to which the SELECT list belongs.
779 static SelectItem* getSelectList( BuildSQLState* state, int query_id ) {
780 SelectItem* select_list = NULL;
782 // The ORDER BY is in descending order so that we can build the list by adding to
783 // the head, and it will wind up in the right order.
784 dbi_result result = dbi_conn_queryf( state->dbhandle,
785 "SELECT id, stored_query, seq_no, expression, column_alias, grouped_by "
786 "FROM query.select_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
788 if( dbi_result_first_row( result ) ) {
790 SelectItem* item = constructSelectItem( state, result );
792 PRINT( "Found a SELECT item\n" );
793 PRINT( "\tid: %d\n", item->id );
794 PRINT( "\tstored_query_id: %d\n", item->stored_query_id );
795 PRINT( "\tseq_no: %d\n", item->seq_no );
796 PRINT( "\tcolumn_alias: %s\n",
797 item->column_alias ? item->column_alias : "(none)" );
798 PRINT( "\tgrouped_by: %d\n", item->grouped_by );
800 item->next = select_list;
803 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
804 "Unable to build select list for query id #%d", query_id ));
805 selectListFree( select_list );
809 if( !dbi_result_next_row( result ) )
815 int errnum = dbi_conn_error( state->dbhandle, &msg );
816 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
817 "Unable to query query.select_list table: #%d %s",
818 errnum, msg ? msg : "No description available" ));
826 @brief Construct a SelectItem.
827 @param Pointer to the query-building context.
828 @param result Database cursor positioned at a row in query.select_item.
829 @return Pointer to a newly constructed SelectItem, if successful, or NULL if not.
831 The calling code is responsible for freeing the SelectItems by calling selectListFree().
833 static SelectItem* constructSelectItem( BuildSQLState* state, dbi_result result ) {
835 // Get the column values
836 int id = dbi_result_get_int_idx( result, 1 );
837 int stored_query_id = dbi_result_get_int_idx( result, 2 );
838 int seq_no = dbi_result_get_int_idx( result, 3 );
839 int expression_id = dbi_result_get_int_idx( result, 4 );
840 const char* column_alias = dbi_result_get_string_idx( result, 5 );
841 int grouped_by = oils_result_get_bool_idx( result, 6 );
843 // Construct an Expression
844 Expression* expression = getExpression( state, expression_id );
846 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
847 "Unable to fetch expression for id = %d", expression_id ));
852 // Allocate a SelectItem: from the free list if possible, from the heap if necessary
855 if( free_select_item_list ) {
856 sel = free_select_item_list;
857 free_select_item_list = free_select_item_list->next;
859 sel = safe_malloc( sizeof( SelectItem ) );
863 sel->stored_query_id = stored_query_id;
864 sel->seq_no = seq_no;
865 sel->expression = expression;
866 sel->column_alias = column_alias ? strdup( column_alias ) : NULL;
867 sel->grouped_by = grouped_by;
873 @brief Free a list of SelectItems.
874 @param sel Pointer to the first item in the list to be freed.
876 Free the column alias and expression owned by each item. Put the entire list into a free
879 static void selectListFree( SelectItem* sel ) {
881 return; // Nothing to free
883 SelectItem* first = sel;
885 free( sel->column_alias );
886 sel->column_alias = NULL;
887 expressionFree( sel->expression );
888 sel->expression = NULL;
890 if( NULL == sel->next ) {
891 sel->next = free_select_item_list;
897 // Transfer the entire list to the free list
898 free_select_item_list = first;
902 @brief Given the name of a bind variable, build a corresponding BindVar.
903 @param state Pointer to the query-building context.
904 @param name Name of the bind variable.
905 @return Pointer to the newly-built BindVar.
907 Since the same bind variable may appear multiple times, we load it only once for the
908 entire query, and reference the one copy wherever needed.
910 static BindVar* getBindVar( BuildSQLState* state, const char* name ) {
911 BindVar* bind = NULL;
912 if( state->bindvar_list ) {
913 bind = osrfHashGet( state->bindvar_list, name );
915 return bind; // Already loaded it...
918 // Load a BindVar from the Database.
919 dbi_result result = dbi_conn_queryf( state->dbhandle,
920 "SELECT name, type, description, default_value, label "
921 "FROM query.bind_variable WHERE name = \'%s\';", name );
923 if( dbi_result_first_row( result ) ) {
924 bind = constructBindVar( state, result );
926 PRINT( "Got a bind variable for %s\n", name );
928 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
929 "Unable to load bind variable \"%s\"", name ));
931 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
932 "No bind variable found with name \"%s\"", name ));
936 int errnum = dbi_conn_error( state->dbhandle, &msg );
937 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
938 "Unable to query query.bind_variable table for \"%s\": #%d %s",
939 name, errnum, msg ? msg : "No description available" ));
944 // Add the new bind variable to the list
945 if( !state->bindvar_list ) {
946 // Don't have a list yet? Start one.
947 state->bindvar_list = osrfNewHash();
948 osrfHashSetCallback( state->bindvar_list, bindVarFree );
950 osrfHashSet( state->bindvar_list, bind, name );
958 @brief Construct a BindVar to represent a bind variable.
959 @param Pointer to the query-building context.
960 @param result Database cursor positioned at a row in query.bind_variable.
961 @return Pointer to a newly constructed BindVar, if successful, or NULL if not.
963 The calling code is responsible for freeing the BindVar by calling bindVarFree().
965 static BindVar* constructBindVar( BuildSQLState* state, dbi_result result ) {
967 const char* name = dbi_result_get_string_idx( result, 1 );
969 const char* type_str = dbi_result_get_string_idx( result, 2 );
971 if( !strcmp( type_str, "string" ))
973 else if( !strcmp( type_str, "number" ))
975 else if( !strcmp( type_str, "string_list" ))
976 type = BIND_STR_LIST;
977 else if( !strcmp( type_str, "number_list" ))
978 type = BIND_NUM_LIST;;
980 const char* description = dbi_result_get_string_idx( result, 3 );
982 // The default value is encoded as JSON. Translate it into a jsonObject.
983 const char* default_value_str = dbi_result_get_string_idx( result, 4 );
984 jsonObject* default_value = NULL;
985 if( default_value_str ) {
986 default_value = jsonParse( default_value_str );
987 if( !default_value ) {
988 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
989 "Unable to parse JSON string for default value of bind variable \"%s\"",
996 const char* label = dbi_result_get_string_idx( result, 5 );
998 // Allocate a BindVar: from the free list if possible, from the heap if necessary
999 BindVar* bind = NULL;
1000 if( free_bindvar_list ) {
1001 bind = free_bindvar_list;
1002 free_bindvar_list = free_bindvar_list->next;
1004 bind = safe_malloc( sizeof( BindVar ) );
1007 bind->name = strdup( name );
1008 bind->label = strdup( label );
1010 bind->description = strdup( description );
1011 bind->default_value = default_value;
1012 bind->actual_value = NULL;
1018 @brief Deallocate a BindVar.
1019 @param key Pointer to the bind variable name (not used).
1020 @param p Pointer to the BindVar to be deallocated, cast to a void pointer.
1022 Free the strings and jsonObjects owned by the BindVar, and put the BindVar itself into a
1025 This function is a callback installed in an osrfHash; hence the peculiar signature.
1027 static void bindVarFree( char* key, void* p ) {
1031 free( bind->label );
1032 free( bind->description );
1033 if( bind->default_value ) {
1034 jsonObjectFree( bind->default_value );
1035 bind->default_value = NULL;
1037 if( bind->actual_value ) {
1038 jsonObjectFree( bind->actual_value );
1039 bind->actual_value = NULL;
1042 // Prepend to free list
1043 bind->next = free_bindvar_list;
1044 free_bindvar_list = bind;
1049 @brief Given an id for a row in query.expression, build an Expression struct.
1050 @param Pointer to the query-building context.
1051 @param id ID of a row in query.expression.
1052 @return Pointer to a newly-created Expression if successful, or NULL if not.
1054 static Expression* getExpression( BuildSQLState* state, int id ) {
1056 // Check the stack to see if the current expression is nested inside itself. If it is,
1057 // then abort in order to avoid infinite recursion. If it isn't, then add it to the
1058 // stack. (Make sure to pop it off the stack before returning.)
1059 if( searchIdStack( state->expr_stack, id, NULL )) {
1060 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1061 "Infinite recursion detected; expression # %d is nested within itself", id ));
1065 push_id( &state->expr_stack, id, NULL );
1067 Expression* exp = NULL;
1068 dbi_result result = dbi_conn_queryf( state->dbhandle,
1069 "SELECT exp.id, exp.type, exp.parenthesize, exp.parent_expr, exp.seq_no, "
1070 "exp.literal, exp.table_alias, exp.column_name, exp.left_operand, exp.operator, "
1071 "exp.right_operand, exp.subquery, exp.cast_type, exp.negate, exp.bind_variable, "
1072 "func.function_name "
1073 "FROM query.expression AS exp LEFT JOIN query.function_sig AS func "
1074 "ON (exp.function_id = func.id) "
1075 "WHERE exp.id = %d;", id );
1077 if( dbi_result_first_row( result ) ) {
1078 exp = constructExpression( state, result );
1080 PRINT( "Got an expression\n" );
1081 PRINT( "\tid = %d\n", exp->id );
1082 PRINT( "\ttype = %d\n", exp->type );
1083 PRINT( "\tparenthesize = %d\n", exp->parenthesize );
1084 PRINT( "\tcolumn_name = %s\n", exp->column_name ? exp->column_name : "(none)" );
1086 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1087 "Unable to construct an Expression for id = %d", id ));
1091 int errnum = dbi_conn_error( state->dbhandle, &msg );
1092 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1093 "Unable to query query.expression table: #%d %s",
1094 errnum, msg ? msg : "No description available" ));
1098 pop_id( &state->expr_stack );
1103 @brief Construct an Expression.
1104 @param Pointer to the query-building context.
1105 @param result Database cursor positioned at a row in query.expression.
1106 @return Pointer to a newly constructed Expression, if successful, or NULL if not.
1108 The calling code is responsible for freeing the Expression by calling expressionFree().
1110 static Expression* constructExpression( BuildSQLState* state, dbi_result result ) {
1112 int id = dbi_result_get_int_idx( result, 1 );
1113 const char* type_str = dbi_result_get_string_idx( result, 2 );
1116 if( !strcmp( type_str, "xbet" ))
1118 else if( !strcmp( type_str, "xbind" ))
1120 else if( !strcmp( type_str, "xbool" ))
1122 else if( !strcmp( type_str, "xcase" ))
1124 else if( !strcmp( type_str, "xcast" ))
1126 else if( !strcmp( type_str, "xcol" ))
1128 else if( !strcmp( type_str, "xex" ))
1130 else if( !strcmp( type_str, "xfld" ))
1132 else if( !strcmp( type_str, "xfunc" ))
1133 type = EXP_FUNCTION;
1134 else if( !strcmp( type_str, "xin" ))
1136 else if( !strcmp( type_str, "xisnull" ))
1138 else if( !strcmp( type_str, "xnull" ))
1140 else if( !strcmp( type_str, "xnum" ))
1142 else if( !strcmp( type_str, "xop" ))
1143 type = EXP_OPERATOR;
1144 else if( !strcmp( type_str, "xser" ))
1146 else if( !strcmp( type_str, "xstr" ))
1148 else if( !strcmp( type_str, "xsubq" ))
1149 type = EXP_SUBQUERY;
1151 type = EXP_NULL; // shouldn't happen due to database constraint
1153 int parenthesize = oils_result_get_bool_idx( result, 3 );
1156 if( dbi_result_field_is_null_idx( result, 4 ))
1157 parent_expr_id = -1;
1159 parent_expr_id = dbi_result_get_int_idx( result, 4 );
1161 int seq_no = dbi_result_get_int_idx( result, 5 );
1162 const char* literal = dbi_result_get_string_idx( result, 6 );
1163 const char* table_alias = dbi_result_get_string_idx( result, 7 );
1164 const char* column_name = dbi_result_get_string_idx( result, 8 );
1166 int left_operand_id;
1167 if( dbi_result_field_is_null_idx( result, 9 ))
1168 left_operand_id = -1;
1170 left_operand_id = dbi_result_get_int_idx( result, 9 );
1172 const char* operator = dbi_result_get_string_idx( result, 10 );
1174 int right_operand_id;
1175 if( dbi_result_field_is_null_idx( result, 11 ))
1176 right_operand_id = -1;
1178 right_operand_id = dbi_result_get_int_idx( result, 11 );
1181 if( dbi_result_field_is_null_idx( result, 12 ))
1184 subquery_id = dbi_result_get_int_idx( result, 12 );
1187 if( dbi_result_field_is_null_idx( result, 13 ))
1190 cast_type_id = dbi_result_get_int_idx( result, 13 );
1192 int negate = oils_result_get_bool_idx( result, 14 );
1193 const char* bind_variable = dbi_result_get_string_idx( result, 15 );
1194 const char* function_name = dbi_result_get_string_idx( result, 16 );
1196 Expression* left_operand = NULL;
1197 Expression* right_operand = NULL;
1198 StoredQ* subquery = NULL;
1199 BindVar* bind = NULL;
1200 Expression* subexp_list = NULL;
1202 if( EXP_BETWEEN == type ) {
1203 // Get the left operand
1204 if( -1 == left_operand_id ) {
1205 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1206 "No left operand defined for BETWEEN expression # %d", id ));
1210 left_operand = getExpression( state, left_operand_id );
1211 if( !left_operand ) {
1212 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1213 "Unable to get left operand in BETWEEN expression # %d", id ));
1219 // Get the end points of the BETWEEN range
1220 subexp_list = getExpressionList( state, id );
1221 if( state->error ) {
1222 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1223 "Unable to get subexpressions for BETWEEN expression # %d", id ));
1224 expressionFree( left_operand );
1226 } else if( !subexp_list ) {
1227 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1228 "BETWEEN range is empty in expression # %d", id ));
1230 expressionFree( left_operand );
1232 } else if( !subexp_list->next ) {
1233 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1234 "BETWEEN range has only one end point in expression # %d", id ));
1236 expressionListFree( subexp_list );
1237 expressionFree( left_operand );
1239 } else if( subexp_list->next->next ) {
1240 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1241 "BETWEEN range has more than two subexpressions in expression # %d", id ));
1243 expressionListFree( subexp_list );
1244 expressionFree( left_operand );
1248 } else if( EXP_BIND == type ) {
1249 if( bind_variable ) {
1250 // To do: Build a BindVar
1251 bind = getBindVar( state, bind_variable );
1253 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1254 "Unable to load bind variable \"%s\" for expression # %d",
1255 bind_variable, id ));
1259 PRINT( "\tBind variable is \"%s\"\n", bind_variable );
1261 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1262 "No variable specified for bind variable expression # %d",
1263 bind_variable, id ));
1267 if( right_operand_id != -1 ) {
1268 right_operand = getExpression( state, right_operand_id );
1269 if( !right_operand ) {
1270 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1271 "Unable to get right operand in expression # %d", id ));
1273 expressionFree( left_operand );
1278 } else if( EXP_EXIST == type ) {
1279 if( -1 == subquery_id ) {
1280 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1281 "Internal error: No subquery found for EXIST expression # %d", id ));
1285 subquery = getStoredQuery( state, subquery_id );
1287 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1288 "Unable to load subquery for EXIST expression # %d", id ));
1294 } else if( EXP_FUNCTION == type ) {
1295 if( !function_name ) {
1296 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1297 "Function call expression # %d provides no function name", id ));
1301 subexp_list = getExpressionList( state, id );
1302 if( state->error ) {
1303 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1304 "Unable to get parameter list for function expression # %d", id ));
1309 } else if( EXP_IN == type ) {
1310 if( -1 == left_operand_id ) {
1311 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1312 "IN condition has no left operand in expression # %d", id ));
1316 left_operand = getExpression( state, left_operand_id );
1317 if( !left_operand ) {
1318 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1319 "Unable to get left operand for IN condition in expression # %d", id ));
1325 if( -1 == subquery_id ) {
1326 // Load an IN list of subexpressions
1327 subexp_list = getExpressionList( state, id );
1328 if( state->error ) {
1329 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1330 "Unable to get subexpressions for IN list" ));
1332 } else if( !subexp_list ) {
1333 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1334 "IN list is empty in expression # %d", id ));
1340 subquery = getStoredQuery( state, subquery_id );
1342 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1343 "Unable to load subquery for IN expression # %d", id ));
1349 } else if( EXP_ISNULL == type ) {
1350 if( -1 == left_operand_id ) {
1351 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1352 "Expression # %d IS NULL has no left operand", id ));
1357 if( left_operand_id != -1 ) {
1358 left_operand = getExpression( state, left_operand_id );
1359 if( !left_operand ) {
1360 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1361 "Unable to get left operand in expression # %d", id ));
1367 } else if( EXP_NUMBER == type ) {
1369 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1370 "Numeric expression # %d provides no numeric value", id ));
1375 } else if( EXP_OPERATOR == type ) {
1376 // Load left and/or right operands
1377 if( -1 == left_operand_id && -1 == right_operand_id ) {
1378 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1379 "Expression # %d is an operator with no operands", id ));
1384 if( left_operand_id != -1 ) {
1385 left_operand = getExpression( state, left_operand_id );
1386 if( !left_operand ) {
1387 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1388 "Unable to get left operand in expression # %d", id ));
1394 if( right_operand_id != -1 ) {
1395 right_operand = getExpression( state, right_operand_id );
1396 if( !right_operand ) {
1397 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1398 "Unable to get right operand in expression # %d", id ));
1404 } else if( EXP_SERIES == type ) {
1405 subexp_list = getExpressionList( state, id );
1406 if( state->error ) {
1407 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1408 "Unable to get subexpressions for expression series using operator \"%s\"",
1409 operator ? operator : "," ));
1411 } else if( !subexp_list ) {
1412 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1413 "Series expression is empty in expression # %d", id ));
1418 } else if( EXP_STRING == type ) {
1420 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1421 "String expression # %d provides no string value", id ));
1426 } else if( EXP_SUBQUERY == type ) {
1427 if( -1 == subquery_id ) {
1428 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1429 "Subquery expression # %d has no query id", id ));
1433 // Load a subquery, if there is one
1434 subquery = getStoredQuery( state, subquery_id );
1436 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1437 "Unable to load subquery for expression # %d", id ));
1441 if( subquery->select_list && subquery->select_list->next ) {
1442 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1443 "Subquery # %d as expression returns more than one column", subquery_id ));
1447 PRINT( "\tExpression is subquery %d\n", subquery_id );
1451 // Allocate an Expression: from the free list if possible, from the heap if necessary
1452 Expression* exp = NULL;
1453 if( free_expression_list ) {
1454 exp = free_expression_list;
1455 free_expression_list = free_expression_list->next;
1457 exp = safe_malloc( sizeof( Expression ) );
1459 // Populate the Expression
1463 exp->parenthesize = parenthesize;
1464 exp->parent_expr_id = parent_expr_id;
1465 exp->seq_no = seq_no;
1466 exp->literal = literal ? strdup( literal ) : NULL;
1467 exp->table_alias = table_alias ? strdup( table_alias ) : NULL;
1468 exp->column_name = column_name ? strdup( column_name ) : NULL;
1469 exp->left_operand = left_operand;
1470 exp->op = operator ? strdup( operator ) : NULL;
1471 exp->right_operand = right_operand;
1472 exp->subquery_id = subquery_id;
1473 exp->subquery = subquery;
1474 exp->cast_type_id = subquery_id;
1475 exp->negate = negate;
1477 exp->subexp_list = subexp_list;
1478 exp->function_name = function_name ? strdup( function_name ) : NULL;
1484 @brief Free all the Expressions in a linked list of Expressions.
1485 @param exp Pointer to the first Expression in the list.
1487 static void expressionListFree( Expression* exp ) {
1489 Expression* next = exp->next;
1490 expressionFree( exp );
1496 @brief Deallocate an Expression.
1497 @param exp Pointer to the Expression to be deallocated.
1499 Free the strings owned by the Expression. Put the Expression itself, and any
1500 subexpressions that it owns, into a free list.
1502 static void expressionFree( Expression* exp ) {
1504 free( exp->literal );
1505 exp->literal = NULL;
1506 free( exp->table_alias );
1507 exp->table_alias = NULL;
1508 free( exp->column_name );
1509 exp->column_name = NULL;
1510 if( exp->left_operand ) {
1511 expressionFree( exp->left_operand );
1512 exp->left_operand = NULL;
1516 if( exp->right_operand ) {
1517 expressionFree( exp->right_operand );
1518 exp->right_operand = NULL;
1520 if( exp->subquery ) {
1521 storedQFree( exp->subquery );
1522 exp->subquery = NULL;
1525 // We don't free the bind member here because the Expression doesn't own it;
1526 // the bindvar_list hash owns it, so that multiple Expressions can reference it.
1528 if( exp->subexp_list ) {
1529 // Free the linked list of subexpressions
1530 expressionListFree( exp->subexp_list );
1531 exp->subexp_list = NULL;
1534 if( exp->function_name ) {
1535 free( exp->function_name );
1536 exp->function_name = NULL;
1539 // Prepend to the free list
1540 exp->next = free_expression_list;
1541 free_expression_list = exp;
1546 @brief Build a list of subexpressions.
1547 @param state Pointer to the query-building context.
1548 @param id ID of the parent Expression.
1549 @return A pointer to the first in a linked list of Expressions, if there are any; or
1550 NULL if there aren't any, or in case of an error.
1552 static Expression* getExpressionList( BuildSQLState* state, int id ) {
1553 Expression* exp_list = NULL;
1555 // The ORDER BY is in descending order so that we can build the list by adding to
1556 // the head, and it will wind up in the right order.
1557 dbi_result result = dbi_conn_queryf( state->dbhandle,
1558 "SELECT exp.id, exp.type, exp.parenthesize, exp.parent_expr, exp.seq_no, "
1559 "exp.literal, exp.table_alias, exp.column_name, exp.left_operand, exp.operator, "
1560 "exp.right_operand, exp.subquery, exp.cast_type, exp.negate, exp.bind_variable, "
1561 "func.function_name "
1562 "FROM query.expression AS exp LEFT JOIN query.function_sig AS func "
1563 "ON (exp.function_id = func.id) "
1564 "WHERE exp.parent_expr = %d "
1565 "ORDER BY exp.seq_no desc;", id );
1568 if( dbi_result_first_row( result ) ) {
1570 Expression* exp = constructExpression( state, result );
1572 PRINT( "Found a subexpression\n" );
1573 exp->next = exp_list;
1576 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1577 "Unable to build subexpression list for expression id #%d", id ));
1578 expressionListFree( exp_list );
1582 if( !dbi_result_next_row( result ) )
1588 int errnum = dbi_conn_error( state->dbhandle, &msg );
1589 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1590 "Unable to query query.expression table for expression list: #%d %s",
1591 errnum, msg ? msg : "No description available" ));
1599 @brief Build a list of ORDER BY items as a linked list of OrderItems.
1600 @param state Pointer to the query-building context.
1601 @param query_id ID for the query to which the ORDER BY belongs.
1602 @return Pointer to the first node in a linked list of OrderItems.
1604 The calling code is responsible for freeing the list by calling orderItemListFree().
1606 static OrderItem* getOrderByList( BuildSQLState* state, int query_id ) {
1607 OrderItem* ord_list = NULL;
1609 // The ORDER BY is in descending order so that we can build the list by adding to
1610 // the head, and it will wind up in the right order.
1611 dbi_result result = dbi_conn_queryf( state->dbhandle,
1612 "SELECT id, stored_query, seq_no, expression "
1613 "FROM query.order_by_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
1615 if( dbi_result_first_row( result ) ) {
1617 OrderItem* item = constructOrderItem( state, result );
1619 PRINT( "Found an ORDER BY item\n" );
1621 item->next = ord_list;
1624 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1625 "Unable to build ORDER BY item for query id #%d", query_id ));
1626 orderItemListFree( ord_list );
1630 if( !dbi_result_next_row( result ) )
1636 int errnum = dbi_conn_error( state->dbhandle, &msg );
1637 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1638 "Unable to query query.order_by_list table: #%d %s",
1639 errnum, msg ? msg : "No description available" ));
1647 @brief Construct an OrderItem.
1648 @param Pointer to the query-building context.
1649 @param result Database cursor positioned at a row in query.order_by_item.
1650 @return Pointer to a newly constructed OrderItem, if successful, or NULL if not.
1652 The calling code is responsible for freeing the OrderItems by calling orderItemListFree().
1654 static OrderItem* constructOrderItem( BuildSQLState* state, dbi_result result ) {
1655 int id = dbi_result_get_int_idx( result, 1 );
1656 int stored_query_id = dbi_result_get_int_idx( result, 2 );
1657 int seq_no = dbi_result_get_int_idx( result, 3 );
1658 int expression_id = dbi_result_get_int_idx( result, 4 );
1659 // Allocate a SelectItem: from the free list if possible, from the heap if necessary
1661 // Construct an Expression
1662 Expression* expression = getExpression( state, expression_id );
1664 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1665 "Unable to fetch ORDER BY expression for id = %d", expression_id ));
1670 // Allocate an OrderItem; from the free list if possible, or from the heap if necessary.
1672 if( free_order_item_list ) {
1673 ord = free_order_item_list;
1674 free_order_item_list = free_order_item_list->next;
1676 ord = safe_malloc( sizeof( OrderItem ));
1680 ord->stored_query_id = stored_query_id;
1681 ord->seq_no = seq_no;
1682 ord->expression = expression;
1688 @brief Deallocate a linked list of OrderItems.
1689 @param exp Pointer to the first OrderItem in the list to be deallocated.
1691 Deallocate the memory owned by the OrderItems. Put the items themselves into a free list.
1693 static void orderItemListFree( OrderItem* ord ) {
1695 return; // Nothing to free
1697 OrderItem* first = ord;
1699 expressionFree( ord->expression );
1700 ord->expression = NULL;
1702 if( NULL == ord->next ) {
1703 ord->next = free_order_item_list;
1709 // Transfer the entire list to the free list
1710 free_order_item_list = first;
1714 @brief Build a list of column names for a specified query.
1715 @param state Pointer to the query-building context.
1716 @param query Pointer to the specified query.
1717 @return Pointer to a newly-allocated JSON_ARRAY of column names.
1719 In the resulting array, each entry is either a JSON_STRING or (when no column name is
1720 available) a JSON_NULL.
1722 The calling code is responsible for freeing the list by calling jsonObjectFree().
1724 jsonObject* oilsGetColNames( BuildSQLState* state, StoredQ* query ) {
1725 if( !state || !query )
1728 // Save the outermost query id for possible use in an error message
1731 while( query->type != QT_SELECT ) {
1732 // If the query is a UNION, INTERSECT, or EXCEPT, there must be a SELECT in
1733 // there somewhere. Find the first one, and use the SELECT list from that.
1734 QSeq* child_list = query->child_list;
1739 query = child_list->child_query;
1744 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1745 "Unable to find first SELECT in query # %d", id ));
1749 // Get the SELECT list for the first SELECT
1750 SelectItem* col = query->select_list;
1753 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1754 "First SELECT in query # %d has empty SELECT list", id ));
1758 jsonObject* col_list = jsonNewObjectType( JSON_ARRAY );
1760 // Traverse the list, adding an entry for each
1762 const char* alias = NULL;
1763 if( col->column_alias )
1764 alias = col->column_alias;
1766 Expression* expression = col->expression;
1767 if( expression && EXP_COLUMN == expression->type && expression->column_name )
1768 alias = expression->column_name;
1771 jsonObjectPush( col_list, jsonNewObject( alias ) );
1779 @brief Push an IdNode onto a stack of IdNodes.
1780 @param stack Pointer to the stack.
1781 @param id Id of the new node.
1782 @param alias Alias, if any, of the new node.
1784 static void push_id( IdNode** stack, int id, const char* alias ) {
1787 // Allocate a node; from the free list if possible, from the heap if necessary.
1788 IdNode* node = NULL;
1789 if( free_id_node_list ) {
1790 node = free_id_node_list;
1791 free_id_node_list = free_id_node_list->next;
1793 node = safe_malloc( sizeof( IdNode ));
1796 node->next = *stack;
1799 node->alias = strdup( alias );
1809 @brief Remove the node at the top of an IdNode stack.
1810 @param stack Pointer to the IdNode stack.
1812 void pop_id( IdNode** stack ) {
1814 IdNode* node = *stack;
1815 *stack = node->next;
1818 free( node->alias );
1822 node->next = free_id_node_list;
1823 free_id_node_list = node;
1828 @brief Search a stack of IDs for a match by either ID or, optionally, by alias.
1829 @param stack Pointer to the stack.
1830 @param id The id to search for.
1831 @param alias (Optional) the alias to search for.
1832 @return A pointer to the matching node if one is found, or NULL if not.
1834 This search is used to detect cases where a query, expression, or FROM clause is nested
1835 inside itself, in order to avoid infinite recursion; or in order to avoid conflicting
1836 table references in a FROM clause.
1838 static const IdNode* searchIdStack( const IdNode* stack, int id, const char* alias ) {
1840 const IdNode* node = stack;
1842 if( node->id == id )
1843 return node; // Matched on id
1844 else if( alias && node->alias && !strcmp( alias, node->alias ))
1845 return node; // Matched on alias
1850 return NULL; // No match found
1854 @brief Free up any resources held by the StoredQ module.
1856 void storedQCleanup( void ) {
1858 // Free all the nodes in the free state list
1859 StoredQ* sq = free_storedq_list;
1861 free_storedq_list = sq->next;
1863 sq = free_storedq_list;
1866 // Free all the nodes in the free from_relation list
1867 FromRelation* fr = free_from_relation_list;
1869 free_from_relation_list = fr->next;
1871 fr = free_from_relation_list;
1874 // Free all the nodes in the free expression list
1875 Expression* exp = free_expression_list;
1877 free_expression_list = exp->next;
1879 exp = free_expression_list;
1882 // Free all the nodes in the free select item list
1883 SelectItem* sel = free_select_item_list;
1885 free_select_item_list = sel->next;
1887 sel = free_select_item_list;
1890 // Free all the nodes in the free select item list
1891 IdNode* node = free_id_node_list;
1893 free_id_node_list = node->next;
1895 node = free_id_node_list;
1898 // Free all the nodes in the free query sequence list
1899 QSeq* seq = free_qseq_list;
1901 free_qseq_list = seq->next;
1903 seq = free_qseq_list;
1906 // Free all the nodes in the free order item list
1907 OrderItem* ord = free_order_item_list;
1909 free_order_item_list = ord->next;
1911 ord = free_order_item_list;
1914 // Free all the nodes in the bind variable free list
1915 BindVar* bind = free_bindvar_list;
1917 free_bindvar_list = bind->next;
1919 bind = free_bindvar_list;
1924 @brief Return a boolean value from a database result.
1925 @param result The database result.
1926 @param i Index of the column in the result, starting with 1 );
1927 @return 1 if true, or 0 for false.
1929 Null values and error conditions are interpreted as FALSE.
1931 static int oils_result_get_bool_idx( dbi_result result, int i ) {
1933 const char* str = dbi_result_get_string_idx( result, i );
1934 return (str && *str == 't' ) ? 1 : 0;
1940 @brief Enable verbose messages.
1942 The messages are written to standard output, which for a server is /dev/null. Hence this
1943 option is useful only for a non-server. It is intended only as a convenience for
1944 development and debugging.
1946 void oilsStoredQSetVerbose( void ) {