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 sqlAddMsg( state, "Functions in FROM clause not yet supported" );
380 // Add a table alias, if possible
381 if( core_from->table_alias ) {
382 buffer_add( state->sql, " AS \"" );
383 buffer_add( state->sql, core_from->table_alias );
384 buffer_add( state->sql, "\" " );
386 else if( core_from->class_name ) {
387 buffer_add( state->sql, " AS \"" );
388 buffer_add( state->sql, core_from->class_name );
389 buffer_add( state->sql, "\" " );
391 buffer_add_char( state->sql, ' ' );
393 incr_indent( state );
394 FromRelation* join = core_from->join_list;
396 buildJoin( state, join );
398 sqlAddMsg( state, "Unable to build JOIN clause(s) for relation # %d",
404 decr_indent( state );
405 decr_indent( state );
409 @brief Add a JOIN clause.
410 @param state Pointer to the query-building context.
411 @param join Pointer to the FromRelation representing the JOIN to be added.
413 static void buildJoin( BuildSQLState* state, const FromRelation* join ) {
414 add_newline( state );
415 switch( join->join_type ) {
417 sqlAddMsg( state, "Non-join relation # %d in JOIN clause", join->id );
421 buffer_add( state->sql, "INNER JOIN " );
424 buffer_add( state->sql, "LEFT JOIN " );
427 buffer_add( state->sql, "RIGHT JOIN " );
430 buffer_add( state->sql, "FULL JOIN " );
433 sqlAddMsg( state, "Unrecognized join type in relation # %d", join->id );
438 switch( join->type ) {
441 if( !join->table_name || ! *join->table_name ) {
442 sqlAddMsg( state, "No relation designated for relation # %d", join->id );
446 buffer_add( state->sql, join->table_name );
450 if( !join->subquery ) {
451 sqlAddMsg( state, "Subquery expected, not found for relation # %d", join->id );
454 } else if( !join->table_alias ) {
455 sqlAddMsg( state, "No table alias for subquery in FROM relation # %d",
460 buffer_add_char( state->sql, '(' );
461 incr_indent( state );
462 build_Query( state, join->subquery );
463 decr_indent( state );
464 add_newline( state );
465 buffer_add_char( state->sql, ')' );
468 if( !join->table_name || ! *join->table_name ) {
469 sqlAddMsg( state, "Joins to functions not yet supported in relation # %d",
477 const char* effective_alias = join->table_alias;
478 if( !effective_alias )
479 effective_alias = join->class_name;
481 if( effective_alias ) {
482 buffer_add( state->sql, " AS \"" );
483 buffer_add( state->sql, effective_alias );
484 buffer_add_char( state->sql, '\"' );
487 if( join->on_clause ) {
488 incr_indent( state );
489 add_newline( state );
490 buffer_add( state->sql, "ON " );
491 buildExpression( state, join->on_clause );
492 decr_indent( state );
495 FromRelation* subjoin = join->join_list;
497 buildJoin( state, subjoin );
499 sqlAddMsg( state, "Unable to build JOIN clause(s) for relation # %d", join->id );
502 subjoin = subjoin->next;
507 @brief Build a SELECT list.
508 @param state Pointer to the query-building context.
509 @param item Pointer to the first in a linked list of SELECT items.
511 static void buildSelectList( BuildSQLState* state, const SelectItem* item ) {
516 buffer_add_char( state->sql, ',' );
517 add_newline( state );
518 buildExpression( state, item->expression );
520 sqlAddMsg( state, "Unable to build an expression for SELECT item # %d", item->id );
525 if( item->column_alias ) {
526 buffer_add( state->sql, " AS \"" );
527 buffer_add( state->sql, item->column_alias );
528 buffer_add_char( state->sql, '\"' );
533 buffer_add_char( state->sql, ' ' );
537 @brief Add a GROUP BY clause, if there is one, to the current query.
538 @param state Pointer to the query-building context.
539 @param sel_list Pointer to the first node in a linked list of SelectItems
541 We reference the GROUP BY items by number, not by repeating the expressions.
543 static void buildGroupBy( BuildSQLState* state, const SelectItem* sel_list ) {
544 int seq = 0; // Sequence number of current SelectItem
545 int first = 1; // Boolean: true for the first GROUPed BY item
549 if( sel_list->grouped_by ) {
551 add_newline( state );
552 buffer_add( state->sql, "GROUP BY " );
556 buffer_add( state->sql, ", " );
558 buffer_fadd( state->sql, "%d", seq );
561 sel_list = sel_list->next;
566 @brief Add an ORDER BY clause to the current query.
567 @param state Pointer to the query-building context.
568 @param ord_list Pointer to the first node in a linked list of OrderItems.
570 static void buildOrderBy( BuildSQLState* state, const OrderItem* ord_list ) {
571 add_newline( state );
572 buffer_add( state->sql, "ORDER BY" );
573 incr_indent( state );
575 int first = 1; // boolean
580 buffer_add_char( state->sql, ',' );
581 add_newline( state );
582 buildExpression( state, ord_list->expression );
584 sqlAddMsg( state, "Unable to add ORDER BY expression # %d", ord_list->id );
588 ord_list = ord_list->next;
591 decr_indent( state );
596 @brief Build an arbitrary expression.
597 @param state Pointer to the query-building context.
598 @param expr Pointer to the Expression representing the expression to be built.
600 static void buildExpression( BuildSQLState* state, const Expression* expr ) {
602 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
603 "Internal error: NULL pointer to Expression" ));
608 if( expr->parenthesize )
609 buffer_add_char( state->sql, '(' );
611 switch( expr->type ) {
614 buffer_add( state->sql, "NOT " );
616 buildExpression( state, expr->left_operand );
618 sqlAddMsg( state, "Unable to emit left operand in BETWEEN expression # %d",
623 buffer_add( state->sql, " BETWEEN " );
625 buildExpression( state, expr->subexp_list );
627 sqlAddMsg( state, "Unable to emit lower limit in BETWEEN expression # %d",
632 buffer_add( state->sql, " AND " );
634 buildExpression( state, expr->subexp_list->next );
636 sqlAddMsg( state, "Unable to emit upper limit in BETWEEN expression # %d",
643 if( !expr->bind ) { // Sanity check
644 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
645 "Internal error: no variable for bind variable expression" ));
648 buildBindVar( state, expr->bind );
652 buffer_add( state->sql, "NOT " );
654 if( expr->literal ) {
655 buffer_add( state->sql, expr->literal );
656 buffer_add_char( state->sql, ' ' );
658 buffer_add( state->sql, "FALSE " );
662 buffer_add( state->sql, "NOT " );
664 sqlAddMsg( state, "CASE expressions not yet supported" );
667 case EXP_CAST : // Type cast
669 buffer_add( state->sql, "NOT " );
671 sqlAddMsg( state, "Cast expressions not yet supported" );
674 case EXP_COLUMN : // Table column
676 buffer_add( state->sql, "NOT " );
678 if( expr->table_alias ) {
679 buffer_add_char( state->sql, '\"' );
680 buffer_add( state->sql, expr->table_alias );
681 buffer_add( state->sql, "\"." );
683 if( expr->column_name ) {
684 buffer_add( state->sql, expr->column_name );
686 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
687 "Column name not present in expression # %d", expr->id ));
692 if( !expr->subquery ) {
693 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
694 "No subquery found for EXIST expression # %d", expr->id ));
698 buffer_add( state->sql, "NOT " );
700 buffer_add( state->sql, "EXISTS (" );
701 incr_indent( state );
702 build_Query( state, expr->subquery );
703 decr_indent( state );
704 add_newline( state );
705 buffer_add_char( state->sql, ')' );
710 buffer_add( state->sql, "NOT " );
712 sqlAddMsg( state, "Field expressions not yet supported" );
716 buildFunction( state, expr );
719 if( expr->left_operand ) {
720 buildExpression( state, expr->left_operand );
721 if( !state->error ) {
723 buffer_add( state->sql, "NOT " );
724 buffer_add( state->sql, " IN (" );
726 if( expr->subquery ) {
727 incr_indent( state );
728 build_Query( state, expr->subquery );
730 sqlAddMsg( state, "Unable to build subquery for IN condition" );
732 decr_indent( state );
733 add_newline( state );
734 buffer_add_char( state->sql, ')' );
737 buildSeries( state, expr->subexp_list, NULL );
739 sqlAddMsg( state, "Unable to build IN list" );
741 buffer_add_char( state->sql, ')' );
747 if( expr->left_operand ) {
748 buildExpression( state, expr->left_operand );
750 sqlAddMsg( state, "Unable to emit left operand in IS NULL expression # %d",
757 buffer_add( state->sql, " IS NOT NULL" );
759 buffer_add( state->sql, " IS NULL" );
763 buffer_add( state->sql, "NOT " );
765 buffer_add( state->sql, "NULL" );
767 case EXP_NUMBER : // Numeric literal
768 if( !expr->literal ) {
769 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
770 "Internal error: No numeric value in string expression # %d", expr->id ));
773 buffer_add( state->sql, expr->literal );
778 buffer_add( state->sql, "NOT (" );
780 if( expr->left_operand ) {
781 buildExpression( state, expr->left_operand );
783 sqlAddMsg( state, "Unable to emit left operand in expression # %d",
788 buffer_add_char( state->sql, ' ' );
789 buffer_add( state->sql, expr->op );
790 buffer_add_char( state->sql, ' ' );
791 if( expr->right_operand ) {
792 buildExpression( state, expr->right_operand );
794 sqlAddMsg( state, "Unable to emit right operand in expression # %d",
801 buffer_add_char( state->sql, ')' );
806 buffer_add( state->sql, "NOT (" );
808 buildSeries( state, expr->subexp_list, expr->op );
810 sqlAddMsg( state, "Unable to build series expression using operator \"%s\"",
811 expr->op ? expr->op : "," );
814 buffer_add_char( state->sql, ')' );
817 case EXP_STRING : // String literal
818 if( !expr->literal ) {
819 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
820 "Internal error: No string value in string expression # %d", expr->id ));
823 // To do: escape special characters in the string
824 buffer_add_char( state->sql, '\'' );
825 buffer_add( state->sql, expr->literal );
826 buffer_add_char( state->sql, '\'' );
831 buffer_add( state->sql, "NOT " );
833 if( expr->subquery ) {
834 buffer_add_char( state->sql, '(' );
835 incr_indent( state );
836 build_Query( state, expr->subquery );
837 decr_indent( state );
838 add_newline( state );
839 buffer_add_char( state->sql, ')' );
841 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
842 "Internal error: No subquery in subquery expression # %d", expr->id ));
848 if( expr->parenthesize )
849 buffer_add_char( state->sql, ')' );
853 @brief Build a function call.
854 @param state Pointer to the query-building context.
855 @param exp Pointer to an Expression representing a function call.
857 This function does not currently accommodate certain functions with idiosyncratic
858 syntax, such as the absence of parentheses, or the use of certain keywords in
859 in the parameter list.
861 static void buildFunction( BuildSQLState* state, const Expression* expr ) {
863 buffer_add( state->sql, "NOT " );
865 // We rely on the input side to ensure that the function name is available
866 buffer_add( state->sql, expr->function_name );
867 buffer_add_char( state->sql, '(' );
869 // Add the parameters, if any
870 buildSeries( state, expr->subexp_list, NULL );
872 buffer_add_char( state->sql, ')' );
876 @brief Build a series of expressions separated by a specified operator, or by commas.
877 @param state Pointer to the query-building context.
878 @param subexp_list Pointer to the first Expression in a linked list.
879 @param op Pointer to the operator, or NULL for commas.
881 If the operator is AND or OR (in upper, lower, or mixed case), the second and all
882 subsequent operators will begin on a new line.
884 static void buildSeries( BuildSQLState* state, const Expression* subexp_list, const char* op ) {
887 return; // List is empty
889 int comma = 0; // Boolean; true if separator is a comma
890 int newline_needed = 0; // Boolean; true if operator is AND or OR
895 } else if( !strcmp( op, "," ))
897 else if( !strcasecmp( op, "AND" ) || !strcasecmp( op, "OR" ))
900 int first = 1; // Boolean; true for first item in list
901 while( subexp_list ) {
903 first = 0; // No separator needed yet
905 // Insert a separator
907 buffer_add( state->sql, ", " );
910 add_newline( state );
912 buffer_add_char( state->sql, ' ' );
914 buffer_add( state->sql, op );
915 buffer_add_char( state->sql, ' ' );
919 buildExpression( state, subexp_list );
920 subexp_list = subexp_list->next;
925 @brief Add the value of a bind variable to an SQL statement.
926 @param state Pointer to the query-building context.
927 @param bind Pointer to the bind variable whose value is to be added to the SQL.
929 The value may be a null, a scalar, or an array of nulls and/or scalars, depending on
930 the type of the bind variable.
932 static void buildBindVar( BuildSQLState* state, const BindVar* bind ) {
934 // Decide where to get the value, if any
935 const jsonObject* value = NULL;
936 if( bind->actual_value )
937 value = bind->actual_value;
938 else if( bind->default_value ) {
939 if( state->defaults_usable )
940 value = bind->default_value;
942 sqlAddMsg( state, "No confirmed value available for bind variable \"%s\"",
947 } else if( state->values_required ) {
948 sqlAddMsg( state, "No value available for bind variable \"%s\"", bind->name );
952 // No value available, and that's okay. Emit the name of the bind variable.
953 buffer_add_char( state->sql, ':' );
954 buffer_add( state->sql, bind->name );
958 // If we get to this point, we know that a value is available. Carry on.
960 int numeric = 0; // Boolean
961 if( BIND_NUM == bind->type || BIND_NUM_LIST == bind->type )
965 switch( bind->type ) {
968 buildScalar( state, numeric, value );
972 if( JSON_ARRAY == value->type ) {
973 // Iterate over array, emit each value
974 int first = 1; // Boolean
975 unsigned long max = value->size;
981 buffer_add( state->sql, ", " );
983 buildScalar( state, numeric, jsonObjectGetIndex( value, i ));
987 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
988 "Invalid value for bind variable; expected a list of values" ));
993 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
994 "Internal error: invalid type for bind variable" ));
1000 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1001 "Unable to emit value of bind variable \"%s\"", bind->name ));
1005 @brief Add a number or quoted string to an SQL statement.
1006 @param state Pointer to the query-building context.
1007 @param numeric Boolean; true if the value is expected to be a number
1008 @param obj Pointer to the jsonObject whose value is to be added to the SQL.
1010 static void buildScalar( BuildSQLState* state, int numeric, const jsonObject* obj ) {
1011 switch( obj->type ) {
1013 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1014 "Internal error: hash value for bind variable" ));
1018 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1019 "Internal error: array value for bind variable" ));
1025 "Invalid value for bind variable: expected a string, found a number" );
1028 // To do: escape special characters in the string
1029 buffer_add_char( state->sql, '\'' );
1030 buffer_add( state->sql, jsonObjectGetString( obj ));
1031 buffer_add_char( state->sql, '\'' );
1036 buffer_add( state->sql, jsonObjectGetString( obj ));
1039 "Invalid value for bind variable: expected a number, found a string" );
1044 buffer_add( state->sql, "NULL" );
1047 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1048 "Internal error: boolean value for bind variable" ));
1052 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1053 "Internal error: corrupted value for bind variable" ));
1060 @brief Start a new line in the output, with the current level of indentation.
1061 @param state Pointer to the query-building context.
1063 static void add_newline( BuildSQLState* state ) {
1064 buffer_add_char( state->sql, '\n' );
1067 static const char blanks[] = " "; // 32 blanks
1068 static const size_t maxlen = sizeof( blanks ) - 1;
1069 const int blanks_per_level = 3;
1070 int n = state->indent * blanks_per_level;
1072 size_t len = n >= maxlen ? maxlen : n;
1073 buffer_add_n( state->sql, blanks, len );
1079 @brief Increase the degree of indentation.
1080 @param state Pointer to the query-building context.
1082 static inline void incr_indent( BuildSQLState* state ) {
1087 @brief Reduce the degree of indentation.
1088 @param state Pointer to the query-building context.
1090 static inline void decr_indent( BuildSQLState* state ) {