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 "openils/oils_buildq.h"
14 #define PRINT if( verbose ) printf
22 static int oils_result_get_bool_idx( dbi_result result, int i );
24 static FromRelation* getFromRelation( BuildSQLState* state, int id );
25 static FromRelation* constructFromRelation( BuildSQLState* state, dbi_result result );
26 static FromRelation* getJoinList( BuildSQLState* state, int id );
27 static void joinListFree( FromRelation* join_list );
28 static void fromRelationFree( FromRelation* fr );
30 static QSeq* loadChildQueries( BuildSQLState* state, int parent_id, const char* type_str );
31 static QSeq* constructQSeq( BuildSQLState* state, dbi_result result );
32 static void freeQSeqList( QSeq* seq );
33 static StoredQ* constructStoredQ( BuildSQLState* state, dbi_result result );
35 static SelectItem* getSelectList( BuildSQLState* state, int query_id );
36 static SelectItem* constructSelectItem( BuildSQLState* state, dbi_result result );
37 static void selectListFree( SelectItem* sel );
39 static Expression* getExpression( BuildSQLState* state, int id );
40 static Expression* constructExpression( BuildSQLState* state, dbi_result result );
41 static void expressionFree( Expression* exp );
43 static OrderItem* getOrderByList( BuildSQLState* state, int query_id );
44 static OrderItem* constructOrderItem( BuildSQLState* state, dbi_result result );
45 static void orderItemListFree( OrderItem* ord );
47 static void push_id( IdNode** stack, int id, const char* alias );
48 static const IdNode* searchIdStack( const IdNode* stack, int id, const char* alias );
50 // A series of free lists to store already-allocated objects that are not in use, for
51 // potential reuse. This is a hack to reduce churning through malloc() and free().
52 static StoredQ* free_storedq_list = NULL;
53 static FromRelation* free_from_relation_list = NULL;
54 static SelectItem* free_select_item_list = NULL;
55 static Expression* free_expression_list = NULL;
56 static IdNode* free_id_node_list = NULL;
57 static QSeq* free_qseq_list = NULL;
58 static OrderItem* free_order_item_list = NULL;
60 // Boolean; settable by call to oilsStoredQSetVerbose(), used by PRINT macro.
61 // The idea is to allow debugging messages from a command line test driver for ease of
62 // testing and development, but not from a real server, where messages to stdout don't
64 static int verbose = 0;
67 @brief Load a stored query.
68 @param state Pointer to the query-building context.
69 @param query_id ID of the query in query.stored_query.
70 @return A pointer to the newly loaded StoredQ if successful, or NULL if not.
72 The calling code is responsible for freeing the StoredQ by calling storedQFree().
74 StoredQ* getStoredQuery( BuildSQLState* state, int query_id ) {
78 // Check the stack to see if the current query is nested inside itself. If it is, then
79 // abort in order to avoid infinite recursion. If it isn't, then add it to the stack.
80 // (Make sure to pop it off the stack before returning.)
81 if( searchIdStack( state->query_stack, query_id, NULL )) {
82 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
83 "Infinite recursion detected; query # %d is nested within itself", query_id ));
87 push_id( &state->query_stack, query_id, NULL );
90 dbi_result result = dbi_conn_queryf( state->dbhandle,
91 "SELECT id, type, use_all, use_distinct, from_clause, where_clause, having_clause "
92 "FROM query.stored_query WHERE id = %d;", query_id );
94 if( dbi_result_first_row( result ) ) {
95 sq = constructStoredQ( state, result );
97 PRINT( "Got a query row\n" );
98 PRINT( "\tid: %d\n", sq->id );
99 PRINT( "\ttype: %d\n", (int) sq->type );
100 PRINT( "\tuse_all: %s\n", sq->use_all ? "true" : "false" );
101 PRINT( "\tuse_distinct: %s\n", sq->use_distinct ? "true" : "false" );
103 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
104 "Unable to build a query for id = %d", query_id ));
106 sqlAddMsg( state, "Stored query not found for id %d", query_id );
109 dbi_result_free( result );
112 int errnum = dbi_conn_error( state->dbhandle, &msg );
113 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
114 "Unable to query query.stored_query table: #%d %s",
115 errnum, msg ? msg : "No description available" ));
118 pop_id( &state->query_stack );
123 @brief Construct a StoredQ.
124 @param Pointer to the query-building context.
125 @param result Database cursor positioned at a row in query.stored_query.
126 @return Pointer to a newly constructed StoredQ, if successful, or NULL if not.
128 The calling code is responsible for freeing the StoredQ by calling storedQFree().
130 static StoredQ* constructStoredQ( BuildSQLState* state, dbi_result result ) {
132 // Get the column values from the result
133 int id = dbi_result_get_int_idx( result, 1 );
134 const char* type_str = dbi_result_get_string_idx( result, 2 );
137 if( !strcmp( type_str, "SELECT" ))
139 else if( !strcmp( type_str, "UNION" ))
141 else if( !strcmp( type_str, "INTERSECT" ))
143 else if( !strcmp( type_str, "EXCEPT" ))
146 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
147 "Invalid query type \"%s\"", type_str ));
151 int use_all = oils_result_get_bool_idx( result, 3 );
152 int use_distinct = oils_result_get_bool_idx( result, 4 );
155 if( dbi_result_field_is_null_idx( result, 5 ) )
158 from_clause_id = dbi_result_get_int_idx( result, 5 );
161 if( dbi_result_field_is_null_idx( result, 6 ) )
162 where_clause_id = -1;
164 where_clause_id = dbi_result_get_int_idx( result, 6 );
166 int having_clause_id;
167 if( dbi_result_field_is_null_idx( result, 7 ) )
168 having_clause_id = -1;
170 having_clause_id = dbi_result_get_int_idx( result, 7 );
172 FromRelation* from_clause = NULL;
173 if( QT_SELECT == type ) {
174 // A SELECT query needs a FROM clause; go get it
175 if( from_clause_id != -1 ) {
176 from_clause = getFromRelation( state, from_clause_id );
178 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
179 "Unable to construct FROM clause for id = %d", from_clause_id ));
184 // Must be one of UNION, INTERSECT, or EXCEPT
185 if( from_clause_id != -1 )
186 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
187 "FROM clause found and ignored for %s query in query #%d", type_str, id ));
190 // If this is a SELECT query, we need a SELECT list. Go get one.
191 SelectItem* select_list = NULL;
192 QSeq* child_list = NULL;
193 if( QT_SELECT == type ) {
194 select_list = getSelectList( state, id );
196 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
197 "No SELECT list found for query id = %d", id ));
198 fromRelationFree( from_clause );
202 // Construct child queries of UNION, INTERSECT, or EXCEPT query
203 child_list = loadChildQueries( state, id, type_str );
205 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
206 "Unable to load child queries for %s query # %d", type_str, id ));
208 fromRelationFree( from_clause );
213 // Get the WHERE clause, if there is one
214 Expression* where_clause = NULL;
215 if( where_clause_id != -1 ) {
216 where_clause = getExpression( state, where_clause_id );
217 if( ! where_clause ) {
218 // shouldn't happen due to foreign key constraint
219 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
220 "Unable to fetch WHERE expression for query id = %d", id ));
221 freeQSeqList( child_list );
222 fromRelationFree( from_clause );
223 selectListFree( select_list );
228 Expression* having_clause = NULL;
229 if( having_clause_id != -1 ) {
230 having_clause = getExpression( state, having_clause_id );
231 if( ! having_clause ) {
232 // shouldn't happen due to foreign key constraint
233 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
234 "Unable to fetch HAVING expression for query id = %d", id ));
235 expressionFree( where_clause );
236 freeQSeqList( child_list );
237 fromRelationFree( from_clause );
238 selectListFree( select_list );
243 // Get the ORDER BY clause, if there is one
244 OrderItem* order_by_list = getOrderByList( state, id );
246 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
247 "Unable to load ORDER BY clause for query %d", id ));
248 expressionFree( having_clause );
249 expressionFree( where_clause );
250 freeQSeqList( child_list );
251 fromRelationFree( from_clause );
252 selectListFree( select_list );
256 // Allocate a StoredQ: from the free list if possible, from the heap if necessary
259 if( free_storedq_list ) {
260 sq = free_storedq_list;
261 free_storedq_list = free_storedq_list->next;
263 sq = safe_malloc( sizeof( StoredQ ) );
265 // Populate the StoredQ
270 sq->use_all = use_all;
271 sq->use_distinct = use_distinct;
272 sq->from_clause = from_clause;
273 sq->where_clause = where_clause;
274 sq->select_list = select_list;
275 sq->child_list = child_list;
276 sq->having_clause = having_clause;
277 sq->order_by_list = order_by_list;
283 @brief Load the child queries subordinate to a UNION, INTERSECT, or EXCEPT query.
284 @param state Pointer to the query-building context.
285 @param parent ID of the UNION, INTERSECT, or EXCEPT query.
286 @param type_str The type of the query ("UNION", "INTERSECT", or "EXCEPT").
287 @return If successful, a pointer to a linked list of QSeq, each bearing a pointer to a
288 StoredQ; otherwise NULL.
290 The @a type_str parameter is used only for building error messages.
292 static QSeq* loadChildQueries( BuildSQLState* state, int parent_id, const char* type_str ) {
293 QSeq* child_list = NULL;
295 // The ORDER BY is in descending order so that we can build the list by adding to
296 // the head, and it will wind up in the right order.
297 dbi_result result = dbi_conn_queryf( state->dbhandle,
298 "SELECT id, parent_query, seq_no, child_query "
299 "FROM query.query_sequence WHERE parent_query = %d ORDER BY seq_no DESC", parent_id );
301 if( dbi_result_first_row( result ) ) {
305 QSeq* seq = constructQSeq( state, result );
307 PRINT( "Found a child query\n" );
308 PRINT( "\tid: %d\n", seq->id );
309 PRINT( "\tparent id: %d\n", seq->parent_query_id );
310 PRINT( "\tseq_no: %d\n", seq->seq_no );
311 // Add to the head of the list
312 seq->next = child_list;
315 freeQSeqList( child_list );
318 if( !dbi_result_next_row( result ))
322 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
323 "%s query # %d has only one child query", type_str, parent_id ));
325 freeQSeqList( child_list );
329 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
330 "%s query # %d has no child queries within it", type_str, parent_id ));
336 int errnum = dbi_conn_error( state->dbhandle, &msg );
337 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
338 "Unable to query query.query_sequence table: # %d %s",
339 errnum, msg ? msg : "No description available" ));
348 @brief Construct a QSeq.
349 @param Pointer to the query-building context.
350 @param result Database cursor positioned at a row in query.query_sequence.
351 @return Pointer to a newly constructed QSeq, if successful, or NULL if not.
353 The calling code is responsible for freeing QSeqs by calling freeQSeqList().
355 static QSeq* constructQSeq( BuildSQLState* state, dbi_result result ) {
356 int id = dbi_result_get_int_idx( result, 1 );
357 int parent_query_id = dbi_result_get_int_idx( result, 2 );
358 int seq_no = dbi_result_get_int_idx( result, 3 );
359 int child_query_id = dbi_result_get_int_idx( result, 4 );
361 StoredQ* child_query = getStoredQuery( state, child_query_id );
363 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
364 "Unable to load child query # %d for parent query %d",
365 child_query_id, parent_query_id ));
370 // Allocate a QSeq; from the free list if possible, from the heap if necessary
372 if( free_qseq_list ) {
373 seq = free_qseq_list;
374 free_qseq_list = free_qseq_list->next;
376 seq = safe_malloc( sizeof( QSeq ));
380 seq->parent_query_id = parent_query_id;
381 seq->seq_no = seq_no;
382 seq->child_query = child_query;
387 static void freeQSeqList( QSeq* seq ) {
393 storedQFree( seq->child_query );
394 seq->child_query = NULL;
399 seq->next = free_qseq_list;
404 free_qseq_list = first;
408 @brief Deallocate the memory owned by a StoredQ.
409 @param sq Pointer to the StoredQ to be deallocated.
411 void storedQFree( StoredQ* sq ) {
413 fromRelationFree( sq->from_clause );
414 sq->from_clause = NULL;
415 selectListFree( sq->select_list );
416 sq->select_list = NULL;
417 expressionFree( sq->where_clause );
418 sq->where_clause = NULL;
419 if( sq->child_list ) {
420 freeQSeqList( sq->child_list );
421 sq->child_list = NULL;
423 if( sq->order_by_list ) {
424 orderItemListFree( sq->order_by_list );
425 sq->order_by_list = NULL;
427 if( sq->having_clause )
428 expressionFree( sq->having_clause );
430 // Stick the empty husk on the free list for potential reuse
431 sq->next = free_storedq_list;
432 free_storedq_list = sq;
436 static FromRelation* getFromRelation( BuildSQLState* state, int id ) {
437 FromRelation* fr = NULL;
438 dbi_result result = dbi_conn_queryf( state->dbhandle,
439 "SELECT id, type, table_name, class_name, subquery, function_call, "
440 "table_alias, parent_relation, seq_no, join_type, on_clause "
441 "FROM query.from_relation WHERE id = %d;", id );
443 if( dbi_result_first_row( result ) ) {
444 fr = constructFromRelation( state, result );
446 PRINT( "Got a from_relation row\n" );
447 PRINT( "\tid: %d\n", fr->id );
448 PRINT( "\ttype: %d\n", (int) fr->type );
449 PRINT( "\ttable_name: %s\n", fr->table_name ? fr->table_name : "(none)" );
450 PRINT( "\tclass_name: %s\n", fr->class_name ? fr->class_name : "(none)" );
451 PRINT( "\tsubquery_id: %d\n", fr->subquery_id );
452 PRINT( "\tfunction_call_id: %d\n", fr->function_call_id );
453 PRINT( "\ttable_alias: %s\n", fr->table_alias ? fr->table_alias : "(none)" );
454 PRINT( "\tparent_relation_id: %d\n", fr->parent_relation_id );
455 PRINT( "\tseq_no: %d\n", fr->seq_no );
456 PRINT( "\tjoin_type = %d\n", fr->join_type );
457 // Check the stack to see if the current from clause is nested inside itself.
458 // If it is, then abort in order to avoid infinite recursion. If it isn't,
459 // then add it to the stack. (Make sure to pop it off the stack before
461 const char* effective_alias = fr->table_alias;
462 if( !effective_alias )
463 effective_alias = fr->class_name;
464 const IdNode* node = searchIdStack( state->from_stack, id, effective_alias );
467 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
468 "Infinite recursion detected; from clause # %d is nested "
469 "within itself", id ));
471 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
472 "Conflicting nested table aliases \"%s\" in from clause # %d",
473 effective_alias, node->id ));
477 push_id( &state->from_stack, id, effective_alias );
479 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
480 "Unable to build a FromRelation for id = %d", id ));
482 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
483 "FROM relation not found for id = %d", id ));
485 dbi_result_free( result );
488 int errnum = dbi_conn_error( state->dbhandle, &msg );
489 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
490 "Unable to query query.from_relation table: #%d %s",
491 errnum, msg ? msg : "No description available" ));
495 pop_id( &state->from_stack );
501 @brief Construct a FromRelation.
502 @param Pointer to the query-building context.
503 @param result Database cursor positioned at a row in query.from_relation.
504 @return Pointer to a newly constructed FromRelation, if successful, or NULL if not.
506 The calling code is responsible for freeing FromRelations by calling joinListFree().
508 static FromRelation* constructFromRelation( BuildSQLState* state, dbi_result result ) {
509 // Get the column values from the result
510 int id = dbi_result_get_int_idx( result, 1 );
511 const char* type_str = dbi_result_get_string_idx( result, 2 );
513 FromRelationType type;
514 if( !strcmp( type_str, "RELATION" ))
516 else if( !strcmp( type_str, "SUBQUERY" ))
518 else if( !strcmp( type_str, "FUNCTION" ))
521 type = FRT_RELATION; // shouldn't happen due to database constraint
523 const char* table_name = dbi_result_get_string_idx( result, 3 );
524 const char* class_name = dbi_result_get_string_idx( result, 4 );
527 if( dbi_result_field_is_null_idx( result, 5 ) )
530 subquery_id = dbi_result_get_int_idx( result, 5 );
532 int function_call_id;
533 if( dbi_result_field_is_null_idx( result, 6 ) )
534 function_call_id = -1;
536 function_call_id = dbi_result_get_int_idx( result, 6 );
538 const char* table_alias = dbi_result_get_string_idx( result, 7 );
540 int parent_relation_id;
541 if( dbi_result_field_is_null_idx( result, 8 ) )
542 parent_relation_id = -1;
544 parent_relation_id = dbi_result_get_int_idx( result, 8 );
546 int seq_no = dbi_result_get_int_idx( result, 9 );
549 const char* join_type_str = dbi_result_get_string_idx( result, 10 );
552 else if( !strcmp( join_type_str, "INNER" ) )
553 join_type = JT_INNER;
554 else if( !strcmp( join_type_str, "LEFT" ) )
556 else if( !strcmp( join_type_str, "RIGHT" ) )
557 join_type = JT_RIGHT;
558 else if( !strcmp( join_type_str, "FULL" ) )
561 join_type = JT_NONE; // shouldn't happen due to database constraint
564 if( dbi_result_field_is_null_idx( result, 11 ) )
567 on_clause_id = dbi_result_get_int_idx( result, 11 );
569 StoredQ* subquery = NULL;
575 if( -1 == subquery_id ) {
576 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
577 "Internal error: no subquery specified for FROM relation # %d", id ));
581 if( ! table_alias ) {
582 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
583 "Subquery needs alias in FROM relation # %d", id ));
587 subquery = getStoredQuery( state, subquery_id );
589 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
590 "Unable to load subquery for FROM relation # %d", id ));
596 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
597 "Functions in FROM clause not yet supported" ));
602 FromRelation* join_list = getJoinList( state, id );
604 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
605 "Unable to load join list for FROM relation # %d", id ));
609 Expression* on_clause = NULL;
610 if( on_clause_id != -1 ) {
611 on_clause = getExpression( state, on_clause_id );
613 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
614 "Unable to load ON condition for FROM relation # %d", id ));
615 joinListFree( join_list );
619 PRINT( "\tGot an ON condition\n" );
622 // Allocate a FromRelation: from the free list if possible, from the heap if necessary
625 if( free_from_relation_list ) {
626 fr = free_from_relation_list;
627 free_from_relation_list = free_from_relation_list->next;
629 fr = safe_malloc( sizeof( FromRelation ) );
631 // Populate the FromRelation
636 fr->table_name = table_name ? strdup( table_name ) : NULL;
637 fr->class_name = class_name ? strdup( class_name ) : NULL;
638 fr->subquery_id = subquery_id;
639 fr->subquery = subquery;
640 fr->function_call_id = function_call_id;
641 fr->table_alias = table_alias ? strdup( table_alias ) : NULL;
642 fr->parent_relation_id = parent_relation_id;
644 fr->join_type = join_type;
645 fr->on_clause = on_clause;
646 fr->join_list = join_list;
652 @brief Build a list of joined relations.
653 @param state Pointer to the query-building context.
654 @param id ID of the parent relation.
655 @return A pointer to the first in a linked list of FromRelations, if there are any; or
656 NULL if there aren't any, or in case of an error.
658 Look for relations joined directly to the parent relation, and make a list of them.
660 static FromRelation* getJoinList( BuildSQLState* state, int id ) {
661 FromRelation* join_list = NULL;
663 // The ORDER BY is in descending order so that we can build the list by adding to
664 // the head, and it will wind up in the right order.
665 dbi_result result = dbi_conn_queryf( state->dbhandle,
666 "SELECT id, type, table_name, class_name, subquery, function_call, "
667 "table_alias, parent_relation, seq_no, join_type, on_clause "
668 "FROM query.from_relation WHERE parent_relation = %d ORDER BY seq_no DESC", id );
671 if( dbi_result_first_row( result ) ) {
673 FromRelation* relation = constructFromRelation( state, result );
675 PRINT( "Found a joined relation\n" );
676 PRINT( "\tjoin_type: %d\n", relation->join_type );
677 PRINT( "\ttable_name: %s\n", relation->table_name );
678 relation->next = join_list;
679 join_list = relation;
681 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
682 "Unable to build join list for from relation id #%d", id ));
683 joinListFree( join_list );
687 if( !dbi_result_next_row( result ) )
693 int errnum = dbi_conn_error( state->dbhandle, &msg );
694 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
695 "Unable to query query.from_relation table for join list: #%d %s",
696 errnum, msg ? msg : "No description available" ));
703 @brief Free a list of FromRelations.
704 @param join_list Pointer to the first FromRelation in the list.
706 static void joinListFree( FromRelation* join_list ) {
708 FromRelation* temp = join_list->next;
709 fromRelationFree( join_list );
715 @brief Deallocate a FromRelation.
716 @param fr Pointer to the FromRelation to be freed.
718 Free the strings that the FromRelation owns. The FromRelation itself goes onto a
719 free list for potential reuse.
721 static void fromRelationFree( FromRelation* fr ) {
723 free( fr->table_name );
724 fr->table_name = NULL;
725 free( fr->class_name );
726 fr->class_name = NULL;
728 storedQFree( fr->subquery );
731 free( fr->table_alias );
732 fr->table_alias = NULL;
733 if( fr->on_clause ) {
734 expressionFree( fr->on_clause );
735 fr->on_clause = NULL;
737 joinListFree( fr->join_list );
738 fr->join_list = NULL;
740 fr->next = free_from_relation_list;
741 free_from_relation_list = fr;
745 static SelectItem* getSelectList( BuildSQLState* state, int query_id ) {
746 SelectItem* select_list = NULL;
748 // The ORDER BY is in descending order so that we can build the list by adding to
749 // the head, and it will wind up in the right order.
750 dbi_result result = dbi_conn_queryf( state->dbhandle,
751 "SELECT id, stored_query, seq_no, expression, column_alias, grouped_by "
752 "FROM query.select_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
754 if( dbi_result_first_row( result ) ) {
756 SelectItem* item = constructSelectItem( state, result );
758 PRINT( "Found a SELECT item\n" );
759 PRINT( "\tid: %d\n", item->id );
760 PRINT( "\tstored_query_id: %d\n", item->stored_query_id );
761 PRINT( "\tseq_no: %d\n", item->seq_no );
762 PRINT( "\tcolumn_alias: %s\n",
763 item->column_alias ? item->column_alias : "(none)" );
764 PRINT( "\tgrouped_by: %d\n", item->grouped_by );
766 item->next = select_list;
769 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
770 "Unable to build select list for query id #%d", query_id ));
771 selectListFree( select_list );
775 if( !dbi_result_next_row( result ) )
781 int errnum = dbi_conn_error( state->dbhandle, &msg );
782 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
783 "Unable to query query.select_list table: #%d %s",
784 errnum, msg ? msg : "No description available" ));
791 @brief Construct a SelectItem.
792 @param Pointer to the query-building context.
793 @param result Database cursor positioned at a row in query.select_item.
794 @return Pointer to a newly constructed SelectItem, if successful, or NULL if not.
796 The calling code is responsible for freeing the SelectItems by calling selectListFree().
798 static SelectItem* constructSelectItem( BuildSQLState* state, dbi_result result ) {
800 // Get the column values
801 int id = dbi_result_get_int_idx( result, 1 );
802 int stored_query_id = dbi_result_get_int_idx( result, 2 );
803 int seq_no = dbi_result_get_int_idx( result, 3 );
804 int expression_id = dbi_result_get_int_idx( result, 4 );
805 const char* column_alias = dbi_result_get_string_idx( result, 5 );
806 int grouped_by = oils_result_get_bool_idx( result, 6 );
808 // Construct an Expression
809 Expression* expression = getExpression( state, expression_id );
811 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
812 "Unable to fetch expression for id = %d", expression_id ));
816 // Allocate a SelectItem: from the free list if possible, from the heap if necessary
819 if( free_select_item_list ) {
820 sel = free_select_item_list;
821 free_select_item_list = free_select_item_list->next;
823 sel = safe_malloc( sizeof( SelectItem ) );
827 sel->stored_query_id = stored_query_id;
828 sel->seq_no = seq_no;
829 sel->expression = expression;
830 sel->column_alias = column_alias ? strdup( column_alias ) : NULL;
831 sel->grouped_by = grouped_by;
836 static void selectListFree( SelectItem* sel ) {
838 return; // Nothing to free
840 SelectItem* first = sel;
842 free( sel->column_alias );
843 sel->column_alias = NULL;
844 expressionFree( sel->expression );
845 sel->expression = NULL;
847 if( NULL == sel->next ) {
848 sel->next = free_select_item_list;
854 // Transfer the entire list to the free list
855 free_select_item_list = first;
858 static Expression* getExpression( BuildSQLState* state, int id ) {
860 // Check the stack to see if the current expression is nested inside itself. If it is,
861 // then abort in order to avoid infinite recursion. If it isn't, then add it to the
862 // stack. (Make sure to pop it off the stack before returning.)
863 if( searchIdStack( state->expr_stack, id, NULL )) {
864 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
865 "Infinite recursion detected; expression # %d is nested within itself", id ));
869 push_id( &state->expr_stack, id, NULL );
871 Expression* exp = NULL;
872 dbi_result result = dbi_conn_queryf( state->dbhandle,
873 "SELECT id, type, parenthesize, parent_expr, seq_no, literal, table_alias, column_name, "
874 "left_operand, operator, right_operand, function_id, subquery, cast_type, negate "
875 "FROM query.expression WHERE id = %d;", id );
877 if( dbi_result_first_row( result ) ) {
878 exp = constructExpression( state, result );
880 PRINT( "Got an expression\n" );
881 PRINT( "\tid = %d\n", exp->id );
882 PRINT( "\ttype = %d\n", exp->type );
883 PRINT( "\tparenthesize = %d\n", exp->parenthesize );
884 PRINT( "\tcolumn_name = %s\n", exp->column_name ? exp->column_name : "(none)" );
886 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
887 "Unable to construct an Expression for id = %d", id ));
891 int errnum = dbi_conn_error( state->dbhandle, &msg );
892 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
893 "Unable to query query.expression table: #%d %s",
894 errnum, msg ? msg : "No description available" ));
897 pop_id( &state->expr_stack );
902 @brief Construct an Expression.
903 @param Pointer to the query-building context.
904 @param result Database cursor positioned at a row in query.expression.
905 @return Pointer to a newly constructed Expression, if successful, or NULL if not.
907 The calling code is responsible for freeing the Expression by calling expressionFree().
909 static Expression* constructExpression( BuildSQLState* state, dbi_result result ) {
911 int id = dbi_result_get_int_idx( result, 1 );
912 const char* type_str = dbi_result_get_string_idx( result, 2 );
915 if( !strcmp( type_str, "xbet" ))
917 else if( !strcmp( type_str, "xbool" ))
919 else if( !strcmp( type_str, "xcase" ))
921 else if( !strcmp( type_str, "xcast" ))
923 else if( !strcmp( type_str, "xcol" ))
925 else if( !strcmp( type_str, "xex" ))
927 else if( !strcmp( type_str, "xfld" ))
929 else if( !strcmp( type_str, "xfunc" ))
931 else if( !strcmp( type_str, "xin" ))
933 else if( !strcmp( type_str, "xnull" ))
935 else if( !strcmp( type_str, "xnum" ))
937 else if( !strcmp( type_str, "xop" ))
939 else if( !strcmp( type_str, "xstr" ))
941 else if( !strcmp( type_str, "xsubq" ))
944 type = EXP_NULL; // shouldn't happen due to database constraint
946 int parenthesize = oils_result_get_bool_idx( result, 3 );
949 if( dbi_result_field_is_null_idx( result, 4 ))
952 parent_expr_id = dbi_result_get_int_idx( result, 4 );
954 int seq_no = dbi_result_get_int_idx( result, 5 );
955 const char* literal = dbi_result_get_string_idx( result, 6 );
956 const char* table_alias = dbi_result_get_string_idx( result, 7 );
957 const char* column_name = dbi_result_get_string_idx( result, 8 );
960 if( dbi_result_field_is_null_idx( result, 9 ))
961 left_operand_id = -1;
963 left_operand_id = dbi_result_get_int_idx( result, 9 );
965 const char* operator = dbi_result_get_string_idx( result, 10 );
967 int right_operand_id;
968 if( dbi_result_field_is_null_idx( result, 11 ))
969 right_operand_id = -1;
971 right_operand_id = dbi_result_get_int_idx( result, 11 );
974 if( dbi_result_field_is_null_idx( result, 12 ))
977 function_id = dbi_result_get_int_idx( result, 12 );
980 if( dbi_result_field_is_null_idx( result, 13 ))
983 subquery_id = dbi_result_get_int_idx( result, 13 );
986 if( dbi_result_field_is_null_idx( result, 14 ))
989 cast_type_id = dbi_result_get_int_idx( result, 14 );
991 int negate = oils_result_get_bool_idx( result, 15 );
993 Expression* left_operand = NULL;
994 Expression* right_operand = NULL;
995 StoredQ* subquery = NULL;
997 if( EXP_OPERATOR == type ) {
998 // Load left and/or right operands
999 if( -1 == left_operand_id && -1 == right_operand_id ) {
1000 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1001 "Expression # %d is an operator with no operands", id ));
1006 if( left_operand_id != -1 ) {
1007 left_operand = getExpression( state, left_operand_id );
1008 if( !left_operand ) {
1009 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1010 "Unable to get left operand in expression # %d", id ));
1016 if( right_operand_id != -1 ) {
1017 right_operand = getExpression( state, right_operand_id );
1018 if( !right_operand ) {
1019 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1020 "Unable to get right operand in expression # %d", id ));
1022 expressionFree( left_operand );
1026 } else if( EXP_IN == type ) {
1027 if( -1 == left_operand_id ) {
1028 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1029 "IN condition has no left operand in expression # %d", id ));
1033 left_operand = getExpression( state, left_operand_id );
1034 if( !left_operand ) {
1035 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1036 "Unable to get left operand for IN condition in expression # %d", id ));
1042 if( -1 == subquery_id ) {
1043 // To do: load IN list of subexpressions
1044 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1045 "IN lists not yet supported for expression # %d", id ));
1049 subquery = getStoredQuery( state, subquery_id );
1051 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1052 "Unable to load subquery for IN expression # %d", id ));
1057 } else if( EXP_EXIST == type ) {
1058 if( -1 == subquery_id ) {
1059 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1060 "Internal error: No subquery found for EXIST expression # %d", id ));
1064 subquery = getStoredQuery( state, subquery_id );
1066 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1067 "Unable to load subquery for EXIST expression # %d", id ));
1072 } else if( EXP_SUBQUERY == type ) {
1073 if( -1 == subquery_id ) {
1074 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1075 "Subquery expression # %d has no query id", id ));
1079 // Load a subquery, if there is one
1080 subquery = getStoredQuery( state, subquery_id );
1082 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1083 "Unable to load subquery for expression # %d", id ));
1087 if( subquery->select_list && subquery->select_list->next ) {
1088 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1089 "Subquery # %d as expression returns more than one column", subquery_id ));
1093 PRINT( "\tExpression is subquery %d\n", subquery_id );
1097 // Allocate an Expression: from the free list if possible, from the heap if necessary
1098 Expression* exp = NULL;
1099 if( free_expression_list ) {
1100 exp = free_expression_list;
1101 free_expression_list = free_expression_list->next;
1103 exp = safe_malloc( sizeof( Expression ) );
1105 // Populate the Expression
1109 exp->parenthesize = parenthesize;
1110 exp->parent_expr_id = parent_expr_id;
1111 exp->seq_no = seq_no;
1112 exp->literal = literal ? strdup( literal ) : NULL;
1113 exp->table_alias = table_alias ? strdup( table_alias ) : NULL;
1114 exp->column_name = column_name ? strdup( column_name ) : NULL;
1115 exp->left_operand = left_operand;
1116 exp->op = operator ? strdup( operator ) : NULL;
1117 exp->right_operand = right_operand;
1118 exp->function_id = function_id;
1119 exp->subquery_id = subquery_id;
1120 exp->subquery = subquery;
1121 exp->cast_type_id = subquery_id;
1122 exp->negate = negate;
1128 @brief Deallocate an Expression.
1129 @param exp Pointer to the Expression to be deallocated.
1131 Free the strings owned by the Expression. Put the Expressions itself into a free list.
1133 static void expressionFree( Expression* exp ) {
1135 free( exp->literal );
1136 exp->literal = NULL;
1137 free( exp->table_alias );
1138 exp->table_alias = NULL;
1139 free( exp->column_name );
1140 exp->column_name = NULL;
1141 if( exp->left_operand ) {
1142 expressionFree( exp->left_operand );
1143 exp->left_operand = NULL;
1147 if( exp->right_operand ) {
1148 expressionFree( exp->right_operand );
1149 exp->right_operand = NULL;
1151 if( exp->subquery ) {
1152 storedQFree( exp->subquery );
1153 exp->subquery = NULL;
1156 exp->next = free_expression_list;
1157 free_expression_list = exp;
1162 @brief Build a list of ORDER BY items as a linked list of OrderItems.
1163 @param state Pointer to the query-building context.
1164 @param query_id ID for the query to which the ORDER BY belongs.
1165 @return Pointer to the first node in a linked list of OrderItems.
1167 The calling code is responsible for freeing the list by calling orderItemListFree().
1169 static OrderItem* getOrderByList( BuildSQLState* state, int query_id ) {
1170 OrderItem* ord_list = NULL;
1172 // The ORDER BY is in descending order so that we can build the list by adding to
1173 // the head, and it will wind up in the right order.
1174 dbi_result result = dbi_conn_queryf( state->dbhandle,
1175 "SELECT id, stored_query, seq_no, expression "
1176 "FROM query.order_by_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
1178 if( dbi_result_first_row( result ) ) {
1180 OrderItem* item = constructOrderItem( state, result );
1182 PRINT( "Found an ORDER BY item\n" );
1184 item->next = ord_list;
1187 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1188 "Unable to build ORDER BY item for query id #%d", query_id ));
1189 orderItemListFree( ord_list );
1193 if( !dbi_result_next_row( result ) )
1199 int errnum = dbi_conn_error( state->dbhandle, &msg );
1200 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1201 "Unable to query query.order_by_list table: #%d %s",
1202 errnum, msg ? msg : "No description available" ));
1209 @brief Construct an OrderItem.
1210 @param Pointer to the query-building context.
1211 @param result Database cursor positioned at a row in query.order_by_item.
1212 @return Pointer to a newly constructed OrderItem, if successful, or NULL if not.
1214 The calling code is responsible for freeing the OrderItems by calling orderItemListFree().
1216 static OrderItem* constructOrderItem( BuildSQLState* state, dbi_result result ) {
1217 int id = dbi_result_get_int_idx( result, 1 );
1218 int stored_query_id = dbi_result_get_int_idx( result, 2 );
1219 int seq_no = dbi_result_get_int_idx( result, 3 );
1220 int expression_id = dbi_result_get_int_idx( result, 4 );
1221 // Allocate a SelectItem: from the free list if possible, from the heap if necessary
1223 // Construct an Expression
1224 Expression* expression = getExpression( state, expression_id );
1226 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1227 "Unable to fetch ORDER BY expression for id = %d", expression_id ));
1231 // Allocate an OrderItem; from the free list if possible, or from the heap if necessary.
1233 if( free_order_item_list ) {
1234 ord = free_order_item_list;
1235 free_order_item_list = free_order_item_list->next;
1237 ord = safe_malloc( sizeof( OrderItem ));
1241 ord->stored_query_id = stored_query_id;
1242 ord->seq_no = seq_no;
1243 ord->expression = expression;
1249 @brief Deallocate a linked list of OrderItems.
1250 @param exp Pointer to the first OrderItem in the list to be deallocated.
1252 Deallocate the memory owned by the OrderItems. Put the items themselves into a free list.
1254 static void orderItemListFree( OrderItem* ord ) {
1256 return; // Nothing to free
1258 OrderItem* first = ord;
1260 expressionFree( ord->expression );
1261 ord->expression = NULL;
1263 if( NULL == ord->next ) {
1264 ord->next = free_order_item_list;
1270 // Transfer the entire list to the free list
1271 free_order_item_list = first;
1275 @brief Build a list of column names for a specified query.
1276 @param state Pointer to the query-building context.
1277 @param query Pointer to the specified query.
1278 @return Pointer to a newly-allocated JSON_ARRAY of column names.
1280 In the resulting array, each entry is either a JSON_STRING or (when no column name is
1281 available) a JSON_NULL.
1283 The calling code is responsible for freeing the list by calling jsonObjectFree().
1285 jsonObject* oilsGetColNames( BuildSQLState* state, StoredQ* query ) {
1286 if( !state || !query )
1289 // Save the outermost query id for possible use in an error message
1292 // Find the first SELECT, from which we will take the column names
1293 while( query->type != QT_SELECT ) {
1294 QSeq* child_list = query->child_list;
1299 query = child_list->child_query;
1304 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1305 "Unable to find first SELECT in query # %d", id ));
1309 // Get the SELECT list for the first SELECT
1310 SelectItem* col = query->select_list;
1313 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1314 "First SELECT in query # %d has empty SELECT list", id ));
1318 jsonObject* col_list = jsonNewObjectType( JSON_ARRAY );
1320 // Traverse the list, adding an entry for each
1322 const char* alias = NULL;
1323 if( col->column_alias )
1324 alias = col->column_alias;
1326 Expression* expression = col->expression;
1327 if( expression && EXP_COLUMN == expression->type && expression->column_name )
1328 alias = expression->column_name;
1331 jsonObjectPush( col_list, jsonNewObject( alias ) );
1339 @brief Push an IdNode onto a stack of IdNodes.
1340 @param stack Pointer to the stack.
1341 @param id Id of the new node.
1342 @param alias Alias, if any, of the new node.
1344 static void push_id( IdNode** stack, int id, const char* alias ) {
1347 // Allocate a node; from the free list if possible, from the heap if necessary.
1348 IdNode* node = NULL;
1349 if( free_id_node_list ) {
1350 node = free_id_node_list;
1351 free_id_node_list = free_id_node_list->next;
1353 node = safe_malloc( sizeof( IdNode ));
1356 node->next = *stack;
1359 node->alias = strdup( alias );
1369 @brief Remove the node at the top of an IdNode stack.
1370 @param stack Pointer to the IdNode stack.
1372 void pop_id( IdNode** stack ) {
1374 IdNode* node = *stack;
1375 *stack = node->next;
1378 free( node->alias );
1382 node->next = free_id_node_list;
1383 free_id_node_list = node;
1388 @brief Search a stack of IDs for a match by either ID or, optionally, by alias.
1389 @param stack Pointer to the stack.
1390 @param id The id to search for.
1391 @param alias (Optional) the alias to search for.
1392 @return A pointer to the matching node if one is found, or NULL if not.
1394 This search is used to detect cases where a query, expression, or FROM clause is nested
1395 inside itself, in order to avoid infinite recursion; or in order to avoid conflicting
1396 table references in a FROM clause.
1398 static const IdNode* searchIdStack( const IdNode* stack, int id, const char* alias ) {
1400 const IdNode* node = stack;
1402 if( node->id == id )
1403 return node; // Matched on id
1404 else if( alias && node->alias && !strcmp( alias, node->alias ))
1405 return node; // Matched on alias
1410 return NULL; // No match found
1414 @brief Free up any resources held by the StoredQ module.
1416 void storedQCleanup( void ) {
1418 // Free all the nodes in the free state list
1419 StoredQ* sq = free_storedq_list;
1421 free_storedq_list = sq->next;
1423 sq = free_storedq_list;
1426 // Free all the nodes in the free from_relation list
1427 FromRelation* fr = free_from_relation_list;
1429 free_from_relation_list = fr->next;
1431 fr = free_from_relation_list;
1434 // Free all the nodes in the free expression list
1435 Expression* exp = free_expression_list;
1437 free_expression_list = exp->next;
1439 exp = free_expression_list;
1442 // Free all the nodes in the free select item list
1443 SelectItem* sel = free_select_item_list;
1445 free_select_item_list = sel->next;
1447 sel = free_select_item_list;
1450 // Free all the nodes in the free select item list
1451 IdNode* node = free_id_node_list;
1453 free_id_node_list = node->next;
1455 node = free_id_node_list;
1458 // Free all the nodes in the free query sequence list
1459 QSeq* seq = free_qseq_list;
1461 free_qseq_list = seq->next;
1463 seq = free_qseq_list;
1466 // Free all the nodes in the free order item list
1467 OrderItem* ord = free_order_item_list;
1469 free_order_item_list = ord->next;
1471 ord = free_order_item_list;
1476 @brief Return a boolean value from a database result.
1477 @param result The database result.
1478 @param i Index of the column in the result, starting with 1 );
1479 @return 1 if true, or 0 for false.
1481 Null values and error conditions are interpreted as FALSE.
1483 static int oils_result_get_bool_idx( dbi_result result, int i ) {
1485 const char* str = dbi_result_get_string_idx( result, i );
1486 return (str && *str == 't' ) ? 1 : 0;
1492 @brief Enable verbose messages.
1494 The messages are written to standard output, which for a server is /dev/null. Hence this
1495 option is useful only for a non-server. It is intended only as a convenience for
1496 development and debugging.
1498 void oilsStoredQSetVerbose( void ) {