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, StoredQ* query );
19 static void buildCombo( BuildSQLState* state, StoredQ* query, const char* type_str );
20 static void buildSelect( BuildSQLState* state, StoredQ* query );
21 static void buildFrom( BuildSQLState* state, FromRelation* core_from );
22 static void buildJoin( BuildSQLState* state, FromRelation* join );
23 static void buildSelectList( BuildSQLState* state, SelectItem* item );
24 static void buildOrderBy( BuildSQLState* state, OrderItem* ord_list );
25 static void buildExpression( BuildSQLState* state, Expression* expr );
26 static void buildBindVar( BuildSQLState* state, BindVar* bind );
27 static void buildScalar( BuildSQLState* state, int numeric, const jsonObject* obj );
29 static void add_newline( BuildSQLState* state );
30 static inline void incr_indent( BuildSQLState* state );
31 static inline void decr_indent( BuildSQLState* state );
34 @brief Apply values to bind variables, overriding the defaults, if any.
35 @param state Pointer to the query-building context.
36 @param bindings A JSON_HASH of values.
37 @return 0 if successful, or 1 if not.
39 The @a bindings parameter must be a JSON_HASH. The keys are the names of bind variables.
40 The values are the corresponding values for the variables.
42 int oilsApplyBindValues( BuildSQLState* state, jsonObject* bindings ) {
44 osrfLogError( OSRF_LOG_MARK, "NULL pointer to state" );
46 } else if( !bindings ) {
47 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
48 "Internal error: No pointer to bindings" ));
50 } else if( bindings->type != JSON_HASH ) {
51 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
52 "Internal error: bindings parameter is not a JSON_HASH" ));
57 jsonObject* value = NULL;
58 jsonIterator* iter = jsonNewIterator( bindings );
59 while(( value = jsonIteratorNext( iter ))) {
60 const char* var_name = iter->key;
61 BindVar* bind = osrfHashGet( state->bindvar_list, var_name );
65 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
66 "Can't assign value to bind variable \"%s\": no such variable", var_name ));
70 jsonIteratorFree( iter );
76 @brief Build an SQL query.
77 @param state Pointer to the query-building context.
78 @param query Pointer to the query to be built.
79 @return Zero if successful, or 1 if not.
81 Clear the output buffer, call build_Query() to do the work, and add a closing semicolon.
83 int buildSQL( BuildSQLState* state, StoredQ* query ) {
85 buffer_reset( state->sql );
87 build_Query( state, query );
88 if( ! state->error ) {
89 // Remove the trailing space, if there is one, and add a semicolon.
90 char c = buffer_chomp( state->sql );
92 buffer_add_char( state->sql, c ); // oops, not a space; put it back
93 buffer_add( state->sql, ";\n" );
99 @brief Build an SQL query, appending it to what has been built so far.
100 @param state Pointer to the query-building context.
101 @param query Pointer to the query to be built.
103 Look at the query type and branch to the corresponding routine.
105 static void build_Query( BuildSQLState* state, StoredQ* query ) {
106 if( buffer_length( state->sql ))
107 add_newline( state );
109 switch( query->type ) {
111 buildSelect( state, query );
114 buildCombo( state, query, "UNION" );
117 buildCombo( state, query, "INTERSECT" );
120 buildCombo( state, query, "EXCEPT" );
123 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
124 "Internal error: invalid query type %d in query # %d",
125 query->type, query->id ));
132 @brief Build a UNION, INTERSECT, or EXCEPT query.
133 @param state Pointer to the query-building context.
134 @param query Pointer to the query to be built.
135 @param type_str The query type, as a string.
137 static void buildCombo( BuildSQLState* state, StoredQ* query, const char* type_str ) {
139 QSeq* seq = query->child_list;
141 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
142 "Internal error: No child queries within %s query # %d",
143 type_str, query->id ));
148 // Traverse the list of child queries
150 build_Query( state, seq->child_query );
152 sqlAddMsg( state, "Unable to build child query # %d within %s query %d",
153 seq->child_query->id, type_str, query->id );
158 add_newline( state );
159 buffer_add( state->sql, type_str );
160 buffer_add_char( state->sql, ' ' );
162 buffer_add( state->sql, "ALL " );
170 @brief Build a SELECT statement.
171 @param state Pointer to the query-building context.
172 @param query Pointer to the StoredQ structure that represents the query.
174 static void buildSelect( BuildSQLState* state, StoredQ* query ) {
176 FromRelation* from_clause = query->from_clause;
178 sqlAddMsg( state, "SELECT has no FROM clause in query # %d", query->id );
183 // To do: get SELECT list; just a stub here
184 buffer_add( state->sql, "SELECT" );
185 incr_indent( state );
186 buildSelectList( state, query->select_list );
188 sqlAddMsg( state, "Unable to build SELECT list for query # %d", query->id );
192 decr_indent( state );
194 // Build FROM clause, if there is one
195 if( query->from_clause ) {
196 buildFrom( state, query->from_clause );
198 sqlAddMsg( state, "Unable to build FROM clause for query # %d", query->id );
204 // Build WHERE clause, if there is one
205 if( query->where_clause ) {
206 add_newline( state );
207 buffer_add( state->sql, "WHERE" );
208 incr_indent( state );
209 add_newline( state );
210 buildExpression( state, query->where_clause );
212 sqlAddMsg( state, "Unable to build WHERE clause for query # %d", query->id );
216 decr_indent( state );
219 // To do: build GROUP BY clause, if there is one
221 // Build HAVING clause, if there is one
222 if( query->having_clause ) {
223 add_newline( state );
224 buffer_add( state->sql, "HAVING" );
225 incr_indent( state );
226 add_newline( state );
227 buildExpression( state, query->having_clause );
229 sqlAddMsg( state, "Unable to build HAVING clause for query # %d", query->id );
233 decr_indent( state );
236 // Build ORDER BY clause, if there is one
237 if( query->order_by_list ) {
238 buildOrderBy( state, query->order_by_list );
240 sqlAddMsg( state, "Unable to build ORDER BY clause for query # %d", query->id );
246 // To do: Build LIMIT clause, if there is one
248 // To do: Build OFFSET clause, if there is one
254 @brief Build a FROM clause.
255 @param Pointer to the query-building context.
256 @param Pointer to the StoredQ query to which the FROM clause belongs.
258 static void buildFrom( BuildSQLState* state, FromRelation* core_from ) {
260 add_newline( state );
261 buffer_add( state->sql, "FROM" );
262 incr_indent( state );
263 add_newline( state );
265 switch( core_from->type ) {
266 case FRT_RELATION : {
267 char* relation = core_from->table_name;
269 if( !core_from->class_name ) {
270 sqlAddMsg( state, "No relation specified for core relation # %d",
276 // Look up table name, view name, or source_definition in the IDL
277 osrfHash* class_hash = osrfHashGet( oilsIDL(), core_from->class_name );
278 relation = oilsGetRelation( class_hash );
282 buffer_add( state->sql, relation );
283 if( !core_from->table_name )
284 free( relation ); // In this case we strdup'd it, must free it
288 buffer_add_char( state->sql, '(' );
289 incr_indent( state );
290 build_Query( state, core_from->subquery );
291 decr_indent( state );
292 add_newline( state );
293 buffer_add_char( state->sql, ')' );
296 sqlAddMsg( state, "Functions in FROM clause not yet supported" );
301 // Add a table alias, if possible
302 if( core_from->table_alias ) {
303 buffer_add( state->sql, " AS \"" );
304 buffer_add( state->sql, core_from->table_alias );
305 buffer_add( state->sql, "\" " );
307 else if( core_from->class_name ) {
308 buffer_add( state->sql, " AS \"" );
309 buffer_add( state->sql, core_from->class_name );
310 buffer_add( state->sql, "\" " );
312 buffer_add_char( state->sql, ' ' );
314 incr_indent( state );
315 FromRelation* join = core_from->join_list;
317 buildJoin( state, join );
319 sqlAddMsg( state, "Unable to build JOIN clause(s) for relation # %d",
325 decr_indent( state );
326 decr_indent( state );
329 static void buildJoin( BuildSQLState* state, FromRelation* join ) {
330 add_newline( state );
331 switch( join->join_type ) {
333 sqlAddMsg( state, "Non-join relation # %d in JOIN clause", join->id );
337 buffer_add( state->sql, "INNER JOIN " );
340 buffer_add( state->sql, "LEFT JOIN " );
343 buffer_add( state->sql, "RIGHT JOIN " );
346 buffer_add( state->sql, "FULL JOIN " );
349 sqlAddMsg( state, "Unrecognized join type in relation # %d", join->id );
354 switch( join->type ) {
357 if( !join->table_name || ! *join->table_name ) {
358 sqlAddMsg( state, "No relation designated for relation # %d", join->id );
362 buffer_add( state->sql, join->table_name );
366 if( !join->subquery ) {
367 sqlAddMsg( state, "Subquery expected, not found for relation # %d", join->id );
370 } else if( !join->table_alias ) {
371 sqlAddMsg( state, "No table alias for subquery in FROM relation # %d",
376 buffer_add_char( state->sql, '(' );
377 incr_indent( state );
378 build_Query( state, join->subquery );
379 decr_indent( state );
380 add_newline( state );
381 buffer_add_char( state->sql, ')' );
384 if( !join->table_name || ! *join->table_name ) {
385 sqlAddMsg( state, "Joins to functions not yet supported in relation # %d",
393 const char* effective_alias = join->table_alias;
394 if( !effective_alias )
395 effective_alias = join->class_name;
397 if( effective_alias ) {
398 buffer_add( state->sql, " AS \"" );
399 buffer_add( state->sql, effective_alias );
400 buffer_add_char( state->sql, '\"' );
403 if( join->on_clause ) {
404 incr_indent( state );
405 add_newline( state );
406 buffer_add( state->sql, "ON " );
407 buildExpression( state, join->on_clause );
408 decr_indent( state );
411 FromRelation* subjoin = join->join_list;
413 buildJoin( state, subjoin );
415 sqlAddMsg( state, "Unable to build JOIN clause(s) for relation # %d", join->id );
418 subjoin = subjoin->next;
422 static void buildSelectList( BuildSQLState* state, SelectItem* item ) {
427 buffer_add_char( state->sql, ',' );
428 add_newline( state );
429 buildExpression( state, item->expression );
431 sqlAddMsg( state, "Unable to build an expression for SELECT item # %d", item->id );
436 if( item->column_alias ) {
437 buffer_add( state->sql, " AS \"" );
438 buffer_add( state->sql, item->column_alias );
439 buffer_add_char( state->sql, '\"' );
444 buffer_add_char( state->sql, ' ' );
448 @brief Add an ORDER BY clause to the current query.
449 @param state Pointer to the query-building context.
450 @param ord_list Pointer to the first node in a linked list of OrderItems.
452 static void buildOrderBy( BuildSQLState* state, OrderItem* ord_list ) {
453 add_newline( state );
454 buffer_add( state->sql, "ORDER BY" );
455 incr_indent( state );
457 int first = 1; // boolean
462 buffer_add_char( state->sql, ',' );
463 add_newline( state );
464 buildExpression( state, ord_list->expression );
466 sqlAddMsg( state, "Unable to add ORDER BY expression # %d", ord_list->id );
470 ord_list = ord_list->next;
473 decr_indent( state );
478 @brief Build an arbitrary expression.
479 @param state Pointer to the query-building context.
480 @param expr Pointer to the Expression representing the expression to be built.
482 static void buildExpression( BuildSQLState* state, Expression* expr ) {
484 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
485 "Internal error: NULL pointer to Expression" ));
490 if( expr->parenthesize )
491 buffer_add_char( state->sql, '(' );
493 switch( expr->type ) {
496 buffer_add( state->sql, "NOT " );
498 sqlAddMsg( state, "BETWEEN expressions not yet supported" );
502 if( !expr->bind ) { // Sanity check
503 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
504 "Internal error: no variable for bind variable expression" ));
507 buildBindVar( state, expr->bind );
511 buffer_add( state->sql, "NOT " );
513 if( expr->literal ) {
514 buffer_add( state->sql, expr->literal );
515 buffer_add_char( state->sql, ' ' );
517 buffer_add( state->sql, "FALSE " );
521 buffer_add( state->sql, "NOT " );
523 sqlAddMsg( state, "CASE expressions not yet supported" );
526 case EXP_CAST : // Type cast
528 buffer_add( state->sql, "NOT " );
530 sqlAddMsg( state, "Cast expressions not yet supported" );
533 case EXP_COLUMN : // Table column
535 buffer_add( state->sql, "NOT " );
537 if( expr->table_alias ) {
538 buffer_add_char( state->sql, '\"' );
539 buffer_add( state->sql, expr->table_alias );
540 buffer_add( state->sql, "\"." );
542 if( expr->column_name ) {
543 buffer_add( state->sql, expr->column_name );
545 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
546 "Column name not present in expression # %d", expr->id ));
551 if( !expr->subquery ) {
552 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
553 "No subquery found for EXIST expression # %d", expr->id ));
557 buffer_add( state->sql, "NOT " );
559 buffer_add( state->sql, "EXISTS (" );
560 incr_indent( state );
561 build_Query( state, expr->subquery );
562 decr_indent( state );
563 add_newline( state );
564 buffer_add_char( state->sql, ')' );
569 buffer_add( state->sql, "NOT " );
571 sqlAddMsg( state, "Field expressions not yet supported" );
576 buffer_add( state->sql, "NOT " );
578 sqlAddMsg( state, "Function expressions not yet supported" );
582 if( expr->left_operand ) {
583 buildExpression( state, expr->left_operand );
584 if( !state->error ) {
586 buffer_add( state->sql, "NOT " );
588 if( expr->subquery ) {
589 buffer_add( state->sql, " IN (" );
590 incr_indent( state );
591 build_Query( state, expr->subquery );
592 decr_indent( state );
593 add_newline( state );
594 buffer_add_char( state->sql, ')' );
596 sqlAddMsg( state, "IN lists not yet supported" );
604 buffer_add( state->sql, "NOT " );
606 buffer_add( state->sql, "NULL" );
608 case EXP_NUMBER : // Numeric literal
609 if( !expr->literal ) {
610 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
611 "Internal error: No numeric value in string expression # %d", expr->id ));
614 buffer_add( state->sql, expr->literal );
619 buffer_add( state->sql, "NOT (" );
621 if( expr->left_operand ) {
622 buildExpression( state, expr->left_operand );
624 sqlAddMsg( state, "Unable to emit left operand in expression # %d",
629 buffer_add_char( state->sql, ' ' );
630 buffer_add( state->sql, expr->op );
631 buffer_add_char( state->sql, ' ' );
632 if( expr->right_operand ) {
633 buildExpression( state, expr->right_operand );
635 sqlAddMsg( state, "Unable to emit right operand in expression # %d",
642 buffer_add_char( state->sql, ')' );
645 case EXP_STRING : // String literal
646 if( !expr->literal ) {
647 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
648 "Internal error: No string value in string expression # %d", expr->id ));
651 // To do: escape special characters in the string
652 buffer_add_char( state->sql, '\'' );
653 buffer_add( state->sql, expr->literal );
654 buffer_add_char( state->sql, '\'' );
659 buffer_add( state->sql, "NOT " );
661 if( expr->subquery ) {
662 buffer_add_char( state->sql, '(' );
663 incr_indent( state );
664 build_Query( state, expr->subquery );
665 decr_indent( state );
666 add_newline( state );
667 buffer_add_char( state->sql, ')' );
669 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
670 "Internal error: No subquery in subquery expression # %d", expr->id ));
676 if( expr->parenthesize )
677 buffer_add_char( state->sql, ')' );
681 @brief Add the value of a bind variable to an SQL statement.
682 @param state Pointer to the query-building context.
683 @param bind Pointer to the bind variable whose value is to be added to the SQL.
685 The value may be a null, a scalar, or an array of nulls and/or scalars, depending on
686 the type of the bind variable.
688 static void buildBindVar( BuildSQLState* state, BindVar* bind ) {
690 // Decide where to get the value, if any
691 const jsonObject* value = NULL;
692 if( bind->actual_value )
693 value = bind->actual_value;
694 else if( bind->default_value ) {
695 if( state->defaults_usable )
696 value = bind->default_value;
698 sqlAddMsg( state, "No confirmed value available for bind variable \"%s\"",
703 } else if( state->values_required ) {
704 sqlAddMsg( state, "No value available for bind variable \"%s\"", bind->name );
708 // No value available, and that's okay. Emit the name of the bind variable.
709 buffer_add_char( state->sql, ':' );
710 buffer_add( state->sql, bind->name );
714 // If we get to this point, we know that a value is available. Carry on.
716 int numeric = 0; // Boolean
717 if( BIND_NUM == bind->type || BIND_NUM_LIST == bind->type )
721 switch( bind->type ) {
724 buildScalar( state, numeric, value );
728 if( JSON_ARRAY == value->type ) {
729 // Iterate over array, emit each value
730 int first = 1; // Boolean
731 unsigned long max = value->size;
737 buffer_add( state->sql, ", " );
739 buildScalar( state, numeric, jsonObjectGetIndex( value, i ));
743 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
744 "Invalid value for bind variable; expected a list of values" ));
749 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
750 "Internal error: invalid type for bind variable" ));
756 osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
757 "Unable to emit value of bind variable \"%s\"", bind->name ));
761 @brief Add a number or quoted string to an SQL statement.
762 @param state Pointer to the query-building context.
763 @param numeric Boolean; true if the value is expected to be a number
764 @param obj Pointer to the jsonObject whose value is to be added to the SQL.
766 static void buildScalar( BuildSQLState* state, int numeric, const jsonObject* obj ) {
767 switch( obj->type ) {
769 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
770 "Internal error: hash value for bind variable" ));
774 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
775 "Internal error: array value for bind variable" ));
781 "Invalid value for bind variable: expected a string, found a number" );
784 // To do: escape special characters in the string
785 buffer_add_char( state->sql, '\'' );
786 buffer_add( state->sql, jsonObjectGetString( obj ));
787 buffer_add_char( state->sql, '\'' );
792 buffer_add( state->sql, jsonObjectGetString( obj ));
795 "Invalid value for bind variable: expected a number, found a string" );
800 buffer_add( state->sql, "NULL" );
803 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
804 "Internal error: boolean value for bind variable" ));
808 osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
809 "Internal error: corrupted value for bind variable" ));
815 static void add_newline( BuildSQLState* state ) {
816 buffer_add_char( state->sql, '\n' );
819 static const char blanks[] = " "; // 32 blanks
820 static const size_t maxlen = sizeof( blanks ) - 1;
821 const int blanks_per_level = 3;
822 int n = state->indent * blanks_per_level;
824 size_t len = n >= maxlen ? maxlen : n;
825 buffer_add_n( state->sql, blanks, len );
830 static inline void incr_indent( BuildSQLState* state ) {
834 static inline void decr_indent( BuildSQLState* state ) {