From 42091320eb3bd0dfcf0b2cadba50f059b2842306 Mon Sep 17 00:00:00 2001 From: scottmk Date: Tue, 13 Jul 2010 20:10:37 +0000 Subject: [PATCH] Qstore: support LIMIT and OFFSET clauses. Also: add some links to the IDL, that should have been there in the first place. 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 M Open-ILS/src/sql/Pg/002.schema.config.sql M Open-ILS/src/sql/Pg/008.schema.query.sql A Open-ILS/src/sql/Pg/upgrade/0336.schema.query-limit-offset-fkey.sql M Open-ILS/examples/fm_IDL.xml git-svn-id: svn://svn.open-ils.org/ILS/trunk@16922 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- Open-ILS/examples/fm_IDL.xml | 19 +++-- Open-ILS/include/openils/oils_buildq.h | 2 + Open-ILS/src/c-apps/buildSQL.c | 35 ++++++++-- Open-ILS/src/c-apps/oils_storedq.c | 70 +++++++++++++++++-- Open-ILS/src/sql/Pg/002.schema.config.sql | 2 +- Open-ILS/src/sql/Pg/008.schema.query.sql | 16 ++++- .../0336.schema.query-limit-offset-fkey.sql | 15 ++++ 7 files changed, 140 insertions(+), 19 deletions(-) create mode 100644 Open-ILS/src/sql/Pg/upgrade/0336.schema.query-limit-offset-fkey.sql diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml index d8c3b01a05..a6c308e62d 100644 --- a/Open-ILS/examples/fm_IDL.xml +++ b/Open-ILS/examples/fm_IDL.xml @@ -6793,13 +6793,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - - - - - - - + + + + + + + + + + + + diff --git a/Open-ILS/include/openils/oils_buildq.h b/Open-ILS/include/openils/oils_buildq.h index a51c0209ad..800e795e41 100644 --- a/Open-ILS/include/openils/oils_buildq.h +++ b/Open-ILS/include/openils/oils_buildq.h @@ -90,6 +90,8 @@ struct StoredQ_ { QSeq* child_list; Expression* having_clause; OrderItem* order_by_list; + Expression* limit_count; + Expression* offset_count; }; typedef enum { diff --git a/Open-ILS/src/c-apps/buildSQL.c b/Open-ILS/src/c-apps/buildSQL.c index e19d9fcb55..07f018f5a0 100644 --- a/Open-ILS/src/c-apps/buildSQL.c +++ b/Open-ILS/src/c-apps/buildSQL.c @@ -25,10 +25,12 @@ 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 int subexp_count( const Expression* expr ); static void buildTypicalFunction( BuildSQLState* state, const Expression* expr ); static void buildExtract( BuildSQLState* state, const Expression* expr ); + static void buildSeries( BuildSQLState* state, const Expression* subexp_list, const char* op ); static void buildBindVar( BuildSQLState* state, const BindVar* bind ); static void buildScalar( BuildSQLState* state, int numeric, const jsonObject* obj ); @@ -262,7 +264,7 @@ static void buildSelect( BuildSQLState* state, const StoredQ* query ) { return; } - // To do: get SELECT list; just a stub here + // Get SELECT list buffer_add( state->sql, "SELECT" ); incr_indent( state ); buildSelectList( state, query->select_list ); @@ -326,9 +328,19 @@ static void buildSelect( BuildSQLState* state, const StoredQ* query ) { } } - // To do: Build LIMIT clause, if there is one + // Build LIMIT clause, if there is one + if( query->limit_count ) { + add_newline( state ); + buffer_add( state->sql, "LIMIT " ); + buildExpression( state, query->limit_count ); + } - // To do: Build OFFSET clause, if there is one + // Build OFFSET clause, if there is one + if( query->offset_count ) { + add_newline( state ); + buffer_add( state->sql, "OFFSET " ); + buildExpression( state, query->offset_count ); + } state->error = 0; } @@ -968,7 +980,19 @@ static void buildFunction( BuildSQLState* state, const Expression* expr ) { buffer_add( state->sql, "LOCALTIME " ); else if( !strcasecmp( expr->function_name, "LOCALTIMESTAMP" ) && ! expr->subexp_list ) buffer_add( state->sql, "LOCALTIMESTAMP " ); - else + else if( !strcasecmp( expr->function_name, "TRIM" )) { + int arg_count = subexp_count( expr ); + + if( (arg_count != 2 && arg_count != 3 ) || expr->subexp_list->type != EXP_STRING ) + buildTypicalFunction( state, expr ); + else { + sqlAddMsg( state, + "TRIM function not supported in expr # %d; use ltrim() and/or rtrim()", + expr->id ); + state->error = 1; + return; + } + } else buildTypicalFunction( state, expr ); // Not a special exception. if( expr->column_name ) { @@ -985,6 +1009,9 @@ static void buildFunction( BuildSQLState* state, const Expression* expr ) { @return The number of subexpressions. */ static int subexp_count( const Expression* expr ) { + if( !expr ) + return 0; + int count = 0; const Expression* sub = expr->subexp_list; while( sub ) { diff --git a/Open-ILS/src/c-apps/oils_storedq.c b/Open-ILS/src/c-apps/oils_storedq.c index 5258bff51f..9d9def4123 100644 --- a/Open-ILS/src/c-apps/oils_storedq.c +++ b/Open-ILS/src/c-apps/oils_storedq.c @@ -184,8 +184,8 @@ StoredQ* getStoredQuery( BuildSQLState* state, int query_id ) { 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 ); + "SELECT id, type, use_all, use_distinct, from_clause, where_clause, having_clause, " + "limit_count, offset_count FROM query.stored_query WHERE id = %d;", query_id ); if( result ) { if( dbi_result_first_row( result ) ) { sq = constructStoredQ( state, result ); @@ -270,6 +270,18 @@ static StoredQ* constructStoredQ( BuildSQLState* state, dbi_result result ) { else having_clause_id = dbi_result_get_int_idx( result, 7 ); + int limit_count_id; + if( dbi_result_field_is_null_idx( result, 8 ) ) + limit_count_id = -1; + else + limit_count_id = dbi_result_get_int_idx( result, 8 ); + + int offset_count_id; + if( dbi_result_field_is_null_idx( result, 9 ) ) + offset_count_id = -1; + else + offset_count_id = dbi_result_get_int_idx( result, 9 ); + FromRelation* from_clause = NULL; if( QT_SELECT == type ) { // A SELECT query needs a FROM clause; go get it @@ -327,6 +339,7 @@ static StoredQ* constructStoredQ( BuildSQLState* state, dbi_result result ) { } } + // Get the HAVING clause, if there is one Expression* having_clause = NULL; if( having_clause_id != -1 ) { having_clause = getExpression( state, having_clause_id ); @@ -336,8 +349,8 @@ static StoredQ* constructStoredQ( BuildSQLState* state, dbi_result result ) { "Unable to fetch HAVING expression for query id = %d", id )); expressionFree( where_clause ); freeQSeqList( child_list ); - fromRelationFree( from_clause ); selectListFree( select_list ); + fromRelationFree( from_clause ); state->error = 1; return NULL; } @@ -351,11 +364,46 @@ static StoredQ* constructStoredQ( BuildSQLState* state, dbi_result result ) { expressionFree( having_clause ); expressionFree( where_clause ); freeQSeqList( child_list ); - fromRelationFree( from_clause ); selectListFree( select_list ); + fromRelationFree( from_clause ); return NULL; } + // Get the LIMIT clause, if there is one + Expression* limit_count = NULL; + if( limit_count_id != -1 ) { + limit_count = getExpression( state, limit_count_id ); + if( ! limit_count ) { + // shouldn't happen due to foreign key constraint + osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, + "Unable to fetch LIMIT expression for query id = %d", id )); + orderItemListFree( order_by_list ); + freeQSeqList( child_list ); + selectListFree( select_list ); + fromRelationFree( from_clause ); + state->error = 1; + return NULL; + } + } + + // Get the OFFSET clause, if there is one + Expression* offset_count = NULL; + if( offset_count_id != -1 ) { + offset_count = getExpression( state, offset_count_id ); + if( ! offset_count ) { + // shouldn't happen due to foreign key constraint + osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, + "Unable to fetch OFFSET expression for query id = %d", id )); + expressionFree( limit_count ); + orderItemListFree( order_by_list ); + freeQSeqList( child_list ); + selectListFree( select_list ); + fromRelationFree( from_clause ); + state->error = 1; + return NULL; + } + } + // Allocate a StoredQ: from the free list if possible, from the heap if necessary StoredQ* sq; @@ -378,6 +426,8 @@ static StoredQ* constructStoredQ( BuildSQLState* state, dbi_result result ) { sq->child_list = child_list; sq->having_clause = having_clause; sq->order_by_list = order_by_list; + sq->limit_count = limit_count; + sq->offset_count = offset_count; return sq; } @@ -535,8 +585,18 @@ void storedQFree( StoredQ* sq ) { orderItemListFree( sq->order_by_list ); sq->order_by_list = NULL; } - if( sq->having_clause ) + if( sq->having_clause ) { expressionFree( sq->having_clause ); + sq->having_clause = NULL; + } + if( sq->limit_count ) { + expressionFree( sq->limit_count ); + sq->limit_count = NULL; + } + if( sq->offset_count ) { + expressionFree( sq->offset_count ); + sq->offset_count = NULL; + } // Stick the empty husk on the free list for potential reuse sq->next = free_storedq_list; diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql index ecf1979470..8675e4db23 100644 --- a/Open-ILS/src/sql/Pg/002.schema.config.sql +++ b/Open-ILS/src/sql/Pg/002.schema.config.sql @@ -68,7 +68,7 @@ CREATE TABLE config.upgrade_log ( install_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW() ); -INSERT INTO config.upgrade_log (version) VALUES ('0335'); -- dbs +INSERT INTO config.upgrade_log (version) VALUES ('0336'); -- Scott McKellar CREATE TABLE config.bib_source ( id SERIAL PRIMARY KEY, diff --git a/Open-ILS/src/sql/Pg/008.schema.query.sql b/Open-ILS/src/sql/Pg/008.schema.query.sql index 8c511f4e67..7f489a0972 100644 --- a/Open-ILS/src/sql/Pg/008.schema.query.sql +++ b/Open-ILS/src/sql/Pg/008.schema.query.sql @@ -39,9 +39,11 @@ CREATE TABLE query.stored_query ( where_clause INT, --REFERENCES query.expression --DEFERRABLE INITIALLY DEFERRED, having_clause INT, --REFERENCES query.expression + --DEFERRABLE INITIALLY DEFERRED, + limit_count INT, --REFERENCES query.expression + --DEFERRABLE INITIALLY DEFERRED, + offset_count INT --REFERENCES query.expression --DEFERRABLE INITIALLY DEFERRED - limit_count INT, - offset_count INT ); -- (Foreign keys to be defined later after other tables are created) @@ -188,6 +190,16 @@ ALTER TABLE query.stored_query REFERENCES query.expression( id ) DEFERRABLE INITIALLY DEFERRED; +ALTER TABLE query.stored_query + ADD FOREIGN KEY ( limit_count ) + REFERENCES query.expression( id ) + DEFERRABLE INITIALLY DEFERRED; + +ALTER TABLE query.stored_query + ADD FOREIGN KEY ( offset_count ) + REFERENCES query.expression( id ) + DEFERRABLE INITIALLY DEFERRED; + CREATE TABLE query.case_branch ( id SERIAL PRIMARY KEY, parent_expr INT NOT NULL REFERENCES query.expression diff --git a/Open-ILS/src/sql/Pg/upgrade/0336.schema.query-limit-offset-fkey.sql b/Open-ILS/src/sql/Pg/upgrade/0336.schema.query-limit-offset-fkey.sql new file mode 100644 index 0000000000..b0263b0acb --- /dev/null +++ b/Open-ILS/src/sql/Pg/upgrade/0336.schema.query-limit-offset-fkey.sql @@ -0,0 +1,15 @@ +BEGIN; + +INSERT INTO config.upgrade_log (version) VALUES ('0336'); -- Scott McKellar + +ALTER TABLE query.stored_query + ADD FOREIGN KEY ( limit_count ) + REFERENCES query.expression( id ) + DEFERRABLE INITIALLY DEFERRED; + +ALTER TABLE query.stored_query + ADD FOREIGN KEY ( offset_count ) + REFERENCES query.expression( id ) + DEFERRABLE INITIALLY DEFERRED; + +COMMIT; -- 2.43.2