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 buildExtract( BuildSQLState* state, const Expression* expr );
30 static void buildSeries( BuildSQLState* state, const Expression* subexp_list, const char* op );
31 static void buildBindVar( BuildSQLState* state, const BindVar* bind );
32 static void buildScalar( BuildSQLState* state, int numeric, const jsonObject* obj );
34 static void add_newline( BuildSQLState* state );
35 static inline void incr_indent( BuildSQLState* state );
36 static inline void decr_indent( BuildSQLState* state );
39 @brief Create a jsonObject representing the current list of bind variables.
40 @param bindvar_list Pointer to the bindvar_list member of a BuildSQLState.
41 @return Pointer to the newly created jsonObject.
43 The returned jsonObject is a (possibly empty) JSON_HASH, keyed on the names of the bind
44 variables. The data for each is another level of JSON_HASH with a fixed set of tags:
48 - "default_value" (as a jsonObject)
49 - "actual_value" (as a jsonObject)
51 Any non-existent values are represented as JSON_NULLs.
53 The calling code is responsible for freeing the returned jsonOjbect by calling
56 jsonObject* oilsBindVarList( osrfHash* bindvar_list ) {
57 jsonObject* list = jsonNewObjectType( JSON_HASH );
59 if( bindvar_list && osrfHashGetCount( bindvar_list )) {
60 // Traverse our internal list of bind variables
62 osrfHashIterator* iter = osrfNewHashIterator( bindvar_list );
63 while(( bind = osrfHashIteratorNext( iter ))) {
64 // Create an hash to represent the bind variable
65 jsonObject* bind_obj = jsonNewObjectType( JSON_HASH );
67 // Add an entry for each attribute
68 jsonObject* attr = jsonNewObject( bind->label );
69 jsonObjectSetKey( bind_obj, "label", attr );
71 const char* type = NULL;
72 switch( bind->type ) {
89 attr = jsonNewObject( type );
90 jsonObjectSetKey( bind_obj, "type", attr );
92 attr = jsonNewObject( bind->description );
93 jsonObjectSetKey( bind_obj, "description", attr );
95 attr = jsonObjectClone( bind->default_value );
96 jsonObjectSetKey( bind_obj, "default_value", attr );
98 attr = jsonObjectClone( bind->actual_value );
99 jsonObjectSetKey( bind_obj, "actual_value", attr );
101 // Add the bind variable to the list
102 jsonObjectSetKey( list, osrfHashIteratorKey( iter ), bind_obj );
104 osrfHashIteratorFree( iter );
111 @brief Apply values to bind variables, overriding the defaults, if any.
112 @param state Pointer to the query-building context.
113 @param bindings A JSON_HASH of values.
114 @return 0 if successful, or 1 if not.
116 The @a bindings parameter must be a JSON_HASH. The keys are the names of bind variables.
117 The values are the corresponding values for the variables.
119 int oilsApplyBindValues( BuildSQLState* state, const jsonObject* bindings ) {
121 osrfLogError( OSRF_LOG_MARK, "NULL pointer to state" );
123 } else if( !bindings ) {
124 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
125 "Internal error: No pointer to bindings" ));
127 } else if( bindings->type != JSON_HASH ) {
128 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
129 "Internal error: bindings parameter is not a JSON_HASH" ));
134 jsonObject* value = NULL;
135 jsonIterator* iter = jsonNewIterator( bindings );
136 while(( value = jsonIteratorNext( iter ))) {
137 const char* var_name = iter->key;
138 BindVar* bind = osrfHashGet( state->bindvar_list, var_name );
140 // Apply or replace the value for the specified variable
141 if( bind->actual_value )
142 jsonObjectFree( bind->actual_value );
143 bind->actual_value = jsonObjectClone( value );
145 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
146 "Can't assign value to bind variable \"%s\": no such variable", var_name ));
150 jsonIteratorFree( iter );
156 @brief Build an SQL query.
157 @param state Pointer to the query-building context.
158 @param query Pointer to the query to be built.
159 @return Zero if successful, or 1 if not.
161 Clear the output buffer, call build_Query() to do the work, and add a closing semicolon.
163 int buildSQL( BuildSQLState* state, const StoredQ* query ) {
165 buffer_reset( state->sql );
167 build_Query( state, query );
168 if( ! state->error ) {
169 // Remove the trailing space, if there is one, and add a semicolon.
170 char c = buffer_chomp( state->sql );
172 buffer_add_char( state->sql, c ); // oops, not a space; put it back
173 buffer_add( state->sql, ";\n" );
179 @brief Build an SQL query, appending it to what has been built so far.
180 @param state Pointer to the query-building context.
181 @param query Pointer to the query to be built.
183 Look at the query type and branch to the corresponding routine.
185 static void build_Query( BuildSQLState* state, const StoredQ* query ) {
186 if( buffer_length( state->sql ))
187 add_newline( state );
189 switch( query->type ) {
191 buildSelect( state, query );
194 buildCombo( state, query, "UNION" );
197 buildCombo( state, query, "INTERSECT" );
200 buildCombo( state, query, "EXCEPT" );
203 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
204 "Internal error: invalid query type %d in query # %d",
205 query->type, query->id ));
212 @brief Build a UNION, INTERSECT, or EXCEPT query.
213 @param state Pointer to the query-building context.
214 @param query Pointer to the query to be built.
215 @param type_str The query type, as a string.
217 static void buildCombo( BuildSQLState* state, const StoredQ* query, const char* type_str ) {
219 QSeq* seq = query->child_list;
221 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
222 "Internal error: No child queries within %s query # %d",
223 type_str, query->id ));
228 // Traverse the list of child queries
230 build_Query( state, seq->child_query );
232 sqlAddMsg( state, "Unable to build child query # %d within %s query %d",
233 seq->child_query->id, type_str, query->id );
238 add_newline( state );
239 buffer_add( state->sql, type_str );
240 buffer_add_char( state->sql, ' ' );
242 buffer_add( state->sql, "ALL " );
250 @brief Build a SELECT statement.
251 @param state Pointer to the query-building context.
252 @param query Pointer to the StoredQ structure that represents the query.
254 static void buildSelect( BuildSQLState* state, const StoredQ* query ) {
256 FromRelation* from_clause = query->from_clause;
258 sqlAddMsg( state, "SELECT has no FROM clause in query # %d", query->id );
263 // To do: get SELECT list; just a stub here
264 buffer_add( state->sql, "SELECT" );
265 incr_indent( state );
266 buildSelectList( state, query->select_list );
268 sqlAddMsg( state, "Unable to build SELECT list for query # %d", query->id );
272 decr_indent( state );
274 // Build FROM clause, if there is one
275 if( query->from_clause ) {
276 buildFrom( state, query->from_clause );
278 sqlAddMsg( state, "Unable to build FROM clause for query # %d", query->id );
284 // Build WHERE clause, if there is one
285 if( query->where_clause ) {
286 add_newline( state );
287 buffer_add( state->sql, "WHERE" );
288 incr_indent( state );
289 add_newline( state );
290 buildExpression( state, query->where_clause );
292 sqlAddMsg( state, "Unable to build WHERE clause for query # %d", query->id );
296 decr_indent( state );
299 // Build GROUP BY clause, if there is one
300 buildGroupBy( state, query->select_list );
302 // Build HAVING clause, if there is one
303 if( query->having_clause ) {
304 add_newline( state );
305 buffer_add( state->sql, "HAVING" );
306 incr_indent( state );
307 add_newline( state );
308 buildExpression( state, query->having_clause );
310 sqlAddMsg( state, "Unable to build HAVING clause for query # %d", query->id );
314 decr_indent( state );
317 // Build ORDER BY clause, if there is one
318 if( query->order_by_list ) {
319 buildOrderBy( state, query->order_by_list );
321 sqlAddMsg( state, "Unable to build ORDER BY clause for query # %d", query->id );
327 // To do: Build LIMIT clause, if there is one
329 // To do: Build OFFSET clause, if there is one
335 @brief Build a FROM clause.
336 @param Pointer to the query-building context.
337 @param Pointer to the StoredQ query to which the FROM clause belongs.
339 static void buildFrom( BuildSQLState* state, const FromRelation* core_from ) {
341 add_newline( state );
342 buffer_add( state->sql, "FROM" );
343 incr_indent( state );
344 add_newline( state );
346 switch( core_from->type ) {
347 case FRT_RELATION : {
348 char* relation = core_from->table_name;
350 if( !core_from->class_name ) {
351 sqlAddMsg( state, "No relation specified for core relation # %d",
357 // Look up table name, view name, or source_definition in the IDL
358 osrfHash* class_hash = osrfHashGet( oilsIDL(), core_from->class_name );
359 relation = oilsGetRelation( class_hash );
363 buffer_add( state->sql, relation );
364 if( !core_from->table_name )
365 free( relation ); // In this case we strdup'd it, must free it
369 buffer_add_char( state->sql, '(' );
370 incr_indent( state );
371 build_Query( state, core_from->subquery );
372 decr_indent( state );
373 add_newline( state );
374 buffer_add_char( state->sql, ')' );
377 buildFunction( state, core_from->function_call );
378 if ( state->error ) {
380 "Unable to include function call # %d in FROM relation # %d",
381 core_from->function_call->id, core_from->id );
386 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
387 "Internal error: Invalid type # %d in FROM relation # %d",
388 core_from->type, core_from->id ));
393 // Add a table alias, if possible
394 if( core_from->table_alias ) {
395 buffer_add( state->sql, " AS \"" );
396 buffer_add( state->sql, core_from->table_alias );
397 buffer_add( state->sql, "\" " );
399 else if( core_from->class_name ) {
400 buffer_add( state->sql, " AS \"" );
401 buffer_add( state->sql, core_from->class_name );
402 buffer_add( state->sql, "\" " );
404 buffer_add_char( state->sql, ' ' );
406 incr_indent( state );
407 FromRelation* join = core_from->join_list;
409 buildJoin( state, join );
411 sqlAddMsg( state, "Unable to build JOIN clause(s) for relation # %d",
417 decr_indent( state );
418 decr_indent( state );
422 @brief Add a JOIN clause.
423 @param state Pointer to the query-building context.
424 @param join Pointer to the FromRelation representing the JOIN to be added.
426 static void buildJoin( BuildSQLState* state, const FromRelation* join ) {
427 add_newline( state );
428 switch( join->join_type ) {
430 sqlAddMsg( state, "Non-join relation # %d in JOIN clause", join->id );
434 buffer_add( state->sql, "INNER JOIN " );
437 buffer_add( state->sql, "LEFT JOIN " );
440 buffer_add( state->sql, "RIGHT JOIN " );
443 buffer_add( state->sql, "FULL JOIN " );
446 sqlAddMsg( state, "Unrecognized join type in relation # %d", join->id );
451 switch( join->type ) {
454 if( !join->table_name || ! *join->table_name ) {
455 sqlAddMsg( state, "No relation designated for relation # %d", join->id );
459 buffer_add( state->sql, join->table_name );
463 if( !join->subquery ) {
464 sqlAddMsg( state, "Subquery expected, not found for relation # %d", join->id );
467 } else if( !join->table_alias ) {
468 sqlAddMsg( state, "No table alias for subquery in FROM relation # %d",
473 buffer_add_char( state->sql, '(' );
474 incr_indent( state );
475 build_Query( state, join->subquery );
476 decr_indent( state );
477 add_newline( state );
478 buffer_add_char( state->sql, ')' );
481 if( !join->table_name || ! *join->table_name ) {
482 sqlAddMsg( state, "Joins to functions not yet supported in relation # %d",
490 const char* effective_alias = join->table_alias;
491 if( !effective_alias )
492 effective_alias = join->class_name;
494 if( effective_alias ) {
495 buffer_add( state->sql, " AS \"" );
496 buffer_add( state->sql, effective_alias );
497 buffer_add_char( state->sql, '\"' );
500 if( join->on_clause ) {
501 incr_indent( state );
502 add_newline( state );
503 buffer_add( state->sql, "ON " );
504 buildExpression( state, join->on_clause );
505 decr_indent( state );
508 FromRelation* subjoin = join->join_list;
510 buildJoin( state, subjoin );
512 sqlAddMsg( state, "Unable to build JOIN clause(s) for relation # %d", join->id );
515 subjoin = subjoin->next;
520 @brief Build a SELECT list.
521 @param state Pointer to the query-building context.
522 @param item Pointer to the first in a linked list of SELECT items.
524 static void buildSelectList( BuildSQLState* state, const SelectItem* item ) {
529 buffer_add_char( state->sql, ',' );
530 add_newline( state );
531 buildExpression( state, item->expression );
533 sqlAddMsg( state, "Unable to build an expression for SELECT item # %d", item->id );
538 if( item->column_alias ) {
539 buffer_add( state->sql, " AS \"" );
540 buffer_add( state->sql, item->column_alias );
541 buffer_add_char( state->sql, '\"' );
546 buffer_add_char( state->sql, ' ' );
550 @brief Add a GROUP BY clause, if there is one, to the current query.
551 @param state Pointer to the query-building context.
552 @param sel_list Pointer to the first node in a linked list of SelectItems
554 We reference the GROUP BY items by number, not by repeating the expressions.
556 static void buildGroupBy( BuildSQLState* state, const SelectItem* sel_list ) {
557 int seq = 0; // Sequence number of current SelectItem
558 int first = 1; // Boolean: true for the first GROUPed BY item
562 if( sel_list->grouped_by ) {
564 add_newline( state );
565 buffer_add( state->sql, "GROUP BY " );
569 buffer_add( state->sql, ", " );
571 buffer_fadd( state->sql, "%d", seq );
574 sel_list = sel_list->next;
579 @brief Add an ORDER BY clause to the current query.
580 @param state Pointer to the query-building context.
581 @param ord_list Pointer to the first node in a linked list of OrderItems.
583 static void buildOrderBy( BuildSQLState* state, const OrderItem* ord_list ) {
584 add_newline( state );
585 buffer_add( state->sql, "ORDER BY" );
586 incr_indent( state );
588 int first = 1; // boolean
593 buffer_add_char( state->sql, ',' );
594 add_newline( state );
595 buildExpression( state, ord_list->expression );
597 sqlAddMsg( state, "Unable to add ORDER BY expression # %d", ord_list->id );
601 ord_list = ord_list->next;
604 decr_indent( state );
609 @brief Build an arbitrary expression.
610 @param state Pointer to the query-building context.
611 @param expr Pointer to the Expression representing the expression to be built.
613 static void buildExpression( BuildSQLState* state, const Expression* expr ) {
615 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
616 "Internal error: NULL pointer to Expression" ));
621 if( expr->parenthesize )
622 buffer_add_char( state->sql, '(' );
624 switch( expr->type ) {
627 buffer_add( state->sql, "NOT " );
629 buildExpression( state, expr->left_operand );
631 sqlAddMsg( state, "Unable to emit left operand in BETWEEN expression # %d",
636 buffer_add( state->sql, " BETWEEN " );
638 buildExpression( state, expr->subexp_list );
640 sqlAddMsg( state, "Unable to emit lower limit in BETWEEN expression # %d",
645 buffer_add( state->sql, " AND " );
647 buildExpression( state, expr->subexp_list->next );
649 sqlAddMsg( state, "Unable to emit upper limit in BETWEEN expression # %d",
656 if( !expr->bind ) { // Sanity check
657 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
658 "Internal error: no variable for bind variable expression" ));
661 buildBindVar( state, expr->bind );
665 buffer_add( state->sql, "NOT " );
667 if( expr->literal ) {
668 buffer_add( state->sql, expr->literal );
669 buffer_add_char( state->sql, ' ' );
671 buffer_add( state->sql, "FALSE " );
674 buildCase( state, expr );
676 sqlAddMsg( state, "Unable to build CASE expression # %d", expr->id );
679 case EXP_CAST : // Type cast
681 buffer_add( state->sql, "NOT " );
683 buffer_add( state->sql, "CAST (" );
684 buildExpression( state, expr->left_operand );
686 sqlAddMsg( state, "Unable to build left operand for CAST expression # %d",
689 buffer_add( state->sql, " AS " );
690 if( expr->cast_type && expr->cast_type->datatype_name ) {
691 buffer_add( state->sql, expr->cast_type->datatype_name );
692 buffer_add_char( state->sql, ')' );
694 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
695 "No datatype available for CAST expression # %d", expr->id ));
700 case EXP_COLUMN : // Table column
702 buffer_add( state->sql, "NOT " );
704 if( expr->table_alias ) {
705 buffer_add_char( state->sql, '\"' );
706 buffer_add( state->sql, expr->table_alias );
707 buffer_add( state->sql, "\"." );
709 if( expr->column_name ) {
710 buffer_add( state->sql, expr->column_name );
712 buffer_add_char( state->sql, '*' );
716 if( !expr->subquery ) {
717 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
718 "No subquery found for EXIST expression # %d", expr->id ));
722 buffer_add( state->sql, "NOT " );
724 buffer_add( state->sql, "EXISTS (" );
725 incr_indent( state );
726 build_Query( state, expr->subquery );
727 decr_indent( state );
728 add_newline( state );
729 buffer_add_char( state->sql, ')' );
733 buildFunction( state, expr );
736 if( expr->left_operand ) {
737 buildExpression( state, expr->left_operand );
738 if( !state->error ) {
740 buffer_add( state->sql, "NOT " );
741 buffer_add( state->sql, " IN (" );
743 if( expr->subquery ) {
744 incr_indent( state );
745 build_Query( state, expr->subquery );
747 sqlAddMsg( state, "Unable to build subquery for IN condition" );
749 decr_indent( state );
750 add_newline( state );
751 buffer_add_char( state->sql, ')' );
754 buildSeries( state, expr->subexp_list, NULL );
756 sqlAddMsg( state, "Unable to build IN list" );
758 buffer_add_char( state->sql, ')' );
764 if( expr->left_operand ) {
765 buildExpression( state, expr->left_operand );
767 sqlAddMsg( state, "Unable to emit left operand in IS NULL expression # %d",
774 buffer_add( state->sql, " IS NOT NULL" );
776 buffer_add( state->sql, " IS NULL" );
780 buffer_add( state->sql, "NOT " );
782 buffer_add( state->sql, "NULL" );
784 case EXP_NUMBER : // Numeric literal
785 if( !expr->literal ) {
786 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
787 "Internal error: No numeric value in string expression # %d", expr->id ));
790 buffer_add( state->sql, expr->literal );
795 buffer_add( state->sql, "NOT (" );
797 if( expr->left_operand ) {
798 buildExpression( state, expr->left_operand );
800 sqlAddMsg( state, "Unable to emit left operand in expression # %d",
805 buffer_add_char( state->sql, ' ' );
806 buffer_add( state->sql, expr->op );
807 buffer_add_char( state->sql, ' ' );
808 if( expr->right_operand ) {
809 buildExpression( state, expr->right_operand );
811 sqlAddMsg( state, "Unable to emit right operand in expression # %d",
818 buffer_add_char( state->sql, ')' );
823 buffer_add( state->sql, "NOT (" );
825 buildSeries( state, expr->subexp_list, expr->op );
827 sqlAddMsg( state, "Unable to build series expression using operator \"%s\"",
828 expr->op ? expr->op : "," );
831 buffer_add_char( state->sql, ')' );
834 case EXP_STRING : // String literal
835 if( !expr->literal ) {
836 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
837 "Internal error: No string value in string expression # %d", expr->id ));
840 char* str = strdup( expr->literal );
841 dbi_conn_quote_string( state->dbhandle, &str );
843 buffer_add( state->sql, str );
846 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
847 "Unable to format string literal \"%s\" for expression # %d",
848 expr->literal, expr->id ));
855 buffer_add( state->sql, "NOT " );
857 if( expr->subquery ) {
858 buffer_add_char( state->sql, '(' );
859 incr_indent( state );
860 build_Query( state, expr->subquery );
861 decr_indent( state );
862 add_newline( state );
863 buffer_add_char( state->sql, ')' );
865 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
866 "Internal error: No subquery in subquery expression # %d", expr->id ));
872 if( expr->parenthesize )
873 buffer_add_char( state->sql, ')' );
877 @brief Build a CASE expression.
878 @param state Pointer to the query-building context.
879 @param exp Pointer to an Expression representing a CASE expression.
881 static void buildCase( BuildSQLState* state, const Expression* expr ) {
883 if( ! expr->left_operand ) {
884 sqlAddMsg( state, "CASE expression # %d has no left operand", expr->id );
887 } else if( ! expr->branch_list ) {
888 sqlAddMsg( state, "CASE expression # %d has no branches", expr->id );
894 buffer_add( state->sql, "NOT (" );
896 // left_operand is the expression on which we shall branch
897 buffer_add( state->sql, "CASE " );
898 buildExpression( state, expr->left_operand );
900 sqlAddMsg( state, "Unable to build operand of CASE expression # %d", expr->id );
904 incr_indent( state );
906 // Emit each branch in turn
907 CaseBranch* branch = expr->branch_list;
909 add_newline( state );
911 if( branch->condition ) {
912 // Emit a WHEN condition
913 buffer_add( state->sql, "WHEN " );
914 buildExpression( state, branch->condition );
915 incr_indent( state );
916 add_newline( state );
917 buffer_add( state->sql, "THEN " );
920 buffer_add( state->sql, "ELSE " );
921 incr_indent( state );
922 add_newline( state );
925 // Emit the THEN expression
926 buildExpression( state, branch->result );
927 decr_indent( state );
929 branch = branch->next;
932 decr_indent( state );
933 add_newline( state );
934 buffer_add( state->sql, "END" );
937 buffer_add( state->sql, ")" );
941 @brief Build a function call.
942 @param state Pointer to the query-building context.
943 @param exp Pointer to an Expression representing a function call.
945 This function does not currently accommodate certain functions with idiosyncratic
946 syntax, such as the absence of parentheses, or the use of certain keywords in
947 in the parameter list.
949 static void buildFunction( BuildSQLState* state, const Expression* expr ) {
951 buffer_add( state->sql, "NOT " );
953 // If a subfield is specified, the function call
954 // needs an extra layer of parentheses
955 if( expr->column_name )
956 buffer_add_char( state->sql, '(' );
958 // First, check for some specific functions with peculiar syntax, and treat them
959 // as special exceptions. We rely on the input side to ensure that the function
960 // name is available.
961 if( !strcasecmp( expr->function_name, "EXTRACT" )) {
962 buildExtract( state, expr );
964 // Not a special exception.
965 buffer_add( state->sql, expr->function_name );
966 buffer_add_char( state->sql, '(' );
968 // Add the parameters, if any
969 buildSeries( state, expr->subexp_list, NULL );
971 buffer_add_char( state->sql, ')' );
974 if( expr->column_name ) {
975 // Add the name of the subfield
976 buffer_add( state->sql, ").\"" );
977 buffer_add( state->sql, expr->column_name );
978 buffer_add_char( state->sql, '\"' );
983 @brief Build a call to the EXTRACT function, with its peculiar syntax.
984 @param state Pointer to the query-building context.
985 @param exp Pointer to an Expression representing an EXTRACT call.
987 static void buildExtract( BuildSQLState* state, const Expression* expr ) {
989 const Expression* arg = expr->subexp_list;
994 "No arguments supplied to EXTRACT function in expression # %d", expr->id );
997 } else if( arg->type != EXP_STRING ) {
999 "First argument to EXTRACT is not a string in expression # %d", expr->id );
1003 // check the first argument against a list of valid values
1004 if( strcasecmp( arg->literal, "century" )
1005 && strcasecmp( arg->literal, "day" )
1006 && strcasecmp( arg->literal, "decade" )
1007 && strcasecmp( arg->literal, "dow" )
1008 && strcasecmp( arg->literal, "doy" )
1009 && strcasecmp( arg->literal, "epoch" )
1010 && strcasecmp( arg->literal, "hour" )
1011 && strcasecmp( arg->literal, "isodow" )
1012 && strcasecmp( arg->literal, "isoyear" )
1013 && strcasecmp( arg->literal, "microseconds" )
1014 && strcasecmp( arg->literal, "millennium" )
1015 && strcasecmp( arg->literal, "milliseconds" )
1016 && strcasecmp( arg->literal, "minute" )
1017 && strcasecmp( arg->literal, "month" )
1018 && strcasecmp( arg->literal, "quarter" )
1019 && strcasecmp( arg->literal, "second" )
1020 && strcasecmp( arg->literal, "timezone" )
1021 && strcasecmp( arg->literal, "timezone_hour" )
1022 && strcasecmp( arg->literal, "timezone_minute" )
1023 && strcasecmp( arg->literal, "week" )
1024 && strcasecmp( arg->literal, "year" )) {
1026 "Invalid name \"%s\" as EXTRACT argument in expression # %d",
1027 expr->literal, expr->id );
1032 buffer_add( state->sql, "EXTRACT(" );
1033 buffer_add( state->sql, arg->literal );
1034 buffer_add( state->sql, " FROM " );
1039 "Only one argument supplied to EXTRACT function in expression # %d", expr->id );
1044 // The second parameter must be of type timestamp, time, or interval. We don't have
1045 // a good way of checking it here, so we rely on PostgreSQL to complain if necessary.
1046 buildExpression( state, arg );
1047 buffer_add_char( state->sql, ')' );
1051 "Too many one arguments supplied to EXTRACT function in expression # %d", expr->id );
1058 @brief Build a series of expressions separated by a specified operator, or by commas.
1059 @param state Pointer to the query-building context.
1060 @param subexp_list Pointer to the first Expression in a linked list.
1061 @param op Pointer to the operator, or NULL for commas.
1063 If the operator is AND or OR (in upper, lower, or mixed case), the second and all
1064 subsequent operators will begin on a new line.
1066 static void buildSeries( BuildSQLState* state, const Expression* subexp_list, const char* op ) {
1069 return; // List is empty
1071 int comma = 0; // Boolean; true if separator is a comma
1072 int newline_needed = 0; // Boolean; true if operator is AND or OR
1077 } else if( !strcmp( op, "," ))
1079 else if( !strcasecmp( op, "AND" ) || !strcasecmp( op, "OR" ))
1082 int first = 1; // Boolean; true for first item in list
1083 while( subexp_list ) {
1085 first = 0; // No separator needed yet
1087 // Insert a separator
1089 buffer_add( state->sql, ", " );
1091 if( newline_needed )
1092 add_newline( state );
1094 buffer_add_char( state->sql, ' ' );
1096 buffer_add( state->sql, op );
1097 buffer_add_char( state->sql, ' ' );
1101 buildExpression( state, subexp_list );
1102 subexp_list = subexp_list->next;
1107 @brief Add the value of a bind variable to an SQL statement.
1108 @param state Pointer to the query-building context.
1109 @param bind Pointer to the bind variable whose value is to be added to the SQL.
1111 The value may be a null, a scalar, or an array of nulls and/or scalars, depending on
1112 the type of the bind variable.
1114 static void buildBindVar( BuildSQLState* state, const BindVar* bind ) {
1116 // Decide where to get the value, if any
1117 const jsonObject* value = NULL;
1118 if( bind->actual_value )
1119 value = bind->actual_value;
1120 else if( bind->default_value ) {
1121 if( state->defaults_usable )
1122 value = bind->default_value;
1124 sqlAddMsg( state, "No confirmed value available for bind variable \"%s\"",
1129 } else if( state->values_required ) {
1130 sqlAddMsg( state, "No value available for bind variable \"%s\"", bind->name );
1134 // No value available, and that's okay. Emit the name of the bind variable.
1135 buffer_add_char( state->sql, ':' );
1136 buffer_add( state->sql, bind->name );
1140 // If we get to this point, we know that a value is available. Carry on.
1142 int numeric = 0; // Boolean
1143 if( BIND_NUM == bind->type || BIND_NUM_LIST == bind->type )
1147 switch( bind->type ) {
1150 buildScalar( state, numeric, value );
1152 case BIND_STR_LIST :
1153 case BIND_NUM_LIST :
1154 if( JSON_ARRAY == value->type ) {
1155 // Iterate over array, emit each value
1156 int first = 1; // Boolean
1157 unsigned long max = value->size;
1158 unsigned long i = 0;
1163 buffer_add( state->sql, ", " );
1165 buildScalar( state, numeric, jsonObjectGetIndex( value, i ));
1169 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1170 "Invalid value for bind variable; expected a list of values" ));
1175 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1176 "Internal error: invalid type for bind variable" ));
1182 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1183 "Unable to emit value of bind variable \"%s\"", bind->name ));
1187 @brief Add a number or quoted string to an SQL statement.
1188 @param state Pointer to the query-building context.
1189 @param numeric Boolean; true if the value is expected to be a number
1190 @param obj Pointer to the jsonObject whose value is to be added to the SQL.
1192 static void buildScalar( BuildSQLState* state, int numeric, const jsonObject* obj ) {
1193 switch( obj->type ) {
1195 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1196 "Internal error: hash value for bind variable" ));
1200 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1201 "Internal error: array value for bind variable" ));
1207 "Invalid value for bind variable: expected a string, found a number" );
1210 char* str = jsonObjectToSimpleString( obj );
1211 dbi_conn_quote_string( state->dbhandle, &str );
1213 buffer_add( state->sql, str );
1216 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
1217 "Unable to format string literal \"%s\" for bind variable",
1218 jsonObjectGetString( obj )));
1225 buffer_add( state->sql, jsonObjectGetString( obj ));
1228 "Invalid value for bind variable: expected a number, found a string" );
1233 buffer_add( state->sql, "NULL" );
1236 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1237 "Internal error: boolean value for bind variable" ));
1241 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
1242 "Internal error: corrupted value for bind variable" ));
1249 @brief Start a new line in the output, with the current level of indentation.
1250 @param state Pointer to the query-building context.
1252 static void add_newline( BuildSQLState* state ) {
1253 buffer_add_char( state->sql, '\n' );
1256 static const char blanks[] = " "; // 32 blanks
1257 static const size_t maxlen = sizeof( blanks ) - 1;
1258 const int blanks_per_level = 3;
1259 int n = state->indent * blanks_per_level;
1261 size_t len = n >= maxlen ? maxlen : n;
1262 buffer_add_n( state->sql, blanks, len );
1268 @brief Increase the degree of indentation.
1269 @param state Pointer to the query-building context.
1271 static inline void incr_indent( BuildSQLState* state ) {
1276 @brief Reduce the degree of indentation.
1277 @param state Pointer to the query-building context.
1279 static inline void decr_indent( BuildSQLState* state ) {