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 "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 );
273 FromRelation* from_clause = NULL;
274 if( QT_SELECT == type ) {
275 // A SELECT query needs a FROM clause; go get it
276 if( from_clause_id != -1 ) {
277 from_clause = getFromRelation( state, from_clause_id );
279 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
280 "Unable to construct FROM clause for id = %d", from_clause_id ));
285 // Must be one of UNION, INTERSECT, or EXCEPT
286 if( from_clause_id != -1 )
287 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
288 "FROM clause found and ignored for %s query in query #%d", type_str, id ));
291 // If this is a SELECT query, we need a SELECT list. Go get one.
292 SelectItem* select_list = NULL;
293 QSeq* child_list = NULL;
294 if( QT_SELECT == type ) {
295 select_list = getSelectList( state, id );
297 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
298 "No SELECT list found for query id = %d", id ));
299 fromRelationFree( from_clause );
303 // Construct child queries of UNION, INTERSECT, or EXCEPT query
304 child_list = loadChildQueries( state, id, type_str );
306 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
307 "Unable to load child queries for %s query # %d", type_str, id ));
309 fromRelationFree( from_clause );
314 // Get the WHERE clause, if there is one
315 Expression* where_clause = NULL;
316 if( where_clause_id != -1 ) {
317 where_clause = getExpression( state, where_clause_id );
318 if( ! where_clause ) {
319 // shouldn't happen due to foreign key constraint
320 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
321 "Unable to fetch WHERE expression for query id = %d", id ));
322 freeQSeqList( child_list );
323 fromRelationFree( from_clause );
324 selectListFree( select_list );
330 Expression* having_clause = NULL;
331 if( having_clause_id != -1 ) {
332 having_clause = getExpression( state, having_clause_id );
333 if( ! having_clause ) {
334 // shouldn't happen due to foreign key constraint
335 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
336 "Unable to fetch HAVING expression for query id = %d", id ));
337 expressionFree( where_clause );
338 freeQSeqList( child_list );
339 fromRelationFree( from_clause );
340 selectListFree( select_list );
346 // Get the ORDER BY clause, if there is one
347 OrderItem* order_by_list = getOrderByList( state, id );
349 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
350 "Unable to load ORDER BY clause for query %d", id ));
351 expressionFree( having_clause );
352 expressionFree( where_clause );
353 freeQSeqList( child_list );
354 fromRelationFree( from_clause );
355 selectListFree( select_list );
359 // Allocate a StoredQ: from the free list if possible, from the heap if necessary
362 if( free_storedq_list ) {
363 sq = free_storedq_list;
364 free_storedq_list = free_storedq_list->next;
366 sq = safe_malloc( sizeof( StoredQ ) );
368 // Populate the StoredQ
373 sq->use_all = use_all;
374 sq->use_distinct = use_distinct;
375 sq->from_clause = from_clause;
376 sq->where_clause = where_clause;
377 sq->select_list = select_list;
378 sq->child_list = child_list;
379 sq->having_clause = having_clause;
380 sq->order_by_list = order_by_list;
386 @brief Load the child queries subordinate to a UNION, INTERSECT, or EXCEPT query.
387 @param state Pointer to the query-building context.
388 @param parent ID of the UNION, INTERSECT, or EXCEPT query.
389 @param type_str The type of the query ("UNION", "INTERSECT", or "EXCEPT").
390 @return If successful, a pointer to a linked list of QSeq, each bearing a pointer to a
391 StoredQ; otherwise NULL.
393 The @a type_str parameter is used only for building error messages.
395 static QSeq* loadChildQueries( BuildSQLState* state, int parent_id, const char* type_str ) {
396 QSeq* child_list = NULL;
398 // The ORDER BY is in descending order so that we can build the list by adding to
399 // the head, and it will wind up in the right order.
400 dbi_result result = dbi_conn_queryf( state->dbhandle,
401 "SELECT id, parent_query, seq_no, child_query "
402 "FROM query.query_sequence WHERE parent_query = %d ORDER BY seq_no DESC", parent_id );
404 if( dbi_result_first_row( result ) ) {
408 QSeq* seq = constructQSeq( state, result );
410 PRINT( "Found a child query\n" );
411 PRINT( "\tid: %d\n", seq->id );
412 PRINT( "\tparent id: %d\n", seq->parent_query_id );
413 PRINT( "\tseq_no: %d\n", seq->seq_no );
414 // Add to the head of the list
415 seq->next = child_list;
418 freeQSeqList( child_list );
421 if( !dbi_result_next_row( result ))
425 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
426 "%s query # %d has only one child query", type_str, parent_id ));
428 freeQSeqList( child_list );
432 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
433 "%s query # %d has no child queries within it", type_str, parent_id ));
435 if( ! oilsIsDBConnected( state->dbhandle ))
441 int errnum = dbi_conn_error( state->dbhandle, &msg );
442 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
443 "Unable to query query.query_sequence table: # %d %s",
444 errnum, msg ? msg : "No description available" ));
453 @brief Construct a QSeq.
454 @param Pointer to the query-building context.
455 @param result Database cursor positioned at a row in query.query_sequence.
456 @return Pointer to a newly constructed QSeq, if successful, or NULL if not.
458 The calling code is responsible for freeing QSeqs by calling freeQSeqList().
460 static QSeq* constructQSeq( BuildSQLState* state, dbi_result result ) {
461 int id = dbi_result_get_int_idx( result, 1 );
462 int parent_query_id = dbi_result_get_int_idx( result, 2 );
463 int seq_no = dbi_result_get_int_idx( result, 3 );
464 int child_query_id = dbi_result_get_int_idx( result, 4 );
466 StoredQ* child_query = getStoredQuery( state, child_query_id );
468 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
469 "Unable to load child query # %d for parent query %d",
470 child_query_id, parent_query_id ));
475 // Allocate a QSeq; from the free list if possible, from the heap if necessary
477 if( free_qseq_list ) {
478 seq = free_qseq_list;
479 free_qseq_list = free_qseq_list->next;
481 seq = safe_malloc( sizeof( QSeq ));
485 seq->parent_query_id = parent_query_id;
486 seq->seq_no = seq_no;
487 seq->child_query = child_query;
493 @brief Free a list of QSeq's.
494 @param seq Pointer to the first in a linked list of QSeq's to be freed.
496 Each QSeq goes onto a free list for potential reuse.
498 static void freeQSeqList( QSeq* seq ) {
504 storedQFree( seq->child_query );
505 seq->child_query = NULL;
510 seq->next = free_qseq_list;
515 free_qseq_list = first;
519 @brief Deallocate the memory owned by a StoredQ.
520 @param sq Pointer to the StoredQ to be deallocated.
522 void storedQFree( StoredQ* sq ) {
524 fromRelationFree( sq->from_clause );
525 sq->from_clause = NULL;
526 selectListFree( sq->select_list );
527 sq->select_list = NULL;
528 expressionFree( sq->where_clause );
529 sq->where_clause = NULL;
530 if( sq->child_list ) {
531 freeQSeqList( sq->child_list );
532 sq->child_list = NULL;
534 if( sq->order_by_list ) {
535 orderItemListFree( sq->order_by_list );
536 sq->order_by_list = NULL;
538 if( sq->having_clause )
539 expressionFree( sq->having_clause );
541 // Stick the empty husk on the free list for potential reuse
542 sq->next = free_storedq_list;
543 free_storedq_list = sq;
548 @brief Given an id from query.from_relation, load a FromRelation.
549 @param state Pointer the the query-building context.
550 @param id Id of the FromRelation.
551 @return Pointer to a newly-created FromRelation if successful, or NULL if not.
553 The calling code is responsible for freeing the new FromRelation by calling
556 static FromRelation* getFromRelation( BuildSQLState* state, int id ) {
557 FromRelation* fr = NULL;
558 dbi_result result = dbi_conn_queryf( state->dbhandle,
559 "SELECT id, type, table_name, class_name, subquery, function_call, "
560 "table_alias, parent_relation, seq_no, join_type, on_clause "
561 "FROM query.from_relation WHERE id = %d;", id );
563 if( dbi_result_first_row( result ) ) {
564 fr = constructFromRelation( state, result );
566 PRINT( "Got a from_relation row\n" );
567 PRINT( "\tid: %d\n", fr->id );
568 PRINT( "\ttype: %d\n", (int) fr->type );
569 PRINT( "\ttable_name: %s\n", fr->table_name ? fr->table_name : "(none)" );
570 PRINT( "\tclass_name: %s\n", fr->class_name ? fr->class_name : "(none)" );
571 PRINT( "\tsubquery_id: %d\n", fr->subquery_id );
572 PRINT( "\tfunction_call_id: %d\n", fr->function_call_id );
573 PRINT( "\ttable_alias: %s\n", fr->table_alias ? fr->table_alias : "(none)" );
574 PRINT( "\tparent_relation_id: %d\n", fr->parent_relation_id );
575 PRINT( "\tseq_no: %d\n", fr->seq_no );
576 PRINT( "\tjoin_type = %d\n", fr->join_type );
577 // Check the stack to see if the current from clause is nested inside itself.
578 // If it is, then abort in order to avoid infinite recursion. If it isn't,
579 // then add it to the stack. (Make sure to pop it off the stack before
581 const char* effective_alias = fr->table_alias;
582 if( !effective_alias )
583 effective_alias = fr->class_name;
584 const IdNode* node = searchIdStack( state->from_stack, id, effective_alias );
587 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
588 "Infinite recursion detected; from clause # %d is nested "
589 "within itself", id ));
591 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
592 "Conflicting nested table aliases \"%s\" in from clause # %d",
593 effective_alias, node->id ));
597 push_id( &state->from_stack, id, effective_alias );
599 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
600 "Unable to build a FromRelation for id = %d", id ));
602 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
603 "FROM relation not found for id = %d", id ));
606 dbi_result_free( result );
609 int errnum = dbi_conn_error( state->dbhandle, &msg );
610 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
611 "Unable to query query.from_relation table: #%d %s",
612 errnum, msg ? msg : "No description available" ));
614 if( ! oilsIsDBConnected( state->dbhandle ))
619 pop_id( &state->from_stack );
625 @brief Construct a FromRelation.
626 @param Pointer to the query-building context.
627 @param result Database cursor positioned at a row in query.from_relation.
628 @return Pointer to a newly constructed FromRelation, if successful, or NULL if not.
630 The calling code is responsible for freeing FromRelations by calling joinListFree().
632 static FromRelation* constructFromRelation( BuildSQLState* state, dbi_result result ) {
633 // Get the column values from the result
634 int id = dbi_result_get_int_idx( result, 1 );
635 const char* type_str = dbi_result_get_string_idx( result, 2 );
637 FromRelationType type;
638 if( !strcmp( type_str, "RELATION" ))
640 else if( !strcmp( type_str, "SUBQUERY" ))
642 else if( !strcmp( type_str, "FUNCTION" ))
645 type = FRT_RELATION; // shouldn't happen due to database constraint
647 const char* table_name = dbi_result_get_string_idx( result, 3 );
648 const char* class_name = dbi_result_get_string_idx( result, 4 );
651 if( dbi_result_field_is_null_idx( result, 5 ) )
654 subquery_id = dbi_result_get_int_idx( result, 5 );
656 int function_call_id;
657 if( dbi_result_field_is_null_idx( result, 6 ) )
658 function_call_id = -1;
660 function_call_id = dbi_result_get_int_idx( result, 6 );
662 Expression* function_call = NULL;
663 const char* table_alias = dbi_result_get_string_idx( result, 7 );
665 int parent_relation_id;
666 if( dbi_result_field_is_null_idx( result, 8 ) )
667 parent_relation_id = -1;
669 parent_relation_id = dbi_result_get_int_idx( result, 8 );
671 int seq_no = dbi_result_get_int_idx( result, 9 );
674 const char* join_type_str = dbi_result_get_string_idx( result, 10 );
677 else if( !strcmp( join_type_str, "INNER" ) )
678 join_type = JT_INNER;
679 else if( !strcmp( join_type_str, "LEFT" ) )
681 else if( !strcmp( join_type_str, "RIGHT" ) )
682 join_type = JT_RIGHT;
683 else if( !strcmp( join_type_str, "FULL" ) )
686 join_type = JT_NONE; // shouldn't happen due to database constraint
689 if( dbi_result_field_is_null_idx( result, 11 ) )
692 on_clause_id = dbi_result_get_int_idx( result, 11 );
694 StoredQ* subquery = NULL;
698 if( table_name && ! is_identifier( table_name )) {
699 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
700 "Table name \"%s\" is not a valid identifier for FROM relation # %d",
707 if( -1 == subquery_id ) {
708 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
709 "Internal error: no subquery specified for FROM relation # %d", id ));
713 if( ! table_alias ) {
714 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
715 "Subquery needs alias in FROM relation # %d", id ));
719 subquery = getStoredQuery( state, subquery_id );
721 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
722 "Unable to load subquery for FROM relation # %d", id ));
728 if( -1 == function_call_id ) {
729 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
730 "FROM clause # %d purports to reference a function; not identified", id ));
735 function_call = getExpression( state, function_call_id );
736 if( !function_call ) {
737 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
738 "Unable to build function call # %d in FROM relation # %d",
739 function_call_id, id ));
742 } else if( function_call->type != EXP_FUNCTION ) {
743 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
744 "In FROM relation # %d: supposed function call expression # %d "
745 "is not a function call", id, function_call_id ));
751 FromRelation* join_list = getJoinList( state, id );
753 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
754 "Unable to load join list for FROM relation # %d", id ));
758 Expression* on_clause = NULL;
759 if( on_clause_id != -1 ) {
760 on_clause = getExpression( state, on_clause_id );
762 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
763 "Unable to load ON condition for FROM relation # %d", id ));
764 joinListFree( join_list );
769 PRINT( "\tGot an ON condition\n" );
772 // Allocate a FromRelation: from the free list if possible, from the heap if necessary
775 if( free_from_relation_list ) {
776 fr = free_from_relation_list;
777 free_from_relation_list = free_from_relation_list->next;
779 fr = safe_malloc( sizeof( FromRelation ) );
781 // Populate the FromRelation
786 fr->table_name = table_name ? strdup( table_name ) : NULL;
787 fr->class_name = class_name ? strdup( class_name ) : NULL;
788 fr->subquery_id = subquery_id;
789 fr->subquery = subquery;
790 fr->function_call_id = function_call_id;
791 fr->function_call = function_call;
792 fr->table_alias = table_alias ? strdup( table_alias ) : NULL;
793 fr->parent_relation_id = parent_relation_id;
795 fr->join_type = join_type;
796 fr->on_clause = on_clause;
797 fr->join_list = join_list;
803 @brief Build a list of joined relations.
804 @param state Pointer to the query-building context.
805 @param id ID of the parent relation.
806 @return A pointer to the first in a linked list of FromRelations, if there are any; or
807 NULL if there aren't any, or in case of an error.
809 Look for relations joined directly to the parent relation, and make a list of them.
811 static FromRelation* getJoinList( BuildSQLState* state, int id ) {
812 FromRelation* join_list = NULL;
814 // The ORDER BY is in descending order so that we can build the list by adding to
815 // the head, and it will wind up in the right order.
816 dbi_result result = dbi_conn_queryf( state->dbhandle,
817 "SELECT id, type, table_name, class_name, subquery, function_call, "
818 "table_alias, parent_relation, seq_no, join_type, on_clause "
819 "FROM query.from_relation WHERE parent_relation = %d ORDER BY seq_no DESC", id );
822 if( dbi_result_first_row( result ) ) {
824 FromRelation* relation = constructFromRelation( state, result );
826 PRINT( "Found a joined relation\n" );
827 PRINT( "\tjoin_type: %d\n", relation->join_type );
828 PRINT( "\ttable_name: %s\n", relation->table_name );
829 relation->next = join_list;
830 join_list = relation;
832 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
833 "Unable to build join list for from relation id #%d", id ));
834 joinListFree( join_list );
838 if( !dbi_result_next_row( result ) )
844 int errnum = dbi_conn_error( state->dbhandle, &msg );
845 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
846 "Unable to query query.from_relation table for join list: #%d %s",
847 errnum, msg ? msg : "No description available" ));
849 if( ! oilsIsDBConnected( state->dbhandle ))
857 @brief Free a list of FromRelations.
858 @param join_list Pointer to the first FromRelation in the list.
860 static void joinListFree( FromRelation* join_list ) {
862 FromRelation* temp = join_list->next;
863 fromRelationFree( join_list );
869 @brief Deallocate a FromRelation.
870 @param fr Pointer to the FromRelation to be freed.
872 Free the strings that the FromRelation owns. The FromRelation itself goes onto a
873 free list for potential reuse.
875 static void fromRelationFree( FromRelation* fr ) {
877 free( fr->table_name );
878 fr->table_name = NULL;
879 free( fr->class_name );
880 fr->class_name = NULL;
882 storedQFree( fr->subquery );
885 if( fr->function_call ) {
886 expressionFree( fr->function_call );
887 fr->function_call = NULL;
889 free( fr->table_alias );
890 fr->table_alias = NULL;
891 if( fr->on_clause ) {
892 expressionFree( fr->on_clause );
893 fr->on_clause = NULL;
895 joinListFree( fr->join_list );
896 fr->join_list = NULL;
898 fr->next = free_from_relation_list;
899 free_from_relation_list = fr;
904 @brief Build a SELECT list for a given query ID.
905 @param state Pointer to the query-building context.
906 @param query_id ID of the query to which the SELECT list belongs.
908 static SelectItem* getSelectList( BuildSQLState* state, int query_id ) {
909 SelectItem* select_list = NULL;
911 // The ORDER BY is in descending order so that we can build the list by adding to
912 // the head, and it will wind up in the right order.
913 dbi_result result = dbi_conn_queryf( state->dbhandle,
914 "SELECT id, stored_query, seq_no, expression, column_alias, grouped_by "
915 "FROM query.select_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
917 if( dbi_result_first_row( result ) ) {
919 SelectItem* item = constructSelectItem( state, result );
921 PRINT( "Found a SELECT item\n" );
922 PRINT( "\tid: %d\n", item->id );
923 PRINT( "\tstored_query_id: %d\n", item->stored_query_id );
924 PRINT( "\tseq_no: %d\n", item->seq_no );
925 PRINT( "\tcolumn_alias: %s\n",
926 item->column_alias ? item->column_alias : "(none)" );
927 PRINT( "\tgrouped_by: %d\n", item->grouped_by );
929 item->next = select_list;
932 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
933 "Unable to build select list for query id #%d", query_id ));
934 selectListFree( select_list );
938 if( !dbi_result_next_row( result ) )
942 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
943 "No SELECT list found for query # %d", query_id ));
948 int errnum = dbi_conn_error( state->dbhandle, &msg );
949 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
950 "Unable to query query.select_list table: #%d %s",
951 errnum, msg ? msg : "No description available" ));
953 if( ! oilsIsDBConnected( state->dbhandle ))
961 @brief Construct a SelectItem.
962 @param Pointer to the query-building context.
963 @param result Database cursor positioned at a row in query.select_item.
964 @return Pointer to a newly constructed SelectItem, if successful, or NULL if not.
966 The calling code is responsible for freeing the SelectItems by calling selectListFree().
968 static SelectItem* constructSelectItem( BuildSQLState* state, dbi_result result ) {
970 // Get the column values
971 int id = dbi_result_get_int_idx( result, 1 );
972 int stored_query_id = dbi_result_get_int_idx( result, 2 );
973 int seq_no = dbi_result_get_int_idx( result, 3 );
974 int expression_id = dbi_result_get_int_idx( result, 4 );
975 const char* column_alias = dbi_result_get_string_idx( result, 5 );
976 int grouped_by = oils_result_get_bool_idx( result, 6 );
978 // Construct an Expression
979 Expression* expression = getExpression( state, expression_id );
981 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
982 "Unable to fetch expression for id = %d", expression_id ));
987 // Allocate a SelectItem: from the free list if possible, from the heap if necessary
990 if( free_select_item_list ) {
991 sel = free_select_item_list;
992 free_select_item_list = free_select_item_list->next;
994 sel = safe_malloc( sizeof( SelectItem ) );
998 sel->stored_query_id = stored_query_id;
999 sel->seq_no = seq_no;
1000 sel->expression = expression;
1001 sel->column_alias = column_alias ? strdup( column_alias ) : NULL;
1002 sel->grouped_by = grouped_by;
1008 @brief Free a list of SelectItems.
1009 @param sel Pointer to the first item in the list to be freed.
1011 Free the column alias and expression owned by each item. Put the entire list into a free
1012 list of SelectItems.
1014 static void selectListFree( SelectItem* sel ) {
1016 return; // Nothing to free
1018 SelectItem* first = sel;
1020 free( sel->column_alias );
1021 sel->column_alias = NULL;
1022 expressionFree( sel->expression );
1023 sel->expression = NULL;
1025 if( NULL == sel->next ) {
1026 sel->next = free_select_item_list;
1032 // Transfer the entire list to the free list
1033 free_select_item_list = first;
1037 @brief Given the name of a bind variable, build a corresponding BindVar.
1038 @param state Pointer to the query-building context.
1039 @param name Name of the bind variable.
1040 @return Pointer to the newly-built BindVar.
1042 Since the same bind variable may appear multiple times, we load it only once for the
1043 entire query, and reference the one copy wherever needed.
1045 static BindVar* getBindVar( BuildSQLState* state, const char* name ) {
1046 BindVar* bind = NULL;
1047 if( state->bindvar_list ) {
1048 bind = osrfHashGet( state->bindvar_list, name );
1050 return bind; // Already loaded it...
1053 // Load a BindVar from the Database.(after escaping any special characters)
1054 char* esc_str = strdup( name );
1055 dbi_conn_quote_string( state->dbhandle, &esc_str );
1057 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1058 "Unable to format bind variable name \"%s\"", name ));
1062 dbi_result result = dbi_conn_queryf( state->dbhandle,
1063 "SELECT name, type, description, default_value, label "
1064 "FROM query.bind_variable WHERE name = %s;", esc_str );
1067 if( dbi_result_first_row( result ) ) {
1068 bind = constructBindVar( state, result );
1070 PRINT( "Got a bind variable for %s\n", name );
1072 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1073 "Unable to load bind variable \"%s\"", name ));
1075 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1076 "No bind variable found with name \"%s\"", name ));
1081 int errnum = dbi_conn_error( state->dbhandle, &msg );
1082 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1083 "Unable to query query.bind_variable table for \"%s\": #%d %s",
1084 name, errnum, msg ? msg : "No description available" ));
1086 if( ! oilsIsDBConnected( state->dbhandle ))
1091 // Add the new bind variable to the list
1092 if( !state->bindvar_list ) {
1093 // Don't have a list yet? Start one.
1094 state->bindvar_list = osrfNewHash();
1095 osrfHashSetCallback( state->bindvar_list, bindVarFree );
1097 osrfHashSet( state->bindvar_list, bind, name );
1105 @brief Construct a BindVar to represent a bind variable.
1106 @param Pointer to the query-building context.
1107 @param result Database cursor positioned at a row in query.bind_variable.
1108 @return Pointer to a newly constructed BindVar, if successful, or NULL if not.
1110 The calling code is responsible for freeing the BindVar by calling bindVarFree().
1112 static BindVar* constructBindVar( BuildSQLState* state, dbi_result result ) {
1114 const char* name = dbi_result_get_string_idx( result, 1 );
1116 const char* type_str = dbi_result_get_string_idx( result, 2 );
1118 if( !strcmp( type_str, "string" ))
1120 else if( !strcmp( type_str, "number" ))
1122 else if( !strcmp( type_str, "string_list" ))
1123 type = BIND_STR_LIST;
1124 else if( !strcmp( type_str, "number_list" ))
1125 type = BIND_NUM_LIST;
1126 else { // Shouldn't happen due to database constraint...
1127 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1128 "Internal error: invalid bind variable type \"%s\" for bind variable \"%s\"",
1134 const char* description = dbi_result_get_string_idx( result, 3 );
1136 // The default value is encoded as JSON. Translate it into a jsonObject.
1137 const char* default_value_str = dbi_result_get_string_idx( result, 4 );
1138 jsonObject* default_value = NULL;
1139 if( default_value_str ) {
1140 default_value = jsonParse( default_value_str );
1141 if( !default_value ) {
1142 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1143 "Unable to parse JSON string for default value of bind variable \"%s\"",
1150 const char* label = dbi_result_get_string_idx( result, 5 );
1152 // Allocate a BindVar: from the free list if possible, from the heap if necessary
1153 BindVar* bind = NULL;
1154 if( free_bindvar_list ) {
1155 bind = free_bindvar_list;
1156 free_bindvar_list = free_bindvar_list->next;
1158 bind = safe_malloc( sizeof( BindVar ) );
1161 bind->name = strdup( name );
1162 bind->label = strdup( label );
1164 bind->description = strdup( description );
1165 bind->default_value = default_value;
1166 bind->actual_value = NULL;
1172 @brief Deallocate a BindVar.
1173 @param key Pointer to the bind variable name (not used).
1174 @param p Pointer to the BindVar to be deallocated, cast to a void pointer.
1176 Free the strings and jsonObjects owned by the BindVar, and put the BindVar itself into a
1179 This function is a callback installed in an osrfHash; hence the peculiar signature.
1181 static void bindVarFree( char* key, void* p ) {
1185 free( bind->label );
1186 free( bind->description );
1187 if( bind->default_value ) {
1188 jsonObjectFree( bind->default_value );
1189 bind->default_value = NULL;
1191 if( bind->actual_value ) {
1192 jsonObjectFree( bind->actual_value );
1193 bind->actual_value = NULL;
1196 // Prepend to free list
1197 bind->next = free_bindvar_list;
1198 free_bindvar_list = bind;
1203 @brief Given an id for a parent expression, build a list of CaseBranch structs.
1204 @param Pointer to the query-building context.
1205 @param id ID of a row in query.case_branch.
1206 @return Pointer to a newly-created CaseBranch if successful, or NULL if not.
1208 static CaseBranch* getCaseBranchList( BuildSQLState* state, int parent_id ) {
1209 CaseBranch* branch_list = NULL;
1210 dbi_result result = dbi_conn_queryf( state->dbhandle,
1211 "SELECT id, condition, result "
1212 "FROM query.case_branch WHERE parent_expr = %d "
1213 "ORDER BY seq_no desc;", parent_id );
1215 int condition_found = 0; // Boolean
1216 if( dbi_result_first_row( result ) ) {
1217 // Boolean: true for the first branch we encounter. That's actually the last
1218 // branch in the CASE, because we're reading them in reverse order. The point
1219 // is to enforce the rule that only the last branch can be an ELSE.
1222 CaseBranch* branch = constructCaseBranch( state, result );
1224 PRINT( "Found a case branch\n" );
1225 branch->next = branch_list;
1226 branch_list = branch;
1228 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1229 "Unable to build CASE branch for expression id #%d", parent_id ));
1230 freeBranchList( branch_list );
1235 if( branch->condition )
1236 condition_found = 1;
1238 sqlAddMsg( state, "ELSE branch # %d not at end of CASE expression # %d",
1239 branch->id, parent_id );
1240 freeBranchList( branch_list );
1246 if( !dbi_result_next_row( result ) )
1250 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1251 "No branches found for CASE expression %d", parent_id ));
1255 // Make sure that at least one branch includes a condition
1256 if( !condition_found ) {
1257 sqlAddMsg( state, "No conditional branch in CASE expression # %d", parent_id );
1258 freeBranchList( branch_list );
1263 int errnum = dbi_conn_error( state->dbhandle, &msg );
1264 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1265 "Unable to query query.case_branch table for parent expression # %d: %s",
1266 parent_id, errnum, msg ? msg : "No description available" ));
1268 if( ! oilsIsDBConnected( state->dbhandle ))
1275 static CaseBranch* constructCaseBranch( BuildSQLState* state, dbi_result result ) {
1276 int id = dbi_result_get_int_idx( result, 1 );
1279 if( dbi_result_field_is_null_idx( result, 2 ))
1282 condition_id = dbi_result_get_int_idx( result, 2 );
1284 Expression* condition = NULL;
1285 if( condition_id != -1 ) { // If it's -1, we have an ELSE condition
1286 condition = getExpression( state, condition_id );
1288 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1289 "Unable to build condition expression for case branch # %d", id ));
1295 int result_id = dbi_result_get_int_idx( result, 3 );
1297 Expression* result_p = NULL;
1298 if( -1 == result_id ) {
1299 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1300 "No result expression defined for case branch # %d", id ));
1303 expressionFree( condition );
1306 result_p = getExpression( state, result_id );
1308 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1309 "Unable to build result expression for case branch # %d", id ));
1312 expressionFree( condition );
1317 // Allocate a CaseBranch: from the free list if possible, from the heap if necessary
1318 CaseBranch* branch = NULL;
1319 if( free_branch_list ) {
1320 branch = free_branch_list;
1321 free_branch_list = free_branch_list->next;
1323 branch = safe_malloc( sizeof( CaseBranch ) );
1325 // Populate the new CaseBranch
1327 branch->condition = condition;
1328 branch->result = result_p;
1334 @brief Free a list of CaseBranches.
1335 @param branch Pointer to the first in a linked list of CaseBranches to be freed.
1337 Each CaseBranch goes onto a free list for potential reuse.
1339 static void freeBranchList( CaseBranch* branch ) {
1343 CaseBranch* first = branch;
1345 if( branch->condition ) {
1346 expressionFree( branch->condition );
1347 branch->condition = NULL;
1349 expressionFree( branch->result );
1350 branch->result = NULL;
1353 branch = branch->next;
1355 branch->next = free_branch_list;
1360 free_branch_list = first;
1364 @brief Given an id for a row in query.datatype, build an Datatype struct.
1365 @param Pointer to the query-building context.
1366 @param id ID of a row in query.datatype.
1367 @return Pointer to a newly-created Datatype if successful, or NULL if not.
1369 static Datatype* getDatatype( BuildSQLState* state, int id ) {
1370 Datatype* datatype = NULL;
1371 dbi_result result = dbi_conn_queryf( state->dbhandle,
1372 "SELECT id, datatype_name, is_numeric, is_composite "
1373 "FROM query.datatype WHERE id = %d", id );
1375 if( dbi_result_first_row( result ) ) {
1376 datatype = constructDatatype( state, result );
1378 PRINT( "Got a datatype\n" );
1379 PRINT( "\tid = %d\n", id );
1380 PRINT( "\tdatatype = %s\n", datatype->datatype_name );
1382 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1383 "Unable to construct a Datatype for id = %d", id ));
1385 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1386 "No datatype found for id = %d", id ));
1391 int errnum = dbi_conn_error( state->dbhandle, &msg );
1392 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1393 "Unable to query query.datatype table: #%d %s",
1394 errnum, msg ? msg : "No description available" ));
1396 if( ! oilsIsDBConnected( state->dbhandle ))
1403 @brief Construct a Datatype.
1404 @param Pointer to the query-building context.
1405 @param result Database cursor positioned at a row in query.datatype.
1406 @return Pointer to a newly constructed Datatype, if successful, or NULL if not.
1408 The calling code is responsible for freeing the Datatype by calling datatypeFree().
1410 static Datatype* constructDatatype( BuildSQLState* state, dbi_result result ) {
1411 int id = dbi_result_get_int_idx( result, 1 );
1412 const char* datatype_name = dbi_result_get_string_idx( result, 2 );
1413 int is_numeric = oils_result_get_bool_idx( result, 3 );
1414 int is_composite = oils_result_get_bool_idx( result, 4 );
1416 if( !datatype_name || !*datatype_name ) {
1417 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1418 "No datatype name provided for CAST expression # %d", id ));
1423 // Make sure that the datatype name is composed entirely of certain approved
1424 // characters. This check is not an attempt to validate the datatype name, but
1425 // only to prevent certain types of SQL injection.
1426 const char* p = datatype_name;
1428 unsigned char c = *p;
1440 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1441 "Invalid datatype name \"%s\" for datatype # %d; "
1442 "contains unexpected character \"%c\"", datatype_name, id, (char) c ));
1448 // Allocate a Datatype: from the free list if possible, from the heap if necessary
1449 Datatype* datatype = NULL;
1450 if( free_datatype_list ) {
1451 datatype = free_datatype_list;
1452 free_datatype_list = free_datatype_list->next;
1454 datatype = safe_malloc( sizeof( Datatype ) );
1457 datatype->datatype_name = strdup( datatype_name );
1458 datatype->is_numeric = is_numeric;
1459 datatype->is_composite = is_composite;
1465 @brief Free a Datatype.
1466 @param datatype Pointer to the Datatype to be freed.
1468 static void datatypeFree( Datatype* datatype ) {
1470 free( datatype->datatype_name );
1476 @brief Given an id for a row in query.expression, build an Expression struct.
1477 @param Pointer to the query-building context.
1478 @param id ID of a row in query.expression.
1479 @return Pointer to a newly-created Expression if successful, or NULL if not.
1481 static Expression* getExpression( BuildSQLState* state, int id ) {
1483 // Check the stack to see if the current expression is nested inside itself. If it is,
1484 // then abort in order to avoid infinite recursion. If it isn't, then add it to the
1485 // stack. (Make sure to pop it off the stack before returning.)
1486 if( searchIdStack( state->expr_stack, id, NULL )) {
1487 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1488 "Infinite recursion detected; expression # %d is nested within itself", id ));
1492 push_id( &state->expr_stack, id, NULL );
1494 Expression* exp = NULL;
1495 dbi_result result = dbi_conn_queryf( state->dbhandle,
1496 "SELECT exp.id, exp.type, exp.parenthesize, exp.parent_expr, exp.seq_no, "
1497 "exp.literal, exp.table_alias, exp.column_name, exp.left_operand, exp.operator, "
1498 "exp.right_operand, exp.subquery, exp.cast_type, exp.negate, exp.bind_variable, "
1499 "func.function_name "
1500 "FROM query.expression AS exp LEFT JOIN query.function_sig AS func "
1501 "ON (exp.function_id = func.id) "
1502 "WHERE exp.id = %d;", id );
1504 if( dbi_result_first_row( result ) ) {
1505 exp = constructExpression( state, result );
1507 PRINT( "Got an expression\n" );
1508 PRINT( "\tid = %d\n", exp->id );
1509 PRINT( "\ttype = %d\n", exp->type );
1510 PRINT( "\tparenthesize = %d\n", exp->parenthesize );
1511 PRINT( "\tcolumn_name = %s\n", exp->column_name ? exp->column_name : "(none)" );
1513 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1514 "Unable to construct an Expression for id = %d", id ));
1516 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1517 "No expression found for id = %d", id ));
1522 int errnum = dbi_conn_error( state->dbhandle, &msg );
1523 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1524 "Unable to query query.expression table: #%d %s",
1525 errnum, msg ? msg : "No description available" ));
1527 if( ! oilsIsDBConnected( state->dbhandle ))
1531 pop_id( &state->expr_stack );
1536 @brief Construct an Expression.
1537 @param Pointer to the query-building context.
1538 @param result Database cursor positioned at a row in query.expression.
1539 @return Pointer to a newly constructed Expression, if successful, or NULL if not.
1541 The calling code is responsible for freeing the Expression by calling expressionFree().
1543 static Expression* constructExpression( BuildSQLState* state, dbi_result result ) {
1545 int id = dbi_result_get_int_idx( result, 1 );
1546 const char* type_str = dbi_result_get_string_idx( result, 2 );
1549 if( !strcmp( type_str, "xbet" ))
1551 else if( !strcmp( type_str, "xbind" ))
1553 else if( !strcmp( type_str, "xbool" ))
1555 else if( !strcmp( type_str, "xcase" ))
1557 else if( !strcmp( type_str, "xcast" ))
1559 else if( !strcmp( type_str, "xcol" ))
1561 else if( !strcmp( type_str, "xex" ))
1563 else if( !strcmp( type_str, "xfunc" ))
1564 type = EXP_FUNCTION;
1565 else if( !strcmp( type_str, "xin" ))
1567 else if( !strcmp( type_str, "xisnull" ))
1569 else if( !strcmp( type_str, "xnull" ))
1571 else if( !strcmp( type_str, "xnum" ))
1573 else if( !strcmp( type_str, "xop" ))
1574 type = EXP_OPERATOR;
1575 else if( !strcmp( type_str, "xser" ))
1577 else if( !strcmp( type_str, "xstr" ))
1579 else if( !strcmp( type_str, "xsubq" ))
1580 type = EXP_SUBQUERY;
1582 type = EXP_NULL; // shouldn't happen due to database constraint
1584 int parenthesize = oils_result_get_bool_idx( result, 3 );
1587 if( dbi_result_field_is_null_idx( result, 4 ))
1588 parent_expr_id = -1;
1590 parent_expr_id = dbi_result_get_int_idx( result, 4 );
1592 int seq_no = dbi_result_get_int_idx( result, 5 );
1593 const char* literal = dbi_result_get_string_idx( result, 6 );
1594 const char* table_alias = dbi_result_get_string_idx( result, 7 );
1595 const char* column_name = dbi_result_get_string_idx( result, 8 );
1597 int left_operand_id;
1598 if( dbi_result_field_is_null_idx( result, 9 ))
1599 left_operand_id = -1;
1601 left_operand_id = dbi_result_get_int_idx( result, 9 );
1603 const char* operator = dbi_result_get_string_idx( result, 10 );
1605 int right_operand_id;
1606 if( dbi_result_field_is_null_idx( result, 11 ))
1607 right_operand_id = -1;
1609 right_operand_id = dbi_result_get_int_idx( result, 11 );
1612 if( dbi_result_field_is_null_idx( result, 12 ))
1615 subquery_id = dbi_result_get_int_idx( result, 12 );
1618 if( dbi_result_field_is_null_idx( result, 13 ))
1621 cast_type_id = dbi_result_get_int_idx( result, 13 );
1623 int negate = oils_result_get_bool_idx( result, 14 );
1624 const char* bind_variable = dbi_result_get_string_idx( result, 15 );
1625 const char* function_name = dbi_result_get_string_idx( result, 16 );
1627 Expression* left_operand = NULL;
1628 Expression* right_operand = NULL;
1629 StoredQ* subquery = NULL;
1630 Datatype* cast_type = NULL;
1631 BindVar* bind = NULL;
1632 CaseBranch* branch_list = NULL;
1633 Expression* subexp_list = NULL;
1635 if( EXP_BETWEEN == type ) {
1636 // Get the left operand
1637 if( -1 == left_operand_id ) {
1638 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1639 "No left operand defined for BETWEEN expression # %d", id ));
1643 left_operand = getExpression( state, left_operand_id );
1644 if( !left_operand ) {
1645 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1646 "Unable to get left operand in BETWEEN expression # %d", id ));
1652 // Get the end points of the BETWEEN range
1653 subexp_list = getExpressionList( state, id );
1654 if( state->error ) {
1655 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1656 "Unable to get subexpressions for BETWEEN expression # %d", id ));
1657 expressionFree( left_operand );
1659 } else if( !subexp_list ) {
1660 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1661 "BETWEEN range is empty in expression # %d", id ));
1663 expressionFree( left_operand );
1665 } else if( !subexp_list->next ) {
1666 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1667 "BETWEEN range has only one end point in expression # %d", id ));
1669 expressionListFree( subexp_list );
1670 expressionFree( left_operand );
1672 } else if( subexp_list->next->next ) {
1673 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1674 "BETWEEN range has more than two subexpressions in expression # %d", id ));
1676 expressionListFree( subexp_list );
1677 expressionFree( left_operand );
1681 } else if( EXP_BIND == type ) {
1682 if( bind_variable ) {
1683 // To do: Build a BindVar
1684 bind = getBindVar( state, bind_variable );
1686 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1687 "Unable to load bind variable \"%s\" for expression # %d",
1688 bind_variable, id ));
1692 PRINT( "\tBind variable is \"%s\"\n", bind_variable );
1694 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1695 "No variable specified for bind variable expression # %d",
1696 bind_variable, id ));
1700 if( right_operand_id != -1 ) {
1701 right_operand = getExpression( state, right_operand_id );
1702 if( !right_operand ) {
1703 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1704 "Unable to get right operand in expression # %d", id ));
1706 expressionFree( left_operand );
1711 } else if( EXP_CASE == type ) {
1712 // Get the left operand
1713 if( -1 == left_operand_id ) {
1714 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1715 "No left operand defined for CASE expression # %d", id ));
1719 left_operand = getExpression( state, left_operand_id );
1720 if( !left_operand ) {
1721 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1722 "Unable to get left operand in CASE expression # %d", id ));
1728 branch_list = getCaseBranchList( state, id );
1729 if( ! branch_list ) {
1730 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1731 "Unable to build branches for CASE expression # %d", id ));
1736 } else if( EXP_CAST == type ) {
1737 // Get the left operand
1738 if( -1 == left_operand_id ) {
1739 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1740 "No left operand defined for CAST expression # %d", id ));
1744 left_operand = getExpression( state, left_operand_id );
1745 if( !left_operand ) {
1746 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1747 "Unable to get left operand for CAST expression # %d", id ));
1753 if( -1 == cast_type_id ) {
1754 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1755 "No datatype specified for CAST expression # %d", id ));
1759 cast_type = getDatatype( state, cast_type_id );
1761 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1762 "Unable to get datatype for CAST expression # %d", id ));
1768 } else if( EXP_COLUMN == type ) {
1769 if( column_name && !is_identifier( column_name )) {
1770 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1771 "Column name \"%s\" is invalid identifier for expression # %d",
1777 } else if( EXP_EXIST == type ) {
1778 if( -1 == subquery_id ) {
1779 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1780 "Internal error: No subquery found for EXIST expression # %d", id ));
1784 subquery = getStoredQuery( state, subquery_id );
1786 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1787 "Unable to load subquery for EXIST expression # %d", id ));
1793 } else if( EXP_FUNCTION == type ) {
1794 if( !function_name ) {
1795 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1796 "Function call expression # %d provides no function name", id ));
1799 } else if( !is_identifier( function_name )) {
1800 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1801 "Function call expression # %d contains an invalid function name \"%s\"",
1802 id, function_name ));
1806 subexp_list = getExpressionList( state, id );
1807 if( state->error ) {
1808 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1809 "Unable to get parameter list for function expression # %d", id ));
1814 } else if( EXP_IN == type ) {
1815 if( -1 == left_operand_id ) {
1816 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1817 "IN condition has no left operand in expression # %d", id ));
1821 left_operand = getExpression( state, left_operand_id );
1822 if( !left_operand ) {
1823 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1824 "Unable to get left operand for IN condition in expression # %d", id ));
1830 if( -1 == subquery_id ) {
1831 // Load an IN list of subexpressions
1832 subexp_list = getExpressionList( state, id );
1833 if( state->error ) {
1834 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1835 "Unable to get subexpressions for IN list" ));
1837 } else if( !subexp_list ) {
1838 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1839 "IN list is empty in expression # %d", id ));
1845 subquery = getStoredQuery( state, subquery_id );
1847 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1848 "Unable to load subquery for IN expression # %d", id ));
1854 } else if( EXP_ISNULL == type ) {
1855 if( -1 == left_operand_id ) {
1856 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1857 "Expression # %d IS NULL has no left operand", id ));
1862 if( left_operand_id != -1 ) {
1863 left_operand = getExpression( state, left_operand_id );
1864 if( !left_operand ) {
1865 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1866 "Unable to get left operand in expression # %d", id ));
1872 } else if( EXP_NUMBER == type ) {
1874 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1875 "Numeric expression # %d provides no numeric value", id ));
1878 } else if( !jsonIsNumeric( literal )) {
1879 // Purported number is not numeric. We use JSON rules here to determine what
1880 // a valid number is, just because we already have a function lying around that
1881 // can do that validation. PostgreSQL probably doesn't use the same exact rules.
1882 // If that's a problem, we can write a separate validation routine to follow
1883 // PostgreSQL's rules, once we figure out what those rules are.
1884 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1885 "Numeric expression # %d is not a valid number: \"%s\"", id, literal ));
1890 } else if( EXP_OPERATOR == type ) {
1891 // Make sure we have a plausible operator
1893 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1894 "Operator expression # %d has no operator", id ));
1897 } else if( !is_good_operator( operator )) {
1898 // The specified operator contains one or more characters that aren't allowed
1899 // in an operator. This isn't a true validation; it's just a protective
1900 // measure to prevent certain kinds of sql injection.
1901 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1902 "Operator expression # %d contains invalid operator \"%s\"", id, operator ));
1907 // Load left and/or right operands
1908 if( -1 == left_operand_id && -1 == right_operand_id ) {
1909 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1910 "Expression # %d is an operator with no operands", id ));
1915 if( left_operand_id != -1 ) {
1916 left_operand = getExpression( state, left_operand_id );
1917 if( !left_operand ) {
1918 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1919 "Unable to get left operand in expression # %d", id ));
1925 if( right_operand_id != -1 ) {
1926 right_operand = getExpression( state, right_operand_id );
1927 if( !right_operand ) {
1928 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1929 "Unable to get right operand in expression # %d", id ));
1935 } else if( EXP_SERIES == type ) {
1936 subexp_list = getExpressionList( state, id );
1937 if( state->error ) {
1938 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1939 "Unable to get subexpressions for expression series using operator \"%s\"",
1940 operator ? operator : "," ));
1942 } else if( !subexp_list ) {
1943 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1944 "Series expression is empty in expression # %d", id ));
1947 } else if( operator && !is_good_operator( operator )) {
1948 // The specified operator contains one or more characters that aren't allowed
1949 // in an operator. This isn't a true validation; it's just a protective
1950 // measure to prevent certain kinds of sql injection.
1951 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1952 "Series expression # %d contains invalid operator \"%s\"", id, operator ));
1957 } else if( EXP_STRING == type ) {
1959 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1960 "String expression # %d provides no string value", id ));
1965 } else if( EXP_SUBQUERY == type ) {
1966 if( -1 == subquery_id ) {
1967 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1968 "Subquery expression # %d has no query id", id ));
1972 // Load a subquery, if there is one
1973 subquery = getStoredQuery( state, subquery_id );
1975 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1976 "Unable to load subquery for expression # %d", id ));
1980 if( subquery->select_list && subquery->select_list->next ) {
1981 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1982 "Subquery # %d as expression returns more than one column", subquery_id ));
1986 PRINT( "\tExpression is subquery %d\n", subquery_id );
1990 // Allocate an Expression: from the free list if possible, from the heap if necessary
1991 Expression* exp = NULL;
1992 if( free_expression_list ) {
1993 exp = free_expression_list;
1994 free_expression_list = free_expression_list->next;
1996 exp = safe_malloc( sizeof( Expression ) );
1998 // Populate the Expression
2002 exp->parenthesize = parenthesize;
2003 exp->parent_expr_id = parent_expr_id;
2004 exp->seq_no = seq_no;
2005 exp->literal = literal ? strdup( literal ) : NULL;
2006 exp->table_alias = table_alias ? strdup( table_alias ) : NULL;
2007 exp->column_name = column_name ? strdup( column_name ) : NULL;
2008 exp->left_operand = left_operand;
2009 exp->op = operator ? strdup( operator ) : NULL;
2010 exp->right_operand = right_operand;
2011 exp->subquery_id = subquery_id;
2012 exp->subquery = subquery;
2013 exp->cast_type = cast_type;
2014 exp->negate = negate;
2016 exp->branch_list = branch_list;
2017 exp->subexp_list = subexp_list;
2018 exp->function_name = function_name ? strdup( function_name ) : NULL;
2024 @brief Free all the Expressions in a linked list of Expressions.
2025 @param exp Pointer to the first Expression in the list.
2027 static void expressionListFree( Expression* exp ) {
2029 Expression* next = exp->next;
2030 expressionFree( exp );
2036 @brief Deallocate an Expression.
2037 @param exp Pointer to the Expression to be deallocated.
2039 Free the strings owned by the Expression. Put the Expression itself, and any
2040 subexpressions that it owns, into a free list.
2042 static void expressionFree( Expression* exp ) {
2044 free( exp->literal );
2045 exp->literal = NULL;
2046 free( exp->table_alias );
2047 exp->table_alias = NULL;
2048 free( exp->column_name );
2049 exp->column_name = NULL;
2050 if( exp->left_operand ) {
2051 expressionFree( exp->left_operand );
2052 exp->left_operand = NULL;
2058 if( exp->right_operand ) {
2059 expressionFree( exp->right_operand );
2060 exp->right_operand = NULL;
2062 if( exp->subquery ) {
2063 storedQFree( exp->subquery );
2064 exp->subquery = NULL;
2066 if( exp->cast_type ) {
2067 datatypeFree( exp->cast_type );
2068 exp->cast_type = NULL;
2071 // We don't free the bind member here because the Expression doesn't own it;
2072 // the bindvar_list hash owns it, so that multiple Expressions can reference it.
2074 if( exp->subexp_list ) {
2075 // Free the linked list of subexpressions
2076 expressionListFree( exp->subexp_list );
2077 exp->subexp_list = NULL;
2080 if( exp->branch_list ) {
2081 freeBranchList( exp->branch_list );
2082 exp->branch_list = NULL;
2085 if( exp->function_name ) {
2086 free( exp->function_name );
2087 exp->function_name = NULL;
2090 // Prepend to the free list
2091 exp->next = free_expression_list;
2092 free_expression_list = exp;
2097 @brief Build a list of subexpressions.
2098 @param state Pointer to the query-building context.
2099 @param id ID of the parent Expression.
2100 @return A pointer to the first in a linked list of Expressions, if there are any; or
2101 NULL if there aren't any, or in case of an error.
2103 static Expression* getExpressionList( BuildSQLState* state, int id ) {
2104 Expression* exp_list = NULL;
2106 // The ORDER BY is in descending order so that we can build the list by adding to
2107 // the head, and it will wind up in the right order.
2108 dbi_result result = dbi_conn_queryf( state->dbhandle,
2109 "SELECT exp.id, exp.type, exp.parenthesize, exp.parent_expr, exp.seq_no, "
2110 "exp.literal, exp.table_alias, exp.column_name, exp.left_operand, exp.operator, "
2111 "exp.right_operand, exp.subquery, exp.cast_type, exp.negate, exp.bind_variable, "
2112 "func.function_name "
2113 "FROM query.expression AS exp LEFT JOIN query.function_sig AS func "
2114 "ON (exp.function_id = func.id) "
2115 "WHERE exp.parent_expr = %d "
2116 "ORDER BY exp.seq_no desc;", id );
2119 if( dbi_result_first_row( result ) ) {
2121 Expression* exp = constructExpression( state, result );
2123 PRINT( "Found a subexpression\n" );
2124 exp->next = exp_list;
2127 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
2128 "Unable to build subexpression list for expression id #%d", id ));
2129 expressionListFree( exp_list );
2133 if( !dbi_result_next_row( result ) )
2139 int errnum = dbi_conn_error( state->dbhandle, &msg );
2140 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
2141 "Unable to query query.expression table for expression list: #%d %s",
2142 errnum, msg ? msg : "No description available" ));
2144 if( ! oilsIsDBConnected( state->dbhandle ))
2152 @brief Build a list of ORDER BY items as a linked list of OrderItems.
2153 @param state Pointer to the query-building context.
2154 @param query_id ID for the query to which the ORDER BY belongs.
2155 @return Pointer to the first node in a linked list of OrderItems.
2157 The calling code is responsible for freeing the list by calling orderItemListFree().
2159 static OrderItem* getOrderByList( BuildSQLState* state, int query_id ) {
2160 OrderItem* ord_list = NULL;
2162 // The ORDER BY is in descending order so that we can build the list by adding to
2163 // the head, and it will wind up in the right order.
2164 dbi_result result = dbi_conn_queryf( state->dbhandle,
2165 "SELECT id, stored_query, seq_no, expression "
2166 "FROM query.order_by_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
2168 if( dbi_result_first_row( result ) ) {
2170 OrderItem* item = constructOrderItem( state, result );
2172 PRINT( "Found an ORDER BY item\n" );
2174 item->next = ord_list;
2177 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
2178 "Unable to build ORDER BY item for query id #%d", query_id ));
2179 orderItemListFree( ord_list );
2183 if( !dbi_result_next_row( result ) )
2189 int errnum = dbi_conn_error( state->dbhandle, &msg );
2190 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
2191 "Unable to query query.order_by_list table: #%d %s",
2192 errnum, msg ? msg : "No description available" ));
2194 if( ! oilsIsDBConnected( state->dbhandle ))
2202 @brief Construct an OrderItem.
2203 @param Pointer to the query-building context.
2204 @param result Database cursor positioned at a row in query.order_by_item.
2205 @return Pointer to a newly constructed OrderItem, if successful, or NULL if not.
2207 The calling code is responsible for freeing the OrderItems by calling orderItemListFree().
2209 static OrderItem* constructOrderItem( BuildSQLState* state, dbi_result result ) {
2210 int id = dbi_result_get_int_idx( result, 1 );
2211 int stored_query_id = dbi_result_get_int_idx( result, 2 );
2212 int seq_no = dbi_result_get_int_idx( result, 3 );
2213 int expression_id = dbi_result_get_int_idx( result, 4 );
2214 // Allocate a SelectItem: from the free list if possible, from the heap if necessary
2216 // Construct an Expression
2217 Expression* expression = getExpression( state, expression_id );
2219 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
2220 "Unable to fetch ORDER BY expression for id = %d", expression_id ));
2225 // Allocate an OrderItem; from the free list if possible, or from the heap if necessary.
2227 if( free_order_item_list ) {
2228 ord = free_order_item_list;
2229 free_order_item_list = free_order_item_list->next;
2231 ord = safe_malloc( sizeof( OrderItem ));
2235 ord->stored_query_id = stored_query_id;
2236 ord->seq_no = seq_no;
2237 ord->expression = expression;
2243 @brief Deallocate a linked list of OrderItems.
2244 @param exp Pointer to the first OrderItem in the list to be deallocated.
2246 Deallocate the memory owned by the OrderItems. Put the items themselves into a free list.
2248 static void orderItemListFree( OrderItem* ord ) {
2250 return; // Nothing to free
2252 OrderItem* first = ord;
2254 expressionFree( ord->expression );
2255 ord->expression = NULL;
2257 if( NULL == ord->next ) {
2258 ord->next = free_order_item_list;
2264 // Transfer the entire list to the free list
2265 free_order_item_list = first;
2269 @brief Push an IdNode onto a stack of IdNodes.
2270 @param stack Pointer to the stack.
2271 @param id Id of the new node.
2272 @param alias Alias, if any, of the new node.
2274 static void push_id( IdNode** stack, int id, const char* alias ) {
2277 // Allocate a node; from the free list if possible, from the heap if necessary.
2278 IdNode* node = NULL;
2279 if( free_id_node_list ) {
2280 node = free_id_node_list;
2281 free_id_node_list = free_id_node_list->next;
2283 node = safe_malloc( sizeof( IdNode ));
2286 node->next = *stack;
2289 node->alias = strdup( alias );
2299 @brief Remove the node at the top of an IdNode stack.
2300 @param stack Pointer to the IdNode stack.
2302 void pop_id( IdNode** stack ) {
2304 IdNode* node = *stack;
2305 *stack = node->next;
2308 free( node->alias );
2312 node->next = free_id_node_list;
2313 free_id_node_list = node;
2318 @brief Search a stack of IDs for a match by either ID or, optionally, by alias.
2319 @param stack Pointer to the stack.
2320 @param id The id to search for.
2321 @param alias (Optional) the alias to search for.
2322 @return A pointer to the matching node if one is found, or NULL if not.
2324 This search is used to detect cases where a query, expression, or FROM clause is nested
2325 inside itself, in order to avoid infinite recursion; or in order to avoid conflicting
2326 table references in a FROM clause.
2328 static const IdNode* searchIdStack( const IdNode* stack, int id, const char* alias ) {
2330 const IdNode* node = stack;
2332 if( node->id == id )
2333 return node; // Matched on id
2334 else if( alias && node->alias && !strcmp( alias, node->alias ))
2335 return node; // Matched on alias
2340 return NULL; // No match found
2344 @brief Free up any resources held by the StoredQ module.
2346 void storedQCleanup( void ) {
2348 // Free all the nodes in the free state list
2349 StoredQ* sq = free_storedq_list;
2351 free_storedq_list = sq->next;
2353 sq = free_storedq_list;
2356 // Free all the nodes in the free from_relation list
2357 FromRelation* fr = free_from_relation_list;
2359 free_from_relation_list = fr->next;
2361 fr = free_from_relation_list;
2364 // Free all the nodes in the free expression list
2365 Expression* exp = free_expression_list;
2367 free_expression_list = exp->next;
2369 exp = free_expression_list;
2372 // Free all the nodes in the free select item list
2373 SelectItem* sel = free_select_item_list;
2375 free_select_item_list = sel->next;
2377 sel = free_select_item_list;
2380 // Free all the nodes in the free select item list
2381 IdNode* node = free_id_node_list;
2383 free_id_node_list = node->next;
2385 node = free_id_node_list;
2388 // Free all the nodes in the free query sequence list
2389 QSeq* seq = free_qseq_list;
2391 free_qseq_list = seq->next;
2393 seq = free_qseq_list;
2396 // Free all the nodes in the free order item list
2397 OrderItem* ord = free_order_item_list;
2399 free_order_item_list = ord->next;
2401 ord = free_order_item_list;
2404 // Free all the nodes in the bind variable free list
2405 BindVar* bind = free_bindvar_list;
2407 free_bindvar_list = bind->next;
2409 bind = free_bindvar_list;
2412 // Free all the nodes in the case branch free list
2413 CaseBranch* branch = free_branch_list;
2415 free_branch_list = branch->next;
2417 branch = free_branch_list;
2420 // Free all the nodes in the datatype free list
2421 Datatype* datatype = free_datatype_list;
2423 free_datatype_list = datatype->next;
2425 datatype = free_datatype_list;
2430 @brief Return a boolean value from a database result.
2431 @param result The database result.
2432 @param i Index of the column in the result, starting with 1 );
2433 @return 1 if true, or 0 for false.
2435 Null values and error conditions are interpreted as FALSE.
2437 static int oils_result_get_bool_idx( dbi_result result, int i ) {
2439 const char* str = dbi_result_get_string_idx( result, i );
2440 return (str && *str == 't' ) ? 1 : 0;
2446 @brief Enable verbose messages.
2448 The messages are written to standard output, which for a server is /dev/null. Hence this
2449 option is useful only for a non-server. It is intended only as a convenience for
2450 development and debugging.
2452 void oilsStoredQSetVerbose( void ) {