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 buildExpression( BuildSQLState* state, const Expression* expr );
27 static void buildFunction( BuildSQLState* state, const Expression* exp );
28 static void buildSeries( BuildSQLState* state, const Expression* subexp_list, const char* op );
29 static void buildBindVar( BuildSQLState* state, const BindVar* bind );
30 static void buildScalar( BuildSQLState* state, int numeric, const jsonObject* obj );
32 static void add_newline( BuildSQLState* state );
33 static inline void incr_indent( BuildSQLState* state );
34 static inline void decr_indent( BuildSQLState* state );
37 @brief Create a jsonObject representing the current list of bind variables.
38 @param bindvar_list Pointer to the bindvar_list member of a BuildSQLState.
39 @return Pointer to the newly created jsonObject.
41 The returned jsonObject is a (possibly empty) JSON_HASH, keyed on the names of the bind
42 variables. The data for each is another level of JSON_HASH with a fixed set of tags:
46 - "default_value" (as a jsonObject)
47 - "actual_value" (as a jsonObject)
49 Any non-existent values are represented as JSON_NULLs.
51 The calling code is responsible for freeing the returned jsonOjbect by calling
54 jsonObject* oilsBindVarList( osrfHash* bindvar_list ) {
55 jsonObject* list = jsonNewObjectType( JSON_HASH );
57 if( bindvar_list && osrfHashGetCount( bindvar_list )) {
58 // Traverse our internal list of bind variables
60 osrfHashIterator* iter = osrfNewHashIterator( bindvar_list );
61 while(( bind = osrfHashIteratorNext( iter ))) {
62 // Create an hash to represent the bind variable
63 jsonObject* bind_obj = jsonNewObjectType( JSON_HASH );
65 // Add an entry for each attribute
66 jsonObject* attr = jsonNewObject( bind->label );
67 jsonObjectSetKey( bind_obj, "label", attr );
69 const char* type = NULL;
70 switch( bind->type ) {
87 attr = jsonNewObject( type );
88 jsonObjectSetKey( bind_obj, "type", attr );
90 attr = jsonNewObject( bind->description );
91 jsonObjectSetKey( bind_obj, "description", attr );
93 attr = jsonObjectClone( bind->default_value );
94 jsonObjectSetKey( bind_obj, "default_value", attr );
96 attr = jsonObjectClone( bind->actual_value );
97 jsonObjectSetKey( bind_obj, "actual_value", attr );
99 // Add the bind variable to the list
100 jsonObjectSetKey( list, osrfHashIteratorKey( iter ), bind_obj );
102 osrfHashIteratorFree( iter );
109 @brief Apply values to bind variables, overriding the defaults, if any.
110 @param state Pointer to the query-building context.
111 @param bindings A JSON_HASH of values.
112 @return 0 if successful, or 1 if not.
114 The @a bindings parameter must be a JSON_HASH. The keys are the names of bind variables.
115 The values are the corresponding values for the variables.
117 int oilsApplyBindValues( BuildSQLState* state, const jsonObject* bindings ) {
119 osrfLogError( OSRF_LOG_MARK, "NULL pointer to state" );
121 } else if( !bindings ) {
122 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
123 "Internal error: No pointer to bindings" ));
125 } else if( bindings->type != JSON_HASH ) {
126 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
127 "Internal error: bindings parameter is not a JSON_HASH" ));
132 jsonObject* value = NULL;
133 jsonIterator* iter = jsonNewIterator( bindings );
134 while(( value = jsonIteratorNext( iter ))) {
135 const char* var_name = iter->key;
136 BindVar* bind = osrfHashGet( state->bindvar_list, var_name );
138 // Apply or replace the value for the specified variable
139 if( bind->actual_value )
140 jsonObjectFree( bind->actual_value );
141 bind->actual_value = jsonObjectClone( value );
143 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
144 "Can't assign value to bind variable \"%s\": no such variable", var_name ));
148 jsonIteratorFree( iter );
154 @brief Build an SQL query.
155 @param state Pointer to the query-building context.
156 @param query Pointer to the query to be built.
157 @return Zero if successful, or 1 if not.
159 Clear the output buffer, call build_Query() to do the work, and add a closing semicolon.
161 int buildSQL( BuildSQLState* state, const StoredQ* query ) {
163 buffer_reset( state->sql );
165 build_Query( state, query );
166 if( ! state->error ) {
167 // Remove the trailing space, if there is one, and add a semicolon.
168 char c = buffer_chomp( state->sql );
170 buffer_add_char( state->sql, c ); // oops, not a space; put it back
171 buffer_add( state->sql, ";\n" );
177 @brief Build an SQL query, appending it to what has been built so far.
178 @param state Pointer to the query-building context.
179 @param query Pointer to the query to be built.
181 Look at the query type and branch to the corresponding routine.
183 static void build_Query( BuildSQLState* state, const StoredQ* query ) {
184 if( buffer_length( state->sql ))
185 add_newline( state );
187 switch( query->type ) {
189 buildSelect( state, query );
192 buildCombo( state, query, "UNION" );
195 buildCombo( state, query, "INTERSECT" );
198 buildCombo( state, query, "EXCEPT" );
201 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
202 "Internal error: invalid query type %d in query # %d",
203 query->type, query->id ));
210 @brief Build a UNION, INTERSECT, or EXCEPT query.
211 @param state Pointer to the query-building context.
212 @param query Pointer to the query to be built.
213 @param type_str The query type, as a string.
215 static void buildCombo( BuildSQLState* state, const StoredQ* query, const char* type_str ) {
217 QSeq* seq = query->child_list;
219 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
220 "Internal error: No child queries within %s query # %d",
221 type_str, query->id ));
226 // Traverse the list of child queries
228 build_Query( state, seq->child_query );
230 sqlAddMsg( state, "Unable to build child query # %d within %s query %d",
231 seq->child_query->id, type_str, query->id );
236 add_newline( state );
237 buffer_add( state->sql, type_str );
238 buffer_add_char( state->sql, ' ' );
240 buffer_add( state->sql, "ALL " );
248 @brief Build a SELECT statement.
249 @param state Pointer to the query-building context.
250 @param query Pointer to the StoredQ structure that represents the query.
252 static void buildSelect( BuildSQLState* state, const StoredQ* query ) {
254 FromRelation* from_clause = query->from_clause;
256 sqlAddMsg( state, "SELECT has no FROM clause in query # %d", query->id );
261 // To do: get SELECT list; just a stub here
262 buffer_add( state->sql, "SELECT" );
263 incr_indent( state );
264 buildSelectList( state, query->select_list );
266 sqlAddMsg( state, "Unable to build SELECT list for query # %d", query->id );
270 decr_indent( state );
272 // Build FROM clause, if there is one
273 if( query->from_clause ) {
274 buildFrom( state, query->from_clause );
276 sqlAddMsg( state, "Unable to build FROM clause for query # %d", query->id );
282 // Build WHERE clause, if there is one
283 if( query->where_clause ) {
284 add_newline( state );
285 buffer_add( state->sql, "WHERE" );
286 incr_indent( state );
287 add_newline( state );
288 buildExpression( state, query->where_clause );
290 sqlAddMsg( state, "Unable to build WHERE clause for query # %d", query->id );
294 decr_indent( state );
297 // Build GROUP BY clause, if there is one
298 buildGroupBy( state, query->select_list );
300 // Build HAVING clause, if there is one
301 if( query->having_clause ) {
302 add_newline( state );
303 buffer_add( state->sql, "HAVING" );
304 incr_indent( state );
305 add_newline( state );
306 buildExpression( state, query->having_clause );
308 sqlAddMsg( state, "Unable to build HAVING clause for query # %d", query->id );
312 decr_indent( state );
315 // Build ORDER BY clause, if there is one
316 if( query->order_by_list ) {
317 buildOrderBy( state, query->order_by_list );
319 sqlAddMsg( state, "Unable to build ORDER BY clause for query # %d", query->id );
325 // To do: Build LIMIT clause, if there is one
327 // To do: Build OFFSET clause, if there is one
333 @brief Build a FROM clause.
334 @param Pointer to the query-building context.
335 @param Pointer to the StoredQ query to which the FROM clause belongs.
337 static void buildFrom( BuildSQLState* state, const FromRelation* core_from ) {
339 add_newline( state );
340 buffer_add( state->sql, "FROM" );
341 incr_indent( state );
342 add_newline( state );
344 switch( core_from->type ) {
345 case FRT_RELATION : {
346 char* relation = core_from->table_name;
348 if( !core_from->class_name ) {
349 sqlAddMsg( state, "No relation specified for core relation # %d",
355 // Look up table name, view name, or source_definition in the IDL
356 osrfHash* class_hash = osrfHashGet( oilsIDL(), core_from->class_name );
357 relation = oilsGetRelation( class_hash );
361 buffer_add( state->sql, relation );
362 if( !core_from->table_name )
363 free( relation ); // In this case we strdup'd it, must free it
367 buffer_add_char( state->sql, '(' );
368 incr_indent( state );
369 build_Query( state, core_from->subquery );
370 decr_indent( state );
371 add_newline( state );
372 buffer_add_char( state->sql, ')' );
375 buildFunction( state, core_from->function_call );
376 if ( state->error ) {
378 "Unable to include function call # %d in FROM relation # %d",
379 core_from->function_call->id, core_from->id );
384 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
385 "Internal error: Invalid type # %d in FROM relation # %d",
386 core_from->type, core_from->id ));
391 // Add a table alias, if possible
392 if( core_from->table_alias ) {
393 buffer_add( state->sql, " AS \"" );
394 buffer_add( state->sql, core_from->table_alias );
395 buffer_add( state->sql, "\" " );
397 else if( core_from->class_name ) {
398 buffer_add( state->sql, " AS \"" );
399 buffer_add( state->sql, core_from->class_name );
400 buffer_add( state->sql, "\" " );
402 buffer_add_char( state->sql, ' ' );
404 incr_indent( state );
405 FromRelation* join = core_from->join_list;
407 buildJoin( state, join );
409 sqlAddMsg( state, "Unable to build JOIN clause(s) for relation # %d",
415 decr_indent( state );
416 decr_indent( state );
420 @brief Add a JOIN clause.
421 @param state Pointer to the query-building context.
422 @param join Pointer to the FromRelation representing the JOIN to be added.
424 static void buildJoin( BuildSQLState* state, const FromRelation* join ) {
425 add_newline( state );
426 switch( join->join_type ) {
428 sqlAddMsg( state, "Non-join relation # %d in JOIN clause", join->id );
432 buffer_add( state->sql, "INNER JOIN " );
435 buffer_add( state->sql, "LEFT JOIN " );
438 buffer_add( state->sql, "RIGHT JOIN " );
441 buffer_add( state->sql, "FULL JOIN " );
444 sqlAddMsg( state, "Unrecognized join type in relation # %d", join->id );
449 switch( join->type ) {
452 if( !join->table_name || ! *join->table_name ) {
453 sqlAddMsg( state, "No relation designated for relation # %d", join->id );
457 buffer_add( state->sql, join->table_name );
461 if( !join->subquery ) {
462 sqlAddMsg( state, "Subquery expected, not found for relation # %d", join->id );
465 } else if( !join->table_alias ) {
466 sqlAddMsg( state, "No table alias for subquery in FROM relation # %d",
471 buffer_add_char( state->sql, '(' );
472 incr_indent( state );
473 build_Query( state, join->subquery );
474 decr_indent( state );
475 add_newline( state );
476 buffer_add_char( state->sql, ')' );
479 if( !join->table_name || ! *join->table_name ) {
480 sqlAddMsg( state, "Joins to functions not yet supported in relation # %d",
488 const char* effective_alias = join->table_alias;
489 if( !effective_alias )
490 effective_alias = join->class_name;
492 if( effective_alias ) {
493 buffer_add( state->sql, " AS \"" );
494 buffer_add( state->sql, effective_alias );
495 buffer_add_char( state->sql, '\"' );
498 if( join->on_clause ) {
499 incr_indent( state );
500 add_newline( state );
501 buffer_add( state->sql, "ON " );
502 buildExpression( state, join->on_clause );
503 decr_indent( state );
506 FromRelation* subjoin = join->join_list;
508 buildJoin( state, subjoin );
510 sqlAddMsg( state, "Unable to build JOIN clause(s) for relation # %d", join->id );
513 subjoin = subjoin->next;
518 @brief Build a SELECT list.
519 @param state Pointer to the query-building context.
520 @param item Pointer to the first in a linked list of SELECT items.
522 static void buildSelectList( BuildSQLState* state, const SelectItem* item ) {
527 buffer_add_char( state->sql, ',' );
528 add_newline( state );
529 buildExpression( state, item->expression );
531 sqlAddMsg( state, "Unable to build an expression for SELECT item # %d", item->id );
536 if( item->column_alias ) {
537 buffer_add( state->sql, " AS \"" );
538 buffer_add( state->sql, item->column_alias );
539 buffer_add_char( state->sql, '\"' );
544 buffer_add_char( state->sql, ' ' );
548 @brief Add a GROUP BY clause, if there is one, to the current query.
549 @param state Pointer to the query-building context.
550 @param sel_list Pointer to the first node in a linked list of SelectItems
552 We reference the GROUP BY items by number, not by repeating the expressions.
554 static void buildGroupBy( BuildSQLState* state, const SelectItem* sel_list ) {
555 int seq = 0; // Sequence number of current SelectItem
556 int first = 1; // Boolean: true for the first GROUPed BY item
560 if( sel_list->grouped_by ) {
562 add_newline( state );
563 buffer_add( state->sql, "GROUP BY " );
567 buffer_add( state->sql, ", " );
569 buffer_fadd( state->sql, "%d", seq );
572 sel_list = sel_list->next;
577 @brief Add an ORDER BY clause to the current query.
578 @param state Pointer to the query-building context.
579 @param ord_list Pointer to the first node in a linked list of OrderItems.
581 static void buildOrderBy( BuildSQLState* state, const OrderItem* ord_list ) {
582 add_newline( state );
583 buffer_add( state->sql, "ORDER BY" );
584 incr_indent( state );
586 int first = 1; // boolean
591 buffer_add_char( state->sql, ',' );
592 add_newline( state );
593 buildExpression( state, ord_list->expression );
595 sqlAddMsg( state, "Unable to add ORDER BY expression # %d", ord_list->id );
599 ord_list = ord_list->next;
602 decr_indent( state );
607 @brief Build an arbitrary expression.
608 @param state Pointer to the query-building context.
609 @param expr Pointer to the Expression representing the expression to be built.
611 static void buildExpression( BuildSQLState* state, const Expression* expr ) {
613 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
614 "Internal error: NULL pointer to Expression" ));
619 if( expr->parenthesize )
620 buffer_add_char( state->sql, '(' );
622 switch( expr->type ) {
625 buffer_add( state->sql, "NOT " );
627 buildExpression( state, expr->left_operand );
629 sqlAddMsg( state, "Unable to emit left operand in BETWEEN expression # %d",
634 buffer_add( state->sql, " BETWEEN " );
636 buildExpression( state, expr->subexp_list );
638 sqlAddMsg( state, "Unable to emit lower limit in BETWEEN expression # %d",
643 buffer_add( state->sql, " AND " );
645 buildExpression( state, expr->subexp_list->next );
647 sqlAddMsg( state, "Unable to emit upper limit in BETWEEN expression # %d",
654 if( !expr->bind ) { // Sanity check
655 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
656 "Internal error: no variable for bind variable expression" ));
659 buildBindVar( state, expr->bind );
663 buffer_add( state->sql, "NOT " );
665 if( expr->literal ) {
666 buffer_add( state->sql, expr->literal );
667 buffer_add_char( state->sql, ' ' );
669 buffer_add( state->sql, "FALSE " );
673 buffer_add( state->sql, "NOT " );
675 sqlAddMsg( state, "CASE expressions not yet supported" );
678 case EXP_CAST : // Type cast
680 buffer_add( state->sql, "NOT " );
682 sqlAddMsg( state, "Cast expressions not yet supported" );
685 case EXP_COLUMN : // Table column
687 buffer_add( state->sql, "NOT " );
689 if( expr->table_alias ) {
690 buffer_add_char( state->sql, '\"' );
691 buffer_add( state->sql, expr->table_alias );
692 buffer_add( state->sql, "\"." );
694 if( expr->column_name ) {
695 buffer_add( state->sql, expr->column_name );
697 //osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
698 // "Column name not present in expression # %d", expr->id ));
700 buffer_add_char( state->sql, '*' );
704 if( !expr->subquery ) {
705 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
706 "No subquery found for EXIST expression # %d", expr->id ));
710 buffer_add( state->sql, "NOT " );
712 buffer_add( state->sql, "EXISTS (" );
713 incr_indent( state );
714 build_Query( state, expr->subquery );
715 decr_indent( state );
716 add_newline( state );
717 buffer_add_char( state->sql, ')' );
722 buffer_add( state->sql, "NOT " );
724 sqlAddMsg( state, "Field expressions not yet supported" );
728 buildFunction( state, expr );
731 if( expr->left_operand ) {
732 buildExpression( state, expr->left_operand );
733 if( !state->error ) {
735 buffer_add( state->sql, "NOT " );
736 buffer_add( state->sql, " IN (" );
738 if( expr->subquery ) {
739 incr_indent( state );
740 build_Query( state, expr->subquery );
742 sqlAddMsg( state, "Unable to build subquery for IN condition" );
744 decr_indent( state );
745 add_newline( state );
746 buffer_add_char( state->sql, ')' );
749 buildSeries( state, expr->subexp_list, NULL );
751 sqlAddMsg( state, "Unable to build IN list" );
753 buffer_add_char( state->sql, ')' );
759 if( expr->left_operand ) {
760 buildExpression( state, expr->left_operand );
762 sqlAddMsg( state, "Unable to emit left operand in IS NULL expression # %d",
769 buffer_add( state->sql, " IS NOT NULL" );
771 buffer_add( state->sql, " IS NULL" );
775 buffer_add( state->sql, "NOT " );
777 buffer_add( state->sql, "NULL" );
779 case EXP_NUMBER : // Numeric literal
780 if( !expr->literal ) {
781 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
782 "Internal error: No numeric value in string expression # %d", expr->id ));
785 buffer_add( state->sql, expr->literal );
790 buffer_add( state->sql, "NOT (" );
792 if( expr->left_operand ) {
793 buildExpression( state, expr->left_operand );
795 sqlAddMsg( state, "Unable to emit left operand in expression # %d",
800 buffer_add_char( state->sql, ' ' );
801 buffer_add( state->sql, expr->op );
802 buffer_add_char( state->sql, ' ' );
803 if( expr->right_operand ) {
804 buildExpression( state, expr->right_operand );
806 sqlAddMsg( state, "Unable to emit right operand in expression # %d",
813 buffer_add_char( state->sql, ')' );
818 buffer_add( state->sql, "NOT (" );
820 buildSeries( state, expr->subexp_list, expr->op );
822 sqlAddMsg( state, "Unable to build series expression using operator \"%s\"",
823 expr->op ? expr->op : "," );
826 buffer_add_char( state->sql, ')' );
829 case EXP_STRING : // String literal
830 if( !expr->literal ) {
831 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
832 "Internal error: No string value in string expression # %d", expr->id ));
835 char* str = strdup( expr->literal );
836 dbi_conn_quote_string( state->dbhandle, &str );
838 buffer_add( state->sql, str );
841 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
842 "Unable to format string literal \"%s\" for expression # %d",
843 expr->literal, expr->id ));
850 buffer_add( state->sql, "NOT " );
852 if( expr->subquery ) {
853 buffer_add_char( state->sql, '(' );
854 incr_indent( state );
855 build_Query( state, expr->subquery );
856 decr_indent( state );
857 add_newline( state );
858 buffer_add_char( state->sql, ')' );
860 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
861 "Internal error: No subquery in subquery expression # %d", expr->id ));
867 if( expr->parenthesize )
868 buffer_add_char( state->sql, ')' );
872 @brief Build a function call.
873 @param state Pointer to the query-building context.
874 @param exp Pointer to an Expression representing a function call.
876 This function does not currently accommodate certain functions with idiosyncratic
877 syntax, such as the absence of parentheses, or the use of certain keywords in
878 in the parameter list.
880 static void buildFunction( BuildSQLState* state, const Expression* expr ) {
882 buffer_add( state->sql, "NOT " );
884 // We rely on the input side to ensure that the function name is available
885 buffer_add( state->sql, expr->function_name );
886 buffer_add_char( state->sql, '(' );
888 // Add the parameters, if any
889 buildSeries( state, expr->subexp_list, NULL );
891 buffer_add_char( state->sql, ')' );
895 @brief Build a series of expressions separated by a specified operator, or by commas.
896 @param state Pointer to the query-building context.
897 @param subexp_list Pointer to the first Expression in a linked list.
898 @param op Pointer to the operator, or NULL for commas.
900 If the operator is AND or OR (in upper, lower, or mixed case), the second and all
901 subsequent operators will begin on a new line.
903 static void buildSeries( BuildSQLState* state, const Expression* subexp_list, const char* op ) {
906 return; // List is empty
908 int comma = 0; // Boolean; true if separator is a comma
909 int newline_needed = 0; // Boolean; true if operator is AND or OR
914 } else if( !strcmp( op, "," ))
916 else if( !strcasecmp( op, "AND" ) || !strcasecmp( op, "OR" ))
919 int first = 1; // Boolean; true for first item in list
920 while( subexp_list ) {
922 first = 0; // No separator needed yet
924 // Insert a separator
926 buffer_add( state->sql, ", " );
929 add_newline( state );
931 buffer_add_char( state->sql, ' ' );
933 buffer_add( state->sql, op );
934 buffer_add_char( state->sql, ' ' );
938 buildExpression( state, subexp_list );
939 subexp_list = subexp_list->next;
944 @brief Add the value of a bind variable to an SQL statement.
945 @param state Pointer to the query-building context.
946 @param bind Pointer to the bind variable whose value is to be added to the SQL.
948 The value may be a null, a scalar, or an array of nulls and/or scalars, depending on
949 the type of the bind variable.
951 static void buildBindVar( BuildSQLState* state, const BindVar* bind ) {
953 // Decide where to get the value, if any
954 const jsonObject* value = NULL;
955 if( bind->actual_value )
956 value = bind->actual_value;
957 else if( bind->default_value ) {
958 if( state->defaults_usable )
959 value = bind->default_value;
961 sqlAddMsg( state, "No confirmed value available for bind variable \"%s\"",
966 } else if( state->values_required ) {
967 sqlAddMsg( state, "No value available for bind variable \"%s\"", bind->name );
971 // No value available, and that's okay. Emit the name of the bind variable.
972 buffer_add_char( state->sql, ':' );
973 buffer_add( state->sql, bind->name );
977 // If we get to this point, we know that a value is available. Carry on.
979 int numeric = 0; // Boolean
980 if( BIND_NUM == bind->type || BIND_NUM_LIST == bind->type )
984 switch( bind->type ) {
987 buildScalar( state, numeric, value );
991 if( JSON_ARRAY == value->type ) {
992 // Iterate over array, emit each value
993 int first = 1; // Boolean
994 unsigned long max = value->size;
1000 buffer_add( state->sql, ", " );
1002 buildScalar( state, numeric, jsonObjectGetIndex( value, i ));
1006 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1007 "Invalid value for bind variable; expected a list of values" ));
1012 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1013 "Internal error: invalid type for bind variable" ));
1019 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1020 "Unable to emit value of bind variable \"%s\"", bind->name ));
1024 @brief Add a number or quoted string to an SQL statement.
1025 @param state Pointer to the query-building context.
1026 @param numeric Boolean; true if the value is expected to be a number
1027 @param obj Pointer to the jsonObject whose value is to be added to the SQL.
1029 static void buildScalar( BuildSQLState* state, int numeric, const jsonObject* obj ) {
1030 switch( obj->type ) {
1032 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1033 "Internal error: hash value for bind variable" ));
1037 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1038 "Internal error: array value for bind variable" ));
1044 "Invalid value for bind variable: expected a string, found a number" );
1047 char* str = jsonObjectToSimpleString( obj );
1048 dbi_conn_quote_string( state->dbhandle, &str );
1050 buffer_add( state->sql, str );
1053 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1054 "Unable to format string literal \"%s\" for bind variable",
1055 jsonObjectGetString( obj )));
1062 buffer_add( state->sql, jsonObjectGetString( obj ));
1065 "Invalid value for bind variable: expected a number, found a string" );
1070 buffer_add( state->sql, "NULL" );
1073 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1074 "Internal error: boolean value for bind variable" ));
1078 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1079 "Internal error: corrupted value for bind variable" ));
1086 @brief Start a new line in the output, with the current level of indentation.
1087 @param state Pointer to the query-building context.
1089 static void add_newline( BuildSQLState* state ) {
1090 buffer_add_char( state->sql, '\n' );
1093 static const char blanks[] = " "; // 32 blanks
1094 static const size_t maxlen = sizeof( blanks ) - 1;
1095 const int blanks_per_level = 3;
1096 int n = state->indent * blanks_per_level;
1098 size_t len = n >= maxlen ? maxlen : n;
1099 buffer_add_n( state->sql, blanks, len );
1105 @brief Increase the degree of indentation.
1106 @param state Pointer to the query-building context.
1108 static inline void incr_indent( BuildSQLState* state ) {
1113 @brief Reduce the degree of indentation.
1114 @param state Pointer to the query-building context.
1116 static inline void decr_indent( BuildSQLState* state ) {