3 @brief Load an abstract representation of a query from the database.
10 #include "opensrf/utils.h"
11 #include "opensrf/log.h"
12 #include "opensrf/string_array.h"
13 #include "opensrf/osrf_hash.h"
14 #include "opensrf/osrf_application.h"
15 #include "openils/oils_sql.h"
16 #include "openils/oils_buildq.h"
18 #define PRINT if( verbose ) printf
26 static int oils_result_get_bool_idx( dbi_result result, int i );
28 static FromRelation* getFromRelation( BuildSQLState* state, int id );
29 static FromRelation* constructFromRelation( BuildSQLState* state, dbi_result result );
30 static FromRelation* getJoinList( BuildSQLState* state, int id );
31 static void joinListFree( FromRelation* join_list );
32 static void fromRelationFree( FromRelation* fr );
34 static QSeq* loadChildQueries( BuildSQLState* state, int parent_id, const char* type_str );
35 static QSeq* constructQSeq( BuildSQLState* state, dbi_result result );
36 static void freeQSeqList( QSeq* seq );
37 static StoredQ* constructStoredQ( BuildSQLState* state, dbi_result result );
39 static SelectItem* getSelectList( BuildSQLState* state, int query_id );
40 static SelectItem* constructSelectItem( BuildSQLState* state, dbi_result result );
41 static void selectListFree( SelectItem* sel );
43 static BindVar* getBindVar( BuildSQLState* state, const char* name );
44 static BindVar* constructBindVar( BuildSQLState* state, dbi_result result );
45 static void bindVarFree( char* name, void* p );
47 static CaseBranch* getCaseBranchList( BuildSQLState* state, int parent_id );
48 static CaseBranch* constructCaseBranch( BuildSQLState* state, dbi_result result );
49 static void freeBranchList( CaseBranch* branch );
51 static Datatype* getDatatype( BuildSQLState* state, int id );
52 static Datatype* constructDatatype( BuildSQLState* state, dbi_result result );
53 static void datatypeFree( Datatype* datatype );
55 static Expression* getExpression( BuildSQLState* state, int id );
56 static Expression* constructExpression( BuildSQLState* state, dbi_result result );
57 static void expressionListFree( Expression* exp );
58 static void expressionFree( Expression* exp );
59 static Expression* getExpressionList( BuildSQLState* state, int id );
61 static OrderItem* getOrderByList( BuildSQLState* state, int query_id );
62 static OrderItem* constructOrderItem( BuildSQLState* state, dbi_result result );
63 static void orderItemListFree( OrderItem* ord );
65 static void push_id( IdNode** stack, int id, const char* alias );
66 static const IdNode* searchIdStack( const IdNode* stack, int id, const char* alias );
68 // A series of free lists to store already-allocated objects that are not in use, for
69 // potential reuse. This is a hack to reduce churning through malloc() and free().
70 static StoredQ* free_storedq_list = NULL;
71 static FromRelation* free_from_relation_list = NULL;
72 static SelectItem* free_select_item_list = NULL;
73 static BindVar* free_bindvar_list = NULL;
74 static CaseBranch* free_branch_list = NULL;
75 static Datatype* free_datatype_list = NULL;
76 static Expression* free_expression_list = NULL;
77 static IdNode* free_id_node_list = NULL;
78 static QSeq* free_qseq_list = NULL;
79 static OrderItem* free_order_item_list = NULL;
81 // Boolean; settable by call to oilsStoredQSetVerbose(), used by PRINT macro.
82 // The idea is to allow debugging messages from a command line test driver for ease of
83 // testing and development, but not from a real server, where messages to stdout don't
85 static int verbose = 0;
88 @brief Build a list of column names for a specified query.
89 @param state Pointer to the query-building context.
90 @param query Pointer to the specified query.
91 @return Pointer to a newly-allocated JSON_ARRAY of column names, if successful;
94 The column names are those assigned by PostgreSQL, e.g.:
95 - a column alias, if an AS clause defines one
96 - a column name (not qualified by a table name or alias, even if the query
98 - where the item is a function call, the name of the function
99 - where the item is a subquery or other expression, whatever PostgreSQL decides on,
102 The resulting column names may include duplicates.
104 The calling code is responsible for freeing the list by calling jsonObjectFree().
106 jsonObject* oilsGetColNames( BuildSQLState* state, StoredQ* query ) {
107 if( !state || !query ) return NULL;
109 // Build SQL in the usual way (with some temporary option settings)
110 int defaults_usable = state->defaults_usable;
111 int values_required = state->values_required;
112 state->defaults_usable = 1; // We can't execute the test query unless we
113 state->values_required = 1; // have a value for every bind variable.
114 int rc = buildSQL( state, query );
115 state->defaults_usable = defaults_usable;
116 state->values_required = values_required;
118 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
119 "Unable to build SQL statement for query id # %d", query->id ));
124 // Wrap it in an outer query to get the column names, but no rows
125 growing_buffer* wrapper = buffer_init( 80 + strlen( OSRF_BUFFER_C_STR( state->sql )));
126 buffer_add( wrapper, "SELECT \"phony query\".* FROM (" );
127 buffer_add( wrapper, OSRF_BUFFER_C_STR( state->sql ));
128 buffer_chomp( wrapper ); // remove the terminating newline
129 buffer_chomp( wrapper ); // remove the terminating semicolon
130 buffer_add( wrapper, ") AS \"phony query\" WHERE FALSE;" );
132 // Execute the wrapped query
133 dbi_result result = dbi_conn_query( state->dbhandle, OSRF_BUFFER_C_STR( wrapper ));
134 buffer_free( wrapper );
137 int errnum = dbi_conn_error( state->dbhandle, &msg );
138 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
139 "Unable to execute dummy query for column names: #%d %s",
140 errnum, msg ? msg : "No description available" ));
142 if( ! oilsIsDBConnected( state->dbhandle ))
147 // Examine the query result to get the column names
149 unsigned int num_cols = dbi_result_get_numfields( result );
150 jsonObject* cols = jsonNewObjectType( JSON_ARRAY );
152 for( i = 1; i <= num_cols; ++i ) {
153 const char* fname = dbi_result_get_field_name( result, i );
155 jsonObjectPush( cols, jsonNewObject( fname ));
158 dbi_result_free( result );
163 @brief Load a stored query.
164 @param state Pointer to the query-building context.
165 @param query_id ID of the query in query.stored_query.
166 @return A pointer to the newly loaded StoredQ if successful, or NULL if not.
168 The calling code is responsible for freeing the StoredQ by calling storedQFree().
170 StoredQ* getStoredQuery( BuildSQLState* state, int query_id ) {
174 // Check the stack to see if the current query is nested inside itself. If it is, then
175 // abort in order to avoid infinite recursion. If it isn't, then add it to the stack.
176 // (Make sure to pop it off the stack before returning.)
177 if( searchIdStack( state->query_stack, query_id, NULL )) {
178 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
179 "Infinite recursion detected; query # %d is nested within itself", query_id ));
183 push_id( &state->query_stack, query_id, NULL );
186 dbi_result result = dbi_conn_queryf( state->dbhandle,
187 "SELECT id, type, use_all, use_distinct, from_clause, where_clause, having_clause, "
188 "limit_count, offset_count FROM query.stored_query WHERE id = %d;", query_id );
190 if( dbi_result_first_row( result ) ) {
191 sq = constructStoredQ( state, result );
193 PRINT( "Got a query row\n" );
194 PRINT( "\tid: %d\n", sq->id );
195 PRINT( "\ttype: %d\n", (int) sq->type );
196 PRINT( "\tuse_all: %s\n", sq->use_all ? "true" : "false" );
197 PRINT( "\tuse_distinct: %s\n", sq->use_distinct ? "true" : "false" );
199 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
200 "Unable to build a query for id = %d", query_id ));
202 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
203 "Stored query not found for id %d", query_id ));
207 dbi_result_free( result );
210 int errnum = dbi_conn_error( state->dbhandle, &msg );
211 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
212 "Unable to query query.stored_query table: #%d %s",
213 errnum, msg ? msg : "No description available" ));
215 if( ! oilsIsDBConnected( state->dbhandle ))
219 pop_id( &state->query_stack );
224 @brief Construct a StoredQ.
225 @param Pointer to the query-building context.
226 @param result Database cursor positioned at a row in query.stored_query.
227 @return Pointer to a newly constructed StoredQ, if successful, or NULL if not.
229 The calling code is responsible for freeing the StoredQ by calling storedQFree().
231 static StoredQ* constructStoredQ( BuildSQLState* state, dbi_result result ) {
233 // Get the column values from the result
234 int id = dbi_result_get_int_idx( result, 1 );
235 const char* type_str = dbi_result_get_string_idx( result, 2 );
238 if( !strcmp( type_str, "SELECT" ))
240 else if( !strcmp( type_str, "UNION" ))
242 else if( !strcmp( type_str, "INTERSECT" ))
244 else if( !strcmp( type_str, "EXCEPT" ))
247 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
248 "Invalid query type \"%s\"", type_str ));
252 int use_all = oils_result_get_bool_idx( result, 3 );
253 int use_distinct = oils_result_get_bool_idx( result, 4 );
256 if( dbi_result_field_is_null_idx( result, 5 ) )
259 from_clause_id = dbi_result_get_int_idx( result, 5 );
262 if( dbi_result_field_is_null_idx( result, 6 ) )
263 where_clause_id = -1;
265 where_clause_id = dbi_result_get_int_idx( result, 6 );
267 int having_clause_id;
268 if( dbi_result_field_is_null_idx( result, 7 ) )
269 having_clause_id = -1;
271 having_clause_id = dbi_result_get_int_idx( result, 7 );
274 if( dbi_result_field_is_null_idx( result, 8 ) )
277 limit_count_id = dbi_result_get_int_idx( result, 8 );
280 if( dbi_result_field_is_null_idx( result, 9 ) )
281 offset_count_id = -1;
283 offset_count_id = dbi_result_get_int_idx( result, 9 );
285 FromRelation* from_clause = NULL;
286 if( QT_SELECT == type ) {
287 // A SELECT query needs a FROM clause; go get it
288 if( from_clause_id != -1 ) {
289 from_clause = getFromRelation( state, from_clause_id );
291 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
292 "Unable to construct FROM clause for id = %d", from_clause_id ));
297 // Must be one of UNION, INTERSECT, or EXCEPT
298 if( from_clause_id != -1 )
299 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
300 "FROM clause found and ignored for %s query in query #%d", type_str, id ));
303 // If this is a SELECT query, we need a SELECT list. Go get one.
304 SelectItem* select_list = NULL;
305 QSeq* child_list = NULL;
306 if( QT_SELECT == type ) {
307 select_list = getSelectList( state, id );
309 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
310 "No SELECT list found for query id = %d", id ));
311 fromRelationFree( from_clause );
315 // Construct child queries of UNION, INTERSECT, or EXCEPT query
316 child_list = loadChildQueries( state, id, type_str );
318 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
319 "Unable to load child queries for %s query # %d", type_str, id ));
321 fromRelationFree( from_clause );
326 // Get the WHERE clause, if there is one
327 Expression* where_clause = NULL;
328 if( where_clause_id != -1 ) {
329 where_clause = getExpression( state, where_clause_id );
330 if( ! where_clause ) {
331 // shouldn't happen due to foreign key constraint
332 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
333 "Unable to fetch WHERE expression for query id = %d", id ));
334 freeQSeqList( child_list );
335 fromRelationFree( from_clause );
336 selectListFree( select_list );
342 // Get the HAVING clause, if there is one
343 Expression* having_clause = NULL;
344 if( having_clause_id != -1 ) {
345 having_clause = getExpression( state, having_clause_id );
346 if( ! having_clause ) {
347 // shouldn't happen due to foreign key constraint
348 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
349 "Unable to fetch HAVING expression for query id = %d", id ));
350 expressionFree( where_clause );
351 freeQSeqList( child_list );
352 selectListFree( select_list );
353 fromRelationFree( from_clause );
359 // Get the ORDER BY clause, if there is one
360 OrderItem* order_by_list = getOrderByList( state, id );
362 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
363 "Unable to load ORDER BY clause for query %d", id ));
364 expressionFree( having_clause );
365 expressionFree( where_clause );
366 freeQSeqList( child_list );
367 selectListFree( select_list );
368 fromRelationFree( from_clause );
372 // Get the LIMIT clause, if there is one
373 Expression* limit_count = NULL;
374 if( limit_count_id != -1 ) {
375 limit_count = getExpression( state, limit_count_id );
376 if( ! limit_count ) {
377 // shouldn't happen due to foreign key constraint
378 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
379 "Unable to fetch LIMIT expression for query id = %d", id ));
380 orderItemListFree( order_by_list );
381 freeQSeqList( child_list );
382 selectListFree( select_list );
383 fromRelationFree( from_clause );
389 // Get the OFFSET clause, if there is one
390 Expression* offset_count = NULL;
391 if( offset_count_id != -1 ) {
392 offset_count = getExpression( state, offset_count_id );
393 if( ! offset_count ) {
394 // shouldn't happen due to foreign key constraint
395 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
396 "Unable to fetch OFFSET expression for query id = %d", id ));
397 expressionFree( limit_count );
398 orderItemListFree( order_by_list );
399 freeQSeqList( child_list );
400 selectListFree( select_list );
401 fromRelationFree( from_clause );
407 // Allocate a StoredQ: from the free list if possible, from the heap if necessary
410 if( free_storedq_list ) {
411 sq = free_storedq_list;
412 free_storedq_list = free_storedq_list->next;
414 sq = safe_malloc( sizeof( StoredQ ) );
416 // Populate the StoredQ
421 sq->use_all = use_all;
422 sq->use_distinct = use_distinct;
423 sq->from_clause = from_clause;
424 sq->where_clause = where_clause;
425 sq->select_list = select_list;
426 sq->child_list = child_list;
427 sq->having_clause = having_clause;
428 sq->order_by_list = order_by_list;
429 sq->limit_count = limit_count;
430 sq->offset_count = offset_count;
436 @brief Load the child queries subordinate to a UNION, INTERSECT, or EXCEPT query.
437 @param state Pointer to the query-building context.
438 @param parent ID of the UNION, INTERSECT, or EXCEPT query.
439 @param type_str The type of the query ("UNION", "INTERSECT", or "EXCEPT").
440 @return If successful, a pointer to a linked list of QSeq, each bearing a pointer to a
441 StoredQ; otherwise NULL.
443 The @a type_str parameter is used only for building error messages.
445 static QSeq* loadChildQueries( BuildSQLState* state, int parent_id, const char* type_str ) {
446 QSeq* child_list = NULL;
448 // The ORDER BY is in descending order so that we can build the list by adding to
449 // the head, and it will wind up in the right order.
450 dbi_result result = dbi_conn_queryf( state->dbhandle,
451 "SELECT id, parent_query, seq_no, child_query "
452 "FROM query.query_sequence WHERE parent_query = %d ORDER BY seq_no DESC", parent_id );
454 if( dbi_result_first_row( result ) ) {
458 QSeq* seq = constructQSeq( state, result );
460 PRINT( "Found a child query\n" );
461 PRINT( "\tid: %d\n", seq->id );
462 PRINT( "\tparent id: %d\n", seq->parent_query_id );
463 PRINT( "\tseq_no: %d\n", seq->seq_no );
464 // Add to the head of the list
465 seq->next = child_list;
468 freeQSeqList( child_list );
471 if( !dbi_result_next_row( result ))
475 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
476 "%s query # %d has only one child query", type_str, parent_id ));
478 freeQSeqList( child_list );
482 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
483 "%s query # %d has no child queries within it", type_str, parent_id ));
485 if( ! oilsIsDBConnected( state->dbhandle ))
491 int errnum = dbi_conn_error( state->dbhandle, &msg );
492 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
493 "Unable to query query.query_sequence table: # %d %s",
494 errnum, msg ? msg : "No description available" ));
503 @brief Construct a QSeq.
504 @param Pointer to the query-building context.
505 @param result Database cursor positioned at a row in query.query_sequence.
506 @return Pointer to a newly constructed QSeq, if successful, or NULL if not.
508 The calling code is responsible for freeing QSeqs by calling freeQSeqList().
510 static QSeq* constructQSeq( BuildSQLState* state, dbi_result result ) {
511 int id = dbi_result_get_int_idx( result, 1 );
512 int parent_query_id = dbi_result_get_int_idx( result, 2 );
513 int seq_no = dbi_result_get_int_idx( result, 3 );
514 int child_query_id = dbi_result_get_int_idx( result, 4 );
516 StoredQ* child_query = getStoredQuery( state, child_query_id );
518 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
519 "Unable to load child query # %d for parent query %d",
520 child_query_id, parent_query_id ));
525 // Allocate a QSeq; from the free list if possible, from the heap if necessary
527 if( free_qseq_list ) {
528 seq = free_qseq_list;
529 free_qseq_list = free_qseq_list->next;
531 seq = safe_malloc( sizeof( QSeq ));
535 seq->parent_query_id = parent_query_id;
536 seq->seq_no = seq_no;
537 seq->child_query = child_query;
543 @brief Free a list of QSeq's.
544 @param seq Pointer to the first in a linked list of QSeq's to be freed.
546 Each QSeq goes onto a free list for potential reuse.
548 static void freeQSeqList( QSeq* seq ) {
554 storedQFree( seq->child_query );
555 seq->child_query = NULL;
560 seq->next = free_qseq_list;
565 free_qseq_list = first;
569 @brief Deallocate the memory owned by a StoredQ.
570 @param sq Pointer to the StoredQ to be deallocated.
572 void storedQFree( StoredQ* sq ) {
574 fromRelationFree( sq->from_clause );
575 sq->from_clause = NULL;
576 selectListFree( sq->select_list );
577 sq->select_list = NULL;
578 expressionFree( sq->where_clause );
579 sq->where_clause = NULL;
580 if( sq->child_list ) {
581 freeQSeqList( sq->child_list );
582 sq->child_list = NULL;
584 if( sq->order_by_list ) {
585 orderItemListFree( sq->order_by_list );
586 sq->order_by_list = NULL;
588 if( sq->having_clause ) {
589 expressionFree( sq->having_clause );
590 sq->having_clause = NULL;
592 if( sq->limit_count ) {
593 expressionFree( sq->limit_count );
594 sq->limit_count = NULL;
596 if( sq->offset_count ) {
597 expressionFree( sq->offset_count );
598 sq->offset_count = NULL;
601 // Stick the empty husk on the free list for potential reuse
602 sq->next = free_storedq_list;
603 free_storedq_list = sq;
608 @brief Given an id from query.from_relation, load a FromRelation.
609 @param state Pointer the the query-building context.
610 @param id Id of the FromRelation.
611 @return Pointer to a newly-created FromRelation if successful, or NULL if not.
613 The calling code is responsible for freeing the new FromRelation by calling
616 static FromRelation* getFromRelation( BuildSQLState* state, int id ) {
617 FromRelation* fr = NULL;
618 dbi_result result = dbi_conn_queryf( state->dbhandle,
619 "SELECT id, type, table_name, class_name, subquery, function_call, "
620 "table_alias, parent_relation, seq_no, join_type, on_clause "
621 "FROM query.from_relation WHERE id = %d;", id );
623 if( dbi_result_first_row( result ) ) {
624 fr = constructFromRelation( state, result );
626 PRINT( "Got a from_relation row\n" );
627 PRINT( "\tid: %d\n", fr->id );
628 PRINT( "\ttype: %d\n", (int) fr->type );
629 PRINT( "\ttable_name: %s\n", fr->table_name ? fr->table_name : "(none)" );
630 PRINT( "\tclass_name: %s\n", fr->class_name ? fr->class_name : "(none)" );
631 PRINT( "\tsubquery_id: %d\n", fr->subquery_id );
632 PRINT( "\tfunction_call_id: %d\n", fr->function_call_id );
633 PRINT( "\ttable_alias: %s\n", fr->table_alias ? fr->table_alias : "(none)" );
634 PRINT( "\tparent_relation_id: %d\n", fr->parent_relation_id );
635 PRINT( "\tseq_no: %d\n", fr->seq_no );
636 PRINT( "\tjoin_type = %d\n", fr->join_type );
637 // Check the stack to see if the current from clause is nested inside itself.
638 // If it is, then abort in order to avoid infinite recursion. If it isn't,
639 // then add it to the stack. (Make sure to pop it off the stack before
641 const char* effective_alias = fr->table_alias;
642 if( !effective_alias )
643 effective_alias = fr->class_name;
644 const IdNode* node = searchIdStack( state->from_stack, id, effective_alias );
647 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
648 "Infinite recursion detected; from clause # %d is nested "
649 "within itself", id ));
651 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
652 "Conflicting nested table aliases \"%s\" in from clause # %d",
653 effective_alias, node->id ));
657 push_id( &state->from_stack, id, effective_alias );
659 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
660 "Unable to build a FromRelation for id = %d", id ));
662 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
663 "FROM relation not found for id = %d", id ));
666 dbi_result_free( result );
669 int errnum = dbi_conn_error( state->dbhandle, &msg );
670 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
671 "Unable to query query.from_relation table: #%d %s",
672 errnum, msg ? msg : "No description available" ));
674 if( ! oilsIsDBConnected( state->dbhandle ))
679 pop_id( &state->from_stack );
685 @brief Construct a FromRelation.
686 @param Pointer to the query-building context.
687 @param result Database cursor positioned at a row in query.from_relation.
688 @return Pointer to a newly constructed FromRelation, if successful, or NULL if not.
690 The calling code is responsible for freeing FromRelations by calling joinListFree().
692 static FromRelation* constructFromRelation( BuildSQLState* state, dbi_result result ) {
693 // Get the column values from the result
694 int id = dbi_result_get_int_idx( result, 1 );
695 const char* type_str = dbi_result_get_string_idx( result, 2 );
697 FromRelationType type;
698 if( !strcmp( type_str, "RELATION" ))
700 else if( !strcmp( type_str, "SUBQUERY" ))
702 else if( !strcmp( type_str, "FUNCTION" ))
705 type = FRT_RELATION; // shouldn't happen due to database constraint
707 const char* table_name = dbi_result_get_string_idx( result, 3 );
708 const char* class_name = dbi_result_get_string_idx( result, 4 );
711 if( dbi_result_field_is_null_idx( result, 5 ) )
714 subquery_id = dbi_result_get_int_idx( result, 5 );
716 int function_call_id;
717 if( dbi_result_field_is_null_idx( result, 6 ) )
718 function_call_id = -1;
720 function_call_id = dbi_result_get_int_idx( result, 6 );
722 Expression* function_call = NULL;
723 const char* table_alias = dbi_result_get_string_idx( result, 7 );
725 int parent_relation_id;
726 if( dbi_result_field_is_null_idx( result, 8 ) )
727 parent_relation_id = -1;
729 parent_relation_id = dbi_result_get_int_idx( result, 8 );
731 int seq_no = dbi_result_get_int_idx( result, 9 );
734 const char* join_type_str = dbi_result_get_string_idx( result, 10 );
737 else if( !strcmp( join_type_str, "INNER" ) )
738 join_type = JT_INNER;
739 else if( !strcmp( join_type_str, "LEFT" ) )
741 else if( !strcmp( join_type_str, "RIGHT" ) )
742 join_type = JT_RIGHT;
743 else if( !strcmp( join_type_str, "FULL" ) )
746 join_type = JT_NONE; // shouldn't happen due to database constraint
749 if( dbi_result_field_is_null_idx( result, 11 ) )
752 on_clause_id = dbi_result_get_int_idx( result, 11 );
754 StoredQ* subquery = NULL;
758 if( table_name && ! is_identifier( table_name )) {
759 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
760 "Table name \"%s\" is not a valid identifier for FROM relation # %d",
767 if( -1 == subquery_id ) {
768 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
769 "Internal error: no subquery specified for FROM relation # %d", id ));
773 if( ! table_alias ) {
774 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
775 "Subquery needs alias in FROM relation # %d", id ));
779 subquery = getStoredQuery( state, subquery_id );
781 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
782 "Unable to load subquery for FROM relation # %d", id ));
788 if( -1 == function_call_id ) {
789 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
790 "FROM clause # %d purports to reference a function; not identified", id ));
795 function_call = getExpression( state, function_call_id );
796 if( !function_call ) {
797 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
798 "Unable to build function call # %d in FROM relation # %d",
799 function_call_id, id ));
802 } else if( function_call->type != EXP_FUNCTION ) {
803 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
804 "In FROM relation # %d: supposed function call expression # %d "
805 "is not a function call", id, function_call_id ));
811 FromRelation* join_list = getJoinList( state, id );
813 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
814 "Unable to load join list for FROM relation # %d", id ));
818 Expression* on_clause = NULL;
819 if( on_clause_id != -1 ) {
820 on_clause = getExpression( state, on_clause_id );
822 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
823 "Unable to load ON condition for FROM relation # %d", id ));
824 joinListFree( join_list );
829 PRINT( "\tGot an ON condition\n" );
832 // Allocate a FromRelation: from the free list if possible, from the heap if necessary
835 if( free_from_relation_list ) {
836 fr = free_from_relation_list;
837 free_from_relation_list = free_from_relation_list->next;
839 fr = safe_malloc( sizeof( FromRelation ) );
841 // Populate the FromRelation
846 fr->table_name = table_name ? strdup( table_name ) : NULL;
847 fr->class_name = class_name ? strdup( class_name ) : NULL;
848 fr->subquery_id = subquery_id;
849 fr->subquery = subquery;
850 fr->function_call_id = function_call_id;
851 fr->function_call = function_call;
852 fr->table_alias = table_alias ? strdup( table_alias ) : NULL;
853 fr->parent_relation_id = parent_relation_id;
855 fr->join_type = join_type;
856 fr->on_clause = on_clause;
857 fr->join_list = join_list;
863 @brief Build a list of joined relations.
864 @param state Pointer to the query-building context.
865 @param id ID of the parent relation.
866 @return A pointer to the first in a linked list of FromRelations, if there are any; or
867 NULL if there aren't any, or in case of an error.
869 Look for relations joined directly to the parent relation, and make a list of them.
871 static FromRelation* getJoinList( BuildSQLState* state, int id ) {
872 FromRelation* join_list = NULL;
874 // The ORDER BY is in descending order so that we can build the list by adding to
875 // the head, and it will wind up in the right order.
876 dbi_result result = dbi_conn_queryf( state->dbhandle,
877 "SELECT id, type, table_name, class_name, subquery, function_call, "
878 "table_alias, parent_relation, seq_no, join_type, on_clause "
879 "FROM query.from_relation WHERE parent_relation = %d ORDER BY seq_no DESC", id );
882 if( dbi_result_first_row( result ) ) {
884 FromRelation* relation = constructFromRelation( state, result );
886 PRINT( "Found a joined relation\n" );
887 PRINT( "\tjoin_type: %d\n", relation->join_type );
888 PRINT( "\ttable_name: %s\n", relation->table_name );
889 relation->next = join_list;
890 join_list = relation;
892 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
893 "Unable to build join list for from relation id #%d", id ));
894 joinListFree( join_list );
898 if( !dbi_result_next_row( result ) )
904 int errnum = dbi_conn_error( state->dbhandle, &msg );
905 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
906 "Unable to query query.from_relation table for join list: #%d %s",
907 errnum, msg ? msg : "No description available" ));
909 if( ! oilsIsDBConnected( state->dbhandle ))
917 @brief Free a list of FromRelations.
918 @param join_list Pointer to the first FromRelation in the list.
920 static void joinListFree( FromRelation* join_list ) {
922 FromRelation* temp = join_list->next;
923 fromRelationFree( join_list );
929 @brief Deallocate a FromRelation.
930 @param fr Pointer to the FromRelation to be freed.
932 Free the strings that the FromRelation owns. The FromRelation itself goes onto a
933 free list for potential reuse.
935 static void fromRelationFree( FromRelation* fr ) {
937 free( fr->table_name );
938 fr->table_name = NULL;
939 free( fr->class_name );
940 fr->class_name = NULL;
942 storedQFree( fr->subquery );
945 if( fr->function_call ) {
946 expressionFree( fr->function_call );
947 fr->function_call = NULL;
949 free( fr->table_alias );
950 fr->table_alias = NULL;
951 if( fr->on_clause ) {
952 expressionFree( fr->on_clause );
953 fr->on_clause = NULL;
955 joinListFree( fr->join_list );
956 fr->join_list = NULL;
958 fr->next = free_from_relation_list;
959 free_from_relation_list = fr;
964 @brief Build a SELECT list for a given query ID.
965 @param state Pointer to the query-building context.
966 @param query_id ID of the query to which the SELECT list belongs.
968 static SelectItem* getSelectList( BuildSQLState* state, int query_id ) {
969 SelectItem* select_list = NULL;
971 // The ORDER BY is in descending order so that we can build the list by adding to
972 // the head, and it will wind up in the right order.
973 dbi_result result = dbi_conn_queryf( state->dbhandle,
974 "SELECT id, stored_query, seq_no, expression, column_alias, grouped_by "
975 "FROM query.select_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
977 if( dbi_result_first_row( result ) ) {
979 SelectItem* item = constructSelectItem( state, result );
981 PRINT( "Found a SELECT item\n" );
982 PRINT( "\tid: %d\n", item->id );
983 PRINT( "\tstored_query_id: %d\n", item->stored_query_id );
984 PRINT( "\tseq_no: %d\n", item->seq_no );
985 PRINT( "\tcolumn_alias: %s\n",
986 item->column_alias ? item->column_alias : "(none)" );
987 PRINT( "\tgrouped_by: %d\n", item->grouped_by );
989 item->next = select_list;
992 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
993 "Unable to build select list for query id #%d", query_id ));
994 selectListFree( select_list );
998 if( !dbi_result_next_row( result ) )
1002 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1003 "No SELECT list found for query # %d", query_id ));
1008 int errnum = dbi_conn_error( state->dbhandle, &msg );
1009 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1010 "Unable to query query.select_list table: #%d %s",
1011 errnum, msg ? msg : "No description available" ));
1013 if( ! oilsIsDBConnected( state->dbhandle ))
1021 @brief Construct a SelectItem.
1022 @param Pointer to the query-building context.
1023 @param result Database cursor positioned at a row in query.select_item.
1024 @return Pointer to a newly constructed SelectItem, if successful, or NULL if not.
1026 The calling code is responsible for freeing the SelectItems by calling selectListFree().
1028 static SelectItem* constructSelectItem( BuildSQLState* state, dbi_result result ) {
1030 // Get the column values
1031 int id = dbi_result_get_int_idx( result, 1 );
1032 int stored_query_id = dbi_result_get_int_idx( result, 2 );
1033 int seq_no = dbi_result_get_int_idx( result, 3 );
1034 int expression_id = dbi_result_get_int_idx( result, 4 );
1035 const char* column_alias = dbi_result_get_string_idx( result, 5 );
1036 int grouped_by = oils_result_get_bool_idx( result, 6 );
1038 // Construct an Expression
1039 Expression* expression = getExpression( state, expression_id );
1041 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1042 "Unable to fetch expression for id = %d", expression_id ));
1047 // Allocate a SelectItem: from the free list if possible, from the heap if necessary
1050 if( free_select_item_list ) {
1051 sel = free_select_item_list;
1052 free_select_item_list = free_select_item_list->next;
1054 sel = safe_malloc( sizeof( SelectItem ) );
1058 sel->stored_query_id = stored_query_id;
1059 sel->seq_no = seq_no;
1060 sel->expression = expression;
1061 sel->column_alias = column_alias ? strdup( column_alias ) : NULL;
1062 sel->grouped_by = grouped_by;
1068 @brief Free a list of SelectItems.
1069 @param sel Pointer to the first item in the list to be freed.
1071 Free the column alias and expression owned by each item. Put the entire list into a free
1072 list of SelectItems.
1074 static void selectListFree( SelectItem* sel ) {
1076 return; // Nothing to free
1078 SelectItem* first = sel;
1080 free( sel->column_alias );
1081 sel->column_alias = NULL;
1082 expressionFree( sel->expression );
1083 sel->expression = NULL;
1085 if( NULL == sel->next ) {
1086 sel->next = free_select_item_list;
1092 // Transfer the entire list to the free list
1093 free_select_item_list = first;
1097 @brief Given the name of a bind variable, build a corresponding BindVar.
1098 @param state Pointer to the query-building context.
1099 @param name Name of the bind variable.
1100 @return Pointer to the newly-built BindVar.
1102 Since the same bind variable may appear multiple times, we load it only once for the
1103 entire query, and reference the one copy wherever needed.
1105 static BindVar* getBindVar( BuildSQLState* state, const char* name ) {
1106 BindVar* bind = NULL;
1107 if( state->bindvar_list ) {
1108 bind = osrfHashGet( state->bindvar_list, name );
1110 return bind; // Already loaded it...
1113 // Load a BindVar from the Database.(after escaping any special characters)
1114 char* esc_str = strdup( name );
1115 dbi_conn_quote_string( state->dbhandle, &esc_str );
1117 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1118 "Unable to format bind variable name \"%s\"", name ));
1122 dbi_result result = dbi_conn_queryf( state->dbhandle,
1123 "SELECT name, type, description, default_value, label "
1124 "FROM query.bind_variable WHERE name = %s;", esc_str );
1127 if( dbi_result_first_row( result ) ) {
1128 bind = constructBindVar( state, result );
1130 PRINT( "Got a bind variable for %s\n", name );
1132 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1133 "Unable to load bind variable \"%s\"", name ));
1135 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1136 "No bind variable found with name \"%s\"", name ));
1141 int errnum = dbi_conn_error( state->dbhandle, &msg );
1142 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1143 "Unable to query query.bind_variable table for \"%s\": #%d %s",
1144 name, errnum, msg ? msg : "No description available" ));
1146 if( ! oilsIsDBConnected( state->dbhandle ))
1151 // Add the new bind variable to the list
1152 if( !state->bindvar_list ) {
1153 // Don't have a list yet? Start one.
1154 state->bindvar_list = osrfNewHash();
1155 osrfHashSetCallback( state->bindvar_list, bindVarFree );
1157 osrfHashSet( state->bindvar_list, bind, name );
1165 @brief Construct a BindVar to represent a bind variable.
1166 @param Pointer to the query-building context.
1167 @param result Database cursor positioned at a row in query.bind_variable.
1168 @return Pointer to a newly constructed BindVar, if successful, or NULL if not.
1170 The calling code is responsible for freeing the BindVar by calling bindVarFree().
1172 static BindVar* constructBindVar( BuildSQLState* state, dbi_result result ) {
1174 const char* name = dbi_result_get_string_idx( result, 1 );
1176 const char* type_str = dbi_result_get_string_idx( result, 2 );
1178 if( !strcmp( type_str, "string" ))
1180 else if( !strcmp( type_str, "number" ))
1182 else if( !strcmp( type_str, "string_list" ))
1183 type = BIND_STR_LIST;
1184 else if( !strcmp( type_str, "number_list" ))
1185 type = BIND_NUM_LIST;
1186 else { // Shouldn't happen due to database constraint...
1187 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1188 "Internal error: invalid bind variable type \"%s\" for bind variable \"%s\"",
1194 const char* description = dbi_result_get_string_idx( result, 3 );
1196 // The default value is encoded as JSON. Translate it into a jsonObject.
1197 const char* default_value_str = dbi_result_get_string_idx( result, 4 );
1198 jsonObject* default_value = NULL;
1199 if( default_value_str ) {
1200 default_value = jsonParse( default_value_str );
1201 if( !default_value ) {
1202 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1203 "Unable to parse JSON string for default value of bind variable \"%s\"",
1210 const char* label = dbi_result_get_string_idx( result, 5 );
1212 // Allocate a BindVar: from the free list if possible, from the heap if necessary
1213 BindVar* bind = NULL;
1214 if( free_bindvar_list ) {
1215 bind = free_bindvar_list;
1216 free_bindvar_list = free_bindvar_list->next;
1218 bind = safe_malloc( sizeof( BindVar ) );
1221 bind->name = strdup( name );
1222 bind->label = strdup( label );
1224 bind->description = strdup( description );
1225 bind->default_value = default_value;
1226 bind->actual_value = NULL;
1232 @brief Deallocate a BindVar.
1233 @param key Pointer to the bind variable name (not used).
1234 @param p Pointer to the BindVar to be deallocated, cast to a void pointer.
1236 Free the strings and jsonObjects owned by the BindVar, and put the BindVar itself into a
1239 This function is a callback installed in an osrfHash; hence the peculiar signature.
1241 static void bindVarFree( char* key, void* p ) {
1245 free( bind->label );
1246 free( bind->description );
1247 if( bind->default_value ) {
1248 jsonObjectFree( bind->default_value );
1249 bind->default_value = NULL;
1251 if( bind->actual_value ) {
1252 jsonObjectFree( bind->actual_value );
1253 bind->actual_value = NULL;
1256 // Prepend to free list
1257 bind->next = free_bindvar_list;
1258 free_bindvar_list = bind;
1263 @brief Given an id for a parent expression, build a list of CaseBranch structs.
1264 @param Pointer to the query-building context.
1265 @param id ID of a row in query.case_branch.
1266 @return Pointer to a newly-created CaseBranch if successful, or NULL if not.
1268 static CaseBranch* getCaseBranchList( BuildSQLState* state, int parent_id ) {
1269 CaseBranch* branch_list = NULL;
1270 dbi_result result = dbi_conn_queryf( state->dbhandle,
1271 "SELECT id, condition, result "
1272 "FROM query.case_branch WHERE parent_expr = %d "
1273 "ORDER BY seq_no desc;", parent_id );
1275 int condition_found = 0; // Boolean
1276 if( dbi_result_first_row( result ) ) {
1277 // Boolean: true for the first branch we encounter. That's actually the last
1278 // branch in the CASE, because we're reading them in reverse order. The point
1279 // is to enforce the rule that only the last branch can be an ELSE.
1282 CaseBranch* branch = constructCaseBranch( state, result );
1284 PRINT( "Found a case branch\n" );
1285 branch->next = branch_list;
1286 branch_list = branch;
1288 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1289 "Unable to build CASE branch for expression id #%d", parent_id ));
1290 freeBranchList( branch_list );
1295 if( branch->condition )
1296 condition_found = 1;
1298 sqlAddMsg( state, "ELSE branch # %d not at end of CASE expression # %d",
1299 branch->id, parent_id );
1300 freeBranchList( branch_list );
1306 if( !dbi_result_next_row( result ) )
1310 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1311 "No branches found for CASE expression %d", parent_id ));
1315 // Make sure that at least one branch includes a condition
1316 if( !condition_found ) {
1317 sqlAddMsg( state, "No conditional branch in CASE expression # %d", parent_id );
1318 freeBranchList( branch_list );
1323 int errnum = dbi_conn_error( state->dbhandle, &msg );
1324 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1325 "Unable to query query.case_branch table for parent expression # %d: %s",
1326 parent_id, errnum, msg ? msg : "No description available" ));
1328 if( ! oilsIsDBConnected( state->dbhandle ))
1335 static CaseBranch* constructCaseBranch( BuildSQLState* state, dbi_result result ) {
1336 int id = dbi_result_get_int_idx( result, 1 );
1339 if( dbi_result_field_is_null_idx( result, 2 ))
1342 condition_id = dbi_result_get_int_idx( result, 2 );
1344 Expression* condition = NULL;
1345 if( condition_id != -1 ) { // If it's -1, we have an ELSE condition
1346 condition = getExpression( state, condition_id );
1348 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1349 "Unable to build condition expression for case branch # %d", id ));
1355 int result_id = dbi_result_get_int_idx( result, 3 );
1357 Expression* result_p = NULL;
1358 if( -1 == result_id ) {
1359 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1360 "No result expression defined for case branch # %d", id ));
1363 expressionFree( condition );
1366 result_p = getExpression( state, result_id );
1368 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1369 "Unable to build result expression for case branch # %d", id ));
1372 expressionFree( condition );
1377 // Allocate a CaseBranch: from the free list if possible, from the heap if necessary
1378 CaseBranch* branch = NULL;
1379 if( free_branch_list ) {
1380 branch = free_branch_list;
1381 free_branch_list = free_branch_list->next;
1383 branch = safe_malloc( sizeof( CaseBranch ) );
1385 // Populate the new CaseBranch
1387 branch->condition = condition;
1388 branch->result = result_p;
1394 @brief Free a list of CaseBranches.
1395 @param branch Pointer to the first in a linked list of CaseBranches to be freed.
1397 Each CaseBranch goes onto a free list for potential reuse.
1399 static void freeBranchList( CaseBranch* branch ) {
1403 CaseBranch* first = branch;
1405 if( branch->condition ) {
1406 expressionFree( branch->condition );
1407 branch->condition = NULL;
1409 expressionFree( branch->result );
1410 branch->result = NULL;
1413 branch = branch->next;
1415 branch->next = free_branch_list;
1420 free_branch_list = first;
1424 @brief Given an id for a row in query.datatype, build an Datatype struct.
1425 @param Pointer to the query-building context.
1426 @param id ID of a row in query.datatype.
1427 @return Pointer to a newly-created Datatype if successful, or NULL if not.
1429 static Datatype* getDatatype( BuildSQLState* state, int id ) {
1430 Datatype* datatype = NULL;
1431 dbi_result result = dbi_conn_queryf( state->dbhandle,
1432 "SELECT id, datatype_name, is_numeric, is_composite "
1433 "FROM query.datatype WHERE id = %d", id );
1435 if( dbi_result_first_row( result ) ) {
1436 datatype = constructDatatype( state, result );
1438 PRINT( "Got a datatype\n" );
1439 PRINT( "\tid = %d\n", id );
1440 PRINT( "\tdatatype = %s\n", datatype->datatype_name );
1442 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1443 "Unable to construct a Datatype for id = %d", id ));
1445 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1446 "No datatype found for id = %d", id ));
1451 int errnum = dbi_conn_error( state->dbhandle, &msg );
1452 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1453 "Unable to query query.datatype table: #%d %s",
1454 errnum, msg ? msg : "No description available" ));
1456 if( ! oilsIsDBConnected( state->dbhandle ))
1463 @brief Construct a Datatype.
1464 @param Pointer to the query-building context.
1465 @param result Database cursor positioned at a row in query.datatype.
1466 @return Pointer to a newly constructed Datatype, if successful, or NULL if not.
1468 The calling code is responsible for freeing the Datatype by calling datatypeFree().
1470 static Datatype* constructDatatype( BuildSQLState* state, dbi_result result ) {
1471 int id = dbi_result_get_int_idx( result, 1 );
1472 const char* datatype_name = dbi_result_get_string_idx( result, 2 );
1473 int is_numeric = oils_result_get_bool_idx( result, 3 );
1474 int is_composite = oils_result_get_bool_idx( result, 4 );
1476 if( !datatype_name || !*datatype_name ) {
1477 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1478 "No datatype name provided for CAST expression # %d", id ));
1483 // Make sure that the datatype name is composed entirely of certain approved
1484 // characters. This check is not an attempt to validate the datatype name, but
1485 // only to prevent certain types of SQL injection.
1486 const char* p = datatype_name;
1488 unsigned char c = *p;
1500 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1501 "Invalid datatype name \"%s\" for datatype # %d; "
1502 "contains unexpected character \"%c\"", datatype_name, id, (char) c ));
1508 // Allocate a Datatype: from the free list if possible, from the heap if necessary
1509 Datatype* datatype = NULL;
1510 if( free_datatype_list ) {
1511 datatype = free_datatype_list;
1512 free_datatype_list = free_datatype_list->next;
1514 datatype = safe_malloc( sizeof( Datatype ) );
1517 datatype->datatype_name = strdup( datatype_name );
1518 datatype->is_numeric = is_numeric;
1519 datatype->is_composite = is_composite;
1525 @brief Free a Datatype.
1526 @param datatype Pointer to the Datatype to be freed.
1528 static void datatypeFree( Datatype* datatype ) {
1530 free( datatype->datatype_name );
1536 @brief Given an id for a row in query.expression, build an Expression struct.
1537 @param Pointer to the query-building context.
1538 @param id ID of a row in query.expression.
1539 @return Pointer to a newly-created Expression if successful, or NULL if not.
1541 static Expression* getExpression( BuildSQLState* state, int id ) {
1543 // Check the stack to see if the current expression is nested inside itself. If it is,
1544 // then abort in order to avoid infinite recursion. If it isn't, then add it to the
1545 // stack. (Make sure to pop it off the stack before returning.)
1546 if( searchIdStack( state->expr_stack, id, NULL )) {
1547 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1548 "Infinite recursion detected; expression # %d is nested within itself", id ));
1552 push_id( &state->expr_stack, id, NULL );
1554 Expression* exp = NULL;
1555 dbi_result result = dbi_conn_queryf( state->dbhandle,
1556 "SELECT exp.id, exp.type, exp.parenthesize, exp.parent_expr, exp.seq_no, "
1557 "exp.literal, exp.table_alias, exp.column_name, exp.left_operand, exp.operator, "
1558 "exp.right_operand, exp.subquery, exp.cast_type, exp.negate, exp.bind_variable, "
1559 "func.function_name "
1560 "FROM query.expression AS exp LEFT JOIN query.function_sig AS func "
1561 "ON (exp.function_id = func.id) "
1562 "WHERE exp.id = %d;", id );
1564 if( dbi_result_first_row( result ) ) {
1565 exp = constructExpression( state, result );
1567 PRINT( "Got an expression\n" );
1568 PRINT( "\tid = %d\n", exp->id );
1569 PRINT( "\ttype = %d\n", exp->type );
1570 PRINT( "\tparenthesize = %d\n", exp->parenthesize );
1571 PRINT( "\tcolumn_name = %s\n", exp->column_name ? exp->column_name : "(none)" );
1573 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1574 "Unable to construct an Expression for id = %d", id ));
1576 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1577 "No expression found for id = %d", id ));
1582 int errnum = dbi_conn_error( state->dbhandle, &msg );
1583 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1584 "Unable to query query.expression table: #%d %s",
1585 errnum, msg ? msg : "No description available" ));
1587 if( ! oilsIsDBConnected( state->dbhandle ))
1591 pop_id( &state->expr_stack );
1596 @brief Construct an Expression.
1597 @param Pointer to the query-building context.
1598 @param result Database cursor positioned at a row in query.expression.
1599 @return Pointer to a newly constructed Expression, if successful, or NULL if not.
1601 The calling code is responsible for freeing the Expression by calling expressionFree().
1603 static Expression* constructExpression( BuildSQLState* state, dbi_result result ) {
1605 int id = dbi_result_get_int_idx( result, 1 );
1606 const char* type_str = dbi_result_get_string_idx( result, 2 );
1609 if( !strcmp( type_str, "xbet" ))
1611 else if( !strcmp( type_str, "xbind" ))
1613 else if( !strcmp( type_str, "xbool" ))
1615 else if( !strcmp( type_str, "xcase" ))
1617 else if( !strcmp( type_str, "xcast" ))
1619 else if( !strcmp( type_str, "xcol" ))
1621 else if( !strcmp( type_str, "xex" ))
1623 else if( !strcmp( type_str, "xfunc" ))
1624 type = EXP_FUNCTION;
1625 else if( !strcmp( type_str, "xin" ))
1627 else if( !strcmp( type_str, "xisnull" ))
1629 else if( !strcmp( type_str, "xnull" ))
1631 else if( !strcmp( type_str, "xnum" ))
1633 else if( !strcmp( type_str, "xop" ))
1634 type = EXP_OPERATOR;
1635 else if( !strcmp( type_str, "xser" ))
1637 else if( !strcmp( type_str, "xstr" ))
1639 else if( !strcmp( type_str, "xsubq" ))
1640 type = EXP_SUBQUERY;
1642 type = EXP_NULL; // shouldn't happen due to database constraint
1644 int parenthesize = oils_result_get_bool_idx( result, 3 );
1647 if( dbi_result_field_is_null_idx( result, 4 ))
1648 parent_expr_id = -1;
1650 parent_expr_id = dbi_result_get_int_idx( result, 4 );
1652 int seq_no = dbi_result_get_int_idx( result, 5 );
1653 const char* literal = dbi_result_get_string_idx( result, 6 );
1654 const char* table_alias = dbi_result_get_string_idx( result, 7 );
1655 const char* column_name = dbi_result_get_string_idx( result, 8 );
1657 int left_operand_id;
1658 if( dbi_result_field_is_null_idx( result, 9 ))
1659 left_operand_id = -1;
1661 left_operand_id = dbi_result_get_int_idx( result, 9 );
1663 const char* operator = dbi_result_get_string_idx( result, 10 );
1665 int right_operand_id;
1666 if( dbi_result_field_is_null_idx( result, 11 ))
1667 right_operand_id = -1;
1669 right_operand_id = dbi_result_get_int_idx( result, 11 );
1672 if( dbi_result_field_is_null_idx( result, 12 ))
1675 subquery_id = dbi_result_get_int_idx( result, 12 );
1678 if( dbi_result_field_is_null_idx( result, 13 ))
1681 cast_type_id = dbi_result_get_int_idx( result, 13 );
1683 int negate = oils_result_get_bool_idx( result, 14 );
1684 const char* bind_variable = dbi_result_get_string_idx( result, 15 );
1685 const char* function_name = dbi_result_get_string_idx( result, 16 );
1687 Expression* left_operand = NULL;
1688 Expression* right_operand = NULL;
1689 StoredQ* subquery = NULL;
1690 Datatype* cast_type = NULL;
1691 BindVar* bind = NULL;
1692 CaseBranch* branch_list = NULL;
1693 Expression* subexp_list = NULL;
1695 if( EXP_BETWEEN == type ) {
1696 // Get the left operand
1697 if( -1 == left_operand_id ) {
1698 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1699 "No left operand defined for BETWEEN expression # %d", id ));
1703 left_operand = getExpression( state, left_operand_id );
1704 if( !left_operand ) {
1705 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1706 "Unable to get left operand in BETWEEN expression # %d", id ));
1712 // Get the end points of the BETWEEN range
1713 subexp_list = getExpressionList( state, id );
1714 if( state->error ) {
1715 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1716 "Unable to get subexpressions for BETWEEN expression # %d", id ));
1717 expressionFree( left_operand );
1719 } else if( !subexp_list ) {
1720 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1721 "BETWEEN range is empty in expression # %d", id ));
1723 expressionFree( left_operand );
1725 } else if( !subexp_list->next ) {
1726 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1727 "BETWEEN range has only one end point in expression # %d", id ));
1729 expressionListFree( subexp_list );
1730 expressionFree( left_operand );
1732 } else if( subexp_list->next->next ) {
1733 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1734 "BETWEEN range has more than two subexpressions in expression # %d", id ));
1736 expressionListFree( subexp_list );
1737 expressionFree( left_operand );
1741 } else if( EXP_BIND == type ) {
1742 if( bind_variable ) {
1743 // To do: Build a BindVar
1744 bind = getBindVar( state, bind_variable );
1746 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1747 "Unable to load bind variable \"%s\" for expression # %d",
1748 bind_variable, id ));
1752 PRINT( "\tBind variable is \"%s\"\n", bind_variable );
1754 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1755 "No variable specified for bind variable expression # %d",
1756 bind_variable, id ));
1760 if( right_operand_id != -1 ) {
1761 right_operand = getExpression( state, right_operand_id );
1762 if( !right_operand ) {
1763 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1764 "Unable to get right operand in expression # %d", id ));
1766 expressionFree( left_operand );
1771 } else if( EXP_CASE == type ) {
1772 // Get the left operand
1773 if( -1 == left_operand_id ) {
1774 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1775 "No left operand defined for CASE expression # %d", id ));
1779 left_operand = getExpression( state, left_operand_id );
1780 if( !left_operand ) {
1781 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1782 "Unable to get left operand in CASE expression # %d", id ));
1788 branch_list = getCaseBranchList( state, id );
1789 if( ! branch_list ) {
1790 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1791 "Unable to build branches for CASE expression # %d", id ));
1796 } else if( EXP_CAST == type ) {
1797 // Get the left operand
1798 if( -1 == left_operand_id ) {
1799 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1800 "No left operand defined for CAST expression # %d", id ));
1804 left_operand = getExpression( state, left_operand_id );
1805 if( !left_operand ) {
1806 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1807 "Unable to get left operand for CAST expression # %d", id ));
1813 if( -1 == cast_type_id ) {
1814 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1815 "No datatype specified for CAST expression # %d", id ));
1819 cast_type = getDatatype( state, cast_type_id );
1821 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1822 "Unable to get datatype for CAST expression # %d", id ));
1828 } else if( EXP_COLUMN == type ) {
1829 if( column_name && !is_identifier( column_name )) {
1830 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1831 "Column name \"%s\" is invalid identifier for expression # %d",
1837 } else if( EXP_EXIST == type ) {
1838 if( -1 == subquery_id ) {
1839 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1840 "Internal error: No subquery found for EXIST expression # %d", id ));
1844 subquery = getStoredQuery( state, subquery_id );
1846 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1847 "Unable to load subquery for EXIST expression # %d", id ));
1853 } else if( EXP_FUNCTION == type ) {
1854 if( !function_name ) {
1855 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1856 "Function call expression # %d provides no function name", id ));
1859 } else if( !is_identifier( function_name )) {
1860 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1861 "Function call expression # %d contains an invalid function name \"%s\"",
1862 id, function_name ));
1866 subexp_list = getExpressionList( state, id );
1867 if( state->error ) {
1868 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1869 "Unable to get parameter list for function expression # %d", id ));
1874 } else if( EXP_IN == type ) {
1875 if( -1 == left_operand_id ) {
1876 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1877 "IN condition has no left operand in expression # %d", id ));
1881 left_operand = getExpression( state, left_operand_id );
1882 if( !left_operand ) {
1883 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1884 "Unable to get left operand for IN condition in expression # %d", id ));
1890 if( -1 == subquery_id ) {
1891 // Load an IN list of subexpressions
1892 subexp_list = getExpressionList( state, id );
1893 if( state->error ) {
1894 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1895 "Unable to get subexpressions for IN list" ));
1897 } else if( !subexp_list ) {
1898 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1899 "IN list is empty in expression # %d", id ));
1905 subquery = getStoredQuery( state, subquery_id );
1907 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1908 "Unable to load subquery for IN expression # %d", id ));
1914 } else if( EXP_ISNULL == type ) {
1915 if( -1 == left_operand_id ) {
1916 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1917 "Expression # %d IS NULL has no left operand", id ));
1922 if( left_operand_id != -1 ) {
1923 left_operand = getExpression( state, left_operand_id );
1924 if( !left_operand ) {
1925 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1926 "Unable to get left operand in expression # %d", id ));
1932 } else if( EXP_NUMBER == type ) {
1934 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1935 "Numeric expression # %d provides no numeric value", id ));
1938 } else if( !jsonIsNumeric( literal )) {
1939 // Purported number is not numeric. We use JSON rules here to determine what
1940 // a valid number is, just because we already have a function lying around that
1941 // can do that validation. PostgreSQL probably doesn't use the same exact rules.
1942 // If that's a problem, we can write a separate validation routine to follow
1943 // PostgreSQL's rules, once we figure out what those rules are.
1944 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1945 "Numeric expression # %d is not a valid number: \"%s\"", id, literal ));
1950 } else if( EXP_OPERATOR == type ) {
1951 // Make sure we have a plausible operator
1953 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1954 "Operator expression # %d has no operator", id ));
1957 } else if( !is_good_operator( operator )) {
1958 // The specified operator contains one or more characters that aren't allowed
1959 // in an operator. This isn't a true validation; it's just a protective
1960 // measure to prevent certain kinds of sql injection.
1961 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1962 "Operator expression # %d contains invalid operator \"%s\"", id, operator ));
1967 // Load left and/or right operands
1968 if( -1 == left_operand_id && -1 == right_operand_id ) {
1969 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1970 "Expression # %d is an operator with no operands", id ));
1975 if( left_operand_id != -1 ) {
1976 left_operand = getExpression( state, left_operand_id );
1977 if( !left_operand ) {
1978 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1979 "Unable to get left operand in expression # %d", id ));
1985 if( right_operand_id != -1 ) {
1986 right_operand = getExpression( state, right_operand_id );
1987 if( !right_operand ) {
1988 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1989 "Unable to get right operand in expression # %d", id ));
1995 } else if( EXP_SERIES == type ) {
1996 subexp_list = getExpressionList( state, id );
1997 if( state->error ) {
1998 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1999 "Unable to get subexpressions for expression series using operator \"%s\"",
2000 operator ? operator : "," ));
2002 } else if( !subexp_list ) {
2003 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
2004 "Series expression is empty in expression # %d", id ));
2007 } else if( operator && !is_good_operator( operator )) {
2008 // The specified operator contains one or more characters that aren't allowed
2009 // in an operator. This isn't a true validation; it's just a protective
2010 // measure to prevent certain kinds of sql injection.
2011 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
2012 "Series expression # %d contains invalid operator \"%s\"", id, operator ));
2017 } else if( EXP_STRING == type ) {
2019 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
2020 "String expression # %d provides no string value", id ));
2025 } else if( EXP_SUBQUERY == type ) {
2026 if( -1 == subquery_id ) {
2027 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
2028 "Subquery expression # %d has no query id", id ));
2032 // Load a subquery, if there is one
2033 subquery = getStoredQuery( state, subquery_id );
2035 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
2036 "Unable to load subquery for expression # %d", id ));
2040 if( subquery->select_list && subquery->select_list->next ) {
2041 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
2042 "Subquery # %d as expression returns more than one column", subquery_id ));
2046 PRINT( "\tExpression is subquery %d\n", subquery_id );
2050 // Allocate an Expression: from the free list if possible, from the heap if necessary
2051 Expression* exp = NULL;
2052 if( free_expression_list ) {
2053 exp = free_expression_list;
2054 free_expression_list = free_expression_list->next;
2056 exp = safe_malloc( sizeof( Expression ) );
2058 // Populate the Expression
2062 exp->parenthesize = parenthesize;
2063 exp->parent_expr_id = parent_expr_id;
2064 exp->seq_no = seq_no;
2065 exp->literal = literal ? strdup( literal ) : NULL;
2066 exp->table_alias = table_alias ? strdup( table_alias ) : NULL;
2067 exp->column_name = column_name ? strdup( column_name ) : NULL;
2068 exp->left_operand = left_operand;
2069 exp->op = operator ? strdup( operator ) : NULL;
2070 exp->right_operand = right_operand;
2071 exp->subquery_id = subquery_id;
2072 exp->subquery = subquery;
2073 exp->cast_type = cast_type;
2074 exp->negate = negate;
2076 exp->branch_list = branch_list;
2077 exp->subexp_list = subexp_list;
2078 exp->function_name = function_name ? strdup( function_name ) : NULL;
2084 @brief Free all the Expressions in a linked list of Expressions.
2085 @param exp Pointer to the first Expression in the list.
2087 static void expressionListFree( Expression* exp ) {
2089 Expression* next = exp->next;
2090 expressionFree( exp );
2096 @brief Deallocate an Expression.
2097 @param exp Pointer to the Expression to be deallocated.
2099 Free the strings owned by the Expression. Put the Expression itself, and any
2100 subexpressions that it owns, into a free list.
2102 static void expressionFree( Expression* exp ) {
2104 free( exp->literal );
2105 exp->literal = NULL;
2106 free( exp->table_alias );
2107 exp->table_alias = NULL;
2108 free( exp->column_name );
2109 exp->column_name = NULL;
2110 if( exp->left_operand ) {
2111 expressionFree( exp->left_operand );
2112 exp->left_operand = NULL;
2118 if( exp->right_operand ) {
2119 expressionFree( exp->right_operand );
2120 exp->right_operand = NULL;
2122 if( exp->subquery ) {
2123 storedQFree( exp->subquery );
2124 exp->subquery = NULL;
2126 if( exp->cast_type ) {
2127 datatypeFree( exp->cast_type );
2128 exp->cast_type = NULL;
2131 // We don't free the bind member here because the Expression doesn't own it;
2132 // the bindvar_list hash owns it, so that multiple Expressions can reference it.
2134 if( exp->subexp_list ) {
2135 // Free the linked list of subexpressions
2136 expressionListFree( exp->subexp_list );
2137 exp->subexp_list = NULL;
2140 if( exp->branch_list ) {
2141 freeBranchList( exp->branch_list );
2142 exp->branch_list = NULL;
2145 if( exp->function_name ) {
2146 free( exp->function_name );
2147 exp->function_name = NULL;
2150 // Prepend to the free list
2151 exp->next = free_expression_list;
2152 free_expression_list = exp;
2157 @brief Build a list of subexpressions.
2158 @param state Pointer to the query-building context.
2159 @param id ID of the parent Expression.
2160 @return A pointer to the first in a linked list of Expressions, if there are any; or
2161 NULL if there aren't any, or in case of an error.
2163 static Expression* getExpressionList( BuildSQLState* state, int id ) {
2164 Expression* exp_list = NULL;
2166 // The ORDER BY is in descending order so that we can build the list by adding to
2167 // the head, and it will wind up in the right order.
2168 dbi_result result = dbi_conn_queryf( state->dbhandle,
2169 "SELECT exp.id, exp.type, exp.parenthesize, exp.parent_expr, exp.seq_no, "
2170 "exp.literal, exp.table_alias, exp.column_name, exp.left_operand, exp.operator, "
2171 "exp.right_operand, exp.subquery, exp.cast_type, exp.negate, exp.bind_variable, "
2172 "func.function_name "
2173 "FROM query.expression AS exp LEFT JOIN query.function_sig AS func "
2174 "ON (exp.function_id = func.id) "
2175 "WHERE exp.parent_expr = %d "
2176 "ORDER BY exp.seq_no desc;", id );
2179 if( dbi_result_first_row( result ) ) {
2181 Expression* exp = constructExpression( state, result );
2183 PRINT( "Found a subexpression\n" );
2184 exp->next = exp_list;
2187 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
2188 "Unable to build subexpression list for expression id #%d", id ));
2189 expressionListFree( exp_list );
2193 if( !dbi_result_next_row( result ) )
2199 int errnum = dbi_conn_error( state->dbhandle, &msg );
2200 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
2201 "Unable to query query.expression table for expression list: #%d %s",
2202 errnum, msg ? msg : "No description available" ));
2204 if( ! oilsIsDBConnected( state->dbhandle ))
2212 @brief Build a list of ORDER BY items as a linked list of OrderItems.
2213 @param state Pointer to the query-building context.
2214 @param query_id ID for the query to which the ORDER BY belongs.
2215 @return Pointer to the first node in a linked list of OrderItems.
2217 The calling code is responsible for freeing the list by calling orderItemListFree().
2219 static OrderItem* getOrderByList( BuildSQLState* state, int query_id ) {
2220 OrderItem* ord_list = NULL;
2222 // The ORDER BY is in descending order so that we can build the list by adding to
2223 // the head, and it will wind up in the right order.
2224 dbi_result result = dbi_conn_queryf( state->dbhandle,
2225 "SELECT id, stored_query, seq_no, expression "
2226 "FROM query.order_by_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
2228 if( dbi_result_first_row( result ) ) {
2230 OrderItem* item = constructOrderItem( state, result );
2232 PRINT( "Found an ORDER BY item\n" );
2234 item->next = ord_list;
2237 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
2238 "Unable to build ORDER BY item for query id #%d", query_id ));
2239 orderItemListFree( ord_list );
2243 if( !dbi_result_next_row( result ) )
2249 int errnum = dbi_conn_error( state->dbhandle, &msg );
2250 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
2251 "Unable to query query.order_by_list table: #%d %s",
2252 errnum, msg ? msg : "No description available" ));
2254 if( ! oilsIsDBConnected( state->dbhandle ))
2262 @brief Construct an OrderItem.
2263 @param Pointer to the query-building context.
2264 @param result Database cursor positioned at a row in query.order_by_item.
2265 @return Pointer to a newly constructed OrderItem, if successful, or NULL if not.
2267 The calling code is responsible for freeing the OrderItems by calling orderItemListFree().
2269 static OrderItem* constructOrderItem( BuildSQLState* state, dbi_result result ) {
2270 int id = dbi_result_get_int_idx( result, 1 );
2271 int stored_query_id = dbi_result_get_int_idx( result, 2 );
2272 int seq_no = dbi_result_get_int_idx( result, 3 );
2273 int expression_id = dbi_result_get_int_idx( result, 4 );
2274 // Allocate a SelectItem: from the free list if possible, from the heap if necessary
2276 // Construct an Expression
2277 Expression* expression = getExpression( state, expression_id );
2279 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
2280 "Unable to fetch ORDER BY expression for id = %d", expression_id ));
2285 // Allocate an OrderItem; from the free list if possible, or from the heap if necessary.
2287 if( free_order_item_list ) {
2288 ord = free_order_item_list;
2289 free_order_item_list = free_order_item_list->next;
2291 ord = safe_malloc( sizeof( OrderItem ));
2295 ord->stored_query_id = stored_query_id;
2296 ord->seq_no = seq_no;
2297 ord->expression = expression;
2303 @brief Deallocate a linked list of OrderItems.
2304 @param exp Pointer to the first OrderItem in the list to be deallocated.
2306 Deallocate the memory owned by the OrderItems. Put the items themselves into a free list.
2308 static void orderItemListFree( OrderItem* ord ) {
2310 return; // Nothing to free
2312 OrderItem* first = ord;
2314 expressionFree( ord->expression );
2315 ord->expression = NULL;
2317 if( NULL == ord->next ) {
2318 ord->next = free_order_item_list;
2324 // Transfer the entire list to the free list
2325 free_order_item_list = first;
2329 @brief Push an IdNode onto a stack of IdNodes.
2330 @param stack Pointer to the stack.
2331 @param id Id of the new node.
2332 @param alias Alias, if any, of the new node.
2334 static void push_id( IdNode** stack, int id, const char* alias ) {
2337 // Allocate a node; from the free list if possible, from the heap if necessary.
2338 IdNode* node = NULL;
2339 if( free_id_node_list ) {
2340 node = free_id_node_list;
2341 free_id_node_list = free_id_node_list->next;
2343 node = safe_malloc( sizeof( IdNode ));
2346 node->next = *stack;
2349 node->alias = strdup( alias );
2359 @brief Remove the node at the top of an IdNode stack.
2360 @param stack Pointer to the IdNode stack.
2362 void pop_id( IdNode** stack ) {
2364 IdNode* node = *stack;
2365 *stack = node->next;
2368 free( node->alias );
2372 node->next = free_id_node_list;
2373 free_id_node_list = node;
2378 @brief Search a stack of IDs for a match by either ID or, optionally, by alias.
2379 @param stack Pointer to the stack.
2380 @param id The id to search for.
2381 @param alias (Optional) the alias to search for.
2382 @return A pointer to the matching node if one is found, or NULL if not.
2384 This search is used to detect cases where a query, expression, or FROM clause is nested
2385 inside itself, in order to avoid infinite recursion; or in order to avoid conflicting
2386 table references in a FROM clause.
2388 static const IdNode* searchIdStack( const IdNode* stack, int id, const char* alias ) {
2390 const IdNode* node = stack;
2392 if( node->id == id )
2393 return node; // Matched on id
2394 else if( alias && node->alias && !strcmp( alias, node->alias ))
2395 return node; // Matched on alias
2400 return NULL; // No match found
2404 @brief Free up any resources held by the StoredQ module.
2406 void storedQCleanup( void ) {
2408 // Free all the nodes in the free state list
2409 StoredQ* sq = free_storedq_list;
2411 free_storedq_list = sq->next;
2413 sq = free_storedq_list;
2416 // Free all the nodes in the free from_relation list
2417 FromRelation* fr = free_from_relation_list;
2419 free_from_relation_list = fr->next;
2421 fr = free_from_relation_list;
2424 // Free all the nodes in the free expression list
2425 Expression* exp = free_expression_list;
2427 free_expression_list = exp->next;
2429 exp = free_expression_list;
2432 // Free all the nodes in the free select item list
2433 SelectItem* sel = free_select_item_list;
2435 free_select_item_list = sel->next;
2437 sel = free_select_item_list;
2440 // Free all the nodes in the free select item list
2441 IdNode* node = free_id_node_list;
2443 free_id_node_list = node->next;
2445 node = free_id_node_list;
2448 // Free all the nodes in the free query sequence list
2449 QSeq* seq = free_qseq_list;
2451 free_qseq_list = seq->next;
2453 seq = free_qseq_list;
2456 // Free all the nodes in the free order item list
2457 OrderItem* ord = free_order_item_list;
2459 free_order_item_list = ord->next;
2461 ord = free_order_item_list;
2464 // Free all the nodes in the bind variable free list
2465 BindVar* bind = free_bindvar_list;
2467 free_bindvar_list = bind->next;
2469 bind = free_bindvar_list;
2472 // Free all the nodes in the case branch free list
2473 CaseBranch* branch = free_branch_list;
2475 free_branch_list = branch->next;
2477 branch = free_branch_list;
2480 // Free all the nodes in the datatype free list
2481 Datatype* datatype = free_datatype_list;
2483 free_datatype_list = datatype->next;
2485 datatype = free_datatype_list;
2490 @brief Return a boolean value from a database result.
2491 @param result The database result.
2492 @param i Index of the column in the result, starting with 1 );
2493 @return 1 if true, or 0 for false.
2495 Null values and error conditions are interpreted as FALSE.
2497 static int oils_result_get_bool_idx( dbi_result result, int i ) {
2499 const char* str = dbi_result_get_string_idx( result, i );
2500 return (str && *str == 't' ) ? 1 : 0;
2506 @brief Enable verbose messages.
2508 The messages are written to standard output, which for a server is /dev/null. Hence this
2509 option is useful only for a non-server. It is intended only as a convenience for
2510 development and debugging.
2512 void oilsStoredQSetVerbose( void ) {