Beispiel #1
0
/**
	@brief Execute an SQL query and return a result set.
	@param ctx Pointer to the current method context.
	@return Zero if successful, or -1 if not.

	Method parameters:
	- query token, as previously returned by the .prepare method.

	Returns: A series of responses, each of them a row represented as an array of column values.
*/
int doExecute( osrfMethodContext* ctx ) {
	if(osrfMethodVerifyContext( ctx )) {
		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
		return -1;
	}

	// Get the query token
	const jsonObject* token_obj = jsonObjectGetIndex( ctx->params, 0 );
	if( token_obj->type != JSON_STRING ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid parameter; query token must be a string" );
		return -1;
	}
	const char* token = jsonObjectGetString( token_obj );

	// Look up the query token in the session-level userData
	CachedQuery* query = search_token( ctx, token );
	if( !query ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid query token" );
		return -1;
	}

	osrfLogInfo( OSRF_LOG_MARK, "Executing query for token \"%s\"", token );
	if( query->state->error ) {
		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( query->state,
			"No valid prepared query available for query id # %d", query->query->id ));
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
							  ctx->request, "No valid prepared query available" );
		return -1;
	} else if( buildSQL( query->state, query->query )) {
		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( query->state,
			"Unable to build SQL statement for query id # %d", query->query->id ));
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Unable to build SQL statement" );
		return -1;
	}

	jsonObject* row = oilsFirstRow( query->state );
	while( row ) {
		osrfAppRespond( ctx, row );
		row = oilsNextRow( query->state );
	}

	if( query->state->error ) {
		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( query->state,
			"Unable to execute SQL statement for query id # %d", query->query->id ));
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Unable to execute SQL statement" );
		if( query->state->panic ) {
			osrfLogError( OSRF_LOG_MARK, sqlAddMsg( query->state,
				"Database connection isn't working" ));
			osrfAppSessionPanic( ctx->session );
		}
		return -1;
	}

	osrfAppRespondComplete( ctx, NULL );
	return 0;
}
Beispiel #2
0
/**
	@brief Load the child queries subordinate to a UNION, INTERSECT, or EXCEPT query.
	@param state Pointer to the query-building context.
	@param parent ID of the UNION, INTERSECT, or EXCEPT query.
	@param type_str The type of the query ("UNION", "INTERSECT", or "EXCEPT").
	@return If successful, a pointer to a linked list of QSeq, each bearing a pointer to a
		StoredQ; otherwise NULL.

	The @a type_str parameter is used only for building error messages.
*/
static QSeq* loadChildQueries( BuildSQLState* state, int parent_id, const char* type_str ) {
	QSeq* child_list = NULL;
	
	// The ORDER BY is in descending order so that we can build the list by adding to
	// the head, and it will wind up in the right order.
	dbi_result result = dbi_conn_queryf( state->dbhandle,
		"SELECT id, parent_query, seq_no, child_query "
		"FROM query.query_sequence WHERE parent_query = %d ORDER BY seq_no DESC", parent_id );
	if( result ) {
		if( dbi_result_first_row( result ) ) {
			int count = 0;
			while( 1 ) {
				++count;
				QSeq* seq = constructQSeq( state, result );
				if( seq ) {
					PRINT( "Found a child query\n" );
					PRINT( "\tid: %d\n", seq->id );
					PRINT( "\tparent id: %d\n", seq->parent_query_id );
					PRINT( "\tseq_no: %d\n", seq->seq_no );
					// Add to the head of the list
					seq->next = child_list;
					child_list = seq;
				} else{
					freeQSeqList( child_list );
					return NULL;
				}
				if( !dbi_result_next_row( result ))
					break;
			}
			if( count < 2 ) {
				osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
					"%s query # %d has only one child query", type_str, parent_id ));
				state->error = 1;
				freeQSeqList( child_list );
				return NULL;
			}
		} else {
			osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
				"%s query # %d has no child queries within it", type_str, parent_id ));
			state->error = 1;
			return NULL;
		}
	} else {
		const char* msg;
		int errnum = dbi_conn_error( state->dbhandle, &msg );
		osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
			"Unable to query query.query_sequence table: # %d %s",
			errnum, msg ? msg : "No description available" ));
		state->error = 1;
		return NULL;
	}

	return child_list;
}
Beispiel #3
0
static OrderItem* constructOrderItem( BuildSQLState* state, dbi_result result ) {
	int id                   = dbi_result_get_int_idx( result, 1 );
	int stored_query_id      = dbi_result_get_int_idx( result, 2 );
	int seq_no               = dbi_result_get_int_idx( result, 3 );
	int expression_id        = dbi_result_get_int_idx( result, 4 );
	// Allocate a SelectItem: from the free list if possible, from the heap if necessary

	// Construct an Expression
	Expression* expression = getExpression( state, expression_id );
	if( !expression ) {
		osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
			"Unable to fetch ORDER BY expression for id = %d", expression_id ));
		return NULL;
	};

	// Allocate an OrderItem; from the free list if possible, or from the heap if necessary.
	OrderItem* ord;
	if( free_order_item_list ) {
		ord = free_order_item_list;
		free_order_item_list = free_order_item_list->next;
	} else
		ord = safe_malloc( sizeof( OrderItem ));

	ord->next            = NULL;
	ord->id              = id;
	ord->stored_query_id = stored_query_id;
	ord->seq_no          = seq_no;
	ord->expression      = expression;

	return ord;
}
Beispiel #4
0
static QSeq* constructQSeq( BuildSQLState* state, dbi_result result ) {
	int id = dbi_result_get_int_idx( result, 1 );
	int parent_query_id = dbi_result_get_int_idx( result, 2 );
	int seq_no = dbi_result_get_int_idx( result, 3 );
	int child_query_id = dbi_result_get_int_idx( result, 4 );

	StoredQ* child_query = getStoredQuery( state, child_query_id );
	if( !child_query ) {
		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
			"Unable to load child query # %d for parent query %d",
			child_query_id, parent_query_id ));
		state->error = 1;
		return NULL;
	}

	// Allocate a QSeq; from the free list if possible, from the heap if necessary
	QSeq* seq = NULL;
	if( free_qseq_list ) {
		seq = free_qseq_list;
		free_qseq_list = free_qseq_list->next;
	} else
		seq = safe_malloc( sizeof( QSeq ));

	seq->next            = NULL;
	seq->id              = id;
	seq->parent_query_id = parent_query_id;
	seq->seq_no          = seq_no;
	seq->child_query     = child_query;

	return seq;
}
Beispiel #5
0
/**
	@brief Load a stored query.
	@param state Pointer to the query-building context.
	@param query_id ID of the query in query.stored_query.
	@return A pointer to the newly loaded StoredQ if successful, or NULL if not.

	The calling code is responsible for freeing the StoredQ by calling storedQFree().
*/
StoredQ* getStoredQuery( BuildSQLState* state, int query_id ) {
	if( !state )
		return NULL;

	// Check the stack to see if the current query is nested inside itself.  If it is, then
	// abort in order to avoid infinite recursion.  If it isn't, then add it to the stack.
	// (Make sure to pop it off the stack before returning.)
	if( searchIdStack( state->query_stack, query_id, NULL )) {
		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
			"Infinite recursion detected; query # %d is nested within itself", query_id ));
		state->error = 1;
		return NULL;
	} else
		push_id( &state->query_stack, query_id, NULL );

	StoredQ* sq = NULL;
	dbi_result result = dbi_conn_queryf( state->dbhandle,
		"SELECT id, type, use_all, use_distinct, from_clause, where_clause, having_clause "
		"FROM query.stored_query WHERE id = %d;", query_id );
	if( result ) {
		if( dbi_result_first_row( result ) ) {
			sq = constructStoredQ( state, result );
			if( sq ) {
				PRINT( "Got a query row\n" );
				PRINT( "\tid: %d\n", sq->id );
				PRINT( "\ttype: %d\n", (int) sq->type );
				PRINT( "\tuse_all: %s\n", sq->use_all ? "true" : "false" );
				PRINT( "\tuse_distinct: %s\n", sq->use_distinct ? "true" : "false" );
			} else
				osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
					"Unable to build a query for id = %d", query_id ));
		} else {
			sqlAddMsg( state, "Stored query not found for id %d", query_id );
		}

		dbi_result_free( result );
	} else {
		const char* msg;
		int errnum = dbi_conn_error( state->dbhandle, &msg );
		osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, 
			"Unable to query query.stored_query table: #%d %s",
			errnum, msg ? msg : "No description available" ));
	}

	pop_id( &state->query_stack );
	return sq;
}
Beispiel #6
0
static SelectItem* getSelectList( BuildSQLState* state, int query_id ) {
	SelectItem* select_list = NULL;

	// The ORDER BY is in descending order so that we can build the list by adding to
	// the head, and it will wind up in the right order.
	dbi_result result = dbi_conn_queryf( state->dbhandle,
		"SELECT id, stored_query, seq_no, expression, column_alias, grouped_by "
		"FROM query.select_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
	if( result ) {
		if( dbi_result_first_row( result ) ) {
			while( 1 ) {
				SelectItem* item = constructSelectItem( state, result );
				if( item ) {
					PRINT( "Found a SELECT item\n" );
					PRINT( "\tid: %d\n", item->id );
					PRINT( "\tstored_query_id: %d\n", item->stored_query_id );
					PRINT( "\tseq_no: %d\n", item->seq_no );
					PRINT( "\tcolumn_alias: %s\n",
							item->column_alias ? item->column_alias : "(none)" );
					PRINT( "\tgrouped_by: %d\n", item->grouped_by );

					item->next = select_list;
					select_list = item;
				} else {
					osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
						"Unable to build select list for query id #%d", query_id ));
					selectListFree( select_list );
					select_list = NULL;
					break;
				}
				if( !dbi_result_next_row( result ) )
					break;
			};
		}
	} else {
		const char* msg;
		int errnum = dbi_conn_error( state->dbhandle, &msg );
		osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
					  "Unable to query query.select_list table: #%d %s",
					  errnum, msg ? msg : "No description available" ));
	}

	return select_list;
}
Beispiel #7
0
static Expression* getExpression( BuildSQLState* state, int id ) {
	
	// Check the stack to see if the current expression is nested inside itself.  If it is,
	// then abort in order to avoid infinite recursion.  If it isn't, then add it to the
	// stack.  (Make sure to pop it off the stack before returning.)
	if( searchIdStack( state->expr_stack, id, NULL )) {
		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
			"Infinite recursion detected; expression # %d is nested within itself", id ));
		state->error = 1;
		return NULL;
	} else
		push_id( &state->expr_stack, id, NULL );

		Expression* exp = NULL;
	dbi_result result = dbi_conn_queryf( state->dbhandle,
		"SELECT id, type, parenthesize, parent_expr, seq_no, literal, table_alias, "
		"column_name, left_operand, operator, right_operand, function_id, subquery, cast_type "
		"FROM query.expression WHERE id = %d;", id );
	if( result ) {
		if( dbi_result_first_row( result ) ) {
			exp = constructExpression( state, result );
			if( exp ) {
				PRINT( "Got an expression\n" );
				PRINT( "\tid = %d\n", exp->id );
				PRINT( "\ttype = %d\n", exp->type );
				PRINT( "\tparenthesize = %d\n", exp->parenthesize );
				PRINT( "\tcolumn_name = %s\n", exp->column_name ? exp->column_name : "(none)" );
			} else 
				osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
					"Unable to construct an Expression for id = %d", id ));
		}
	} else {
		const char* msg;
		int errnum = dbi_conn_error( state->dbhandle, &msg );
		osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
			"Unable to query query.expression table: #%d %s",
			errnum, msg ? msg : "No description available" ));
	}

	pop_id( &state->expr_stack );
	return exp;
}
Beispiel #8
0
/**
	@brief Construct an SQL query, but without executing it.
	@param ctx Pointer to the current method context.
	@return Zero if successful, or -1 if not.

	Method parameters:
	- query token, as previously returned by the .prepare method.

	Returns: A string containing an SQL query..
*/
int doSql( osrfMethodContext* ctx ) {
	if(osrfMethodVerifyContext( ctx )) {
		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
		return -1;
	}

	// Get the query token
	const jsonObject* token_obj = jsonObjectGetIndex( ctx->params, 0 );
	if( token_obj->type != JSON_STRING ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid parameter; query token must be a string" );
		return -1;
	}
	const char* token = jsonObjectGetString( token_obj );

	// Look up the query token in the session-level userData
	CachedQuery* query = search_token( ctx, token );
	if( !query ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid query token" );
		return -1;
	}

	osrfLogInfo( OSRF_LOG_MARK, "Returning SQL for token \"%s\"", token );
	if( query->state->error ) {
		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( query->state,
			"No valid prepared query available for query id # %d", query->query->id ));
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "No valid prepared query available" );
		return -1;
	} else if( buildSQL( query->state, query->query )) {
		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( query->state,
			"Unable to build SQL statement for query id # %d", query->query->id ));
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Unable to build SQL statement" );
		return -1;
	}

	osrfAppRespondComplete( ctx, jsonNewObject( OSRF_BUFFER_C_STR( query->state->sql )));
	return 0;
}
Beispiel #9
0
/**
	@brief Build a list of joined relations.
	@param state Pointer to the query-building context.
	@param id ID of the parent relation.
	@return A pointer to the first in a linked list of FromRelations, if there are any; or
		NULL if there aren't any, or in case of an error.

	Look for relations joined directly to the parent relation, and make a list of them.
*/
static FromRelation* getJoinList( BuildSQLState* state, int id ) {
	FromRelation* join_list = NULL;
	
	// The ORDER BY is in descending order so that we can build the list by adding to
	// the head, and it will wind up in the right order.
	dbi_result result = dbi_conn_queryf( state->dbhandle,
		"SELECT id, type, table_name, class_name, subquery, function_call, "
		"table_alias, parent_relation, seq_no, join_type, on_clause "
		"FROM query.from_relation WHERE parent_relation = %d ORDER BY seq_no DESC", id );

	if( result ) {
		if( dbi_result_first_row( result ) ) {
			while( 1 ) {
				FromRelation* relation = constructFromRelation( state, result );
				if( relation ) {
					PRINT( "Found a joined relation\n" );
					PRINT( "\tjoin_type: %d\n", relation->join_type );
					PRINT( "\ttable_name: %s\n", relation->table_name );
					relation->next = join_list;
					join_list = relation;
				} else {
					osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
						"Unable to build join list for from relation id #%d", id ));
					joinListFree( join_list );
					join_list = NULL;
					break;
				}
				if( !dbi_result_next_row( result ) )
					break;
			};
		}
	} else {
		const char* msg;
		int errnum = dbi_conn_error( state->dbhandle, &msg );
		osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
			"Unable to query query.from_relation table for join list: #%d %s",
			errnum, msg ? msg : "No description available" ));
	}

	return join_list;
}
Beispiel #10
0
/**
	@brief Load a specified query from the database query tables.
	@param ctx Pointer to the current method context.
	@return Zero if successful, or -1 if not.

	Method parameters:
	- query id (key of query.stored_query table)

	Returns: a hash with two entries:
	- "token": A character string serving as a token for future references to the query.
	- "bind_variables" A hash of bind variables; see notes for doParamList().
*/
int doPrepare( osrfMethodContext* ctx ) {
	if(osrfMethodVerifyContext( ctx )) {
		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
		return -1;
	}

	// Get the query id from a method parameter
	const jsonObject* query_id_obj = jsonObjectGetIndex( ctx->params, 0 );
	if( query_id_obj->type != JSON_NUMBER ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid parameter; query id must be a number" );
		return -1;
	}

	int query_id = atoi( jsonObjectGetString( query_id_obj ));
	if( query_id <= 0 ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid parameter: query id must be greater than zero" );
		return -1;
	}

	osrfLogInfo( OSRF_LOG_MARK, "Loading query for id # %d", query_id );

	BuildSQLState* state = buildSQLStateNew( dbhandle );
	state->defaults_usable = 1;
	state->values_required = 0;
	StoredQ* query = getStoredQuery( state, query_id );
	if( state->error ) {
		osrfLogWarning( OSRF_LOG_MARK, "Unable to load stored query # %d", query_id );
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Unable to load stored query" );
		if( state->panic ) {
			osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, 
				"Database connection isn't working" ));
			osrfAppSessionPanic( ctx->session );
		}
		return -1;
	}

	const char* token = save_query( ctx, state, query );

	osrfLogInfo( OSRF_LOG_MARK, "Token for query id # %d is \"%s\"", query_id, token );

	// Build an object to return.  It will be a hash containing the query token and a
	// list of bind variables.
	jsonObject* returned_obj = jsonNewObjectType( JSON_HASH );
	jsonObjectSetKey( returned_obj, "token", jsonNewObject( token ));
	jsonObjectSetKey( returned_obj, "bind_variables",
		oilsBindVarList( state->bindvar_list ));

	osrfAppRespondComplete( ctx, returned_obj );
	return 0;
}
Beispiel #11
0
static OrderItem* getOrderByList( BuildSQLState* state, int query_id ) {
	OrderItem* ord_list = NULL;

	// The ORDER BY is in descending order so that we can build the list by adding to
	// the head, and it will wind up in the right order.
	dbi_result result = dbi_conn_queryf( state->dbhandle,
		"SELECT id, stored_query, seq_no, expression "
		"FROM query.order_by_item WHERE stored_query = %d ORDER BY seq_no DESC", query_id );
	if( result ) {
		if( dbi_result_first_row( result ) ) {
			while( 1 ) {
				OrderItem* item = constructOrderItem( state, result );
				if( item ) {
					PRINT( "Found an ORDER BY item\n" );

					item->next = ord_list;
					ord_list = item;
				} else {
					osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
						"Unable to build ORDER BY item for query id #%d", query_id ));
					orderItemListFree( ord_list );
					ord_list = NULL;
					break;
				}
				if( !dbi_result_next_row( result ) )
					break;
			};
		}
	}  else {
		const char* msg;
		int errnum = dbi_conn_error( state->dbhandle, &msg );
		osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
			"Unable to query query.order_by_list table: #%d %s",
			errnum, msg ? msg : "No description available" ));
	}

	return ord_list;
}
Beispiel #12
0
/**
	@brief Return a list of column names for the SELECT list.
	@param ctx Pointer to the current method context.
	@return Zero if successful, or -1 if not.

	Method parameters:
	- query token, as previously returned by the .prepare method.

	Returns: An array of column names; unavailable names are represented as nulls.
*/
int doColumns( osrfMethodContext* ctx ) {
	if(osrfMethodVerifyContext( ctx )) {
		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
		return -1;
	}

	// Get the query token from a method parameter
	const jsonObject* token_obj = jsonObjectGetIndex( ctx->params, 0 );
	if( token_obj->type != JSON_STRING ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid parameter; query token must be a string" );
		return -1;
	}
	const char* token = jsonObjectGetString( token_obj );

	// Look up the query token in the session-level userData
	CachedQuery* query = search_token( ctx, token );
	if( !query ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid query token" );
		return -1;
	}

	osrfLogInfo( OSRF_LOG_MARK, "Listing column names for token %s", token );

	jsonObject* col_list = oilsGetColNames( query->state, query->query );
	if( query->state->error ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Unable to get column names" );
		if( query->state->panic ) {
			osrfLogError( OSRF_LOG_MARK, sqlAddMsg( query->state,
				"Database connection isn't working" ));
			osrfAppSessionPanic( ctx->session );
		}
		return -1;
	} else {
		osrfAppRespondComplete( ctx, col_list );
		return 0;
	}
}
Beispiel #13
0
static SelectItem* constructSelectItem( BuildSQLState* state, dbi_result result ) {

	// Get the column values
	int id                   = dbi_result_get_int_idx( result, 1 );
	int stored_query_id      = dbi_result_get_int_idx( result, 2 );
	int seq_no               = dbi_result_get_int_idx( result, 3 );
	int expression_id        = dbi_result_get_int_idx( result, 4 );
	const char* column_alias = dbi_result_get_string_idx( result, 5 );
	int grouped_by           = oils_result_get_bool_idx( result, 6 );
	
	// Construct an Expression
	Expression* expression = getExpression( state, expression_id );
	if( !expression ) {
		osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
			"Unable to fetch expression for id = %d", expression_id ));
		return NULL;
	};

	// Allocate a SelectItem: from the free list if possible, from the heap if necessary

	SelectItem* sel;
	if( free_select_item_list ) {
		sel = free_select_item_list;
		free_select_item_list = free_select_item_list->next;
	} else
		sel = safe_malloc( sizeof( SelectItem ) );

	sel->next            = NULL;
	sel->id              = id;
	sel->stored_query_id = stored_query_id;
	sel->seq_no          = seq_no;
	sel->expression      = expression;
	sel->column_alias    = column_alias ? strdup( column_alias ) : NULL;
	sel->grouped_by      = grouped_by;

	return sel;
}
Beispiel #14
0
static Expression* constructExpression( BuildSQLState* state, dbi_result result ) {

	int id = dbi_result_get_int_idx( result, 1 );
	const char* type_str = dbi_result_get_string_idx( result, 2 );
	
	ExprType type;
	if( !strcmp( type_str, "xbet" ))
		type = EXP_BETWEEN;
	else if( !strcmp( type_str, "xbool" ))
		type = EXP_BOOL;
	else if( !strcmp( type_str, "xcase" ))
		type = EXP_CASE;
	else if( !strcmp( type_str, "xcast" ))
		type = EXP_CAST;
	else if( !strcmp( type_str, "xcol" ))
		type = EXP_COLUMN;
	else if( !strcmp( type_str, "xex" ))
		type = EXP_EXIST;
	else if( !strcmp( type_str, "xfld" ))
		type = EXP_FIELD;
	else if( !strcmp( type_str, "xfunc" ))
		type = EXP_FUNCTION;
	else if( !strcmp( type_str, "xin" ))
		type = EXP_IN;
	else if( !strcmp( type_str, "xnbet" ))
		type = EXP_NOT_BETWEEN;
	else if( !strcmp( type_str, "xnex" ))
		type = EXP_NOT_EXIST;
	else if( !strcmp( type_str, "xnin" ))
		type = EXP_NOT_IN;
	else if( !strcmp( type_str, "xnull" ))
		type = EXP_NULL;
	else if( !strcmp( type_str, "xnum" ))
		type = EXP_NUMBER;
	else if( !strcmp( type_str, "xop" ))
		type = EXP_OPERATOR;
	else if( !strcmp( type_str, "xstr" ))
		type = EXP_STRING;
	else if( !strcmp( type_str, "xsubq" ))
		type = EXP_SUBQUERY;
	else
		type = EXP_NULL;     // shouldn't happen due to database constraint

	int parenthesize = oils_result_get_bool_idx( result, 3 );

	int parent_expr_id;
	if( dbi_result_field_is_null_idx( result, 4 ))
		parent_expr_id = -1;
	else
		parent_expr_id = dbi_result_get_int_idx( result, 4 );
	
	int seq_no = dbi_result_get_int_idx( result, 5 );
	const char* literal = dbi_result_get_string_idx( result, 6 );
	const char* table_alias = dbi_result_get_string_idx( result, 7 );
	const char* column_name = dbi_result_get_string_idx( result, 8 );

	int left_operand_id;
	if( dbi_result_field_is_null_idx( result, 9 ))
		left_operand_id = -1;
	else
		left_operand_id = dbi_result_get_int_idx( result, 9 );

	const char* operator = dbi_result_get_string_idx( result, 10 );

	int right_operand_id;
	if( dbi_result_field_is_null_idx( result, 11 ))
		right_operand_id = -1;
	else
		right_operand_id = dbi_result_get_int_idx( result, 11 );

	int function_id;
	if( dbi_result_field_is_null_idx( result, 12 ))
		function_id = -1;
	else
		function_id = dbi_result_get_int_idx( result, 12 );

	int subquery_id;
	if( dbi_result_field_is_null_idx( result, 13 ))
		subquery_id = -1;
	else
		subquery_id = dbi_result_get_int_idx( result, 13 );

	int cast_type_id;
	if( dbi_result_field_is_null_idx( result, 14 ))
		cast_type_id = -1;
	else
		cast_type_id = dbi_result_get_int_idx( result, 14 );

	Expression* left_operand = NULL;
	Expression* right_operand = NULL;
	StoredQ* subquery = NULL;

	if( EXP_OPERATOR == type ) {
		// Load left and/or right operands
		if( -1 == left_operand_id && -1 == right_operand_id ) {
			osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
				"Expression # %d is an operator with no operands", 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;
			}
		}

		if( right_operand_id != -1 ) {
			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 ));
				state->error = 1;
				expressionFree( left_operand );
				return NULL;
			}
		}
	} else if( EXP_IN == type ) {
		if( -1 == left_operand_id ) {
			osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
				"IN condition has no left operand in 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 for IN condition in expression # %d", id ));
				state->error = 1;
				return NULL;
			}
		}

		if( -1 == subquery_id ) {
			// To do: load IN list of subexpressions
			osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
				"IN lists not yet supported for expression # %d", id ));
			state->error = 1;
			return NULL;
		} else {
			subquery = getStoredQuery( state, subquery_id );
			if( !subquery ) {
				osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
					"Unable to load subquery for IN expression # %d", id ));
				state->error = 1;
				return NULL;
			}
		}
	} else if( EXP_EXIST == type ) {
		if( -1 == subquery_id ) {
			osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
				"Internal error: No subquery found for EXIST expression # %d", id ));
			state->error = 1;
			return NULL;
		} else {
			subquery = getStoredQuery( state, subquery_id );
			if( !subquery ) {
				osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
					"Unable to load subquery for EXIST expression # %d", id ));
				state->error = 1;
				return NULL;
			}
		}
	} else if( EXP_SUBQUERY == type ) {
		if( -1 == subquery_id ) {
			osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
				"Subquery expression # %d has no query id", id ));
			state->error = 1;
			return NULL;
		} else {
			// Load a subquery, if there is one
			subquery = getStoredQuery( state, subquery_id );
			if( !subquery ) {
				osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
					"Unable to load subquery for expression # %d", id ));
				state->error = 1;
				return NULL;
			}
			if( subquery->select_list && subquery->select_list->next ) {
				osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
					"Subquery # %d as expression returns more than one column", subquery_id ));
				state->error = 1;
				return NULL;
			}
			PRINT( "\tExpression is subquery %d\n", subquery_id );
		}
	}

	// Allocate an Expression: from the free list if possible, from the heap if necessary
	Expression* exp = NULL;
	if( free_expression_list ) {
		exp = free_expression_list;
		free_expression_list = free_expression_list->next;
	} else
		exp = safe_malloc( sizeof( Expression ) );

	// Populate the Expression
	exp->next = NULL;
	exp->id = id;
	exp->type = type;
	exp->parenthesize = parenthesize;
	exp->parent_expr_id = parent_expr_id;
	exp->seq_no = seq_no;
	exp->literal = literal ? strdup( literal ) : NULL;
	exp->table_alias = table_alias ? strdup( table_alias ) : NULL;
	exp->column_name = column_name ? strdup( column_name ) : NULL;
	exp->left_operand = left_operand;
	exp->op = operator ? strdup( operator ) : NULL;
	exp->right_operand = right_operand;
	exp->function_id = function_id;
	exp->subquery_id = subquery_id;
	exp->subquery = subquery;
	exp->cast_type_id = subquery_id;

	return exp;
}
Beispiel #15
0
static StoredQ* constructStoredQ( BuildSQLState* state, dbi_result result ) {

	// Get the column values from the result
	int id               = dbi_result_get_int_idx( result, 1 );
	const char* type_str = dbi_result_get_string_idx( result, 2 );

	QueryType type;
	if( !strcmp( type_str, "SELECT" ))
		type = QT_SELECT;
	else if( !strcmp( type_str, "UNION" ))
		type = QT_UNION;
	else if( !strcmp( type_str, "INTERSECT" ))
		type = QT_INTERSECT;
	else if( !strcmp( type_str, "EXCEPT" ))
		type = QT_EXCEPT;
	else {
		osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
			"Invalid query type \"%s\"", type_str ));
		return NULL;
	}

	int use_all             = oils_result_get_bool_idx( result, 3 );
	int use_distinct        = oils_result_get_bool_idx( result, 4 );

	int from_clause_id;
	if( dbi_result_field_is_null_idx( result, 5 ) )
		from_clause_id = -1;
	else
		from_clause_id = dbi_result_get_int_idx( result, 5 );

	int where_clause_id;
	if( dbi_result_field_is_null_idx( result, 6 ) )
		where_clause_id = -1;
	else
		where_clause_id = dbi_result_get_int_idx( result, 6 );

	int having_clause_id;
	if( dbi_result_field_is_null_idx( result, 7 ) )
		having_clause_id = -1;
	else
		having_clause_id = dbi_result_get_int_idx( result, 7 );

	FromRelation* from_clause = NULL;
	if( QT_SELECT == type ) {
		// A SELECT query needs a FROM clause; go get it
		if( from_clause_id != -1 ) {
			from_clause = getFromRelation( state, from_clause_id );
			if( !from_clause ) {
				osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
					"Unable to construct FROM clause for id = %d", from_clause_id ));
				return NULL;
			}
		}
	} else {
		// Must be one of UNION, INTERSECT, or EXCEPT
		if( from_clause_id != -1 )
			osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
				"FROM clause found and ignored for %s query in query #%d", type_str, id ));
	}

	// If this is a SELECT query, we need a SELECT list.  Go get one.
	SelectItem* select_list = NULL;
	QSeq* child_list = NULL;
	if( QT_SELECT == type ) {
		select_list = getSelectList( state, id );
		if( !select_list ) {
			osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
				"No SELECT list found for query id = %d", id ));
			fromRelationFree( from_clause );
			return NULL;
		}
	} else {
		// Construct child queries of UNION, INTERSECT, or EXCEPT query
		child_list = loadChildQueries( state, id, type_str );
		if( !child_list ) {
			osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
				"Unable to load child queries for %s query # %d", type_str, id ));
			state->error = 1;
			fromRelationFree( from_clause );
			return NULL;
		}
	}

	// Get the WHERE clause, if there is one
	Expression* where_clause = NULL;
	if( where_clause_id != -1 ) {
		where_clause = getExpression( state, where_clause_id );
		if( ! where_clause ) {
			// shouldn't happen due to foreign key constraint
			osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
				"Unable to fetch WHERE expression for query id = %d", id ));
			freeQSeqList( child_list );
			fromRelationFree( from_clause );
			selectListFree( select_list );
			return NULL;
		}
	}

	// Get the ORDER BY clause, if there is one
	OrderItem* order_by_list = getOrderByList( state, id );
	if( state->error ) {
		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
			"Unable to load ORDER BY clause for query %d", id ));
		expressionFree( where_clause );
		freeQSeqList( child_list );
		fromRelationFree( from_clause );
		selectListFree( select_list );
		return NULL;
	}
	
	// Allocate a StoredQ: from the free list if possible, from the heap if necessary

	StoredQ* sq;
	if( free_storedq_list ) {
		sq = free_storedq_list;
		free_storedq_list = free_storedq_list->next;
	} else
		sq = safe_malloc( sizeof( StoredQ ) );

	// Populate the StoredQ
	sq->next = NULL;
	sq->id = id;

	sq->type = type;
	sq->use_all = use_all;
	sq->use_distinct = use_distinct;
	sq->from_clause = from_clause;
	sq->where_clause = where_clause;
	sq->select_list = select_list;
	sq->child_list = child_list;
	sq->order_by_list = order_by_list;

	return sq;
}
Beispiel #16
0
static FromRelation* getFromRelation( BuildSQLState* state, int id ) {
	FromRelation* fr = NULL;
	dbi_result result = dbi_conn_queryf( state->dbhandle,
		"SELECT id, type, table_name, class_name, subquery, function_call, "
		"table_alias, parent_relation, seq_no, join_type, on_clause "
		"FROM query.from_relation WHERE id = %d;", id );
	if( result ) {
		if( dbi_result_first_row( result ) ) {
			fr = constructFromRelation( state, result );
			if( fr ) {
				PRINT( "Got a from_relation row\n" );
				PRINT( "\tid: %d\n", fr->id );
				PRINT( "\ttype: %d\n", (int) fr->type );
				PRINT( "\ttable_name: %s\n", fr->table_name ? fr->table_name : "(none)" );
				PRINT( "\tclass_name: %s\n", fr->class_name ? fr->class_name : "(none)" );
				PRINT( "\tsubquery_id: %d\n", fr->subquery_id );
				PRINT( "\tfunction_call_id: %d\n", fr->function_call_id );
				PRINT( "\ttable_alias: %s\n", fr->table_alias ? fr->table_alias : "(none)" );
				PRINT( "\tparent_relation_id: %d\n", fr->parent_relation_id );
				PRINT( "\tseq_no: %d\n", fr->seq_no );
				PRINT( "\tjoin_type = %d\n", fr->join_type );
				// Check the stack to see if the current from clause is nested inside itself.
				// If it is, then abort in order to avoid infinite recursion.  If it isn't,
				// then add it to the stack.  (Make sure to pop it off the stack before
				// returning.)
				const char* effective_alias = fr->table_alias;
				if( !effective_alias )
					effective_alias = fr->class_name;
				const IdNode* node = searchIdStack( state->from_stack, id, effective_alias );
				if( node ) {
					if( node->id == id )
						osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
							"Infinite recursion detected; from clause # %d is nested "
							"within itself", id ));
					else
						osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
							"Conflicting nested table aliases \"%s\" in from clause # %d",
							effective_alias, node->id ));
					state->error = 1;
					return NULL;
				} else
					push_id( &state->from_stack, id, effective_alias );
			} else
				osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
					"Unable to build a FromRelation for id = %d", id ));
		} else {
			osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
				"FROM relation not found for id = %d", id ));
		}
		dbi_result_free( result );
	} else {
		const char* msg;
		int errnum = dbi_conn_error( state->dbhandle, &msg );
		osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
			"Unable to query query.from_relation table: #%d %s",
			errnum, msg ? msg : "No description available" ));
	}

	if( fr )
		pop_id( &state->from_stack );

	return fr;
}
Beispiel #17
0
static FromRelation* constructFromRelation( BuildSQLState* state, dbi_result result ) {
	// Get the column values from the result
	int id                  = dbi_result_get_int_idx( result, 1 );
	const char* type_str    = dbi_result_get_string_idx( result, 2 );

	FromRelationType type;
	if( !strcmp( type_str, "RELATION" ))
		type = FRT_RELATION;
	else if( !strcmp( type_str, "SUBQUERY" ))
		type = FRT_SUBQUERY;
	else if( !strcmp( type_str, "FUNCTION" ))
		type = FRT_FUNCTION;
	else
		type = FRT_RELATION;     // shouldn't happen due to database constraint

	const char* table_name  = dbi_result_get_string_idx( result, 3 );
	const char* class_name  = dbi_result_get_string_idx( result, 4 );

	int subquery_id;
	if( dbi_result_field_is_null_idx( result, 5 ) )
		subquery_id          = -1;
	else
		subquery_id          = dbi_result_get_int_idx( result, 5 );

	int function_call_id;
	if( dbi_result_field_is_null_idx( result, 6 ) )
		function_call_id     = -1;
	else
		function_call_id     = dbi_result_get_int_idx( result, 6 );

	const char* table_alias  = dbi_result_get_string_idx( result, 7 );

	int parent_relation_id;
	if( dbi_result_field_is_null_idx( result, 8 ) )
		parent_relation_id   = -1;
	else
		parent_relation_id   = dbi_result_get_int_idx( result, 8 );

	int seq_no               = dbi_result_get_int_idx( result, 9 );

	JoinType join_type;
	const char* join_type_str = dbi_result_get_string_idx( result, 10 );
	if( !join_type_str )
		join_type = JT_NONE;
	else if( !strcmp( join_type_str, "INNER" ) )
		join_type = JT_INNER;
	else if( !strcmp( join_type_str, "LEFT" ) )
		join_type = JT_LEFT;
	else if( !strcmp( join_type_str, "RIGHT" ) )
		join_type = JT_RIGHT;
	else if( !strcmp( join_type_str, "FULL" ) )
		join_type = JT_FULL;
	else
		join_type = JT_NONE;     // shouldn't happen due to database constraint

	int on_clause_id;
	if( dbi_result_field_is_null_idx( result, 11 ) )
		on_clause_id   = -1;
	else
		on_clause_id   = dbi_result_get_int_idx( result, 11 );

	StoredQ* subquery = NULL;

	switch ( type ) {
		case FRT_RELATION :
			break;
		case FRT_SUBQUERY :
			if( -1 == subquery_id ) {
				osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
					"Internal error: no subquery specified for FROM relation # %d", id ));
				state->error = 1;
				return NULL;
			}
			if( ! table_alias ) {
				osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
					"Subquery needs alias in FROM relation # %d", id ));
				state->error = 1;
				return NULL;
			}
			subquery = getStoredQuery( state, subquery_id );
			if( ! subquery ) {
				osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
					"Unable to load subquery for FROM relation # %d", id ));
				state->error = 1;
				return NULL;
			}
			break;
		case FRT_FUNCTION :
			osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
				"Functions in FROM clause not yet supported" ));
			state->error = 1;
			return NULL;
	}

	FromRelation* join_list = getJoinList( state, id );
	if( state->error ) {
		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
			"Unable to load join list for FROM relation # %d", id ));
		return NULL;
	}

	Expression* on_clause = NULL;
	if( on_clause_id != -1 ) {
		on_clause = getExpression( state, on_clause_id );
		if( !on_clause ) {
			osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state,
				"Unable to load ON condition for FROM relation # %d", id ));
			joinListFree( join_list );
			return NULL;
		}
		else
			PRINT( "\tGot an ON condition\n" );
	}

	// Allocate a FromRelation: from the free list if possible, from the heap if necessary

	FromRelation* fr;
	if( free_from_relation_list ) {
		fr = free_from_relation_list;
		free_from_relation_list = free_from_relation_list->next;
	} else
		fr = safe_malloc( sizeof( FromRelation ) );

	// Populate the FromRelation

	fr->next = NULL;
	fr->id = id;
	fr->type = type;
	fr->table_name = table_name ? strdup( table_name ) : NULL;
	fr->class_name = class_name ? strdup( class_name ) : NULL;
	fr->subquery_id = subquery_id;
	fr->subquery = subquery;
	fr->function_call_id = function_call_id;
	fr->table_alias = table_alias ? strdup( table_alias ) : NULL;
	fr->parent_relation_id = parent_relation_id;
	fr->seq_no = seq_no;
	fr->join_type = join_type;
	fr->on_clause = on_clause;
	fr->join_list = join_list;

	return fr;
}