3 @brief Translate an abstract representation of a query into an SQL statement.
10 #include "opensrf/utils.h"
11 #include "opensrf/string_array.h"
12 #include "opensrf/osrf_hash.h"
13 #include "opensrf/osrf_application.h"
14 #include "openils/oils_idl.h"
15 #include "openils/oils_sql.h"
16 #include "openils/oils_buildq.h"
18 static void build_Query( BuildSQLState* state, const StoredQ* query );
19 static void buildCombo( BuildSQLState* state, const StoredQ* query, const char* type_str );
20 static void buildSelect( BuildSQLState* state, const StoredQ* query );
21 static void buildFrom( BuildSQLState* state, const FromRelation* core_from );
22 static void buildJoin( BuildSQLState* state, const FromRelation* join );
23 static void buildSelectList( BuildSQLState* state, const SelectItem* item );
24 static void buildGroupBy( BuildSQLState* state, const SelectItem* sel_list );
25 static void buildOrderBy( BuildSQLState* state, const OrderItem* ord_list );
26 static void buildCase( BuildSQLState* state, const Expression* expr );
27 static void buildExpression( BuildSQLState* state, const Expression* expr );
28 static void buildFunction( BuildSQLState* state, const Expression* exp );
29 static void buildSeries( BuildSQLState* state, const Expression* subexp_list, const char* op );
30 static void buildBindVar( BuildSQLState* state, const BindVar* bind );
31 static void buildScalar( BuildSQLState* state, int numeric, const jsonObject* obj );
33 static void add_newline( BuildSQLState* state );
34 static inline void incr_indent( BuildSQLState* state );
35 static inline void decr_indent( BuildSQLState* state );
38 @brief Create a jsonObject representing the current list of bind variables.
39 @param bindvar_list Pointer to the bindvar_list member of a BuildSQLState.
40 @return Pointer to the newly created jsonObject.
42 The returned jsonObject is a (possibly empty) JSON_HASH, keyed on the names of the bind
43 variables. The data for each is another level of JSON_HASH with a fixed set of tags:
47 - "default_value" (as a jsonObject)
48 - "actual_value" (as a jsonObject)
50 Any non-existent values are represented as JSON_NULLs.
52 The calling code is responsible for freeing the returned jsonOjbect by calling
55 jsonObject* oilsBindVarList( osrfHash* bindvar_list ) {
56 jsonObject* list = jsonNewObjectType( JSON_HASH );
58 if( bindvar_list && osrfHashGetCount( bindvar_list )) {
59 // Traverse our internal list of bind variables
61 osrfHashIterator* iter = osrfNewHashIterator( bindvar_list );
62 while(( bind = osrfHashIteratorNext( iter ))) {
63 // Create an hash to represent the bind variable
64 jsonObject* bind_obj = jsonNewObjectType( JSON_HASH );
66 // Add an entry for each attribute
67 jsonObject* attr = jsonNewObject( bind->label );
68 jsonObjectSetKey( bind_obj, "label", attr );
70 const char* type = NULL;
71 switch( bind->type ) {
88 attr = jsonNewObject( type );
89 jsonObjectSetKey( bind_obj, "type", attr );
91 attr = jsonNewObject( bind->description );
92 jsonObjectSetKey( bind_obj, "description", attr );
94 attr = jsonObjectClone( bind->default_value );
95 jsonObjectSetKey( bind_obj, "default_value", attr );
97 attr = jsonObjectClone( bind->actual_value );
98 jsonObjectSetKey( bind_obj, "actual_value", attr );
100 // Add the bind variable to the list
101 jsonObjectSetKey( list, osrfHashIteratorKey( iter ), bind_obj );
103 osrfHashIteratorFree( iter );
110 @brief Apply values to bind variables, overriding the defaults, if any.
111 @param state Pointer to the query-building context.
112 @param bindings A JSON_HASH of values.
113 @return 0 if successful, or 1 if not.
115 The @a bindings parameter must be a JSON_HASH. The keys are the names of bind variables.
116 The values are the corresponding values for the variables.
118 int oilsApplyBindValues( BuildSQLState* state, const jsonObject* bindings ) {
120 osrfLogError( OSRF_LOG_MARK, "NULL pointer to state" );
122 } else if( !bindings ) {
123 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
124 "Internal error: No pointer to bindings" ));
126 } else if( bindings->type != JSON_HASH ) {
127 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
128 "Internal error: bindings parameter is not a JSON_HASH" ));
133 jsonObject* value = NULL;
134 jsonIterator* iter = jsonNewIterator( bindings );
135 while(( value = jsonIteratorNext( iter ))) {
136 const char* var_name = iter->key;
137 BindVar* bind = osrfHashGet( state->bindvar_list, var_name );
139 // Apply or replace the value for the specified variable
140 if( bind->actual_value )
141 jsonObjectFree( bind->actual_value );
142 bind->actual_value = jsonObjectClone( value );
144 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
145 "Can't assign value to bind variable \"%s\": no such variable", var_name ));
149 jsonIteratorFree( iter );
155 @brief Build an SQL query.
156 @param state Pointer to the query-building context.
157 @param query Pointer to the query to be built.
158 @return Zero if successful, or 1 if not.
160 Clear the output buffer, call build_Query() to do the work, and add a closing semicolon.
162 int buildSQL( BuildSQLState* state, const StoredQ* query ) {
164 buffer_reset( state->sql );
166 build_Query( state, query );
167 if( ! state->error ) {
168 // Remove the trailing space, if there is one, and add a semicolon.
169 char c = buffer_chomp( state->sql );
171 buffer_add_char( state->sql, c ); // oops, not a space; put it back
172 buffer_add( state->sql, ";\n" );
178 @brief Build an SQL query, appending it to what has been built so far.
179 @param state Pointer to the query-building context.
180 @param query Pointer to the query to be built.
182 Look at the query type and branch to the corresponding routine.
184 static void build_Query( BuildSQLState* state, const StoredQ* query ) {
185 if( buffer_length( state->sql ))
186 add_newline( state );
188 switch( query->type ) {
190 buildSelect( state, query );
193 buildCombo( state, query, "UNION" );
196 buildCombo( state, query, "INTERSECT" );
199 buildCombo( state, query, "EXCEPT" );
202 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
203 "Internal error: invalid query type %d in query # %d",
204 query->type, query->id ));
211 @brief Build a UNION, INTERSECT, or EXCEPT query.
212 @param state Pointer to the query-building context.
213 @param query Pointer to the query to be built.
214 @param type_str The query type, as a string.
216 static void buildCombo( BuildSQLState* state, const StoredQ* query, const char* type_str ) {
218 QSeq* seq = query->child_list;
220 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
221 "Internal error: No child queries within %s query # %d",
222 type_str, query->id ));
227 // Traverse the list of child queries
229 build_Query( state, seq->child_query );
231 sqlAddMsg( state, "Unable to build child query # %d within %s query %d",
232 seq->child_query->id, type_str, query->id );
237 add_newline( state );
238 buffer_add( state->sql, type_str );
239 buffer_add_char( state->sql, ' ' );
241 buffer_add( state->sql, "ALL " );
249 @brief Build a SELECT statement.
250 @param state Pointer to the query-building context.
251 @param query Pointer to the StoredQ structure that represents the query.
253 static void buildSelect( BuildSQLState* state, const StoredQ* query ) {
255 FromRelation* from_clause = query->from_clause;
257 sqlAddMsg( state, "SELECT has no FROM clause in query # %d", query->id );
262 // To do: get SELECT list; just a stub here
263 buffer_add( state->sql, "SELECT" );
264 incr_indent( state );
265 buildSelectList( state, query->select_list );
267 sqlAddMsg( state, "Unable to build SELECT list for query # %d", query->id );
271 decr_indent( state );
273 // Build FROM clause, if there is one
274 if( query->from_clause ) {
275 buildFrom( state, query->from_clause );
277 sqlAddMsg( state, "Unable to build FROM clause for query # %d", query->id );
283 // Build WHERE clause, if there is one
284 if( query->where_clause ) {
285 add_newline( state );
286 buffer_add( state->sql, "WHERE" );
287 incr_indent( state );
288 add_newline( state );
289 buildExpression( state, query->where_clause );
291 sqlAddMsg( state, "Unable to build WHERE clause for query # %d", query->id );
295 decr_indent( state );
298 // Build GROUP BY clause, if there is one
299 buildGroupBy( state, query->select_list );
301 // Build HAVING clause, if there is one
302 if( query->having_clause ) {
303 add_newline( state );
304 buffer_add( state->sql, "HAVING" );
305 incr_indent( state );
306 add_newline( state );
307 buildExpression( state, query->having_clause );
309 sqlAddMsg( state, "Unable to build HAVING clause for query # %d", query->id );
313 decr_indent( state );
316 // Build ORDER BY clause, if there is one
317 if( query->order_by_list ) {
318 buildOrderBy( state, query->order_by_list );
320 sqlAddMsg( state, "Unable to build ORDER BY clause for query # %d", query->id );
326 // To do: Build LIMIT clause, if there is one
328 // To do: Build OFFSET clause, if there is one
334 @brief Build a FROM clause.
335 @param Pointer to the query-building context.
336 @param Pointer to the StoredQ query to which the FROM clause belongs.
338 static void buildFrom( BuildSQLState* state, const FromRelation* core_from ) {
340 add_newline( state );
341 buffer_add( state->sql, "FROM" );
342 incr_indent( state );
343 add_newline( state );
345 switch( core_from->type ) {
346 case FRT_RELATION : {
347 char* relation = core_from->table_name;
349 if( !core_from->class_name ) {
350 sqlAddMsg( state, "No relation specified for core relation # %d",
356 // Look up table name, view name, or source_definition in the IDL
357 osrfHash* class_hash = osrfHashGet( oilsIDL(), core_from->class_name );
358 relation = oilsGetRelation( class_hash );
362 buffer_add( state->sql, relation );
363 if( !core_from->table_name )
364 free( relation ); // In this case we strdup'd it, must free it
368 buffer_add_char( state->sql, '(' );
369 incr_indent( state );
370 build_Query( state, core_from->subquery );
371 decr_indent( state );
372 add_newline( state );
373 buffer_add_char( state->sql, ')' );
376 buildFunction( state, core_from->function_call );
377 if ( state->error ) {
379 "Unable to include function call # %d in FROM relation # %d",
380 core_from->function_call->id, core_from->id );
385 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
386 "Internal error: Invalid type # %d in FROM relation # %d",
387 core_from->type, core_from->id ));
392 // Add a table alias, if possible
393 if( core_from->table_alias ) {
394 buffer_add( state->sql, " AS \"" );
395 buffer_add( state->sql, core_from->table_alias );
396 buffer_add( state->sql, "\" " );
398 else if( core_from->class_name ) {
399 buffer_add( state->sql, " AS \"" );
400 buffer_add( state->sql, core_from->class_name );
401 buffer_add( state->sql, "\" " );
403 buffer_add_char( state->sql, ' ' );
405 incr_indent( state );
406 FromRelation* join = core_from->join_list;
408 buildJoin( state, join );
410 sqlAddMsg( state, "Unable to build JOIN clause(s) for relation # %d",
416 decr_indent( state );
417 decr_indent( state );
421 @brief Add a JOIN clause.
422 @param state Pointer to the query-building context.
423 @param join Pointer to the FromRelation representing the JOIN to be added.
425 static void buildJoin( BuildSQLState* state, const FromRelation* join ) {
426 add_newline( state );
427 switch( join->join_type ) {
429 sqlAddMsg( state, "Non-join relation # %d in JOIN clause", join->id );
433 buffer_add( state->sql, "INNER JOIN " );
436 buffer_add( state->sql, "LEFT JOIN " );
439 buffer_add( state->sql, "RIGHT JOIN " );
442 buffer_add( state->sql, "FULL JOIN " );
445 sqlAddMsg( state, "Unrecognized join type in relation # %d", join->id );
450 switch( join->type ) {
453 if( !join->table_name || ! *join->table_name ) {
454 sqlAddMsg( state, "No relation designated for relation # %d", join->id );
458 buffer_add( state->sql, join->table_name );
462 if( !join->subquery ) {
463 sqlAddMsg( state, "Subquery expected, not found for relation # %d", join->id );
466 } else if( !join->table_alias ) {
467 sqlAddMsg( state, "No table alias for subquery in FROM relation # %d",
472 buffer_add_char( state->sql, '(' );
473 incr_indent( state );
474 build_Query( state, join->subquery );
475 decr_indent( state );
476 add_newline( state );
477 buffer_add_char( state->sql, ')' );
480 if( !join->table_name || ! *join->table_name ) {
481 sqlAddMsg( state, "Joins to functions not yet supported in relation # %d",
489 const char* effective_alias = join->table_alias;
490 if( !effective_alias )
491 effective_alias = join->class_name;
493 if( effective_alias ) {
494 buffer_add( state->sql, " AS \"" );
495 buffer_add( state->sql, effective_alias );
496 buffer_add_char( state->sql, '\"' );
499 if( join->on_clause ) {
500 incr_indent( state );
501 add_newline( state );
502 buffer_add( state->sql, "ON " );
503 buildExpression( state, join->on_clause );
504 decr_indent( state );
507 FromRelation* subjoin = join->join_list;
509 buildJoin( state, subjoin );
511 sqlAddMsg( state, "Unable to build JOIN clause(s) for relation # %d", join->id );
514 subjoin = subjoin->next;
519 @brief Build a SELECT list.
520 @param state Pointer to the query-building context.
521 @param item Pointer to the first in a linked list of SELECT items.
523 static void buildSelectList( BuildSQLState* state, const SelectItem* item ) {
528 buffer_add_char( state->sql, ',' );
529 add_newline( state );
530 buildExpression( state, item->expression );
532 sqlAddMsg( state, "Unable to build an expression for SELECT item # %d", item->id );
537 if( item->column_alias ) {
538 buffer_add( state->sql, " AS \"" );
539 buffer_add( state->sql, item->column_alias );
540 buffer_add_char( state->sql, '\"' );
545 buffer_add_char( state->sql, ' ' );
549 @brief Add a GROUP BY clause, if there is one, to the current query.
550 @param state Pointer to the query-building context.
551 @param sel_list Pointer to the first node in a linked list of SelectItems
553 We reference the GROUP BY items by number, not by repeating the expressions.
555 static void buildGroupBy( BuildSQLState* state, const SelectItem* sel_list ) {
556 int seq = 0; // Sequence number of current SelectItem
557 int first = 1; // Boolean: true for the first GROUPed BY item
561 if( sel_list->grouped_by ) {
563 add_newline( state );
564 buffer_add( state->sql, "GROUP BY " );
568 buffer_add( state->sql, ", " );
570 buffer_fadd( state->sql, "%d", seq );
573 sel_list = sel_list->next;
578 @brief Add an ORDER BY clause to the current query.
579 @param state Pointer to the query-building context.
580 @param ord_list Pointer to the first node in a linked list of OrderItems.
582 static void buildOrderBy( BuildSQLState* state, const OrderItem* ord_list ) {
583 add_newline( state );
584 buffer_add( state->sql, "ORDER BY" );
585 incr_indent( state );
587 int first = 1; // boolean
592 buffer_add_char( state->sql, ',' );
593 add_newline( state );
594 buildExpression( state, ord_list->expression );
596 sqlAddMsg( state, "Unable to add ORDER BY expression # %d", ord_list->id );
600 ord_list = ord_list->next;
603 decr_indent( state );
608 @brief Build an arbitrary expression.
609 @param state Pointer to the query-building context.
610 @param expr Pointer to the Expression representing the expression to be built.
612 static void buildExpression( BuildSQLState* state, const Expression* expr ) {
614 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
615 "Internal error: NULL pointer to Expression" ));
620 if( expr->parenthesize )
621 buffer_add_char( state->sql, '(' );
623 switch( expr->type ) {
626 buffer_add( state->sql, "NOT " );
628 buildExpression( state, expr->left_operand );
630 sqlAddMsg( state, "Unable to emit left operand in BETWEEN expression # %d",
635 buffer_add( state->sql, " BETWEEN " );
637 buildExpression( state, expr->subexp_list );
639 sqlAddMsg( state, "Unable to emit lower limit in BETWEEN expression # %d",
644 buffer_add( state->sql, " AND " );
646 buildExpression( state, expr->subexp_list->next );
648 sqlAddMsg( state, "Unable to emit upper limit in BETWEEN expression # %d",
655 if( !expr->bind ) { // Sanity check
656 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
657 "Internal error: no variable for bind variable expression" ));
660 buildBindVar( state, expr->bind );
664 buffer_add( state->sql, "NOT " );
666 if( expr->literal ) {
667 buffer_add( state->sql, expr->literal );
668 buffer_add_char( state->sql, ' ' );
670 buffer_add( state->sql, "FALSE " );
673 buildCase( state, expr );
675 sqlAddMsg( state, "Unable to build CASE expression # %d", expr->id );
678 case EXP_CAST : // Type cast
680 buffer_add( state->sql, "NOT " );
682 buffer_add( state->sql, "CAST (" );
683 buildExpression( state, expr->left_operand );
685 sqlAddMsg( state, "Unable to build left operand for CAST expression # %d",
688 buffer_add( state->sql, " AS " );
689 if( expr->cast_type && expr->cast_type->datatype_name ) {
690 buffer_add( state->sql, expr->cast_type->datatype_name );
691 buffer_add_char( state->sql, ')' );
693 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
694 "No datatype available for CAST expression # %d", expr->id ));
699 case EXP_COLUMN : // Table column
701 buffer_add( state->sql, "NOT " );
703 if( expr->table_alias ) {
704 buffer_add_char( state->sql, '\"' );
705 buffer_add( state->sql, expr->table_alias );
706 buffer_add( state->sql, "\"." );
708 if( expr->column_name ) {
709 buffer_add( state->sql, expr->column_name );
711 buffer_add_char( state->sql, '*' );
715 if( !expr->subquery ) {
716 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
717 "No subquery found for EXIST expression # %d", expr->id ));
721 buffer_add( state->sql, "NOT " );
723 buffer_add( state->sql, "EXISTS (" );
724 incr_indent( state );
725 build_Query( state, expr->subquery );
726 decr_indent( state );
727 add_newline( state );
728 buffer_add_char( state->sql, ')' );
733 buffer_add( state->sql, "NOT " );
735 sqlAddMsg( state, "Field expressions not yet supported" );
739 buildFunction( state, expr );
742 if( expr->left_operand ) {
743 buildExpression( state, expr->left_operand );
744 if( !state->error ) {
746 buffer_add( state->sql, "NOT " );
747 buffer_add( state->sql, " IN (" );
749 if( expr->subquery ) {
750 incr_indent( state );
751 build_Query( state, expr->subquery );
753 sqlAddMsg( state, "Unable to build subquery for IN condition" );
755 decr_indent( state );
756 add_newline( state );
757 buffer_add_char( state->sql, ')' );
760 buildSeries( state, expr->subexp_list, NULL );
762 sqlAddMsg( state, "Unable to build IN list" );
764 buffer_add_char( state->sql, ')' );
770 if( expr->left_operand ) {
771 buildExpression( state, expr->left_operand );
773 sqlAddMsg( state, "Unable to emit left operand in IS NULL expression # %d",
780 buffer_add( state->sql, " IS NOT NULL" );
782 buffer_add( state->sql, " IS NULL" );
786 buffer_add( state->sql, "NOT " );
788 buffer_add( state->sql, "NULL" );
790 case EXP_NUMBER : // Numeric literal
791 if( !expr->literal ) {
792 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
793 "Internal error: No numeric value in string expression # %d", expr->id ));
796 buffer_add( state->sql, expr->literal );
801 buffer_add( state->sql, "NOT (" );
803 if( expr->left_operand ) {
804 buildExpression( state, expr->left_operand );
806 sqlAddMsg( state, "Unable to emit left operand in expression # %d",
811 buffer_add_char( state->sql, ' ' );
812 buffer_add( state->sql, expr->op );
813 buffer_add_char( state->sql, ' ' );
814 if( expr->right_operand ) {
815 buildExpression( state, expr->right_operand );
817 sqlAddMsg( state, "Unable to emit right operand in expression # %d",
824 buffer_add_char( state->sql, ')' );
829 buffer_add( state->sql, "NOT (" );
831 buildSeries( state, expr->subexp_list, expr->op );
833 sqlAddMsg( state, "Unable to build series expression using operator \"%s\"",
834 expr->op ? expr->op : "," );
837 buffer_add_char( state->sql, ')' );
840 case EXP_STRING : // String literal
841 if( !expr->literal ) {
842 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
843 "Internal error: No string value in string expression # %d", expr->id ));
846 char* str = strdup( expr->literal );
847 dbi_conn_quote_string( state->dbhandle, &str );
849 buffer_add( state->sql, str );
852 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
853 "Unable to format string literal \"%s\" for expression # %d",
854 expr->literal, expr->id ));
861 buffer_add( state->sql, "NOT " );
863 if( expr->subquery ) {
864 buffer_add_char( state->sql, '(' );
865 incr_indent( state );
866 build_Query( state, expr->subquery );
867 decr_indent( state );
868 add_newline( state );
869 buffer_add_char( state->sql, ')' );
871 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
872 "Internal error: No subquery in subquery expression # %d", expr->id ));
878 if( expr->parenthesize )
879 buffer_add_char( state->sql, ')' );
883 @brief Build a CASE expression.
884 @param state Pointer to the query-building context.
885 @param exp Pointer to an Expression representing a CASE expression.
887 static void buildCase( BuildSQLState* state, const Expression* expr ) {
889 if( ! expr->left_operand ) {
890 sqlAddMsg( state, "CASE expression # %d has no left operand", expr->id );
893 } else if( ! expr->branch_list ) {
894 sqlAddMsg( state, "CASE expression # %d has no branches", expr->id );
900 buffer_add( state->sql, "NOT (" );
902 // left_operand is the expression on which we shall branch
903 buffer_add( state->sql, "CASE " );
904 buildExpression( state, expr->left_operand );
906 sqlAddMsg( state, "Unable to build operand of CASE expression # %d", expr->id );
910 incr_indent( state );
912 // Emit each branch in turn
913 CaseBranch* branch = expr->branch_list;
915 add_newline( state );
917 if( branch->condition ) {
918 // Emit a WHEN condition
919 buffer_add( state->sql, "WHEN " );
920 buildExpression( state, branch->condition );
921 incr_indent( state );
922 add_newline( state );
923 buffer_add( state->sql, "THEN " );
926 buffer_add( state->sql, "ELSE " );
927 incr_indent( state );
928 add_newline( state );
931 // Emit the THEN expression
932 buildExpression( state, branch->result );
933 decr_indent( state );
935 branch = branch->next;
938 decr_indent( state );
939 add_newline( state );
940 buffer_add( state->sql, "END" );
943 buffer_add( state->sql, ")" );
947 @brief Build a function call.
948 @param state Pointer to the query-building context.
949 @param exp Pointer to an Expression representing a function call.
951 This function does not currently accommodate certain functions with idiosyncratic
952 syntax, such as the absence of parentheses, or the use of certain keywords in
953 in the parameter list.
955 static void buildFunction( BuildSQLState* state, const Expression* expr ) {
957 buffer_add( state->sql, "NOT " );
959 // We rely on the input side to ensure that the function name is available
960 buffer_add( state->sql, expr->function_name );
961 buffer_add_char( state->sql, '(' );
963 // Add the parameters, if any
964 buildSeries( state, expr->subexp_list, NULL );
966 buffer_add_char( state->sql, ')' );
970 @brief Build a series of expressions separated by a specified operator, or by commas.
971 @param state Pointer to the query-building context.
972 @param subexp_list Pointer to the first Expression in a linked list.
973 @param op Pointer to the operator, or NULL for commas.
975 If the operator is AND or OR (in upper, lower, or mixed case), the second and all
976 subsequent operators will begin on a new line.
978 static void buildSeries( BuildSQLState* state, const Expression* subexp_list, const char* op ) {
981 return; // List is empty
983 int comma = 0; // Boolean; true if separator is a comma
984 int newline_needed = 0; // Boolean; true if operator is AND or OR
989 } else if( !strcmp( op, "," ))
991 else if( !strcasecmp( op, "AND" ) || !strcasecmp( op, "OR" ))
994 int first = 1; // Boolean; true for first item in list
995 while( subexp_list ) {
997 first = 0; // No separator needed yet
999 // Insert a separator
1001 buffer_add( state->sql, ", " );
1003 if( newline_needed )
1004 add_newline( state );
1006 buffer_add_char( state->sql, ' ' );
1008 buffer_add( state->sql, op );
1009 buffer_add_char( state->sql, ' ' );
1013 buildExpression( state, subexp_list );
1014 subexp_list = subexp_list->next;
1019 @brief Add the value of a bind variable to an SQL statement.
1020 @param state Pointer to the query-building context.
1021 @param bind Pointer to the bind variable whose value is to be added to the SQL.
1023 The value may be a null, a scalar, or an array of nulls and/or scalars, depending on
1024 the type of the bind variable.
1026 static void buildBindVar( BuildSQLState* state, const BindVar* bind ) {
1028 // Decide where to get the value, if any
1029 const jsonObject* value = NULL;
1030 if( bind->actual_value )
1031 value = bind->actual_value;
1032 else if( bind->default_value ) {
1033 if( state->defaults_usable )
1034 value = bind->default_value;
1036 sqlAddMsg( state, "No confirmed value available for bind variable \"%s\"",
1041 } else if( state->values_required ) {
1042 sqlAddMsg( state, "No value available for bind variable \"%s\"", bind->name );
1046 // No value available, and that's okay. Emit the name of the bind variable.
1047 buffer_add_char( state->sql, ':' );
1048 buffer_add( state->sql, bind->name );
1052 // If we get to this point, we know that a value is available. Carry on.
1054 int numeric = 0; // Boolean
1055 if( BIND_NUM == bind->type || BIND_NUM_LIST == bind->type )
1059 switch( bind->type ) {
1062 buildScalar( state, numeric, value );
1064 case BIND_STR_LIST :
1065 case BIND_NUM_LIST :
1066 if( JSON_ARRAY == value->type ) {
1067 // Iterate over array, emit each value
1068 int first = 1; // Boolean
1069 unsigned long max = value->size;
1070 unsigned long i = 0;
1075 buffer_add( state->sql, ", " );
1077 buildScalar( state, numeric, jsonObjectGetIndex( value, i ));
1081 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1082 "Invalid value for bind variable; expected a list of values" ));
1087 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1088 "Internal error: invalid type for bind variable" ));
1094 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1095 "Unable to emit value of bind variable \"%s\"", bind->name ));
1099 @brief Add a number or quoted string to an SQL statement.
1100 @param state Pointer to the query-building context.
1101 @param numeric Boolean; true if the value is expected to be a number
1102 @param obj Pointer to the jsonObject whose value is to be added to the SQL.
1104 static void buildScalar( BuildSQLState* state, int numeric, const jsonObject* obj ) {
1105 switch( obj->type ) {
1107 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1108 "Internal error: hash value for bind variable" ));
1112 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1113 "Internal error: array value for bind variable" ));
1119 "Invalid value for bind variable: expected a string, found a number" );
1122 char* str = jsonObjectToSimpleString( obj );
1123 dbi_conn_quote_string( state->dbhandle, &str );
1125 buffer_add( state->sql, str );
1128 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1129 "Unable to format string literal \"%s\" for bind variable",
1130 jsonObjectGetString( obj )));
1137 buffer_add( state->sql, jsonObjectGetString( obj ));
1140 "Invalid value for bind variable: expected a number, found a string" );
1145 buffer_add( state->sql, "NULL" );
1148 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1149 "Internal error: boolean value for bind variable" ));
1153 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1154 "Internal error: corrupted value for bind variable" ));
1161 @brief Start a new line in the output, with the current level of indentation.
1162 @param state Pointer to the query-building context.
1164 static void add_newline( BuildSQLState* state ) {
1165 buffer_add_char( state->sql, '\n' );
1168 static const char blanks[] = " "; // 32 blanks
1169 static const size_t maxlen = sizeof( blanks ) - 1;
1170 const int blanks_per_level = 3;
1171 int n = state->indent * blanks_per_level;
1173 size_t len = n >= maxlen ? maxlen : n;
1174 buffer_add_n( state->sql, blanks, len );
1180 @brief Increase the degree of indentation.
1181 @param state Pointer to the query-building context.
1183 static inline void incr_indent( BuildSQLState* state ) {
1188 @brief Reduce the degree of indentation.
1189 @param state Pointer to the query-building context.
1191 static inline void decr_indent( BuildSQLState* state ) {