[open-ils-commits] r16696 - in trunk/Open-ILS: include/openils src/c-apps (scottmk)

svn at svn.open-ils.org svn at svn.open-ils.org
Sun Jun 13 13:15:32 EDT 2010


Author: scottmk
Date: 2010-06-13 13:15:28 -0400 (Sun, 13 Jun 2010)
New Revision: 16696

Modified:
   trunk/Open-ILS/include/openils/oils_buildq.h
   trunk/Open-ILS/src/c-apps/buildSQL.c
   trunk/Open-ILS/src/c-apps/oils_storedq.c
Log:
In qstore: support CASE expressions.

M    Open-ILS/include/openils/oils_buildq.h
M    Open-ILS/src/c-apps/oils_storedq.c
M    Open-ILS/src/c-apps/buildSQL.c


Modified: trunk/Open-ILS/include/openils/oils_buildq.h
===================================================================
--- trunk/Open-ILS/include/openils/oils_buildq.h	2010-06-12 13:50:45 UTC (rev 16695)
+++ trunk/Open-ILS/include/openils/oils_buildq.h	2010-06-13 17:15:28 UTC (rev 16696)
@@ -24,6 +24,9 @@
 struct BindVar_;
 typedef struct BindVar_ BindVar;
 
+struct CaseBranch_;
+typedef struct CaseBranch_ CaseBranch;
+
 struct Expression_;
 typedef struct Expression_ Expression;
 
@@ -144,6 +147,13 @@
 	jsonObject* actual_value;
 };
 
+struct CaseBranch_ {
+	CaseBranch* next;
+	int id;
+	Expression* condition;
+	Expression* result;
+};
+
 typedef enum {
 	EXP_BETWEEN,
 	EXP_BIND,
@@ -183,6 +193,7 @@
 	int         negate;             // Boolean
 	BindVar*    bind;
 	Expression* subexp_list;        // Linked list of subexpressions
+	CaseBranch* branch_list;        // Linked list of CASE branches
 	// The next column comes, not from query.expression,
 	// but from query.function_sig:
 	char*       function_name;

Modified: trunk/Open-ILS/src/c-apps/buildSQL.c
===================================================================
--- trunk/Open-ILS/src/c-apps/buildSQL.c	2010-06-12 13:50:45 UTC (rev 16695)
+++ trunk/Open-ILS/src/c-apps/buildSQL.c	2010-06-13 17:15:28 UTC (rev 16696)
@@ -23,6 +23,7 @@
 static void buildSelectList( BuildSQLState* state, const SelectItem* item );
 static void buildGroupBy( BuildSQLState* state, const SelectItem* sel_list );
 static void buildOrderBy( BuildSQLState* state, const OrderItem* ord_list );
+static void buildCase( BuildSQLState* state, const Expression* expr );
 static void buildExpression( BuildSQLState* state, const Expression* expr );
 static void buildFunction( BuildSQLState* state, const Expression* exp );
 static void buildSeries( BuildSQLState* state, const Expression* subexp_list, const char* op );
@@ -376,7 +377,7 @@
 			if ( state->error ) {
 				sqlAddMsg( state,
 					"Unable to include function call # %d in FROM relation # %d",
-	 				core_from->function_call->id, core_from->id );
+					core_from->function_call->id, core_from->id );
 				return;
 			}
 			break;
@@ -669,11 +670,10 @@
 				buffer_add( state->sql, "FALSE " );
 			break;
 		case EXP_CASE :
-			if( expr->negate )
-				buffer_add( state->sql, "NOT " );
+			buildCase( state, expr );
+			if( state->error )
+				sqlAddMsg( state, "Unable to build CASE expression # %d", expr->id );
 
-			sqlAddMsg( state, "CASE expressions not yet supported" );
-			state->error = 1;
 			break;
 		case EXP_CAST :                   // Type cast
 			if( expr->negate )
@@ -694,9 +694,6 @@
 			if( expr->column_name ) {
 				buffer_add( state->sql, expr->column_name );
 			} else {
-				//osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
-				//	"Column name not present in expression # %d", expr->id ));
-				//state->error = 1;
 				buffer_add_char( state->sql, '*' );
 			}
 			break;
@@ -869,6 +866,70 @@
 }
 
 /**
+	@brief Build a CASE expression.
+	@param state Pointer to the query-building context.
+	@param exp Pointer to an Expression representing a CASE expression.
+*/
+static void buildCase( BuildSQLState* state, const Expression* expr ) {
+	// Sanity checks
+	if( ! expr->left_operand ) {
+		sqlAddMsg( state, "CASE expression # %d has no left operand", expr->id );
+		state->error  = 1;
+		return;
+	} else if( ! expr->branch_list ) {
+		sqlAddMsg( state, "CASE expression # %d has no branches", expr->id );
+		state->error  = 1;
+		return;
+	}
+
+	if( expr->negate )
+		buffer_add( state->sql, "NOT (" );
+
+	// left_operand is the expression on which we shall branch
+	buffer_add( state->sql, "CASE " );
+	buildExpression( state, expr->left_operand );
+	if( state->error ) {
+		sqlAddMsg( state, "Unable to build operand of CASE expression # %d", expr->id );
+		return;
+	}
+
+	incr_indent( state );
+
+	// Emit each branch in turn
+	CaseBranch* branch = expr->branch_list;
+	while( branch ) {
+		add_newline( state );
+
+		if( branch->condition ) {
+			// Emit a WHEN condition
+			buffer_add( state->sql, "WHEN " );
+			buildExpression( state, branch->condition );
+			incr_indent( state );
+			add_newline( state );
+			buffer_add( state->sql, "THEN " );
+		} else {
+			// Emit ELSE
+			buffer_add( state->sql, "ELSE " );
+			incr_indent( state );
+			add_newline( state );
+		}
+
+		// Emit the THEN expression
+		buildExpression( state, branch->result );
+		decr_indent( state );
+
+		branch = branch->next;
+	}
+
+	decr_indent( state );
+	add_newline( state );
+	buffer_add( state->sql, "END" );
+
+	if( expr->negate )
+		buffer_add( state->sql, ")" );
+}
+
+/**
 	@brief Build a function call.
 	@param state Pointer to the query-building context.
 	@param exp Pointer to an Expression representing a function call.

Modified: trunk/Open-ILS/src/c-apps/oils_storedq.c
===================================================================
--- trunk/Open-ILS/src/c-apps/oils_storedq.c	2010-06-12 13:50:45 UTC (rev 16695)
+++ trunk/Open-ILS/src/c-apps/oils_storedq.c	2010-06-13 17:15:28 UTC (rev 16696)
@@ -41,6 +41,10 @@
 static BindVar* constructBindVar( BuildSQLState* state, dbi_result result );
 static void bindVarFree( char* name, void* p );
 
+static CaseBranch* getCaseBranchList( BuildSQLState* state, int parent_id );
+static CaseBranch* constructCaseBranch( BuildSQLState* state, dbi_result result );
+static void freeBranchList( CaseBranch* branch );
+
 static Expression* getExpression( BuildSQLState* state, int id );
 static Expression* constructExpression( BuildSQLState* state, dbi_result result );
 static void expressionListFree( Expression* exp );
@@ -60,6 +64,7 @@
 static FromRelation* free_from_relation_list = NULL;
 static SelectItem* free_select_item_list = NULL;
 static BindVar* free_bindvar_list = NULL;
+static CaseBranch* free_branch_list = NULL;
 static Expression* free_expression_list = NULL;
 static IdNode* free_id_node_list = NULL;
 static QSeq* free_qseq_list = NULL;
@@ -1158,6 +1163,161 @@
 }
 
 /**
+	@brief Given an id for a parent expression, build a list of CaseBranch structs.
+	@param Pointer to the query-building context.
+	@param id ID of a row in query.case_branch.
+	@return Pointer to a newly-created CaseBranch if successful, or NULL if not.
+*/
+static CaseBranch* getCaseBranchList( BuildSQLState* state, int parent_id ) {
+	CaseBranch* branch_list = NULL;
+	dbi_result result = dbi_conn_queryf( state->dbhandle,
+		"SELECT id, condition, result "
+		"FROM query.case_branch WHERE parent_expr = %d "
+		"ORDER BY seq_no desc;", parent_id );
+	if( result ) {
+		int condition_found = 0;   // Boolean
+		if( dbi_result_first_row( result ) ) {
+			// Boolean: true for the first branch we encounter.  That's actually the last
+			// branch in the CASE, because we're reading them in reverse order.  The point
+			// is to enforce the rule that only the last branch can be an ELSE.
+			int first = 1;
+			while( 1 ) {
+				CaseBranch* branch = constructCaseBranch( state, result );
+				if( branch ) {
+					PRINT( "Found a case branch\n" );
+					branch->next = branch_list;
+					branch_list  = branch;
+				} else {
+					osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
+						"Unable to build CASE branch for expression id #%d", parent_id ));
+					freeBranchList( branch_list );
+					branch_list = NULL;
+					break;
+				}
+
+				if( branch->condition )
+					condition_found = 1;
+				else if( !first ) {
+					sqlAddMsg( state, "ELSE branch # %d not at end of CASE expression # %d",
+						branch->id, parent_id );
+					freeBranchList( branch_list );
+					branch_list = NULL;
+					break;
+				}
+				first = 0;
+
+				if( !dbi_result_next_row( result ) )
+					break;
+			};  // end while
+		}
+
+		// Make sure that at least one branch includes a condition
+		if( !condition_found ) {
+			sqlAddMsg( state, "No conditional branch in CASE expression # %d", parent_id );
+			freeBranchList( branch_list );
+			branch_list = NULL;
+		}
+	} else {
+		const char* msg;
+		int errnum = dbi_conn_error( state->dbhandle, &msg );
+		osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
+			"Unable to query query.case_branch table for parent expression # %d: %s",
+			parent_id, errnum, msg ? msg : "No description available" ));
+		state->error = 1;
+	}
+
+	return branch_list;
+}
+
+static CaseBranch* constructCaseBranch( BuildSQLState* state, dbi_result result ) {
+	int id = dbi_result_get_int_idx( result, 1 );
+
+	int condition_id;
+	if( dbi_result_field_is_null_idx( result, 2 ))
+		condition_id = -1;
+	else
+		condition_id = dbi_result_get_int_idx( result, 2 );
+
+	Expression* condition = NULL;
+	if( condition_id != -1 ) {   // If it's -1, we have an ELSE condition
+		condition = getExpression( state, condition_id );
+		if( ! condition ) {
+			osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
+				"Unable to build condition expression for case branch # %d", id ));
+			state->error = 1;
+			return NULL;
+		}
+	}
+
+	int result_id = dbi_result_get_int_idx( result, 3 );
+
+	Expression* result_p = NULL;
+	if( -1 == result_id ) {
+		osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
+			"No result expression defined for case branch # %d", id ));
+		state->error = 1;
+		if( condition )
+			expressionFree( condition );
+		return NULL;
+	} else {
+		result_p = getExpression( state, result_id );
+		if( ! result_p ) {
+			osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
+				"Unable to build result expression for case branch # %d", id ));
+			state->error = 1;
+			if( condition )
+				expressionFree( condition );
+			return NULL;
+		}
+	}
+
+	// Allocate a CaseBranch: from the free list if possible, from the heap if necessary
+	CaseBranch* branch = NULL;
+	if( free_branch_list ) {
+		branch = free_branch_list;
+		free_branch_list = free_branch_list->next;
+	} else
+		branch = safe_malloc( sizeof( CaseBranch ) );
+
+	// Populate the new CaseBranch
+	branch->id = id;
+	branch->condition = condition;
+	branch->result = result_p;
+
+	return branch;
+}
+
+/**
+	@brief Free a list of CaseBranches.
+	@param branch Pointer to the first in a linked list of CaseBranches to be freed.
+
+	Each CaseBranch goes onto a free list for potential reuse.
+*/
+static void freeBranchList( CaseBranch* branch ) {
+	if( !branch )
+		return;
+
+	CaseBranch* first = branch;
+	while( branch ) {
+		if( branch->condition ) {
+			expressionFree( branch->condition );
+			branch->condition = NULL;
+		}
+		expressionFree( branch->result );
+		branch->result = NULL;
+
+		if( branch->next )
+			branch = branch->next;
+		else {
+			branch->next = free_branch_list;
+			branch = NULL;
+		}
+	}
+
+	free_branch_list = first;
+}
+
+/**
 	@brief Given an id for a row in query.expression, build an Expression struct.
 	@param Pointer to the query-building context.
 	@param id ID of a row in query.expression.
@@ -1309,6 +1469,7 @@
 	Expression* right_operand = NULL;
 	StoredQ* subquery = NULL;
 	BindVar* bind = NULL;
+	CaseBranch* branch_list = NULL;
 	Expression* subexp_list = NULL;
 
 	if( EXP_BETWEEN == type ) {
@@ -1363,7 +1524,7 @@
 			bind = getBindVar( state, bind_variable );
 			if( ! bind ) {
 				osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
-								"Unable to load bind variable \"%s\" for expression # %d",
+					"Unable to load bind variable \"%s\" for expression # %d",
 		bind_variable, id ));
 				state->error = 1;
 				return NULL;
@@ -1371,7 +1532,7 @@
 			PRINT( "\tBind variable is \"%s\"\n", bind_variable );
 		} else {
 			osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
-							"No variable specified for bind variable expression # %d",
+				"No variable specified for bind variable expression # %d",
 	   bind_variable, id ));
 			state->error = 1;
 			return NULL;
@@ -1387,6 +1548,31 @@
 			}
 		}
 
+	} else if( EXP_CASE == type ) {
+		// Get the left operand
+		if( -1 == left_operand_id ) {
+			osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
+				"No left operand defined for CASE expression # %d", id ));
+			state->error = 1;
+			return NULL;
+		} else {
+			left_operand = getExpression( state, left_operand_id );
+			if( !left_operand ) {
+				osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
+					"Unable to get left operand in CASE expression # %d", id ));
+				state->error = 1;
+				return NULL;
+			}
+		}
+
+		branch_list = getCaseBranchList( state, id );
+		if( ! branch_list ) {
+			osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
+				"Unable to build branches for CASE expression # %d", id ));
+			state->error = 1;
+			return NULL;
+		}
+
 	} else if( EXP_EXIST == type ) {
 		if( -1 == subquery_id ) {
 			osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
@@ -1507,7 +1693,7 @@
 			right_operand = getExpression( state, right_operand_id );
 			if( !right_operand ) {
 				osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
-								"Unable to get right operand in expression # %d", id ));
+					"Unable to get right operand in expression # %d", id ));
 				state->error = 1;
 				return NULL;
 			}
@@ -1586,6 +1772,7 @@
 	exp->cast_type_id = subquery_id;
 	exp->negate = negate;
 	exp->bind = bind;
+	exp->branch_list = branch_list;
 	exp->subexp_list = subexp_list;
 	exp->function_name = function_name ? strdup( function_name ) : NULL;
 
@@ -1623,8 +1810,10 @@
 			expressionFree( exp->left_operand );
 			exp->left_operand = NULL;
 		}
-		free( exp->op );
-		exp->op = NULL;
+		if( exp->op ) {
+			free( exp->op );
+			exp->op = NULL;
+		}
 		if( exp->right_operand ) {
 			expressionFree( exp->right_operand );
 			exp->right_operand = NULL;
@@ -1643,6 +1832,11 @@
 			exp->subexp_list = NULL;
 		}
 
+		if( exp->branch_list ) {
+			freeBranchList( exp->branch_list );
+			exp->branch_list = NULL;
+		}
+
 		if( exp->function_name ) {
 			free( exp->function_name );
 			exp->function_name = NULL;
@@ -1965,6 +2159,14 @@
 		free( bind );
 		bind = free_bindvar_list;
 	}
+
+	// Free all the nodes in the case branch free list
+	CaseBranch* branch = free_branch_list;
+	while( branch ) {
+		free_branch_list = branch->next;
+		free( branch );
+		branch = free_branch_list;
+	}
 }
 
 /**



More information about the open-ils-commits mailing list