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 buildOrderBy( BuildSQLState* state, const OrderItem* ord_list );
25 static void buildExpression( BuildSQLState* state, const Expression* expr );
26 static void buildFunction( BuildSQLState* state, const Expression* exp );
27 static void buildSeries( BuildSQLState* state, const Expression* subexp_list, const char* op );
28 static void buildBindVar( BuildSQLState* state, const BindVar* bind );
29 static void buildScalar( BuildSQLState* state, int numeric, const jsonObject* obj );
31 static void add_newline( BuildSQLState* state );
32 static inline void incr_indent( BuildSQLState* state );
33 static inline void decr_indent( BuildSQLState* state );
36 @brief Create a jsonObject representing the current list of bind variables.
37 @param bindvar_list Pointer to the bindvar_list member of a BuildSQLState.
38 @return Pointer to the newly created jsonObject.
40 The returned jsonObject is a (possibly empty) JSON_HASH, keyed on the names of the bind
41 variables. The data for each is another level of JSON_HASH with a fixed set of tags:
45 - "default_value" (as a jsonObject)
46 - "actual_value" (as a jsonObject)
48 Any non-existent values are represented as JSON_NULLs.
50 The calling code is responsible for freeing the returned jsonOjbect by calling
53 jsonObject* oilsBindVarList( osrfHash* bindvar_list ) {
54 jsonObject* list = jsonNewObjectType( JSON_HASH );
56 if( bindvar_list && osrfHashGetCount( bindvar_list )) {
57 // Traverse our internal list of bind variables
59 osrfHashIterator* iter = osrfNewHashIterator( bindvar_list );
60 while(( bind = osrfHashIteratorNext( iter ))) {
61 // Create an hash to represent the bind variable
62 jsonObject* bind_obj = jsonNewObjectType( JSON_HASH );
64 // Add an entry for each attribute
65 jsonObject* attr = jsonNewObject( bind->label );
66 jsonObjectSetKey( bind_obj, "label", attr );
68 const char* type = NULL;
69 switch( bind->type ) {
86 attr = jsonNewObject( type );
87 jsonObjectSetKey( bind_obj, "type", attr );
89 attr = jsonNewObject( bind->description );
90 jsonObjectSetKey( bind_obj, "description", attr );
92 attr = jsonObjectClone( bind->default_value );
93 jsonObjectSetKey( bind_obj, "default_value", attr );
95 attr = jsonObjectClone( bind->actual_value );
96 jsonObjectSetKey( bind_obj, "actual_value", attr );
98 // Add the bind variable to the list
99 jsonObjectSetKey( list, osrfHashIteratorKey( iter ), bind_obj );
101 osrfHashIteratorFree( iter );
108 @brief Apply values to bind variables, overriding the defaults, if any.
109 @param state Pointer to the query-building context.
110 @param bindings A JSON_HASH of values.
111 @return 0 if successful, or 1 if not.
113 The @a bindings parameter must be a JSON_HASH. The keys are the names of bind variables.
114 The values are the corresponding values for the variables.
116 int oilsApplyBindValues( BuildSQLState* state, const jsonObject* bindings ) {
118 osrfLogError( OSRF_LOG_MARK, "NULL pointer to state" );
120 } else if( !bindings ) {
121 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
122 "Internal error: No pointer to bindings" ));
124 } else if( bindings->type != JSON_HASH ) {
125 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
126 "Internal error: bindings parameter is not a JSON_HASH" ));
131 jsonObject* value = NULL;
132 jsonIterator* iter = jsonNewIterator( bindings );
133 while(( value = jsonIteratorNext( iter ))) {
134 const char* var_name = iter->key;
135 BindVar* bind = osrfHashGet( state->bindvar_list, var_name );
137 // Apply or replace the value for the specified variable
138 if( bind->actual_value )
139 jsonObjectFree( bind->actual_value );
140 bind->actual_value = jsonObjectClone( value );
142 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
143 "Can't assign value to bind variable \"%s\": no such variable", var_name ));
147 jsonIteratorFree( iter );
153 @brief Build an SQL query.
154 @param state Pointer to the query-building context.
155 @param query Pointer to the query to be built.
156 @return Zero if successful, or 1 if not.
158 Clear the output buffer, call build_Query() to do the work, and add a closing semicolon.
160 int buildSQL( BuildSQLState* state, const StoredQ* query ) {
162 buffer_reset( state->sql );
164 build_Query( state, query );
165 if( ! state->error ) {
166 // Remove the trailing space, if there is one, and add a semicolon.
167 char c = buffer_chomp( state->sql );
169 buffer_add_char( state->sql, c ); // oops, not a space; put it back
170 buffer_add( state->sql, ";\n" );
176 @brief Build an SQL query, appending it to what has been built so far.
177 @param state Pointer to the query-building context.
178 @param query Pointer to the query to be built.
180 Look at the query type and branch to the corresponding routine.
182 static void build_Query( BuildSQLState* state, const StoredQ* query ) {
183 if( buffer_length( state->sql ))
184 add_newline( state );
186 switch( query->type ) {
188 buildSelect( state, query );
191 buildCombo( state, query, "UNION" );
194 buildCombo( state, query, "INTERSECT" );
197 buildCombo( state, query, "EXCEPT" );
200 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
201 "Internal error: invalid query type %d in query # %d",
202 query->type, query->id ));
209 @brief Build a UNION, INTERSECT, or EXCEPT query.
210 @param state Pointer to the query-building context.
211 @param query Pointer to the query to be built.
212 @param type_str The query type, as a string.
214 static void buildCombo( BuildSQLState* state, const StoredQ* query, const char* type_str ) {
216 QSeq* seq = query->child_list;
218 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
219 "Internal error: No child queries within %s query # %d",
220 type_str, query->id ));
225 // Traverse the list of child queries
227 build_Query( state, seq->child_query );
229 sqlAddMsg( state, "Unable to build child query # %d within %s query %d",
230 seq->child_query->id, type_str, query->id );
235 add_newline( state );
236 buffer_add( state->sql, type_str );
237 buffer_add_char( state->sql, ' ' );
239 buffer_add( state->sql, "ALL " );
247 @brief Build a SELECT statement.
248 @param state Pointer to the query-building context.
249 @param query Pointer to the StoredQ structure that represents the query.
251 static void buildSelect( BuildSQLState* state, const StoredQ* query ) {
253 FromRelation* from_clause = query->from_clause;
255 sqlAddMsg( state, "SELECT has no FROM clause in query # %d", query->id );
260 // To do: get SELECT list; just a stub here
261 buffer_add( state->sql, "SELECT" );
262 incr_indent( state );
263 buildSelectList( state, query->select_list );
265 sqlAddMsg( state, "Unable to build SELECT list for query # %d", query->id );
269 decr_indent( state );
271 // Build FROM clause, if there is one
272 if( query->from_clause ) {
273 buildFrom( state, query->from_clause );
275 sqlAddMsg( state, "Unable to build FROM clause for query # %d", query->id );
281 // Build WHERE clause, if there is one
282 if( query->where_clause ) {
283 add_newline( state );
284 buffer_add( state->sql, "WHERE" );
285 incr_indent( state );
286 add_newline( state );
287 buildExpression( state, query->where_clause );
289 sqlAddMsg( state, "Unable to build WHERE clause for query # %d", query->id );
293 decr_indent( state );
296 // To do: build GROUP BY clause, if there is one
298 // Build HAVING clause, if there is one
299 if( query->having_clause ) {
300 add_newline( state );
301 buffer_add( state->sql, "HAVING" );
302 incr_indent( state );
303 add_newline( state );
304 buildExpression( state, query->having_clause );
306 sqlAddMsg( state, "Unable to build HAVING clause for query # %d", query->id );
310 decr_indent( state );
313 // Build ORDER BY clause, if there is one
314 if( query->order_by_list ) {
315 buildOrderBy( state, query->order_by_list );
317 sqlAddMsg( state, "Unable to build ORDER BY clause for query # %d", query->id );
323 // To do: Build LIMIT clause, if there is one
325 // To do: Build OFFSET clause, if there is one
331 @brief Build a FROM clause.
332 @param Pointer to the query-building context.
333 @param Pointer to the StoredQ query to which the FROM clause belongs.
335 static void buildFrom( BuildSQLState* state, const FromRelation* core_from ) {
337 add_newline( state );
338 buffer_add( state->sql, "FROM" );
339 incr_indent( state );
340 add_newline( state );
342 switch( core_from->type ) {
343 case FRT_RELATION : {
344 char* relation = core_from->table_name;
346 if( !core_from->class_name ) {
347 sqlAddMsg( state, "No relation specified for core relation # %d",
353 // Look up table name, view name, or source_definition in the IDL
354 osrfHash* class_hash = osrfHashGet( oilsIDL(), core_from->class_name );
355 relation = oilsGetRelation( class_hash );
359 buffer_add( state->sql, relation );
360 if( !core_from->table_name )
361 free( relation ); // In this case we strdup'd it, must free it
365 buffer_add_char( state->sql, '(' );
366 incr_indent( state );
367 build_Query( state, core_from->subquery );
368 decr_indent( state );
369 add_newline( state );
370 buffer_add_char( state->sql, ')' );
373 sqlAddMsg( state, "Functions in FROM clause not yet supported" );
378 // Add a table alias, if possible
379 if( core_from->table_alias ) {
380 buffer_add( state->sql, " AS \"" );
381 buffer_add( state->sql, core_from->table_alias );
382 buffer_add( state->sql, "\" " );
384 else if( core_from->class_name ) {
385 buffer_add( state->sql, " AS \"" );
386 buffer_add( state->sql, core_from->class_name );
387 buffer_add( state->sql, "\" " );
389 buffer_add_char( state->sql, ' ' );
391 incr_indent( state );
392 FromRelation* join = core_from->join_list;
394 buildJoin( state, join );
396 sqlAddMsg( state, "Unable to build JOIN clause(s) for relation # %d",
402 decr_indent( state );
403 decr_indent( state );
406 static void buildJoin( BuildSQLState* state, const FromRelation* join ) {
407 add_newline( state );
408 switch( join->join_type ) {
410 sqlAddMsg( state, "Non-join relation # %d in JOIN clause", join->id );
414 buffer_add( state->sql, "INNER JOIN " );
417 buffer_add( state->sql, "LEFT JOIN " );
420 buffer_add( state->sql, "RIGHT JOIN " );
423 buffer_add( state->sql, "FULL JOIN " );
426 sqlAddMsg( state, "Unrecognized join type in relation # %d", join->id );
431 switch( join->type ) {
434 if( !join->table_name || ! *join->table_name ) {
435 sqlAddMsg( state, "No relation designated for relation # %d", join->id );
439 buffer_add( state->sql, join->table_name );
443 if( !join->subquery ) {
444 sqlAddMsg( state, "Subquery expected, not found for relation # %d", join->id );
447 } else if( !join->table_alias ) {
448 sqlAddMsg( state, "No table alias for subquery in FROM relation # %d",
453 buffer_add_char( state->sql, '(' );
454 incr_indent( state );
455 build_Query( state, join->subquery );
456 decr_indent( state );
457 add_newline( state );
458 buffer_add_char( state->sql, ')' );
461 if( !join->table_name || ! *join->table_name ) {
462 sqlAddMsg( state, "Joins to functions not yet supported in relation # %d",
470 const char* effective_alias = join->table_alias;
471 if( !effective_alias )
472 effective_alias = join->class_name;
474 if( effective_alias ) {
475 buffer_add( state->sql, " AS \"" );
476 buffer_add( state->sql, effective_alias );
477 buffer_add_char( state->sql, '\"' );
480 if( join->on_clause ) {
481 incr_indent( state );
482 add_newline( state );
483 buffer_add( state->sql, "ON " );
484 buildExpression( state, join->on_clause );
485 decr_indent( state );
488 FromRelation* subjoin = join->join_list;
490 buildJoin( state, subjoin );
492 sqlAddMsg( state, "Unable to build JOIN clause(s) for relation # %d", join->id );
495 subjoin = subjoin->next;
500 @brief Build a SELECT list.
501 @param state Pointer to the query-building context.
502 @param item Pointer to the first in a linked list of SELECT items.
504 static void buildSelectList( BuildSQLState* state, const SelectItem* item ) {
509 buffer_add_char( state->sql, ',' );
510 add_newline( state );
511 buildExpression( state, item->expression );
513 sqlAddMsg( state, "Unable to build an expression for SELECT item # %d", item->id );
518 if( item->column_alias ) {
519 buffer_add( state->sql, " AS \"" );
520 buffer_add( state->sql, item->column_alias );
521 buffer_add_char( state->sql, '\"' );
526 buffer_add_char( state->sql, ' ' );
530 @brief Add an ORDER BY clause to the current query.
531 @param state Pointer to the query-building context.
532 @param ord_list Pointer to the first node in a linked list of OrderItems.
534 static void buildOrderBy( BuildSQLState* state, const OrderItem* ord_list ) {
535 add_newline( state );
536 buffer_add( state->sql, "ORDER BY" );
537 incr_indent( state );
539 int first = 1; // boolean
544 buffer_add_char( state->sql, ',' );
545 add_newline( state );
546 buildExpression( state, ord_list->expression );
548 sqlAddMsg( state, "Unable to add ORDER BY expression # %d", ord_list->id );
552 ord_list = ord_list->next;
555 decr_indent( state );
560 @brief Build an arbitrary expression.
561 @param state Pointer to the query-building context.
562 @param expr Pointer to the Expression representing the expression to be built.
564 static void buildExpression( BuildSQLState* state, const Expression* expr ) {
566 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
567 "Internal error: NULL pointer to Expression" ));
572 if( expr->parenthesize )
573 buffer_add_char( state->sql, '(' );
575 switch( expr->type ) {
578 buffer_add( state->sql, "NOT " );
580 buildExpression( state, expr->left_operand );
582 sqlAddMsg( state, "Unable to emit left operand in BETWEEN expression # %d",
587 buffer_add( state->sql, " BETWEEN " );
589 buildExpression( state, expr->subexp_list );
591 sqlAddMsg( state, "Unable to emit lower limit in BETWEEN expression # %d",
596 buffer_add( state->sql, " AND " );
598 buildExpression( state, expr->subexp_list->next );
600 sqlAddMsg( state, "Unable to emit upper limit in BETWEEN expression # %d",
607 if( !expr->bind ) { // Sanity check
608 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
609 "Internal error: no variable for bind variable expression" ));
612 buildBindVar( state, expr->bind );
616 buffer_add( state->sql, "NOT " );
618 if( expr->literal ) {
619 buffer_add( state->sql, expr->literal );
620 buffer_add_char( state->sql, ' ' );
622 buffer_add( state->sql, "FALSE " );
626 buffer_add( state->sql, "NOT " );
628 sqlAddMsg( state, "CASE expressions not yet supported" );
631 case EXP_CAST : // Type cast
633 buffer_add( state->sql, "NOT " );
635 sqlAddMsg( state, "Cast expressions not yet supported" );
638 case EXP_COLUMN : // Table column
640 buffer_add( state->sql, "NOT " );
642 if( expr->table_alias ) {
643 buffer_add_char( state->sql, '\"' );
644 buffer_add( state->sql, expr->table_alias );
645 buffer_add( state->sql, "\"." );
647 if( expr->column_name ) {
648 buffer_add( state->sql, expr->column_name );
650 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
651 "Column name not present in expression # %d", expr->id ));
656 if( !expr->subquery ) {
657 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
658 "No subquery found for EXIST expression # %d", expr->id ));
662 buffer_add( state->sql, "NOT " );
664 buffer_add( state->sql, "EXISTS (" );
665 incr_indent( state );
666 build_Query( state, expr->subquery );
667 decr_indent( state );
668 add_newline( state );
669 buffer_add_char( state->sql, ')' );
674 buffer_add( state->sql, "NOT " );
676 sqlAddMsg( state, "Field expressions not yet supported" );
680 buildFunction( state, expr );
683 if( expr->left_operand ) {
684 buildExpression( state, expr->left_operand );
685 if( !state->error ) {
687 buffer_add( state->sql, "NOT " );
688 buffer_add( state->sql, " IN (" );
690 if( expr->subquery ) {
691 incr_indent( state );
692 build_Query( state, expr->subquery );
694 sqlAddMsg( state, "Unable to build subquery for IN condition" );
696 decr_indent( state );
697 add_newline( state );
698 buffer_add_char( state->sql, ')' );
701 buildSeries( state, expr->subexp_list, NULL );
703 sqlAddMsg( state, "Unable to build IN list" );
705 buffer_add_char( state->sql, ')' );
711 if( expr->left_operand ) {
712 buildExpression( state, expr->left_operand );
714 sqlAddMsg( state, "Unable to emit left operand in IS NULL expression # %d",
721 buffer_add( state->sql, " IS NOT NULL" );
723 buffer_add( state->sql, " IS NULL" );
727 buffer_add( state->sql, "NOT " );
729 buffer_add( state->sql, "NULL" );
731 case EXP_NUMBER : // Numeric literal
732 if( !expr->literal ) {
733 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
734 "Internal error: No numeric value in string expression # %d", expr->id ));
737 buffer_add( state->sql, expr->literal );
742 buffer_add( state->sql, "NOT (" );
744 if( expr->left_operand ) {
745 buildExpression( state, expr->left_operand );
747 sqlAddMsg( state, "Unable to emit left operand in expression # %d",
752 buffer_add_char( state->sql, ' ' );
753 buffer_add( state->sql, expr->op );
754 buffer_add_char( state->sql, ' ' );
755 if( expr->right_operand ) {
756 buildExpression( state, expr->right_operand );
758 sqlAddMsg( state, "Unable to emit right operand in expression # %d",
765 buffer_add_char( state->sql, ')' );
770 buffer_add( state->sql, "NOT (" );
772 buildSeries( state, expr->subexp_list, expr->op );
774 sqlAddMsg( state, "Unable to build series expression using operator \"%s\"",
775 expr->op ? expr->op : "," );
778 buffer_add_char( state->sql, ')' );
781 case EXP_STRING : // String literal
782 if( !expr->literal ) {
783 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
784 "Internal error: No string value in string expression # %d", expr->id ));
787 // To do: escape special characters in the string
788 buffer_add_char( state->sql, '\'' );
789 buffer_add( state->sql, expr->literal );
790 buffer_add_char( state->sql, '\'' );
795 buffer_add( state->sql, "NOT " );
797 if( expr->subquery ) {
798 buffer_add_char( state->sql, '(' );
799 incr_indent( state );
800 build_Query( state, expr->subquery );
801 decr_indent( state );
802 add_newline( state );
803 buffer_add_char( state->sql, ')' );
805 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
806 "Internal error: No subquery in subquery expression # %d", expr->id ));
812 if( expr->parenthesize )
813 buffer_add_char( state->sql, ')' );
817 @brief Build a function call.
818 @param state Pointer to the query-building context.
819 @param exp Pointer to an Expression representing a function call.
821 This function does not currently accommodate certain functions with idiosyncratic
822 syntax, such as the absence of parentheses, or the use of certain keywords in
823 in the parameter list.
825 static void buildFunction( BuildSQLState* state, const Expression* expr ) {
827 buffer_add( state->sql, "NOT " );
829 // We rely on the input side to ensure that the function name is available
830 buffer_add( state->sql, expr->function_name );
831 buffer_add_char( state->sql, '(' );
833 // Add the parameters, if any
834 buildSeries( state, expr->subexp_list, NULL );
836 buffer_add_char( state->sql, ')' );
840 @brief Build a series of expressions separated by a specified operator, or by commas.
841 @param state Pointer to the query-building context.
842 @param subexp_list Pointer to the first Expression in a linked list.
843 @param op Pointer to the operator, or NULL for commas.
845 If the operator is AND or OR (in upper, lower, or mixed case), the second and all
846 subsequent operators will begin on a new line.
848 static void buildSeries( BuildSQLState* state, const Expression* subexp_list, const char* op ) {
851 return; // List is empty
853 int comma = 0; // Boolean; true if separator is a comma
854 int newline_needed = 0; // Boolean; true if operator is AND or OR
859 } else if( !strcmp( op, "," ))
861 else if( !strcasecmp( op, "AND" ) || !strcasecmp( op, "OR" ))
864 int first = 1; // Boolean; true for first item in list
865 while( subexp_list ) {
867 first = 0; // No separator needed yet
869 // Insert a separator
871 buffer_add( state->sql, ", " );
874 add_newline( state );
876 buffer_add_char( state->sql, ' ' );
878 buffer_add( state->sql, op );
879 buffer_add_char( state->sql, ' ' );
883 buildExpression( state, subexp_list );
884 subexp_list = subexp_list->next;
889 @brief Add the value of a bind variable to an SQL statement.
890 @param state Pointer to the query-building context.
891 @param bind Pointer to the bind variable whose value is to be added to the SQL.
893 The value may be a null, a scalar, or an array of nulls and/or scalars, depending on
894 the type of the bind variable.
896 static void buildBindVar( BuildSQLState* state, const BindVar* bind ) {
898 // Decide where to get the value, if any
899 const jsonObject* value = NULL;
900 if( bind->actual_value )
901 value = bind->actual_value;
902 else if( bind->default_value ) {
903 if( state->defaults_usable )
904 value = bind->default_value;
906 sqlAddMsg( state, "No confirmed value available for bind variable \"%s\"",
911 } else if( state->values_required ) {
912 sqlAddMsg( state, "No value available for bind variable \"%s\"", bind->name );
916 // No value available, and that's okay. Emit the name of the bind variable.
917 buffer_add_char( state->sql, ':' );
918 buffer_add( state->sql, bind->name );
922 // If we get to this point, we know that a value is available. Carry on.
924 int numeric = 0; // Boolean
925 if( BIND_NUM == bind->type || BIND_NUM_LIST == bind->type )
929 switch( bind->type ) {
932 buildScalar( state, numeric, value );
936 if( JSON_ARRAY == value->type ) {
937 // Iterate over array, emit each value
938 int first = 1; // Boolean
939 unsigned long max = value->size;
945 buffer_add( state->sql, ", " );
947 buildScalar( state, numeric, jsonObjectGetIndex( value, i ));
951 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
952 "Invalid value for bind variable; expected a list of values" ));
957 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
958 "Internal error: invalid type for bind variable" ));
964 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
965 "Unable to emit value of bind variable \"%s\"", bind->name ));
969 @brief Add a number or quoted string to an SQL statement.
970 @param state Pointer to the query-building context.
971 @param numeric Boolean; true if the value is expected to be a number
972 @param obj Pointer to the jsonObject whose value is to be added to the SQL.
974 static void buildScalar( BuildSQLState* state, int numeric, const jsonObject* obj ) {
975 switch( obj->type ) {
977 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
978 "Internal error: hash value for bind variable" ));
982 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
983 "Internal error: array value for bind variable" ));
989 "Invalid value for bind variable: expected a string, found a number" );
992 // To do: escape special characters in the string
993 buffer_add_char( state->sql, '\'' );
994 buffer_add( state->sql, jsonObjectGetString( obj ));
995 buffer_add_char( state->sql, '\'' );
1000 buffer_add( state->sql, jsonObjectGetString( obj ));
1003 "Invalid value for bind variable: expected a number, found a string" );
1008 buffer_add( state->sql, "NULL" );
1011 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1012 "Internal error: boolean value for bind variable" ));
1016 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1017 "Internal error: corrupted value for bind variable" ));
1024 @brief Start a new line in the output, with the current level of indentation.
1025 @param state Pointer to the query-building context.
1027 static void add_newline( BuildSQLState* state ) {
1028 buffer_add_char( state->sql, '\n' );
1031 static const char blanks[] = " "; // 32 blanks
1032 static const size_t maxlen = sizeof( blanks ) - 1;
1033 const int blanks_per_level = 3;
1034 int n = state->indent * blanks_per_level;
1036 size_t len = n >= maxlen ? maxlen : n;
1037 buffer_add_n( state->sql, blanks, len );
1043 @brief Increase the degree of indentation.
1044 @param state Pointer to the query-building context.
1046 static inline void incr_indent( BuildSQLState* state ) {
1051 @brief Reduce the degree of indentation.
1052 @param state Pointer to the query-building context.
1054 static inline void decr_indent( BuildSQLState* state ) {