From a66ff4de2151b3177abb4537e42b1740b5d73b03 Mon Sep 17 00:00:00 2001
From: scottmk <scottmk@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Date: Fri, 21 May 2010 14:00:03 +0000
Subject: [PATCH] Support IS NULL and IS NOT NULL 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


git-svn-id: svn://svn.open-ils.org/ILS/trunk@16470 dcc99617-32d9-48b4-a31d-7c20da2025e4
---
 Open-ILS/include/openils/oils_buildq.h |  1 +
 Open-ILS/src/c-apps/buildSQL.c         | 15 +++++++++++++++
 Open-ILS/src/c-apps/oils_storedq.c     | 34 +++++++++++++++++++++++++++++++++-
 3 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/Open-ILS/include/openils/oils_buildq.h b/Open-ILS/include/openils/oils_buildq.h
index 1d18fc57a3..42ea5f1533 100644
--- a/Open-ILS/include/openils/oils_buildq.h
+++ b/Open-ILS/include/openils/oils_buildq.h
@@ -154,6 +154,7 @@ typedef enum {
 	EXP_FIELD,
 	EXP_FUNCTION,
 	EXP_IN,
+	EXP_ISNULL,
 	EXP_NULL,
 	EXP_NUMBER,
 	EXP_OPERATOR,
diff --git a/Open-ILS/src/c-apps/buildSQL.c b/Open-ILS/src/c-apps/buildSQL.c
index 34b277a851..0951074cf9 100644
--- a/Open-ILS/src/c-apps/buildSQL.c
+++ b/Open-ILS/src/c-apps/buildSQL.c
@@ -674,6 +674,21 @@ static void buildExpression( BuildSQLState* state, Expression* expr ) {
 				}
 			}
 			break;
+		case EXP_ISNULL :
+			if( expr->left_operand ) {
+				buildExpression( state, expr->left_operand );
+				if( state->error ) {
+					sqlAddMsg( state, "Unable to emit left operand in IS NULL expression # %d",
+						expr->id );
+					break;
+				}
+			}
+
+			if( expr->negate )
+				buffer_add( state->sql, " IS NOT NULL" );
+			else
+				buffer_add( state->sql, " IS NULL" );
+			break;
 		case EXP_NULL :
 			if( expr->negate )
 				buffer_add( state->sql, "NOT " );
diff --git a/Open-ILS/src/c-apps/oils_storedq.c b/Open-ILS/src/c-apps/oils_storedq.c
index 6eac1326eb..6fb97694fc 100644
--- a/Open-ILS/src/c-apps/oils_storedq.c
+++ b/Open-ILS/src/c-apps/oils_storedq.c
@@ -392,6 +392,12 @@ static QSeq* constructQSeq( BuildSQLState* state, dbi_result result ) {
 	return seq;
 }
 
+/**
+	@brief Free a list of QSeq's.
+	@param seq Pointer to the first in a linked list of QSeq's to be freed.
+
+	Each QSeq goes onto a free list for potential reuse.
+*/
 static void freeQSeqList( QSeq* seq ) {
 	if( !seq )
 		return;
@@ -1018,6 +1024,12 @@ static void bindVarFree( char* key, void* p ) {
 	}
 }
 
+/**
+	@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.
+	@return Pointer to a newly-created Expression if successful, or NULL if not.
+*/
 static Expression* getExpression( BuildSQLState* state, int id ) {
 	
 	// Check the stack to see if the current expression is nested inside itself.  If it is,
@@ -1096,6 +1108,8 @@ static Expression* constructExpression( BuildSQLState* state, dbi_result result
 		type = EXP_FUNCTION;
 	else if( !strcmp( type_str, "xin" ))
 		type = EXP_IN;
+	else if( !strcmp( type_str, "xisnull" ))
+		type = EXP_ISNULL;
 	else if( !strcmp( type_str, "xnull" ))
 		type = EXP_NULL;
 	else if( !strcmp( type_str, "xnum" ))
@@ -1222,6 +1236,23 @@ static Expression* constructExpression( BuildSQLState* state, dbi_result result
 				return NULL;
 			}
 		}
+	} else if( EXP_ISNULL == type ) {
+		if( -1 == left_operand_id ) {
+			osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
+				"Expression # %d IS NULL has no left operand", id ));
+			state->error = 1;
+			return NULL;
+		}
+
+		if( left_operand_id != -1 ) {
+			left_operand = getExpression( state, left_operand_id );
+			if( !left_operand ) {
+				osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
+					"Unable to get left operand in expression # %d", id ));
+				state->error = 1;
+				return NULL;
+			}
+		}
 	} else if( EXP_EXIST == type ) {
 		if( -1 == subquery_id ) {
 			osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
@@ -1481,8 +1512,9 @@ jsonObject* oilsGetColNames( BuildSQLState* state, StoredQ* query ) {
 	// Save the outermost query id for possible use in an error message
 	int id = query->id;
 
-	// Find the first SELECT, from which we will take the column names
 	while( query->type != QT_SELECT ) {
+		// If the query is a UNION, INTERSECT, or EXCEPT, there must be a SELECT in
+		// there somewhere.  Find the first one, and use the SELECT list from that.
 		QSeq* child_list = query->child_list;
 		if( !child_list ) {
 			query = NULL;
-- 
2.11.0