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 expressionFree( Expression* exp );
48 static OrderItem* getOrderByList( BuildSQLState* state, int query_id );
49 static OrderItem* constructOrderItem( BuildSQLState* state, dbi_result result );
50 static void orderItemListFree( OrderItem* ord );
52 static void push_id( IdNode** stack, int id, const char* alias );
53 static const IdNode* searchIdStack( const IdNode* stack, int id, const char* alias );
55 // A series of free lists to store already-allocated objects that are not in use, for
56 // potential reuse. This is a hack to reduce churning through malloc() and free().
57 static StoredQ* free_storedq_list = NULL;
58 static FromRelation* free_from_relation_list = NULL;
59 static SelectItem* free_select_item_list = NULL;
60 static BindVar* free_bindvar_list = NULL;
61 static Expression* free_expression_list = NULL;
62 static IdNode* free_id_node_list = NULL;
63 static QSeq* free_qseq_list = NULL;
64 static OrderItem* free_order_item_list = NULL;
66 // Boolean; settable by call to oilsStoredQSetVerbose(), used by PRINT macro.
67 // The idea is to allow debugging messages from a command line test driver for ease of
68 // testing and development, but not from a real server, where messages to stdout don't
70 static int verbose = 0;
73 @brief Load a stored query.
74 @param state Pointer to the query-building context.
75 @param query_id ID of the query in query.stored_query.
76 @return A pointer to the newly loaded StoredQ if successful, or NULL if not.
78 The calling code is responsible for freeing the StoredQ by calling storedQFree().
80 StoredQ* getStoredQuery( BuildSQLState* state, int query_id ) {
84 // Check the stack to see if the current query is nested inside itself. If it is, then
85 // abort in order to avoid infinite recursion. If it isn't, then add it to the stack.
86 // (Make sure to pop it off the stack before returning.)
87 if( searchIdStack( state->query_stack, query_id, NULL )) {
88 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
89 "Infinite recursion detected; query # %d is nested within itself", query_id ));
93 push_id( &state->query_stack, query_id, NULL );
96 dbi_result result = dbi_conn_queryf( state->dbhandle,
97 "SELECT id, type, use_all, use_distinct, from_clause, where_clause, having_clause "
98 "FROM query.stored_query WHERE id = %d;", query_id );
100 if( dbi_result_first_row( result ) ) {
101 sq = constructStoredQ( state, result );
103 PRINT( "Got a query row\n" );
104 PRINT( "\tid: %d\n", sq->id );
105 PRINT( "\ttype: %d\n", (int) sq->type );
106 PRINT( "\tuse_all: %s\n", sq->use_all ? "true" : "false" );
107 PRINT( "\tuse_distinct: %s\n", sq->use_distinct ? "true" : "false" );
109 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
110 "Unable to build a query for id = %d", query_id ));
112 sqlAddMsg( state, "Stored query not found for id %d", query_id );
115 dbi_result_free( result );
118 int errnum = dbi_conn_error( state->dbhandle, &msg );
119 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
120 "Unable to query query.stored_query table: #%d %s",
121 errnum, msg ? msg : "No description available" ));
124 pop_id( &state->query_stack );
129 @brief Construct a StoredQ.
130 @param Pointer to the query-building context.
131 @param result Database cursor positioned at a row in query.stored_query.
132 @return Pointer to a newly constructed StoredQ, if successful, or NULL if not.
134 The calling code is responsible for freeing the StoredQ by calling storedQFree().
136 static StoredQ* constructStoredQ( BuildSQLState* state, dbi_result result ) {
138 // Get the column values from the result
139 int id = dbi_result_get_int_idx( result, 1 );
140 const char* type_str = dbi_result_get_string_idx( result, 2 );
143 if( !strcmp( type_str, "SELECT" ))
145 else if( !strcmp( type_str, "UNION" ))
147 else if( !strcmp( type_str, "INTERSECT" ))
149 else if( !strcmp( type_str, "EXCEPT" ))
152 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
153 "Invalid query type \"%s\"", type_str ));
157 int use_all = oils_result_get_bool_idx( result, 3 );
158 int use_distinct = oils_result_get_bool_idx( result, 4 );
161 if( dbi_result_field_is_null_idx( result, 5 ) )
164 from_clause_id = dbi_result_get_int_idx( result, 5 );
167 if( dbi_result_field_is_null_idx( result, 6 ) )
168 where_clause_id = -1;
170 where_clause_id = dbi_result_get_int_idx( result, 6 );
172 int having_clause_id;
173 if( dbi_result_field_is_null_idx( result, 7 ) )
174 having_clause_id = -1;
176 having_clause_id = dbi_result_get_int_idx( result, 7 );
178 FromRelation* from_clause = NULL;
179 if( QT_SELECT == type ) {
180 // A SELECT query needs a FROM clause; go get it
181 if( from_clause_id != -1 ) {
182 from_clause = getFromRelation( state, from_clause_id );
184 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
185 "Unable to construct FROM clause for id = %d", from_clause_id ));
190 // Must be one of UNION, INTERSECT, or EXCEPT
191 if( from_clause_id != -1 )
192 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
193 "FROM clause found and ignored for %s query in query #%d", type_str, id ));
196 // If this is a SELECT query, we need a SELECT list. Go get one.
197 SelectItem* select_list = NULL;
198 QSeq* child_list = NULL;
199 if( QT_SELECT == type ) {
200 select_list = getSelectList( state, id );
202 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
203 "No SELECT list found for query id = %d", id ));
204 fromRelationFree( from_clause );
208 // Construct child queries of UNION, INTERSECT, or EXCEPT query
209 child_list = loadChildQueries( state, id, type_str );
211 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
212 "Unable to load child queries for %s query # %d", type_str, id ));
214 fromRelationFree( from_clause );
219 // Get the WHERE clause, if there is one
220 Expression* where_clause = NULL;
221 if( where_clause_id != -1 ) {
222 where_clause = getExpression( state, where_clause_id );
223 if( ! where_clause ) {
224 // shouldn't happen due to foreign key constraint
225 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
226 "Unable to fetch WHERE expression for query id = %d", id ));
227 freeQSeqList( child_list );
228 fromRelationFree( from_clause );
229 selectListFree( select_list );
235 Expression* having_clause = NULL;
236 if( having_clause_id != -1 ) {
237 having_clause = getExpression( state, having_clause_id );
238 if( ! having_clause ) {
239 // shouldn't happen due to foreign key constraint
240 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
241 "Unable to fetch HAVING expression for query id = %d", id ));
242 expressionFree( where_clause );
243 freeQSeqList( child_list );
244 fromRelationFree( from_clause );
245 selectListFree( select_list );
251 // Get the ORDER BY clause, if there is one
252 OrderItem* order_by_list = getOrderByList( state, id );
254 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
255 "Unable to load ORDER BY clause for query %d", id ));
256 expressionFree( having_clause );
257 expressionFree( where_clause );
258 freeQSeqList( child_list );
259 fromRelationFree( from_clause );
260 selectListFree( select_list );
264 // Allocate a StoredQ: from the free list if possible, from the heap if necessary
267 if( free_storedq_list ) {
268 sq = free_storedq_list;
269 free_storedq_list = free_storedq_list->next;
271 sq = safe_malloc( sizeof( StoredQ ) );
273 // Populate the StoredQ
278 sq->use_all = use_all;
279 sq->use_distinct = use_distinct;
280 sq->from_clause = from_clause;
281 sq->where_clause = where_clause;
282 sq->select_list = select_list;
283 sq->child_list = child_list;
284 sq->having_clause = having_clause;
285 sq->order_by_list = order_by_list;
291 @brief Load the child queries subordinate to a UNION, INTERSECT, or EXCEPT query.
292 @param state Pointer to the query-building context.
293 @param parent ID of the UNION, INTERSECT, or EXCEPT query.
294 @param type_str The type of the query ("UNION", "INTERSECT", or "EXCEPT").
295 @return If successful, a pointer to a linked list of QSeq, each bearing a pointer to a
296 StoredQ; otherwise NULL.
298 The @a type_str parameter is used only for building error messages.
300 static QSeq* loadChildQueries( BuildSQLState* state, int parent_id, const char* type_str ) {
301 QSeq* child_list = NULL;
303 // The ORDER BY is in descending order so that we can build the list by adding to
304 // the head, and it will wind up in the right order.
305 dbi_result result = dbi_conn_queryf( state->dbhandle,
306 "SELECT id, parent_query, seq_no, child_query "
307 "FROM query.query_sequence WHERE parent_query = %d ORDER BY seq_no DESC", parent_id );
309 if( dbi_result_first_row( result ) ) {
313 QSeq* seq = constructQSeq( state, result );
315 PRINT( "Found a child query\n" );
316 PRINT( "\tid: %d\n", seq->id );
317 PRINT( "\tparent id: %d\n", seq->parent_query_id );
318 PRINT( "\tseq_no: %d\n", seq->seq_no );
319 // Add to the head of the list
320 seq->next = child_list;
323 freeQSeqList( child_list );
326 if( !dbi_result_next_row( result ))
330 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
331 "%s query # %d has only one child query", type_str, parent_id ));
333 freeQSeqList( child_list );
337 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
338 "%s query # %d has no child queries within it", type_str, parent_id ));
344 int errnum = dbi_conn_error( state->dbhandle, &msg );
345 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
346 "Unable to query query.query_sequence table: # %d %s",
347 errnum, msg ? msg : "No description available" ));
356 @brief Construct a QSeq.
357 @param Pointer to the query-building context.
358 @param result Database cursor positioned at a row in query.query_sequence.
359 @return Pointer to a newly constructed QSeq, if successful, or NULL if not.
361 The calling code is responsible for freeing QSeqs by calling freeQSeqList().
363 static QSeq* constructQSeq( BuildSQLState* state, dbi_result result ) {
364 int id = dbi_result_get_int_idx( result, 1 );
365 int parent_query_id = dbi_result_get_int_idx( result, 2 );
366 int seq_no = dbi_result_get_int_idx( result, 3 );
367 int child_query_id = dbi_result_get_int_idx( result, 4 );
369 StoredQ* child_query = getStoredQuery( state, child_query_id );
371 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
372 "Unable to load child query # %d for parent query %d",
373 child_query_id, parent_query_id ));
378 // Allocate a QSeq; from the free list if possible, from the heap if necessary
380 if( free_qseq_list ) {
381 seq = free_qseq_list;
382 free_qseq_list = free_qseq_list->next;
384 seq = safe_malloc( sizeof( QSeq ));
388 seq->parent_query_id = parent_query_id;
389 seq->seq_no = seq_no;
390 seq->child_query = child_query;
396 @brief Free a list of QSeq's.
397 @param seq Pointer to the first in a linked list of QSeq's to be freed.
399 Each QSeq goes onto a free list for potential reuse.
401 static void freeQSeqList( QSeq* seq ) {
407 storedQFree( seq->child_query );
408 seq->child_query = NULL;
413 seq->next = free_qseq_list;
418 free_qseq_list = first;
422 @brief Deallocate the memory owned by a StoredQ.
423 @param sq Pointer to the StoredQ to be deallocated.
425 void storedQFree( StoredQ* sq ) {
427 fromRelationFree( sq->from_clause );
428 sq->from_clause = NULL;
429 selectListFree( sq->select_list );
430 sq->select_list = NULL;
431 expressionFree( sq->where_clause );
432 sq->where_clause = NULL;
433 if( sq->child_list ) {
434 freeQSeqList( sq->child_list );
435 sq->child_list = NULL;
437 if( sq->order_by_list ) {
438 orderItemListFree( sq->order_by_list );
439 sq->order_by_list = NULL;
441 if( sq->having_clause )
442 expressionFree( sq->having_clause );
444 // Stick the empty husk on the free list for potential reuse
445 sq->next = free_storedq_list;
446 free_storedq_list = sq;
450 static FromRelation* getFromRelation( BuildSQLState* state, int id ) {
451 FromRelation* fr = NULL;
452 dbi_result result = dbi_conn_queryf( state->dbhandle,
453 "SELECT id, type, table_name, class_name, subquery, function_call, "
454 "table_alias, parent_relation, seq_no, join_type, on_clause "
455 "FROM query.from_relation WHERE id = %d;", id );
457 if( dbi_result_first_row( result ) ) {
458 fr = constructFromRelation( state, result );
460 PRINT( "Got a from_relation row\n" );
461 PRINT( "\tid: %d\n", fr->id );
462 PRINT( "\ttype: %d\n", (int) fr->type );
463 PRINT( "\ttable_name: %s\n", fr->table_name ? fr->table_name : "(none)" );
464 PRINT( "\tclass_name: %s\n", fr->class_name ? fr->class_name : "(none)" );
465 PRINT( "\tsubquery_id: %d\n", fr->subquery_id );
466 PRINT( "\tfunction_call_id: %d\n", fr->function_call_id );
467 PRINT( "\ttable_alias: %s\n", fr->table_alias ? fr->table_alias : "(none)" );
468 PRINT( "\tparent_relation_id: %d\n", fr->parent_relation_id );
469 PRINT( "\tseq_no: %d\n", fr->seq_no );
470 PRINT( "\tjoin_type = %d\n", fr->join_type );
471 // Check the stack to see if the current from clause is nested inside itself.
472 // If it is, then abort in order to avoid infinite recursion. If it isn't,
473 // then add it to the stack. (Make sure to pop it off the stack before
475 const char* effective_alias = fr->table_alias;
476 if( !effective_alias )
477 effective_alias = fr->class_name;
478 const IdNode* node = searchIdStack( state->from_stack, id, effective_alias );
481 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
482 "Infinite recursion detected; from clause # %d is nested "
483 "within itself", id ));
485 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
486 "Conflicting nested table aliases \"%s\" in from clause # %d",
487 effective_alias, node->id ));
491 push_id( &state->from_stack, id, effective_alias );
493 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
494 "Unable to build a FromRelation for id = %d", id ));
496 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
497 "FROM relation not found for id = %d", id ));
499 dbi_result_free( result );
502 int errnum = dbi_conn_error( state->dbhandle, &msg );
503 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
504 "Unable to query query.from_relation table: #%d %s",
505 errnum, msg ? msg : "No description available" ));
509 pop_id( &state->from_stack );
515 @brief Construct a FromRelation.
516 @param Pointer to the query-building context.
517 @param result Database cursor positioned at a row in query.from_relation.
518 @return Pointer to a newly constructed FromRelation, if successful, or NULL if not.
520 The calling code is responsible for freeing FromRelations by calling joinListFree().
522 static FromRelation* constructFromRelation( BuildSQLState* state, dbi_result result ) {
523 // Get the column values from the result
524 int id = dbi_result_get_int_idx( result, 1 );
525 const char* type_str = dbi_result_get_string_idx( result, 2 );
527 FromRelationType type;
528 if( !strcmp( type_str, "RELATION" ))
530 else if( !strcmp( type_str, "SUBQUERY" ))
532 else if( !strcmp( type_str, "FUNCTION" ))
535 type = FRT_RELATION; // shouldn't happen due to database constraint
537 const char* table_name = dbi_result_get_string_idx( result, 3 );
538 const char* class_name = dbi_result_get_string_idx( result, 4 );
541 if( dbi_result_field_is_null_idx( result, 5 ) )
544 subquery_id = dbi_result_get_int_idx( result, 5 );
546 int function_call_id;
547 if( dbi_result_field_is_null_idx( result, 6 ) )
548 function_call_id = -1;
550 function_call_id = dbi_result_get_int_idx( result, 6 );
552 const char* table_alias = dbi_result_get_string_idx( result, 7 );
554 int parent_relation_id;
555 if( dbi_result_field_is_null_idx( result, 8 ) )
556 parent_relation_id = -1;
558 parent_relation_id = dbi_result_get_int_idx( result, 8 );
560 int seq_no = dbi_result_get_int_idx( result, 9 );
563 const char* join_type_str = dbi_result_get_string_idx( result, 10 );
566 else if( !strcmp( join_type_str, "INNER" ) )
567 join_type = JT_INNER;
568 else if( !strcmp( join_type_str, "LEFT" ) )
570 else if( !strcmp( join_type_str, "RIGHT" ) )
571 join_type = JT_RIGHT;
572 else if( !strcmp( join_type_str, "FULL" ) )
575 join_type = JT_NONE; // shouldn't happen due to database constraint
578 if( dbi_result_field_is_null_idx( result, 11 ) )
581 on_clause_id = dbi_result_get_int_idx( result, 11 );
583 StoredQ* subquery = NULL;
589 if( -1 == subquery_id ) {
590 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
591 "Internal error: no subquery specified for FROM relation # %d", id ));
595 if( ! table_alias ) {
596 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
597 "Subquery needs alias in FROM relation # %d", id ));
601 subquery = getStoredQuery( state, subquery_id );
603 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
604 "Unable to load subquery for FROM relation # %d", id ));
610 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
611 "Functions in FROM clause not yet supported" ));
616 FromRelation* join_list = getJoinList( state, id );
618 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
619 "Unable to load join list for FROM relation # %d", id ));
623 Expression* on_clause = NULL;
624 if( on_clause_id != -1 ) {
625 on_clause = getExpression( state, on_clause_id );
627 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
628 "Unable to load ON condition for FROM relation # %d", id ));
629 joinListFree( join_list );
634 PRINT( "\tGot an ON condition\n" );
637 // Allocate a FromRelation: from the free list if possible, from the heap if necessary
640 if( free_from_relation_list ) {
641 fr = free_from_relation_list;
642 free_from_relation_list = free_from_relation_list->next;
644 fr = safe_malloc( sizeof( FromRelation ) );
646 // Populate the FromRelation
651 fr->table_name = table_name ? strdup( table_name ) : NULL;
652 fr->class_name = class_name ? strdup( class_name ) : NULL;
653 fr->subquery_id = subquery_id;
654 fr->subquery = subquery;
655 fr->function_call_id = function_call_id;
656 fr->table_alias = table_alias ? strdup( table_alias ) : NULL;
657 fr->parent_relation_id = parent_relation_id;
659 fr->join_type = join_type;
660 fr->on_clause = on_clause;
661 fr->join_list = join_list;
667 @brief Build a list of joined relations.
668 @param state Pointer to the query-building context.
669 @param id ID of the parent relation.
670 @return A pointer to the first in a linked list of FromRelations, if there are any; or
671 NULL if there aren't any, or in case of an error.
673 Look for relations joined directly to the parent relation, and make a list of them.
675 static FromRelation* getJoinList( BuildSQLState* state, int id ) {
676 FromRelation* join_list = NULL;
678 // The ORDER BY is in descending order so that we can build the list by adding to
679 // the head, and it will wind up in the right order.
680 dbi_result result = dbi_conn_queryf( state->dbhandle,
681 "SELECT id, type, table_name, class_name, subquery, function_call, "
682 "table_alias, parent_relation, seq_no, join_type, on_clause "
683 "FROM query.from_relation WHERE parent_relation = %d ORDER BY seq_no DESC", id );
686 if( dbi_result_first_row( result ) ) {
688 FromRelation* relation = constructFromRelation( state, result );
690 PRINT( "Found a joined relation\n" );
691 PRINT( "\tjoin_type: %d\n", relation->join_type );
692 PRINT( "\ttable_name: %s\n", relation->table_name );
693 relation->next = join_list;
694 join_list = relation;
696 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
697 "Unable to build join list for from relation id #%d", id ));
698 joinListFree( join_list );
702 if( !dbi_result_next_row( result ) )
708 int errnum = dbi_conn_error( state->dbhandle, &msg );
709 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
710 "Unable to query query.from_relation table for join list: #%d %s",
711 errnum, msg ? msg : "No description available" ));
718 @brief Free a list of FromRelations.
719 @param join_list Pointer to the first FromRelation in the list.
721 static void joinListFree( FromRelation* join_list ) {
723 FromRelation* temp = join_list->next;
724 fromRelationFree( join_list );
730 @brief Deallocate a FromRelation.
731 @param fr Pointer to the FromRelation to be freed.
733 Free the strings that the FromRelation owns. The FromRelation itself goes onto a
734 free list for potential reuse.
736 static void fromRelationFree( FromRelation* fr ) {
738 free( fr->table_name );
739 fr->table_name = NULL;
740 free( fr->class_name );
741 fr->class_name = NULL;
743 storedQFree( fr->subquery );
746 free( fr->table_alias );
747 fr->table_alias = NULL;
748 if( fr->on_clause ) {
749 expressionFree( fr->on_clause );
750 fr->on_clause = NULL;
752 joinListFree( fr->join_list );
753 fr->join_list = NULL;
755 fr->next = free_from_relation_list;
756 free_from_relation_list = fr;
760 static SelectItem* getSelectList( BuildSQLState* state, int query_id ) {
761 SelectItem* select_list = NULL;
763 // The ORDER BY is in descending order so that we can build the list by adding to
764 // the head, and it will wind up in the right order.
765 dbi_result result = dbi_conn_queryf( state->dbhandle,
766 "SELECT id, stored_query, seq_no, expression, column_alias, grouped_by "
767 "FROM query.select_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
769 if( dbi_result_first_row( result ) ) {
771 SelectItem* item = constructSelectItem( state, result );
773 PRINT( "Found a SELECT item\n" );
774 PRINT( "\tid: %d\n", item->id );
775 PRINT( "\tstored_query_id: %d\n", item->stored_query_id );
776 PRINT( "\tseq_no: %d\n", item->seq_no );
777 PRINT( "\tcolumn_alias: %s\n",
778 item->column_alias ? item->column_alias : "(none)" );
779 PRINT( "\tgrouped_by: %d\n", item->grouped_by );
781 item->next = select_list;
784 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
785 "Unable to build select list for query id #%d", query_id ));
786 selectListFree( select_list );
790 if( !dbi_result_next_row( result ) )
796 int errnum = dbi_conn_error( state->dbhandle, &msg );
797 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
798 "Unable to query query.select_list table: #%d %s",
799 errnum, msg ? msg : "No description available" ));
806 @brief Construct a SelectItem.
807 @param Pointer to the query-building context.
808 @param result Database cursor positioned at a row in query.select_item.
809 @return Pointer to a newly constructed SelectItem, if successful, or NULL if not.
811 The calling code is responsible for freeing the SelectItems by calling selectListFree().
813 static SelectItem* constructSelectItem( BuildSQLState* state, dbi_result result ) {
815 // Get the column values
816 int id = dbi_result_get_int_idx( result, 1 );
817 int stored_query_id = dbi_result_get_int_idx( result, 2 );
818 int seq_no = dbi_result_get_int_idx( result, 3 );
819 int expression_id = dbi_result_get_int_idx( result, 4 );
820 const char* column_alias = dbi_result_get_string_idx( result, 5 );
821 int grouped_by = oils_result_get_bool_idx( result, 6 );
823 // Construct an Expression
824 Expression* expression = getExpression( state, expression_id );
826 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
827 "Unable to fetch expression for id = %d", expression_id ));
832 // Allocate a SelectItem: from the free list if possible, from the heap if necessary
835 if( free_select_item_list ) {
836 sel = free_select_item_list;
837 free_select_item_list = free_select_item_list->next;
839 sel = safe_malloc( sizeof( SelectItem ) );
843 sel->stored_query_id = stored_query_id;
844 sel->seq_no = seq_no;
845 sel->expression = expression;
846 sel->column_alias = column_alias ? strdup( column_alias ) : NULL;
847 sel->grouped_by = grouped_by;
853 @brief Free a list of SelectItems.
854 @param sel Pointer to the first item in the list to be freed.
856 Free the column alias and expression owned by each item. Put the entire list into a free
859 static void selectListFree( SelectItem* sel ) {
861 return; // Nothing to free
863 SelectItem* first = sel;
865 free( sel->column_alias );
866 sel->column_alias = NULL;
867 expressionFree( sel->expression );
868 sel->expression = NULL;
870 if( NULL == sel->next ) {
871 sel->next = free_select_item_list;
877 // Transfer the entire list to the free list
878 free_select_item_list = first;
882 @brief Given the name of a bind variable, build a corresponding BindVar.
883 @param state Pointer to the query-building context.
884 @param name Name of the bind variable.
885 @return Pointer to the newly-built BindVar.
887 Since the same bind variable may appear multiple times, we load it only once for the
888 entire query, and reference the one copy wherever needed.
890 static BindVar* getBindVar( BuildSQLState* state, const char* name ) {
891 BindVar* bind = NULL;
892 if( state->bindvar_list ) {
893 bind = osrfHashGet( state->bindvar_list, name );
895 return bind; // Already loaded it...
898 // Load a BindVar from the Database.
899 dbi_result result = dbi_conn_queryf( state->dbhandle,
900 "SELECT name, type, description, default_value, label "
901 "FROM query.bind_variable WHERE name = \'%s\';", name );
903 if( dbi_result_first_row( result ) ) {
904 bind = constructBindVar( state, result );
906 PRINT( "Got a bind variable for %s\n", name );
908 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
909 "Unable to load bind variable \"%s\"", name ));
911 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
912 "No bind variable found with name \"%s\"", name ));
916 int errnum = dbi_conn_error( state->dbhandle, &msg );
917 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
918 "Unable to query query.bind_variable table for \"%s\": #%d %s",
919 name, errnum, msg ? msg : "No description available" ));
923 // Add the new bind variable to the list
924 if( !state->bindvar_list ) {
925 // Don't have a list yet? Start one.
926 state->bindvar_list = osrfNewHash();
927 osrfHashSetCallback( state->bindvar_list, bindVarFree );
929 osrfHashSet( state->bindvar_list, bind, name );
937 @brief Construct a BindVar to represent a bind variable.
938 @param Pointer to the query-building context.
939 @param result Database cursor positioned at a row in query.bind_variable.
940 @return Pointer to a newly constructed BindVar, if successful, or NULL if not.
942 The calling code is responsible for freeing the BindVar by calling bindVarFree().
944 static BindVar* constructBindVar( BuildSQLState* state, dbi_result result ) {
946 const char* name = dbi_result_get_string_idx( result, 1 );
948 const char* type_str = dbi_result_get_string_idx( result, 2 );
950 if( !strcmp( type_str, "string" ))
952 else if( !strcmp( type_str, "number" ))
954 else if( !strcmp( type_str, "string_list" ))
955 type = BIND_STR_LIST;
956 else if( !strcmp( type_str, "number_list" ))
957 type = BIND_NUM_LIST;;
959 const char* description = dbi_result_get_string_idx( result, 3 );
961 // The default value is encoded as JSON. Translate it into a jsonObject.
962 const char* default_value_str = dbi_result_get_string_idx( result, 4 );
963 jsonObject* default_value = NULL;
964 if( default_value_str ) {
965 default_value = jsonParse( default_value_str );
966 if( !default_value ) {
967 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
968 "Unable to parse JSON string for default value of bind variable \"%s\"",
975 const char* label = dbi_result_get_string_idx( result, 5 );
977 // Allocate a BindVar: from the free list if possible, from the heap if necessary
978 BindVar* bind = NULL;
979 if( free_bindvar_list ) {
980 bind = free_bindvar_list;
981 free_bindvar_list = free_bindvar_list->next;
983 bind = safe_malloc( sizeof( BindVar ) );
986 bind->name = strdup( name );
987 bind->label = strdup( label );
989 bind->description = strdup( description );
990 bind->default_value = default_value;
991 bind->actual_value = NULL;
997 @brief Deallocate a BindVar.
998 @param key Pointer to the bind variable name (not used).
999 @param p Pointer to the BindVar to be deallocated, cast to a void pointer.
1001 Free the strings and jsonObjects owned by the BindVar, and put the BindVar itself into a
1004 This function is a callback installed in an osrfHash; hence the peculiar signature.
1006 static void bindVarFree( char* key, void* p ) {
1010 free( bind->label );
1011 free( bind->description );
1012 if( bind->default_value ) {
1013 jsonObjectFree( bind->default_value );
1014 bind->default_value = NULL;
1016 if( bind->actual_value ) {
1017 jsonObjectFree( bind->actual_value );
1018 bind->actual_value = NULL;
1021 // Prepend to free list
1022 bind->next = free_bindvar_list;
1023 free_bindvar_list = bind;
1028 @brief Given an id for a row in query.expression, build an Expression struct.
1029 @param Pointer to the query-building context.
1030 @param id ID of a row in query.expression.
1031 @return Pointer to a newly-created Expression if successful, or NULL if not.
1033 static Expression* getExpression( BuildSQLState* state, int id ) {
1035 // Check the stack to see if the current expression is nested inside itself. If it is,
1036 // then abort in order to avoid infinite recursion. If it isn't, then add it to the
1037 // stack. (Make sure to pop it off the stack before returning.)
1038 if( searchIdStack( state->expr_stack, id, NULL )) {
1039 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1040 "Infinite recursion detected; expression # %d is nested within itself", id ));
1044 push_id( &state->expr_stack, id, NULL );
1046 Expression* exp = NULL;
1047 dbi_result result = dbi_conn_queryf( state->dbhandle,
1048 "SELECT id, type, parenthesize, parent_expr, seq_no, literal, table_alias, column_name, "
1049 "left_operand, operator, right_operand, function_id, subquery, cast_type, negate, "
1051 "FROM query.expression WHERE id = %d;", id );
1053 if( dbi_result_first_row( result ) ) {
1054 exp = constructExpression( state, result );
1056 PRINT( "Got an expression\n" );
1057 PRINT( "\tid = %d\n", exp->id );
1058 PRINT( "\ttype = %d\n", exp->type );
1059 PRINT( "\tparenthesize = %d\n", exp->parenthesize );
1060 PRINT( "\tcolumn_name = %s\n", exp->column_name ? exp->column_name : "(none)" );
1062 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1063 "Unable to construct an Expression for id = %d", id ));
1067 int errnum = dbi_conn_error( state->dbhandle, &msg );
1068 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1069 "Unable to query query.expression table: #%d %s",
1070 errnum, msg ? msg : "No description available" ));
1073 pop_id( &state->expr_stack );
1078 @brief Construct an Expression.
1079 @param Pointer to the query-building context.
1080 @param result Database cursor positioned at a row in query.expression.
1081 @return Pointer to a newly constructed Expression, if successful, or NULL if not.
1083 The calling code is responsible for freeing the Expression by calling expressionFree().
1085 static Expression* constructExpression( BuildSQLState* state, dbi_result result ) {
1087 int id = dbi_result_get_int_idx( result, 1 );
1088 const char* type_str = dbi_result_get_string_idx( result, 2 );
1091 if( !strcmp( type_str, "xbet" ))
1093 else if( !strcmp( type_str, "xbind" ))
1095 else if( !strcmp( type_str, "xbool" ))
1097 else if( !strcmp( type_str, "xcase" ))
1099 else if( !strcmp( type_str, "xcast" ))
1101 else if( !strcmp( type_str, "xcol" ))
1103 else if( !strcmp( type_str, "xex" ))
1105 else if( !strcmp( type_str, "xfld" ))
1107 else if( !strcmp( type_str, "xfunc" ))
1108 type = EXP_FUNCTION;
1109 else if( !strcmp( type_str, "xin" ))
1111 else if( !strcmp( type_str, "xisnull" ))
1113 else if( !strcmp( type_str, "xnull" ))
1115 else if( !strcmp( type_str, "xnum" ))
1117 else if( !strcmp( type_str, "xop" ))
1118 type = EXP_OPERATOR;
1119 else if( !strcmp( type_str, "xstr" ))
1121 else if( !strcmp( type_str, "xsubq" ))
1122 type = EXP_SUBQUERY;
1124 type = EXP_NULL; // shouldn't happen due to database constraint
1126 int parenthesize = oils_result_get_bool_idx( result, 3 );
1129 if( dbi_result_field_is_null_idx( result, 4 ))
1130 parent_expr_id = -1;
1132 parent_expr_id = dbi_result_get_int_idx( result, 4 );
1134 int seq_no = dbi_result_get_int_idx( result, 5 );
1135 const char* literal = dbi_result_get_string_idx( result, 6 );
1136 const char* table_alias = dbi_result_get_string_idx( result, 7 );
1137 const char* column_name = dbi_result_get_string_idx( result, 8 );
1139 int left_operand_id;
1140 if( dbi_result_field_is_null_idx( result, 9 ))
1141 left_operand_id = -1;
1143 left_operand_id = dbi_result_get_int_idx( result, 9 );
1145 const char* operator = dbi_result_get_string_idx( result, 10 );
1147 int right_operand_id;
1148 if( dbi_result_field_is_null_idx( result, 11 ))
1149 right_operand_id = -1;
1151 right_operand_id = dbi_result_get_int_idx( result, 11 );
1154 if( dbi_result_field_is_null_idx( result, 12 ))
1157 function_id = dbi_result_get_int_idx( result, 12 );
1160 if( dbi_result_field_is_null_idx( result, 13 ))
1163 subquery_id = dbi_result_get_int_idx( result, 13 );
1166 if( dbi_result_field_is_null_idx( result, 14 ))
1169 cast_type_id = dbi_result_get_int_idx( result, 14 );
1171 int negate = oils_result_get_bool_idx( result, 15 );
1172 const char* bind_variable = dbi_result_get_string_idx( result, 16 );
1174 Expression* left_operand = NULL;
1175 Expression* right_operand = NULL;
1176 StoredQ* subquery = NULL;
1177 BindVar* bind = NULL;
1179 if( EXP_OPERATOR == type ) {
1180 // Load left and/or right operands
1181 if( -1 == left_operand_id && -1 == right_operand_id ) {
1182 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1183 "Expression # %d is an operator with no operands", id ));
1188 if( left_operand_id != -1 ) {
1189 left_operand = getExpression( state, left_operand_id );
1190 if( !left_operand ) {
1191 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1192 "Unable to get left operand in expression # %d", id ));
1198 if( right_operand_id != -1 ) {
1199 right_operand = getExpression( state, right_operand_id );
1200 if( !right_operand ) {
1201 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1202 "Unable to get right operand in expression # %d", id ));
1204 expressionFree( left_operand );
1208 } else if( EXP_IN == type ) {
1209 if( -1 == left_operand_id ) {
1210 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1211 "IN condition has no left operand in expression # %d", id ));
1215 left_operand = getExpression( state, left_operand_id );
1216 if( !left_operand ) {
1217 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1218 "Unable to get left operand for IN condition in expression # %d", id ));
1224 if( -1 == subquery_id ) {
1225 // To do: load IN list of subexpressions
1226 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1227 "IN lists not yet supported for expression # %d", id ));
1231 subquery = getStoredQuery( state, subquery_id );
1233 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1234 "Unable to load subquery for IN expression # %d", id ));
1239 } else if( EXP_ISNULL == type ) {
1240 if( -1 == left_operand_id ) {
1241 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1242 "Expression # %d IS NULL has no left operand", id ));
1247 if( left_operand_id != -1 ) {
1248 left_operand = getExpression( state, left_operand_id );
1249 if( !left_operand ) {
1250 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1251 "Unable to get left operand in expression # %d", id ));
1256 } else if( EXP_EXIST == type ) {
1257 if( -1 == subquery_id ) {
1258 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1259 "Internal error: No subquery found for EXIST expression # %d", id ));
1263 subquery = getStoredQuery( state, subquery_id );
1265 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1266 "Unable to load subquery for EXIST expression # %d", id ));
1271 } else if( EXP_SUBQUERY == type ) {
1272 if( -1 == subquery_id ) {
1273 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1274 "Subquery expression # %d has no query id", id ));
1278 // Load a subquery, if there is one
1279 subquery = getStoredQuery( state, subquery_id );
1281 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1282 "Unable to load subquery for expression # %d", id ));
1286 if( subquery->select_list && subquery->select_list->next ) {
1287 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1288 "Subquery # %d as expression returns more than one column", subquery_id ));
1292 PRINT( "\tExpression is subquery %d\n", subquery_id );
1294 } else if( EXP_BIND == type ) {
1295 if( bind_variable ) {
1296 // To do: Build a BindVar
1297 bind = getBindVar( state, bind_variable );
1299 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1300 "Unable to load bind variable \"%s\" for expression # %d",
1301 bind_variable, id ));
1305 PRINT( "\tBind variable is \"%s\"\n", bind_variable );
1307 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1308 "No variable specified for bind variable expression # %d",
1309 bind_variable, id ));
1315 // Allocate an Expression: from the free list if possible, from the heap if necessary
1316 Expression* exp = NULL;
1317 if( free_expression_list ) {
1318 exp = free_expression_list;
1319 free_expression_list = free_expression_list->next;
1321 exp = safe_malloc( sizeof( Expression ) );
1323 // Populate the Expression
1327 exp->parenthesize = parenthesize;
1328 exp->parent_expr_id = parent_expr_id;
1329 exp->seq_no = seq_no;
1330 exp->literal = literal ? strdup( literal ) : NULL;
1331 exp->table_alias = table_alias ? strdup( table_alias ) : NULL;
1332 exp->column_name = column_name ? strdup( column_name ) : NULL;
1333 exp->left_operand = left_operand;
1334 exp->op = operator ? strdup( operator ) : NULL;
1335 exp->right_operand = right_operand;
1336 exp->function_id = function_id;
1337 exp->subquery_id = subquery_id;
1338 exp->subquery = subquery;
1339 exp->cast_type_id = subquery_id;
1340 exp->negate = negate;
1347 @brief Deallocate an Expression.
1348 @param exp Pointer to the Expression to be deallocated.
1350 Free the strings owned by the Expression. Put the Expressions itself into a free list.
1352 static void expressionFree( Expression* exp ) {
1354 free( exp->literal );
1355 exp->literal = NULL;
1356 free( exp->table_alias );
1357 exp->table_alias = NULL;
1358 free( exp->column_name );
1359 exp->column_name = NULL;
1360 if( exp->left_operand ) {
1361 expressionFree( exp->left_operand );
1362 exp->left_operand = NULL;
1366 if( exp->right_operand ) {
1367 expressionFree( exp->right_operand );
1368 exp->right_operand = NULL;
1370 if( exp->subquery ) {
1371 storedQFree( exp->subquery );
1372 exp->subquery = NULL;
1374 // We don't free the bind member here because the Expression doesn't own it;
1375 // the bindvar_list hash owns it, so that multiple Expressions can reference it.
1377 // Prepend to the free list
1378 exp->next = free_expression_list;
1379 free_expression_list = exp;
1384 @brief Build a list of ORDER BY items as a linked list of OrderItems.
1385 @param state Pointer to the query-building context.
1386 @param query_id ID for the query to which the ORDER BY belongs.
1387 @return Pointer to the first node in a linked list of OrderItems.
1389 The calling code is responsible for freeing the list by calling orderItemListFree().
1391 static OrderItem* getOrderByList( BuildSQLState* state, int query_id ) {
1392 OrderItem* ord_list = NULL;
1394 // The ORDER BY is in descending order so that we can build the list by adding to
1395 // the head, and it will wind up in the right order.
1396 dbi_result result = dbi_conn_queryf( state->dbhandle,
1397 "SELECT id, stored_query, seq_no, expression "
1398 "FROM query.order_by_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
1400 if( dbi_result_first_row( result ) ) {
1402 OrderItem* item = constructOrderItem( state, result );
1404 PRINT( "Found an ORDER BY item\n" );
1406 item->next = ord_list;
1409 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1410 "Unable to build ORDER BY item for query id #%d", query_id ));
1411 orderItemListFree( ord_list );
1415 if( !dbi_result_next_row( result ) )
1421 int errnum = dbi_conn_error( state->dbhandle, &msg );
1422 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1423 "Unable to query query.order_by_list table: #%d %s",
1424 errnum, msg ? msg : "No description available" ));
1431 @brief Construct an OrderItem.
1432 @param Pointer to the query-building context.
1433 @param result Database cursor positioned at a row in query.order_by_item.
1434 @return Pointer to a newly constructed OrderItem, if successful, or NULL if not.
1436 The calling code is responsible for freeing the OrderItems by calling orderItemListFree().
1438 static OrderItem* constructOrderItem( BuildSQLState* state, dbi_result result ) {
1439 int id = dbi_result_get_int_idx( result, 1 );
1440 int stored_query_id = dbi_result_get_int_idx( result, 2 );
1441 int seq_no = dbi_result_get_int_idx( result, 3 );
1442 int expression_id = dbi_result_get_int_idx( result, 4 );
1443 // Allocate a SelectItem: from the free list if possible, from the heap if necessary
1445 // Construct an Expression
1446 Expression* expression = getExpression( state, expression_id );
1448 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1449 "Unable to fetch ORDER BY expression for id = %d", expression_id ));
1454 // Allocate an OrderItem; from the free list if possible, or from the heap if necessary.
1456 if( free_order_item_list ) {
1457 ord = free_order_item_list;
1458 free_order_item_list = free_order_item_list->next;
1460 ord = safe_malloc( sizeof( OrderItem ));
1464 ord->stored_query_id = stored_query_id;
1465 ord->seq_no = seq_no;
1466 ord->expression = expression;
1472 @brief Deallocate a linked list of OrderItems.
1473 @param exp Pointer to the first OrderItem in the list to be deallocated.
1475 Deallocate the memory owned by the OrderItems. Put the items themselves into a free list.
1477 static void orderItemListFree( OrderItem* ord ) {
1479 return; // Nothing to free
1481 OrderItem* first = ord;
1483 expressionFree( ord->expression );
1484 ord->expression = NULL;
1486 if( NULL == ord->next ) {
1487 ord->next = free_order_item_list;
1493 // Transfer the entire list to the free list
1494 free_order_item_list = first;
1498 @brief Build a list of column names for a specified query.
1499 @param state Pointer to the query-building context.
1500 @param query Pointer to the specified query.
1501 @return Pointer to a newly-allocated JSON_ARRAY of column names.
1503 In the resulting array, each entry is either a JSON_STRING or (when no column name is
1504 available) a JSON_NULL.
1506 The calling code is responsible for freeing the list by calling jsonObjectFree().
1508 jsonObject* oilsGetColNames( BuildSQLState* state, StoredQ* query ) {
1509 if( !state || !query )
1512 // Save the outermost query id for possible use in an error message
1515 while( query->type != QT_SELECT ) {
1516 // If the query is a UNION, INTERSECT, or EXCEPT, there must be a SELECT in
1517 // there somewhere. Find the first one, and use the SELECT list from that.
1518 QSeq* child_list = query->child_list;
1523 query = child_list->child_query;
1528 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1529 "Unable to find first SELECT in query # %d", id ));
1533 // Get the SELECT list for the first SELECT
1534 SelectItem* col = query->select_list;
1537 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1538 "First SELECT in query # %d has empty SELECT list", id ));
1542 jsonObject* col_list = jsonNewObjectType( JSON_ARRAY );
1544 // Traverse the list, adding an entry for each
1546 const char* alias = NULL;
1547 if( col->column_alias )
1548 alias = col->column_alias;
1550 Expression* expression = col->expression;
1551 if( expression && EXP_COLUMN == expression->type && expression->column_name )
1552 alias = expression->column_name;
1555 jsonObjectPush( col_list, jsonNewObject( alias ) );
1563 @brief Push an IdNode onto a stack of IdNodes.
1564 @param stack Pointer to the stack.
1565 @param id Id of the new node.
1566 @param alias Alias, if any, of the new node.
1568 static void push_id( IdNode** stack, int id, const char* alias ) {
1571 // Allocate a node; from the free list if possible, from the heap if necessary.
1572 IdNode* node = NULL;
1573 if( free_id_node_list ) {
1574 node = free_id_node_list;
1575 free_id_node_list = free_id_node_list->next;
1577 node = safe_malloc( sizeof( IdNode ));
1580 node->next = *stack;
1583 node->alias = strdup( alias );
1593 @brief Remove the node at the top of an IdNode stack.
1594 @param stack Pointer to the IdNode stack.
1596 void pop_id( IdNode** stack ) {
1598 IdNode* node = *stack;
1599 *stack = node->next;
1602 free( node->alias );
1606 node->next = free_id_node_list;
1607 free_id_node_list = node;
1612 @brief Search a stack of IDs for a match by either ID or, optionally, by alias.
1613 @param stack Pointer to the stack.
1614 @param id The id to search for.
1615 @param alias (Optional) the alias to search for.
1616 @return A pointer to the matching node if one is found, or NULL if not.
1618 This search is used to detect cases where a query, expression, or FROM clause is nested
1619 inside itself, in order to avoid infinite recursion; or in order to avoid conflicting
1620 table references in a FROM clause.
1622 static const IdNode* searchIdStack( const IdNode* stack, int id, const char* alias ) {
1624 const IdNode* node = stack;
1626 if( node->id == id )
1627 return node; // Matched on id
1628 else if( alias && node->alias && !strcmp( alias, node->alias ))
1629 return node; // Matched on alias
1634 return NULL; // No match found
1638 @brief Free up any resources held by the StoredQ module.
1640 void storedQCleanup( void ) {
1642 // Free all the nodes in the free state list
1643 StoredQ* sq = free_storedq_list;
1645 free_storedq_list = sq->next;
1647 sq = free_storedq_list;
1650 // Free all the nodes in the free from_relation list
1651 FromRelation* fr = free_from_relation_list;
1653 free_from_relation_list = fr->next;
1655 fr = free_from_relation_list;
1658 // Free all the nodes in the free expression list
1659 Expression* exp = free_expression_list;
1661 free_expression_list = exp->next;
1663 exp = free_expression_list;
1666 // Free all the nodes in the free select item list
1667 SelectItem* sel = free_select_item_list;
1669 free_select_item_list = sel->next;
1671 sel = free_select_item_list;
1674 // Free all the nodes in the free select item list
1675 IdNode* node = free_id_node_list;
1677 free_id_node_list = node->next;
1679 node = free_id_node_list;
1682 // Free all the nodes in the free query sequence list
1683 QSeq* seq = free_qseq_list;
1685 free_qseq_list = seq->next;
1687 seq = free_qseq_list;
1690 // Free all the nodes in the free order item list
1691 OrderItem* ord = free_order_item_list;
1693 free_order_item_list = ord->next;
1695 ord = free_order_item_list;
1698 // Free all the nodes in the bind variable free list
1699 BindVar* bind = free_bindvar_list;
1701 free_bindvar_list = bind->next;
1703 bind = free_bindvar_list;
1708 @brief Return a boolean value from a database result.
1709 @param result The database result.
1710 @param i Index of the column in the result, starting with 1 );
1711 @return 1 if true, or 0 for false.
1713 Null values and error conditions are interpreted as FALSE.
1715 static int oils_result_get_bool_idx( dbi_result result, int i ) {
1717 const char* str = dbi_result_get_string_idx( result, i );
1718 return (str && *str == 't' ) ? 1 : 0;
1724 @brief Enable verbose messages.
1726 The messages are written to standard output, which for a server is /dev/null. Hence this
1727 option is useful only for a non-server. It is intended only as a convenience for
1728 development and debugging.
1730 void oilsStoredQSetVerbose( void ) {