From e45657bffa32e4642207996d7f876a75ddcfecd9 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Thu, 22 Sep 2011 21:24:58 +0000 Subject: [PATCH] [#825] Add List> Factory.fetchMany(String) to allow for fetching several result sets from stored procedures, such as Sybase ASE's "sp_help" --- .../org/jooq/util/ase/ASETableDefinition.java | 59 ++++++----------- .../src/org/jooq/test/jOOQAbstractTest.java | 64 +++++++++++-------- jOOQ/src/main/java/org/jooq/ResultQuery.java | 13 ++++ .../jooq/impl/AbstractDelegatingSelect.java | 5 ++ .../org/jooq/impl/AbstractResultQuery.java | 45 +++++++++++-- jOOQ/src/main/java/org/jooq/impl/Factory.java | 44 +++++++++++++ 6 files changed, 161 insertions(+), 69 deletions(-) diff --git a/jOOQ-meta/src/main/java/org/jooq/util/ase/ASETableDefinition.java b/jOOQ-meta/src/main/java/org/jooq/util/ase/ASETableDefinition.java index f43bc720f8..c6947fe61f 100644 --- a/jOOQ-meta/src/main/java/org/jooq/util/ase/ASETableDefinition.java +++ b/jOOQ-meta/src/main/java/org/jooq/util/ase/ASETableDefinition.java @@ -35,13 +35,11 @@ */ package org.jooq.util.ase; -import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Statement; import java.util.ArrayList; import java.util.List; +import org.jooq.Record; import org.jooq.util.AbstractTableDefinition; import org.jooq.util.ColumnDefinition; import org.jooq.util.DataTypeDefinition; @@ -65,47 +63,30 @@ public class ASETableDefinition extends AbstractTableDefinition { protected List getElements0() throws SQLException { List result = new ArrayList(); - PreparedStatement stmt = null; - try { - stmt = create().getConnection().prepareStatement("sp_help '" + getName() + "'"); - stmt.execute(); - stmt.getMoreResults(); + int position = 1; + for (Record record : create().fetchMany("sp_help '" + getName() + "'").get(1)) { + String p = record.getValueAsString("Prec"); + String s = record.getValueAsString("Scale"); - stmt.getResultSet().close(); - stmt.getMoreResults(); + int precision = 0; + int scale = 0; - ResultSet rs = stmt.getResultSet(); + if (p != null && !"null".equalsIgnoreCase(p.trim())) precision = Integer.valueOf(p.trim()); + if (s != null && !"null".equalsIgnoreCase(s.trim())) scale = Integer.valueOf(s.trim()); - int position = 1; - while (rs.next()) { - String p = rs.getString("Prec"); - String s = rs.getString("Scale"); + DataTypeDefinition type = new DefaultDataTypeDefinition(getDatabase(), + record.getValueAsString("Type"), + precision, scale); - int precision = 0; - int scale = 0; - - if (p != null && !"null".equalsIgnoreCase(p.trim())) precision = Integer.valueOf(p.trim()); - if (s != null && !"null".equalsIgnoreCase(s.trim())) scale = Integer.valueOf(s.trim()); - - DataTypeDefinition type = new DefaultDataTypeDefinition(getDatabase(), - rs.getString("Type"), - precision, scale); - - result.add(new DefaultColumnDefinition( - getDatabase().getTable(getName()), - rs.getString("Column_name"), - position++, - type, - rs.getBoolean("Identity"), - null)); - } - } - finally { - if (stmt != null) { - stmt.getMoreResults(Statement.CLOSE_ALL_RESULTS); - stmt.close(); - } + result.add(new DefaultColumnDefinition( + getDatabase().getTable(getName()), + record.getValueAsString("Column_name"), + position++, + type, + record.getValueAsBoolean("Identity", false), + null)); } + return result; } } diff --git a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java index bb20531867..36f0ca2345 100644 --- a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java +++ b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java @@ -174,6 +174,9 @@ public abstract class jOOQAbstractTest< T639 extends UpdatableRecord, T785 extends TableRecord> { + private static final List BOOK_IDS = Arrays.asList(1, 2, 3, 4); + private static final List BOOK_TITLES = Arrays.asList("1984", "Animal Farm", "O Alquimista", "Brida"); + private static final String JDBC_SCHEMA = "jdbc.Schema"; private static final String JDBC_PASSWORD = "jdbc.Password"; private static final String JDBC_USER = "jdbc.User"; @@ -882,13 +885,13 @@ public abstract class jOOQAbstractTest< for (Entry entry : map1.entrySet()) { assertEquals(entry.getKey(), entry.getValue().getValue(TBook_ID())); } - assertEquals(Arrays.asList(1, 2, 3, 4), new ArrayList(map1.keySet())); + assertEquals(BOOK_IDS, new ArrayList(map1.keySet())); // Key -> Value Map // ---------------- Map map2 = create().selectFrom(TBook()).orderBy(TBook_ID()).fetchMap(TBook_ID(), TBook_TITLE()); - assertEquals(Arrays.asList(1, 2, 3, 4), new ArrayList(map2.keySet())); - assertEquals(Arrays.asList("1984", "Animal Farm", "O Alquimista", "Brida"), new ArrayList(map2.values())); + assertEquals(BOOK_IDS, new ArrayList(map2.keySet())); + assertEquals(BOOK_TITLES, new ArrayList(map2.values())); // List of Map // ----------- @@ -1268,19 +1271,13 @@ public abstract class jOOQAbstractTest< Result result = create().select().from("t_book").orderBy(ID).fetch(); assertEquals(4, result.size()); - assertEquals( - Arrays.asList(1, 2, 3, 4), - result.getValues(ID)); - assertEquals( - Arrays.asList("1984", "Animal Farm", "O Alquimista", "Brida"), - result.getValues(TBook_TITLE())); + assertEquals(BOOK_IDS, result.getValues(ID)); + assertEquals(BOOK_TITLES, result.getValues(TBook_TITLE())); // [#271] Aliased plain SQL table result = create().select(ID).from("(select * from t_book) b").orderBy(ID).fetch(); assertEquals(4, result.size()); - assertEquals( - Arrays.asList(1, 2, 3, 4), - result.getValues(ID)); + assertEquals(BOOK_IDS, result.getValues(ID)); // [#271] TODO: Aliased plain SQL table // result = create().select().from("(select * from t_book) b").orderBy(ID).fetch(); @@ -1292,9 +1289,7 @@ public abstract class jOOQAbstractTest< // [#836] Aliased plain SQL table result = create().select().from(create().table("t_book").as("b")).orderBy(ID).fetch(); assertEquals(4, result.size()); - assertEquals( - Arrays.asList(1, 2, 3, 4), - result.getValues(ID)); + assertEquals(BOOK_IDS, result.getValues(ID)); // [#271] TODO: Check for aliased nested selects. The DescribeQuery does not seem to work // [#836] Aliased plain SQL nested select @@ -2342,7 +2337,7 @@ public abstract class jOOQAbstractTest< .from(TBook()) .where(TBook_ID().in(new Integer[0])) .fetch(TBook_ID())); - assertEquals(Arrays.asList(1, 2, 3, 4), create().select() + assertEquals(BOOK_IDS, create().select() .from(TBook()) .where(TBook_ID().notIn(new Integer[0])) .orderBy(TBook_ID()) @@ -2588,6 +2583,25 @@ public abstract class jOOQAbstractTest< assertEquals("Coelho", record.getValue(TAuthor_LAST_NAME())); } + @Test + public void testFetchMany() throws Exception { + switch (getDialect()) { + case ORACLE: + case SQLITE: + case SYBASE: + log.info("SKIPPING", "Fetch Many tests"); + return; + } + + List> results = create().fetchMany( + "select * from t_book order by " + TBook_ID().getName()); + + assertEquals(1, results.size()); + assertEquals(4, results.get(0).size()); + assertEquals(BOOK_IDS, results.get(0).getValues(TBook_ID(), Integer.class)); + assertEquals(BOOK_TITLES, results.get(0).getValues(TBook_TITLE())); + } + @Test public void testFetchIntoWithAnnotations() throws Exception { // TODO [#791] Fix test data and have all upper case columns everywhere @@ -2731,8 +2745,8 @@ public abstract class jOOQAbstractTest< final Queue ids = new LinkedList(); final Queue titles = new LinkedList(); - ids.addAll(Arrays.asList(1, 2, 3, 4)); - titles.addAll(Arrays.asList("1984", "Animal Farm", "O Alquimista", "Brida")); + ids.addAll(BOOK_IDS); + titles.addAll(BOOK_TITLES); create().selectFrom(TBook()) .orderBy(TBook_ID()) @@ -2749,8 +2763,8 @@ public abstract class jOOQAbstractTest< // Test lazy fetching // -------------------------------------- - ids.addAll(Arrays.asList(1, 2, 3, 4)); - titles.addAll(Arrays.asList("1984", "Animal Farm", "O Alquimista", "Brida")); + ids.addAll(BOOK_IDS); + titles.addAll(BOOK_TITLES); create().selectFrom(TBook()) .orderBy(TBook_ID()) @@ -2812,7 +2826,7 @@ public abstract class jOOQAbstractTest< // Check the data assertEquals(4, result.size()); - assertEquals(Arrays.asList(1, 2, 3, 4), result.getValues(TBook_ID())); + assertEquals(BOOK_IDS, result.getValues(TBook_ID())); // Start new threads later = create().selectFrom(TBook()).orderBy(TBook_ID()).fetchLater(); @@ -3374,7 +3388,7 @@ public abstract class jOOQAbstractTest< " from " + T725().getName() + " order by " + T725_ID().getName()); assertEquals(4, result.size()); - assertEquals(Arrays.asList(1, 2, 3, 4), result.getValues(0)); + assertEquals(BOOK_IDS, result.getValues(0)); assertNull(result.getValue(1, 1)); switch (getDialect()) { @@ -4208,7 +4222,7 @@ public abstract class jOOQAbstractTest< @Test public void testOrderByIndirection() throws Exception { - assertEquals(Arrays.asList(1, 2, 3, 4), + assertEquals(BOOK_IDS, create().selectFrom(TBook()) .orderBy(TBook_ID().sortAsc(), TBook_ID().asc()) .fetch(TBook_ID())); @@ -4512,11 +4526,11 @@ public abstract class jOOQAbstractTest< List ids = create().select(b_ID).from(b).orderBy(b_ID).fetch(b_ID); assertEquals(4, ids.size()); - assertEquals(Arrays.asList(1, 2, 3, 4), ids); + assertEquals(BOOK_IDS, ids); Result books = create().select().from(b).orderBy(b_ID).fetch(); assertEquals(4, books.size()); - assertEquals(Arrays.asList(1, 2, 3, 4), books.getValues(b_ID)); + assertEquals(BOOK_IDS, books.getValues(b_ID)); } // @Test // TODO [#579] re-enable this test when fixing this bug diff --git a/jOOQ/src/main/java/org/jooq/ResultQuery.java b/jOOQ/src/main/java/org/jooq/ResultQuery.java index 8e4d281600..d9b4101054 100644 --- a/jOOQ/src/main/java/org/jooq/ResultQuery.java +++ b/jOOQ/src/main/java/org/jooq/ResultQuery.java @@ -92,6 +92,19 @@ public interface ResultQuery extends Query { */ Cursor fetchLazy() throws SQLException; + /** + * Execute a query, possibly returning several result + * sets. + *

+ * Example (Sybase ASE): + *

+ *

+     * String sql = "sp_help 'my_table'";
+ * + * @return The resulting records + */ + List> fetchMany() throws SQLException; + /** * Execute the query and return all values for a field from the generated * result. diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java b/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java index 11c748f83f..b8a3ae8e16 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingSelect.java @@ -106,6 +106,11 @@ abstract class AbstractDelegatingSelect extends AbstractQueryP return query.fetchLazy(); } + @Override + public final List> fetchMany() throws SQLException { + return query.fetchMany(); + } + @Override public final List fetch(Field field) throws SQLException { return query.fetch(field); diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java index 7d56586bfd..2902f588a3 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java @@ -43,6 +43,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.sql.Statement; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; @@ -54,6 +55,7 @@ import java.util.concurrent.Future; import org.jooq.Configuration; import org.jooq.Cursor; import org.jooq.Field; +import org.jooq.FieldProvider; import org.jooq.FutureResult; import org.jooq.Record; import org.jooq.RecordHandler; @@ -74,8 +76,10 @@ abstract class AbstractResultQuery extends AbstractQuery imple private static final long serialVersionUID = -5588344253566055707L; private transient boolean lazy; + private transient boolean many; private transient Cursor cursor; private Result result; + private List> results; AbstractResultQuery(Configuration configuration) { super(configuration); @@ -103,12 +107,34 @@ abstract class AbstractResultQuery extends AbstractQuery imple try { ResultSet rs = statement.executeQuery(); - FieldList fields = new FieldList(getFields(rs.getMetaData())); - cursor = new CursorImpl(configuration, fields, rs, statement, getRecordType()); - if (!lazy) { - result = cursor.fetchResult(); - cursor = null; + if (!many) { + FieldList fields = new FieldList(getFields(rs.getMetaData())); + cursor = new CursorImpl(configuration, fields, rs, statement, getRecordType()); + + if (!lazy) { + result = cursor.fetchResult(); + cursor = null; + } + } + else { + results = new ArrayList>(); + + for (;;) { + FieldProvider fields = new MetaDataFieldProvider(configuration, rs.getMetaData()); + Cursor c = new CursorImpl(configuration, fields, rs); + results.add(c.fetchResult()); + + if (statement.getMoreResults()) { + rs = statement.getResultSet(); + } + else { + break; + } + } + + statement.getMoreResults(Statement.CLOSE_ALL_RESULTS); + statement.close(); } } finally { @@ -145,6 +171,15 @@ abstract class AbstractResultQuery extends AbstractQuery imple return cursor; } + @Override + public final List> fetchMany() throws SQLException { + many = true; + execute(); + many = false; + + return results; + } + @Override public final List fetch(Field field) throws SQLException { return fetch().getValues(field); diff --git a/jOOQ/src/main/java/org/jooq/impl/Factory.java b/jOOQ/src/main/java/org/jooq/impl/Factory.java index 75cc7488ff..d574013cfb 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Factory.java +++ b/jOOQ/src/main/java/org/jooq/impl/Factory.java @@ -1080,6 +1080,50 @@ public class Factory implements Configuration { return new SQLResultQuery(this, sql, bindings).fetch(); } + /** + * Execute a new query holding plain SQL, possibly returning several result + * sets + *

+ * Example (Sybase ASE): + *

+ *

+     * String sql = "sp_help 'my_table'";
+ *

+ * NOTE: When inserting plain SQL into jOOQ objects, you must + * guarantee syntax integrity. You may also create the possibility of + * malicious SQL injection. Be sure to properly use bind variables and/or + * escape literals when concatenated into SQL clauses! + * + * @param sql The SQL + * @return The results from the executed query + */ + public final List> fetchMany(String sql) throws SQLException { + return fetchMany(sql, new Object[0]); + } + + /** + * Execute a new query holding plain SQL, possibly returning several result + * sets. There must be as many binding variables contained in the SQL, as + * passed in the bindings parameter + *

+ * Example (Sybase ASE): + *

+ *

+     * String sql = "sp_help 'my_table'";
+ *

+ * NOTE: When inserting plain SQL into jOOQ objects, you must + * guarantee syntax integrity. You may also create the possibility of + * malicious SQL injection. Be sure to properly use bind variables and/or + * escape literals when concatenated into SQL clauses! + * + * @param sql The SQL + * @param bindings The bindings + * @return A query wrapping the plain SQL + */ + public final List> fetchMany(String sql, Object... bindings) throws SQLException { + return new SQLResultQuery(this, sql, bindings).fetchMany(); + } + /** * Execute a new query holding plain SQL. *