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 );
122 static StoredQ* constructStoredQ( BuildSQLState* state, dbi_result result ) {
124 // Get the column values from the result
125 int id = dbi_result_get_int_idx( result, 1 );
126 const char* type_str = dbi_result_get_string_idx( result, 2 );
129 if( !strcmp( type_str, "SELECT" ))
131 else if( !strcmp( type_str, "UNION" ))
133 else if( !strcmp( type_str, "INTERSECT" ))
135 else if( !strcmp( type_str, "EXCEPT" ))
138 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
139 "Invalid query type \"%s\"", type_str ));
143 int use_all = oils_result_get_bool_idx( result, 3 );
144 int use_distinct = oils_result_get_bool_idx( result, 4 );
147 if( dbi_result_field_is_null_idx( result, 5 ) )
150 from_clause_id = dbi_result_get_int_idx( result, 5 );
153 if( dbi_result_field_is_null_idx( result, 6 ) )
154 where_clause_id = -1;
156 where_clause_id = dbi_result_get_int_idx( result, 6 );
158 int having_clause_id;
159 if( dbi_result_field_is_null_idx( result, 7 ) )
160 having_clause_id = -1;
162 having_clause_id = dbi_result_get_int_idx( result, 7 );
164 FromRelation* from_clause = NULL;
165 if( QT_SELECT == type ) {
166 // A SELECT query needs a FROM clause; go get it
167 if( from_clause_id != -1 ) {
168 from_clause = getFromRelation( state, from_clause_id );
170 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
171 "Unable to construct FROM clause for id = %d", from_clause_id ));
176 // Must be one of UNION, INTERSECT, or EXCEPT
177 if( from_clause_id != -1 )
178 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
179 "FROM clause found and ignored for %s query in query #%d", type_str, id ));
182 // If this is a SELECT query, we need a SELECT list. Go get one.
183 SelectItem* select_list = NULL;
184 QSeq* child_list = NULL;
185 if( QT_SELECT == type ) {
186 select_list = getSelectList( state, id );
188 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
189 "No SELECT list found for query id = %d", id ));
190 fromRelationFree( from_clause );
194 // Construct child queries of UNION, INTERSECT, or EXCEPT query
195 child_list = loadChildQueries( state, id, type_str );
197 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
198 "Unable to load child queries for %s query # %d", type_str, id ));
200 fromRelationFree( from_clause );
205 // Get the WHERE clause, if there is one
206 Expression* where_clause = NULL;
207 if( where_clause_id != -1 ) {
208 where_clause = getExpression( state, where_clause_id );
209 if( ! where_clause ) {
210 // shouldn't happen due to foreign key constraint
211 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
212 "Unable to fetch WHERE expression for query id = %d", id ));
213 freeQSeqList( child_list );
214 fromRelationFree( from_clause );
215 selectListFree( select_list );
220 // Get the ORDER BY clause, if there is one
221 OrderItem* order_by_list = getOrderByList( state, id );
223 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
224 "Unable to load ORDER BY clause for query %d", id ));
225 expressionFree( where_clause );
226 freeQSeqList( child_list );
227 fromRelationFree( from_clause );
228 selectListFree( select_list );
232 // Allocate a StoredQ: from the free list if possible, from the heap if necessary
235 if( free_storedq_list ) {
236 sq = free_storedq_list;
237 free_storedq_list = free_storedq_list->next;
239 sq = safe_malloc( sizeof( StoredQ ) );
241 // Populate the StoredQ
246 sq->use_all = use_all;
247 sq->use_distinct = use_distinct;
248 sq->from_clause = from_clause;
249 sq->where_clause = where_clause;
250 sq->select_list = select_list;
251 sq->child_list = child_list;
252 sq->order_by_list = order_by_list;
258 @brief Load the child queries subordinate to a UNION, INTERSECT, or EXCEPT query.
259 @param state Pointer to the query-building context.
260 @param parent ID of the UNION, INTERSECT, or EXCEPT query.
261 @param type_str The type of the query ("UNION", "INTERSECT", or "EXCEPT").
262 @return If successful, a pointer to a linked list of QSeq, each bearing a pointer to a
263 StoredQ; otherwise NULL.
265 The @a type_str parameter is used only for building error messages.
267 static QSeq* loadChildQueries( BuildSQLState* state, int parent_id, const char* type_str ) {
268 QSeq* child_list = NULL;
270 // The ORDER BY is in descending order so that we can build the list by adding to
271 // the head, and it will wind up in the right order.
272 dbi_result result = dbi_conn_queryf( state->dbhandle,
273 "SELECT id, parent_query, seq_no, child_query "
274 "FROM query.query_sequence WHERE parent_query = %d ORDER BY seq_no DESC", parent_id );
276 if( dbi_result_first_row( result ) ) {
280 QSeq* seq = constructQSeq( state, result );
282 PRINT( "Found a child query\n" );
283 PRINT( "\tid: %d\n", seq->id );
284 PRINT( "\tparent id: %d\n", seq->parent_query_id );
285 PRINT( "\tseq_no: %d\n", seq->seq_no );
286 // Add to the head of the list
287 seq->next = child_list;
290 freeQSeqList( child_list );
293 if( !dbi_result_next_row( result ))
297 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
298 "%s query # %d has only one child query", type_str, parent_id ));
300 freeQSeqList( child_list );
304 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
305 "%s query # %d has no child queries within it", type_str, parent_id ));
311 int errnum = dbi_conn_error( state->dbhandle, &msg );
312 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
313 "Unable to query query.query_sequence table: # %d %s",
314 errnum, msg ? msg : "No description available" ));
322 static QSeq* constructQSeq( BuildSQLState* state, dbi_result result ) {
323 int id = dbi_result_get_int_idx( result, 1 );
324 int parent_query_id = dbi_result_get_int_idx( result, 2 );
325 int seq_no = dbi_result_get_int_idx( result, 3 );
326 int child_query_id = dbi_result_get_int_idx( result, 4 );
328 StoredQ* child_query = getStoredQuery( state, child_query_id );
330 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
331 "Unable to load child query # %d for parent query %d",
332 child_query_id, parent_query_id ));
337 // Allocate a QSeq; from the free list if possible, from the heap if necessary
339 if( free_qseq_list ) {
340 seq = free_qseq_list;
341 free_qseq_list = free_qseq_list->next;
343 seq = safe_malloc( sizeof( QSeq ));
347 seq->parent_query_id = parent_query_id;
348 seq->seq_no = seq_no;
349 seq->child_query = child_query;
354 static void freeQSeqList( QSeq* seq ) {
360 storedQFree( seq->child_query );
361 seq->child_query = NULL;
366 seq->next = free_qseq_list;
371 free_qseq_list = first;
375 @brief Deallocate the memory owned by a StoredQ.
376 @param sq Pointer to the StoredQ to be deallocated.
378 void storedQFree( StoredQ* sq ) {
380 fromRelationFree( sq->from_clause );
381 sq->from_clause = NULL;
382 selectListFree( sq->select_list );
383 sq->select_list = NULL;
384 expressionFree( sq->where_clause );
385 sq->where_clause = NULL;
386 if( sq->child_list ) {
387 freeQSeqList( sq->child_list );
388 sq->child_list = NULL;
390 if( sq->order_by_list ) {
391 orderItemListFree( sq->order_by_list );
392 sq->order_by_list = NULL;
395 // Stick the empty husk on the free list for potential reuse
396 sq->next = free_storedq_list;
397 free_storedq_list = sq;
401 static FromRelation* getFromRelation( BuildSQLState* state, int id ) {
402 FromRelation* fr = NULL;
403 dbi_result result = dbi_conn_queryf( state->dbhandle,
404 "SELECT id, type, table_name, class_name, subquery, function_call, "
405 "table_alias, parent_relation, seq_no, join_type, on_clause "
406 "FROM query.from_relation WHERE id = %d;", id );
408 if( dbi_result_first_row( result ) ) {
409 fr = constructFromRelation( state, result );
411 PRINT( "Got a from_relation row\n" );
412 PRINT( "\tid: %d\n", fr->id );
413 PRINT( "\ttype: %d\n", (int) fr->type );
414 PRINT( "\ttable_name: %s\n", fr->table_name ? fr->table_name : "(none)" );
415 PRINT( "\tclass_name: %s\n", fr->class_name ? fr->class_name : "(none)" );
416 PRINT( "\tsubquery_id: %d\n", fr->subquery_id );
417 PRINT( "\tfunction_call_id: %d\n", fr->function_call_id );
418 PRINT( "\ttable_alias: %s\n", fr->table_alias ? fr->table_alias : "(none)" );
419 PRINT( "\tparent_relation_id: %d\n", fr->parent_relation_id );
420 PRINT( "\tseq_no: %d\n", fr->seq_no );
421 PRINT( "\tjoin_type = %d\n", fr->join_type );
422 // Check the stack to see if the current from clause is nested inside itself.
423 // If it is, then abort in order to avoid infinite recursion. If it isn't,
424 // then add it to the stack. (Make sure to pop it off the stack before
426 const char* effective_alias = fr->table_alias;
427 if( !effective_alias )
428 effective_alias = fr->class_name;
429 const IdNode* node = searchIdStack( state->from_stack, id, effective_alias );
432 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
433 "Infinite recursion detected; from clause # %d is nested "
434 "within itself", id ));
436 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
437 "Conflicting nested table aliases \"%s\" in from clause # %d",
438 effective_alias, node->id ));
442 push_id( &state->from_stack, id, effective_alias );
444 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
445 "Unable to build a FromRelation for id = %d", id ));
447 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
448 "FROM relation not found for id = %d", id ));
450 dbi_result_free( result );
453 int errnum = dbi_conn_error( state->dbhandle, &msg );
454 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
455 "Unable to query query.from_relation table: #%d %s",
456 errnum, msg ? msg : "No description available" ));
460 pop_id( &state->from_stack );
465 static FromRelation* constructFromRelation( BuildSQLState* state, dbi_result result ) {
466 // Get the column values from the result
467 int id = dbi_result_get_int_idx( result, 1 );
468 const char* type_str = dbi_result_get_string_idx( result, 2 );
470 FromRelationType type;
471 if( !strcmp( type_str, "RELATION" ))
473 else if( !strcmp( type_str, "SUBQUERY" ))
475 else if( !strcmp( type_str, "FUNCTION" ))
478 type = FRT_RELATION; // shouldn't happen due to database constraint
480 const char* table_name = dbi_result_get_string_idx( result, 3 );
481 const char* class_name = dbi_result_get_string_idx( result, 4 );
484 if( dbi_result_field_is_null_idx( result, 5 ) )
487 subquery_id = dbi_result_get_int_idx( result, 5 );
489 int function_call_id;
490 if( dbi_result_field_is_null_idx( result, 6 ) )
491 function_call_id = -1;
493 function_call_id = dbi_result_get_int_idx( result, 6 );
495 const char* table_alias = dbi_result_get_string_idx( result, 7 );
497 int parent_relation_id;
498 if( dbi_result_field_is_null_idx( result, 8 ) )
499 parent_relation_id = -1;
501 parent_relation_id = dbi_result_get_int_idx( result, 8 );
503 int seq_no = dbi_result_get_int_idx( result, 9 );
506 const char* join_type_str = dbi_result_get_string_idx( result, 10 );
509 else if( !strcmp( join_type_str, "INNER" ) )
510 join_type = JT_INNER;
511 else if( !strcmp( join_type_str, "LEFT" ) )
513 else if( !strcmp( join_type_str, "RIGHT" ) )
514 join_type = JT_RIGHT;
515 else if( !strcmp( join_type_str, "FULL" ) )
518 join_type = JT_NONE; // shouldn't happen due to database constraint
521 if( dbi_result_field_is_null_idx( result, 11 ) )
524 on_clause_id = dbi_result_get_int_idx( result, 11 );
526 StoredQ* subquery = NULL;
532 if( -1 == subquery_id ) {
533 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
534 "Internal error: no subquery specified for FROM relation # %d", id ));
538 if( ! table_alias ) {
539 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
540 "Subquery needs alias in FROM relation # %d", id ));
544 subquery = getStoredQuery( state, subquery_id );
546 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
547 "Unable to load subquery for FROM relation # %d", id ));
553 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
554 "Functions in FROM clause not yet supported" ));
559 FromRelation* join_list = getJoinList( state, id );
561 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
562 "Unable to load join list for FROM relation # %d", id ));
566 Expression* on_clause = NULL;
567 if( on_clause_id != -1 ) {
568 on_clause = getExpression( state, on_clause_id );
570 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
571 "Unable to load ON condition for FROM relation # %d", id ));
572 joinListFree( join_list );
576 PRINT( "\tGot an ON condition\n" );
579 // Allocate a FromRelation: from the free list if possible, from the heap if necessary
582 if( free_from_relation_list ) {
583 fr = free_from_relation_list;
584 free_from_relation_list = free_from_relation_list->next;
586 fr = safe_malloc( sizeof( FromRelation ) );
588 // Populate the FromRelation
593 fr->table_name = table_name ? strdup( table_name ) : NULL;
594 fr->class_name = class_name ? strdup( class_name ) : NULL;
595 fr->subquery_id = subquery_id;
596 fr->subquery = subquery;
597 fr->function_call_id = function_call_id;
598 fr->table_alias = table_alias ? strdup( table_alias ) : NULL;
599 fr->parent_relation_id = parent_relation_id;
601 fr->join_type = join_type;
602 fr->on_clause = on_clause;
603 fr->join_list = join_list;
609 @brief Build a list of joined relations.
610 @param state Pointer to the query-building context.
611 @param id ID of the parent relation.
612 @return A pointer to the first in a linked list of FromRelations, if there are any; or
613 NULL if there aren't any, or in case of an error.
615 Look for relations joined directly to the parent relation, and make a list of them.
617 static FromRelation* getJoinList( BuildSQLState* state, int id ) {
618 FromRelation* join_list = NULL;
620 // The ORDER BY is in descending order so that we can build the list by adding to
621 // the head, and it will wind up in the right order.
622 dbi_result result = dbi_conn_queryf( state->dbhandle,
623 "SELECT id, type, table_name, class_name, subquery, function_call, "
624 "table_alias, parent_relation, seq_no, join_type, on_clause "
625 "FROM query.from_relation WHERE parent_relation = %d ORDER BY seq_no DESC", id );
628 if( dbi_result_first_row( result ) ) {
630 FromRelation* relation = constructFromRelation( state, result );
632 PRINT( "Found a joined relation\n" );
633 PRINT( "\tjoin_type: %d\n", relation->join_type );
634 PRINT( "\ttable_name: %s\n", relation->table_name );
635 relation->next = join_list;
636 join_list = relation;
638 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
639 "Unable to build join list for from relation id #%d", id ));
640 joinListFree( join_list );
644 if( !dbi_result_next_row( result ) )
650 int errnum = dbi_conn_error( state->dbhandle, &msg );
651 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
652 "Unable to query query.from_relation table for join list: #%d %s",
653 errnum, msg ? msg : "No description available" ));
660 @brief Free a list of FromRelations.
661 @param join_list Pointer to the first FromRelation in the list.
663 static void joinListFree( FromRelation* join_list ) {
665 FromRelation* temp = join_list->next;
666 fromRelationFree( join_list );
672 @brief Deallocate a FromRelation.
673 @param fr Pointer to the FromRelation to be freed.
675 Free the strings that the FromRelation owns. The FromRelation itself goes onto a
676 free list for potential reuse.
678 static void fromRelationFree( FromRelation* fr ) {
680 free( fr->table_name );
681 fr->table_name = NULL;
682 free( fr->class_name );
683 fr->class_name = NULL;
685 storedQFree( fr->subquery );
688 free( fr->table_alias );
689 fr->table_alias = NULL;
690 if( fr->on_clause ) {
691 expressionFree( fr->on_clause );
692 fr->on_clause = NULL;
694 joinListFree( fr->join_list );
695 fr->join_list = NULL;
697 fr->next = free_from_relation_list;
698 free_from_relation_list = fr;
702 static SelectItem* getSelectList( BuildSQLState* state, int query_id ) {
703 SelectItem* select_list = NULL;
705 // The ORDER BY is in descending order so that we can build the list by adding to
706 // the head, and it will wind up in the right order.
707 dbi_result result = dbi_conn_queryf( state->dbhandle,
708 "SELECT id, stored_query, seq_no, expression, column_alias, grouped_by "
709 "FROM query.select_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
711 if( dbi_result_first_row( result ) ) {
713 SelectItem* item = constructSelectItem( state, result );
715 PRINT( "Found a SELECT item\n" );
716 PRINT( "\tid: %d\n", item->id );
717 PRINT( "\tstored_query_id: %d\n", item->stored_query_id );
718 PRINT( "\tseq_no: %d\n", item->seq_no );
719 PRINT( "\tcolumn_alias: %s\n",
720 item->column_alias ? item->column_alias : "(none)" );
721 PRINT( "\tgrouped_by: %d\n", item->grouped_by );
723 item->next = select_list;
726 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
727 "Unable to build select list for query id #%d", query_id ));
728 selectListFree( select_list );
732 if( !dbi_result_next_row( result ) )
738 int errnum = dbi_conn_error( state->dbhandle, &msg );
739 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
740 "Unable to query query.select_list table: #%d %s",
741 errnum, msg ? msg : "No description available" ));
747 static SelectItem* constructSelectItem( BuildSQLState* state, dbi_result result ) {
749 // Get the column values
750 int id = dbi_result_get_int_idx( result, 1 );
751 int stored_query_id = dbi_result_get_int_idx( result, 2 );
752 int seq_no = dbi_result_get_int_idx( result, 3 );
753 int expression_id = dbi_result_get_int_idx( result, 4 );
754 const char* column_alias = dbi_result_get_string_idx( result, 5 );
755 int grouped_by = oils_result_get_bool_idx( result, 6 );
757 // Construct an Expression
758 Expression* expression = getExpression( state, expression_id );
760 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
761 "Unable to fetch expression for id = %d", expression_id ));
765 // Allocate a SelectItem: from the free list if possible, from the heap if necessary
768 if( free_select_item_list ) {
769 sel = free_select_item_list;
770 free_select_item_list = free_select_item_list->next;
772 sel = safe_malloc( sizeof( SelectItem ) );
776 sel->stored_query_id = stored_query_id;
777 sel->seq_no = seq_no;
778 sel->expression = expression;
779 sel->column_alias = column_alias ? strdup( column_alias ) : NULL;
780 sel->grouped_by = grouped_by;
785 static void selectListFree( SelectItem* sel ) {
787 return; // Nothing to free
789 SelectItem* first = sel;
791 free( sel->column_alias );
792 sel->column_alias = NULL;
793 expressionFree( sel->expression );
794 sel->expression = NULL;
796 if( NULL == sel->next ) {
797 sel->next = free_select_item_list;
803 // Transfer the entire list to the free list
804 free_select_item_list = first;
807 static Expression* getExpression( BuildSQLState* state, int id ) {
809 // Check the stack to see if the current expression is nested inside itself. If it is,
810 // then abort in order to avoid infinite recursion. If it isn't, then add it to the
811 // stack. (Make sure to pop it off the stack before returning.)
812 if( searchIdStack( state->expr_stack, id, NULL )) {
813 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
814 "Infinite recursion detected; expression # %d is nested within itself", id ));
818 push_id( &state->expr_stack, id, NULL );
820 Expression* exp = NULL;
821 dbi_result result = dbi_conn_queryf( state->dbhandle,
822 "SELECT id, type, parenthesize, parent_expr, seq_no, literal, table_alias, "
823 "column_name, left_operand, operator, right_operand, function_id, subquery, cast_type "
824 "FROM query.expression WHERE id = %d;", id );
826 if( dbi_result_first_row( result ) ) {
827 exp = constructExpression( state, result );
829 PRINT( "Got an expression\n" );
830 PRINT( "\tid = %d\n", exp->id );
831 PRINT( "\ttype = %d\n", exp->type );
832 PRINT( "\tparenthesize = %d\n", exp->parenthesize );
833 PRINT( "\tcolumn_name = %s\n", exp->column_name ? exp->column_name : "(none)" );
835 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
836 "Unable to construct an Expression for id = %d", id ));
840 int errnum = dbi_conn_error( state->dbhandle, &msg );
841 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
842 "Unable to query query.expression table: #%d %s",
843 errnum, msg ? msg : "No description available" ));
846 pop_id( &state->expr_stack );
850 static Expression* constructExpression( BuildSQLState* state, dbi_result result ) {
852 int id = dbi_result_get_int_idx( result, 1 );
853 const char* type_str = dbi_result_get_string_idx( result, 2 );
856 if( !strcmp( type_str, "xbet" ))
858 else if( !strcmp( type_str, "xbool" ))
860 else if( !strcmp( type_str, "xcase" ))
862 else if( !strcmp( type_str, "xcast" ))
864 else if( !strcmp( type_str, "xcol" ))
866 else if( !strcmp( type_str, "xex" ))
868 else if( !strcmp( type_str, "xfld" ))
870 else if( !strcmp( type_str, "xfunc" ))
872 else if( !strcmp( type_str, "xin" ))
874 else if( !strcmp( type_str, "xnbet" ))
875 type = EXP_NOT_BETWEEN;
876 else if( !strcmp( type_str, "xnex" ))
877 type = EXP_NOT_EXIST;
878 else if( !strcmp( type_str, "xnin" ))
880 else if( !strcmp( type_str, "xnull" ))
882 else if( !strcmp( type_str, "xnum" ))
884 else if( !strcmp( type_str, "xop" ))
886 else if( !strcmp( type_str, "xstr" ))
888 else if( !strcmp( type_str, "xsubq" ))
891 type = EXP_NULL; // shouldn't happen due to database constraint
893 int parenthesize = oils_result_get_bool_idx( result, 3 );
896 if( dbi_result_field_is_null_idx( result, 4 ))
899 parent_expr_id = dbi_result_get_int_idx( result, 4 );
901 int seq_no = dbi_result_get_int_idx( result, 5 );
902 const char* literal = dbi_result_get_string_idx( result, 6 );
903 const char* table_alias = dbi_result_get_string_idx( result, 7 );
904 const char* column_name = dbi_result_get_string_idx( result, 8 );
907 if( dbi_result_field_is_null_idx( result, 9 ))
908 left_operand_id = -1;
910 left_operand_id = dbi_result_get_int_idx( result, 9 );
912 const char* operator = dbi_result_get_string_idx( result, 10 );
914 int right_operand_id;
915 if( dbi_result_field_is_null_idx( result, 11 ))
916 right_operand_id = -1;
918 right_operand_id = dbi_result_get_int_idx( result, 11 );
921 if( dbi_result_field_is_null_idx( result, 12 ))
924 function_id = dbi_result_get_int_idx( result, 12 );
927 if( dbi_result_field_is_null_idx( result, 13 ))
930 subquery_id = dbi_result_get_int_idx( result, 13 );
933 if( dbi_result_field_is_null_idx( result, 14 ))
936 cast_type_id = dbi_result_get_int_idx( result, 14 );
938 Expression* left_operand = NULL;
939 Expression* right_operand = NULL;
940 StoredQ* subquery = NULL;
942 if( EXP_OPERATOR == type ) {
943 // Load left and/or right operands
944 if( -1 == left_operand_id && -1 == right_operand_id ) {
945 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
946 "Expression # %d is an operator with no operands", id ));
951 if( left_operand_id != -1 ) {
952 left_operand = getExpression( state, left_operand_id );
953 if( !left_operand ) {
954 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
955 "Unable to get left operand in expression # %d", id ));
961 if( right_operand_id != -1 ) {
962 right_operand = getExpression( state, right_operand_id );
963 if( !right_operand ) {
964 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
965 "Unable to get right operand in expression # %d", id ));
967 expressionFree( left_operand );
971 } else if( EXP_IN == type ) {
972 if( -1 == left_operand_id ) {
973 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
974 "IN condition has no left operand in expression # %d", id ));
978 left_operand = getExpression( state, left_operand_id );
979 if( !left_operand ) {
980 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
981 "Unable to get left operand for IN condition in expression # %d", id ));
987 if( -1 == subquery_id ) {
988 // To do: load IN list of subexpressions
989 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
990 "IN lists not yet supported for expression # %d", id ));
994 subquery = getStoredQuery( state, subquery_id );
996 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
997 "Unable to load subquery for IN expression # %d", id ));
1002 } else if( EXP_EXIST == type ) {
1003 if( -1 == subquery_id ) {
1004 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1005 "Internal error: No subquery found for EXIST expression # %d", id ));
1009 subquery = getStoredQuery( state, subquery_id );
1011 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1012 "Unable to load subquery for EXIST expression # %d", id ));
1017 } else if( EXP_SUBQUERY == type ) {
1018 if( -1 == subquery_id ) {
1019 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1020 "Subquery expression # %d has no query id", id ));
1024 // Load a subquery, if there is one
1025 subquery = getStoredQuery( state, subquery_id );
1027 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1028 "Unable to load subquery for expression # %d", id ));
1032 if( subquery->select_list && subquery->select_list->next ) {
1033 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1034 "Subquery # %d as expression returns more than one column", subquery_id ));
1038 PRINT( "\tExpression is subquery %d\n", subquery_id );
1042 // Allocate an Expression: from the free list if possible, from the heap if necessary
1043 Expression* exp = NULL;
1044 if( free_expression_list ) {
1045 exp = free_expression_list;
1046 free_expression_list = free_expression_list->next;
1048 exp = safe_malloc( sizeof( Expression ) );
1050 // Populate the Expression
1054 exp->parenthesize = parenthesize;
1055 exp->parent_expr_id = parent_expr_id;
1056 exp->seq_no = seq_no;
1057 exp->literal = literal ? strdup( literal ) : NULL;
1058 exp->table_alias = table_alias ? strdup( table_alias ) : NULL;
1059 exp->column_name = column_name ? strdup( column_name ) : NULL;
1060 exp->left_operand = left_operand;
1061 exp->op = operator ? strdup( operator ) : NULL;
1062 exp->right_operand = right_operand;
1063 exp->function_id = function_id;
1064 exp->subquery_id = subquery_id;
1065 exp->subquery = subquery;
1066 exp->cast_type_id = subquery_id;
1072 @brief Deallocate an Expression.
1073 @param exp Pointer to the Expression to be deallocated.
1075 Free the strings owned by the Expression. Put the Expressions itself into a free list.
1077 static void expressionFree( Expression* exp ) {
1079 free( exp->literal );
1080 exp->literal = NULL;
1081 free( exp->table_alias );
1082 exp->table_alias = NULL;
1083 free( exp->column_name );
1084 exp->column_name = NULL;
1085 if( exp->left_operand ) {
1086 expressionFree( exp->left_operand );
1087 exp->left_operand = NULL;
1091 if( exp->right_operand ) {
1092 expressionFree( exp->right_operand );
1093 exp->right_operand = NULL;
1095 if( exp->subquery ) {
1096 storedQFree( exp->subquery );
1097 exp->subquery = NULL;
1100 exp->next = free_expression_list;
1101 free_expression_list = exp;
1105 static OrderItem* getOrderByList( BuildSQLState* state, int query_id ) {
1106 OrderItem* ord_list = NULL;
1108 // The ORDER BY is in descending order so that we can build the list by adding to
1109 // the head, and it will wind up in the right order.
1110 dbi_result result = dbi_conn_queryf( state->dbhandle,
1111 "SELECT id, stored_query, seq_no, expression "
1112 "FROM query.order_by_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
1114 if( dbi_result_first_row( result ) ) {
1116 OrderItem* item = constructOrderItem( state, result );
1118 PRINT( "Found an ORDER BY item\n" );
1120 item->next = ord_list;
1123 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1124 "Unable to build ORDER BY item for query id #%d", query_id ));
1125 orderItemListFree( ord_list );
1129 if( !dbi_result_next_row( result ) )
1135 int errnum = dbi_conn_error( state->dbhandle, &msg );
1136 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1137 "Unable to query query.order_by_list table: #%d %s",
1138 errnum, msg ? msg : "No description available" ));
1144 static OrderItem* constructOrderItem( BuildSQLState* state, dbi_result result ) {
1145 int id = dbi_result_get_int_idx( result, 1 );
1146 int stored_query_id = dbi_result_get_int_idx( result, 2 );
1147 int seq_no = dbi_result_get_int_idx( result, 3 );
1148 int expression_id = dbi_result_get_int_idx( result, 4 );
1149 // Allocate a SelectItem: from the free list if possible, from the heap if necessary
1151 // Construct an Expression
1152 Expression* expression = getExpression( state, expression_id );
1154 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1155 "Unable to fetch ORDER BY expression for id = %d", expression_id ));
1159 // Allocate an OrderItem; from the free list if possible, or from the heap if necessary.
1161 if( free_order_item_list ) {
1162 ord = free_order_item_list;
1163 free_order_item_list = free_order_item_list->next;
1165 ord = safe_malloc( sizeof( OrderItem ));
1169 ord->stored_query_id = stored_query_id;
1170 ord->seq_no = seq_no;
1171 ord->expression = expression;
1177 @brief Deallocate a linked list of OrderItems.
1178 @param exp Pointer to the first OrderItem in the list to be deallocated.
1180 Deallocate the memory owned by the OrderItems. Put the items themselves into a free list.
1182 static void orderItemListFree( OrderItem* ord ) {
1184 return; // Nothing to free
1186 OrderItem* first = ord;
1188 expressionFree( ord->expression );
1189 ord->expression = NULL;
1191 if( NULL == ord->next ) {
1192 ord->next = free_order_item_list;
1198 // Transfer the entire list to the free list
1199 free_order_item_list = first;
1203 @brief Push an IdNode onto a stack of IdNodes.
1204 @param stack Pointer to the stack.
1205 @param id Id of the new node.
1206 @param alias Alias, if any, of the new node.
1208 static void push_id( IdNode** stack, int id, const char* alias ) {
1211 // Allocate a node; from the free list if possible, from the heap if necessary.
1212 IdNode* node = NULL;
1213 if( free_id_node_list ) {
1214 node = free_id_node_list;
1215 free_id_node_list = free_id_node_list->next;
1217 node = safe_malloc( sizeof( IdNode ));
1220 node->next = *stack;
1223 node->alias = strdup( alias );
1233 @brief Remove the node at the top of an IdNode stack.
1234 @param stack Pointer to the IdNode stack.
1236 void pop_id( IdNode** stack ) {
1238 IdNode* node = *stack;
1239 *stack = node->next;
1242 free( node->alias );
1246 node->next = free_id_node_list;
1247 free_id_node_list = node;
1252 @brief Search a stack of IDs for a match by either ID or, optionally, by alias.
1253 @param stack Pointer to the stack.
1254 @param id The id to search for.
1255 @param alias (Optional) the alias to search for.
1256 @return A pointer to the matching node if one is found, or NULL if not.
1258 This search is used to detect cases where a query, expression, or FROM clause is nested
1259 inside itself, in order to avoid infinite recursion; or in order to avoid conflicting
1260 table references in a FROM clause.
1262 static const IdNode* searchIdStack( const IdNode* stack, int id, const char* alias ) {
1264 const IdNode* node = stack;
1266 if( node->id == id )
1267 return node; // Matched on id
1268 else if( alias && node->alias && !strcmp( alias, node->alias ))
1269 return node; // Matched on alias
1274 return NULL; // No match found
1278 @brief Free up any resources held by the StoredQ module.
1280 void storedQCleanup( void ) {
1282 // Free all the nodes in the free state list
1283 StoredQ* sq = free_storedq_list;
1285 free_storedq_list = sq->next;
1287 sq = free_storedq_list;
1290 // Free all the nodes in the free from_relation list
1291 FromRelation* fr = free_from_relation_list;
1293 free_from_relation_list = fr->next;
1295 fr = free_from_relation_list;
1298 // Free all the nodes in the free expression list
1299 Expression* exp = free_expression_list;
1301 free_expression_list = exp->next;
1303 exp = free_expression_list;
1306 // Free all the nodes in the free select item list
1307 SelectItem* sel = free_select_item_list;
1309 free_select_item_list = sel->next;
1311 sel = free_select_item_list;
1314 // Free all the nodes in the free select item list
1315 IdNode* node = free_id_node_list;
1317 free_id_node_list = node->next;
1319 node = free_id_node_list;
1322 // Free all the nodes in the free query sequence list
1323 QSeq* seq = free_qseq_list;
1325 free_qseq_list = seq->next;
1327 seq = free_qseq_list;
1330 // Free all the nodes in the free order item list
1331 OrderItem* ord = free_order_item_list;
1333 free_order_item_list = ord->next;
1335 ord = free_order_item_list;
1340 @brief Return a boolean value from a database result.
1341 @param result The database result.
1342 @param i Index of the column in the result, starting with 1 );
1343 @return 1 if true, or 0 for false.
1345 Null values and error conditions are interpreted as FALSE.
1347 static int oils_result_get_bool_idx( dbi_result result, int i ) {
1349 const char* str = dbi_result_get_string_idx( result, i );
1350 return (str && *str == 't' ) ? 1 : 0;
1355 void oilsStoredQSetVerbose( void ) {