From 9e67e21e13abd76e7cc63b963aee26e099a08de0 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Mon, 3 Nov 2014 19:32:05 +0100 Subject: [PATCH 01/12] [#3666] [#3666] [#3730] - [#3666] Allow to match with precision and scale - [#3732] Improve performance of regex evaluation - [#3730] Cannot use regex COMMENTS in 's --- .../java/org/jooq/util/AbstractDatabase.java | 44 +++++++++++++++---- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/jOOQ-meta/src/main/java/org/jooq/util/AbstractDatabase.java b/jOOQ-meta/src/main/java/org/jooq/util/AbstractDatabase.java index 5777ce4f6d..49148fc993 100644 --- a/jOOQ-meta/src/main/java/org/jooq/util/AbstractDatabase.java +++ b/jOOQ-meta/src/main/java/org/jooq/util/AbstractDatabase.java @@ -141,6 +141,8 @@ public abstract class AbstractDatabase implements Database { private transient Map> routinesBySchema; private transient Map> packagesBySchema; + private static Map patterns; + // Other caches private final Map, Boolean> exists; @@ -196,6 +198,19 @@ public abstract class AbstractDatabase implements Database { return result; } + final static Pattern pattern(String regex) { + if (patterns == null) + patterns = new HashMap(); + + Pattern pattern = patterns.get(regex); + if (pattern == null) { + pattern = Pattern.compile(regex, Pattern.COMMENTS); + patterns.put(regex, pattern); + } + + return pattern; + } + @Override public final List getSchemata() { if (schemata == null) { @@ -679,14 +694,27 @@ public abstract class AbstractDatabase implements Database { String types = forcedType.getTypes(); - if (expression != null - && !definition.getName().matches(expression) - && !definition.getQualifiedName().matches(expression)) { - continue forcedTypeLoop; + if (expression != null) { + Pattern p = pattern(expression); + + if ( !p.matcher(definition.getName()).matches() + && !p.matcher(definition.getQualifiedName()).matches()) { + continue forcedTypeLoop; + } } - if (types != null && definedType != null && !definedType.getType().matches(types)) { - continue forcedTypeLoop; + if (types != null && definedType != null) { + Pattern p = pattern(types); + + if ( ( !p.matcher(definedType.getType()).matches() ) + && ( definedType.getLength() == 0 + || !p.matcher(definedType.getType() + "(" + definedType.getLength() + ")").matches()) + && ( definedType.getScale() != 0 + || !p.matcher(definedType.getType() + "(" + definedType.getPrecision() + ")").matches()) + && ( !p.matcher(definedType.getType() + "(" + definedType.getPrecision() + "," + definedType.getScale() + ")").matches() ) + && ( !p.matcher(definedType.getType() + "(" + definedType.getPrecision() + ", " + definedType.getScale() + ")").matches() )) { + continue forcedTypeLoop; + } } return forcedType; @@ -883,7 +911,7 @@ public abstract class AbstractDatabase implements Database { definitionsLoop: for (T definition : definitions) { if (excludes != null) { for (String exclude : excludes) { - Pattern p = Pattern.compile(exclude, Pattern.COMMENTS); + Pattern p = pattern(exclude); if (exclude != null && (p.matcher(definition.getName()).matches() || @@ -899,7 +927,7 @@ public abstract class AbstractDatabase implements Database { if (includes != null) { for (String include : includes) { - Pattern p = Pattern.compile(include, Pattern.COMMENTS); + Pattern p = pattern(include); if (include != null && (p.matcher(definition.getName()).matches() || From d3170f223c2d857b4ba8caf296a2cee4fd1c90fb Mon Sep 17 00:00:00 2001 From: lukaseder Date: Wed, 5 Nov 2014 08:20:38 +0100 Subject: [PATCH 02/12] [#3740] Add Table.newRecord() and UDT.newRecord() to construct new records with DefaultConfiguration --- jOOQ/src/main/java/org/jooq/Table.java | 7 +++++++ jOOQ/src/main/java/org/jooq/UDT.java | 7 +++++++ jOOQ/src/main/java/org/jooq/impl/AbstractTable.java | 5 +++++ jOOQ/src/main/java/org/jooq/impl/UDTImpl.java | 5 +++++ 4 files changed, 24 insertions(+) diff --git a/jOOQ/src/main/java/org/jooq/Table.java b/jOOQ/src/main/java/org/jooq/Table.java index 7ef95f958a..49c6313bbf 100644 --- a/jOOQ/src/main/java/org/jooq/Table.java +++ b/jOOQ/src/main/java/org/jooq/Table.java @@ -106,6 +106,13 @@ public interface Table extends TableLike { */ Class getRecordType(); + /** + * Create a new {@link Record} of this table's type. + * + * @see DSLContext#newRecord(Table) + */ + R newRecord(); + /** * Retrieve the table's IDENTITY information, if available. *

diff --git a/jOOQ/src/main/java/org/jooq/UDT.java b/jOOQ/src/main/java/org/jooq/UDT.java index e12a97b0fa..3735795513 100644 --- a/jOOQ/src/main/java/org/jooq/UDT.java +++ b/jOOQ/src/main/java/org/jooq/UDT.java @@ -98,6 +98,13 @@ public interface UDT> extends QueryPart { */ Class getRecordType(); + /** + * Create a new {@link Record} of this UDT's type. + * + * @see DSLContext#newRecord(UDT) + */ + R newRecord(); + /** * The UDT's data type as known to the database */ diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java b/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java index c8e9157fba..9e3345ef73 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java @@ -143,6 +143,11 @@ abstract class AbstractTable extends AbstractQueryPart impleme return fields0(); } + @Override + public final R newRecord() { + return DSL.using(new DefaultConfiguration()).newRecord(this); + } + @SuppressWarnings({ "rawtypes" }) @Override public final Row fieldsRow() { diff --git a/jOOQ/src/main/java/org/jooq/impl/UDTImpl.java b/jOOQ/src/main/java/org/jooq/impl/UDTImpl.java index 1a770c59a3..515893781b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UDTImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UDTImpl.java @@ -123,6 +123,11 @@ public class UDTImpl> extends AbstractQueryPart implement throw new UnsupportedOperationException(); } + @Override + public final R newRecord() { + return DSL.using(new DefaultConfiguration()).newRecord(this); + } + @Override public final DataType getDataType() { if (type == null) { From 1ce98dc089ddaf40eb0df23e903609c7bf8109e4 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Fri, 7 Nov 2014 19:26:31 +0100 Subject: [PATCH 03/12] [#3746] Add DSLContext.fetchExists(Select) --- jOOQ/src/main/java/org/jooq/DSLContext.java | 43 +++++++++++++++++++ .../java/org/jooq/impl/DefaultDSLContext.java | 15 +++++++ 2 files changed, 58 insertions(+) diff --git a/jOOQ/src/main/java/org/jooq/DSLContext.java b/jOOQ/src/main/java/org/jooq/DSLContext.java index bb06977955..970bb4511a 100644 --- a/jOOQ/src/main/java/org/jooq/DSLContext.java +++ b/jOOQ/src/main/java/org/jooq/DSLContext.java @@ -5863,6 +5863,49 @@ public interface DSLContext extends Scope { */ int fetchCount(Table table, Condition condition) throws DataAccessException; + /** + * Check if a {@link Select} would return any records, if it were executed. + *

+ * This wraps a pre-existing SELECT query in another one to + * check for result existence, without modifying the original + * SELECT. An example:

+     * -- Original query:
+     * SELECT id, title FROM book WHERE title LIKE '%a%'
+     *
+     * -- Wrapped query:
+     * SELECT EXISTS (
+     *   SELECT id, title FROM book WHERE title LIKE '%a%'
+     * )
+     * 
+ * + * @param query The wrapped query + * @return The EXISTS(...) result + * @throws DataAccessException if something went wrong executing the query + */ + boolean fetchExists(Select query) throws DataAccessException; + + /** + * Check if a table has any records. + *

+ * This executes

SELECT EXISTS(SELECT * FROM table)
+ * + * @param table The table whose records to count + * @return The number or records in the table + * @throws DataAccessException if something went wrong executing the query + */ + boolean fetchExists(Table table) throws DataAccessException; + + /** + * Check if a table has any records that satisfy a condition. + *

+ * This executes

SELECT EXISTS(SELECT * FROM table WHERE condition)
+ * + * @param table The table whose records to count + * @return The number or records in the table + * @throws DataAccessException if something went wrong executing the query + */ + boolean fetchExists(Table table, Condition condition) throws DataAccessException; + /** * Execute a {@link Query} in the context of this DSLContext. * diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java index 45e2e904ee..c9019da144 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java @@ -2241,6 +2241,21 @@ public class DefaultDSLContext extends AbstractScope implements DSLContext, Seri return selectCount().from(table).where(condition).fetchOne(0, int.class); } + @Override + public boolean fetchExists(Select query) throws DataAccessException { + return selectOne().whereExists(query).fetchOne() != null; + } + + @Override + public boolean fetchExists(Table table) throws DataAccessException { + return fetchExists(table, trueCondition()); + } + + @Override + public boolean fetchExists(Table table, Condition condition) throws DataAccessException { + return fetchExists(selectOne().from(table).where(condition)); + } + @Override public int execute(Query query) { final Configuration previous = Utils.getConfiguration(query); From a7c33f16da548d16338fc97b830cf89dd2286c09 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Mon, 10 Nov 2014 22:31:53 +0100 Subject: [PATCH 04/12] [#3754] Explain primitive type conversion in Convert.convert() Javadoc --- jOOQ/src/main/java/org/jooq/tools/Convert.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/jOOQ/src/main/java/org/jooq/tools/Convert.java b/jOOQ/src/main/java/org/jooq/tools/Convert.java index cfe1eee12f..f25bca7389 100644 --- a/jOOQ/src/main/java/org/jooq/tools/Convert.java +++ b/jOOQ/src/main/java/org/jooq/tools/Convert.java @@ -305,7 +305,8 @@ public final class Convert { *
    *
  • null is always converted to null, * regardless of the target type.
  • - *
  • Identity conversion is always possible
  • + *
  • Identity conversion (converting a value to its own type) is always + * possible.
  • *
  • All types can be converted to String
  • *
  • All types can be converted to Object
  • *
  • All Number types can be converted to other @@ -315,6 +316,7 @@ public final class Convert { * true: *
      *
    • 1
    • + *
    • 1.0
    • *
    • y
    • *
    • yes
    • *
    • true
    • @@ -325,6 +327,7 @@ public final class Convert { * Possible (case-insensitive) values for false: *
        *
      • 0
      • + *
      • 0.0
      • *
      • n
      • *
      • no
      • *
      • false
      • @@ -336,6 +339,10 @@ public final class Convert { *
      • All Date types can be converted into each other
      • *
      • All String types can be converted into {@link URI}, * {@link URL} and {@link File}
      • + *
      • Primitive target types behave like their wrapper types, except that + * null is converted into the initialisation value (e.g. + * 0 for int, false for + * boolean)
      • *
      • byte[] can be converted into String, using * the platform's default charset
      • *
      • Object[] can be converted into any other array type, if From 094ac23a8c1ad3543805c3b457a0b1bbdd63d886 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Mon, 10 Nov 2014 23:24:52 +0100 Subject: [PATCH 05/12] [#3752] Make element optional in code generation configuration --- jOOQ-meta/src/main/resources/xsd/jooq-codegen-3.5.0.xsd | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/jOOQ-meta/src/main/resources/xsd/jooq-codegen-3.5.0.xsd b/jOOQ-meta/src/main/resources/xsd/jooq-codegen-3.5.0.xsd index cebb936d83..79aadc57f8 100644 --- a/jOOQ-meta/src/main/resources/xsd/jooq-codegen-3.5.0.xsd +++ b/jOOQ-meta/src/main/resources/xsd/jooq-codegen-3.5.0.xsd @@ -12,15 +12,13 @@ The JDBC configuration element contains information about how to set up the database connection used for source code generation --> - + - + From 0fb96558c2f6ed51fd9ea12fdf24c8ec3d9300ff Mon Sep 17 00:00:00 2001 From: lukaseder Date: Tue, 11 Nov 2014 13:05:47 +0100 Subject: [PATCH 06/12] [#3758] ExecuteListener.warning() is not listed as a method in the Javadoc --- jOOQ/src/main/java/org/jooq/ExecuteListener.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/jOOQ/src/main/java/org/jooq/ExecuteListener.java b/jOOQ/src/main/java/org/jooq/ExecuteListener.java index 25ea17d701..36d5096a9e 100644 --- a/jOOQ/src/main/java/org/jooq/ExecuteListener.java +++ b/jOOQ/src/main/java/org/jooq/ExecuteListener.java @@ -236,6 +236,15 @@ import org.jooq.tools.StopWatchListener; * Yes, 1x * * + * {@link #warning(ExecuteContext)} + * Maybe, 1x + * Maybe, 1x + * Maybe, 1x + * Maybe, 1x + * Maybe, 1x + * Maybe, 1x + * + * * {@link #exception(ExecuteContext)} * Maybe, 1x * Maybe, 1x From ba7ad5b7c3d626f965252b6af1eec61558d28b06 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Tue, 11 Nov 2014 13:33:43 +0100 Subject: [PATCH 07/12] [#3741] MySQL ResultSet streaming conflicts with internal SHOW WARNINGS call --- .../java/org/jooq/impl/AbstractQuery.java | 5 -- .../org/jooq/impl/AbstractResultQuery.java | 32 ++++----- .../java/org/jooq/impl/AbstractRoutine.java | 5 -- .../org/jooq/impl/AbstractStoreQuery.java | 65 ++++++------------- .../java/org/jooq/impl/BatchMultiple.java | 21 ++---- .../main/java/org/jooq/impl/BatchSingle.java | 20 ++---- jOOQ/src/main/java/org/jooq/impl/Utils.java | 23 +++++-- 7 files changed, 64 insertions(+), 107 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java index e116b7571d..ee1e1853eb 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractQuery.java @@ -52,7 +52,6 @@ import static org.jooq.impl.DSL.using; import static org.jooq.impl.Utils.DATA_COUNT_BIND_VALUES; import static org.jooq.impl.Utils.DATA_FORCE_STATIC_STATEMENT; import static org.jooq.impl.Utils.consumeExceptions; -import static org.jooq.impl.Utils.consumeWarnings; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -413,10 +412,6 @@ abstract class AbstractQuery extends AbstractQueryPart implements Query, Attacha consumeExceptions(ctx.configuration(), stmt, e); throw e; } - - finally { - consumeWarnings(ctx, listener); - } } /** diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java index 32f86d88d0..d0824b4a99 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java @@ -48,7 +48,6 @@ import static java.util.concurrent.Executors.newSingleThreadExecutor; import static org.jooq.SQLDialect.CUBRID; // ... import static org.jooq.impl.Utils.DATA_LOCK_ROWS_FOR_UPDATE; -import static org.jooq.impl.Utils.consumeWarnings; import java.sql.ResultSet; import java.sql.ResultSetMetaData; @@ -250,28 +249,23 @@ abstract class AbstractResultQuery extends AbstractQuery imple @Override protected final int execute(ExecuteContext ctx, ExecuteListener listener) throws SQLException { - try { - listener.executeStart(ctx); + listener.executeStart(ctx); - /* [pro] xx - xx xxxx xxxxxxx xxxx xx xxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx - xx xxxxxxxxxx xx xxxxx xxx xxx xxxxxxx - xx xxxxxxxxxxxxx xx xxxx x - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - x + /* [pro] xx + xx xxxx xxxxxxx xxxx xx xxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx + xx xxxxxxxxxx xx xxxxx xxx xxx xxxxxxx + xx xxxxxxxxxxxxx xx xxxx x + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + x - xx xxxxxxx xxxxx xxxxxxxxxxxxxx xx xxxxx xx xxxxxx xxxxxxx xxxx xxx - xx xxx xxxxxx x xxxxxxxxxx xxxx xxxxxxxx xxxxxx xxxxxxxxxxxxxxxxxxxxxxx - xxxx xx [/pro] */if (ctx.statement().execute()) { - ctx.resultSet(ctx.statement().getResultSet()); - } - - listener.executeEnd(ctx); - } - finally { - consumeWarnings(ctx, listener); + xx xxxxxxx xxxxx xxxxxxxxxxxxxx xx xxxxx xx xxxxxx xxxxxxx xxxx xxx + xx xxx xxxxxx x xxxxxxxxxx xxxx xxxxxxxx xxxxxx xxxxxxxxxxxxxxxxxxxxxxx + xxxx xx [/pro] */if (ctx.statement().execute()) { + ctx.resultSet(ctx.statement().getResultSet()); } + listener.executeEnd(ctx); + // Fetch a single result set if (!many) { if (ctx.resultSet() != null) { diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java index 115ed06247..7bf42f17fa 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java @@ -51,7 +51,6 @@ import static org.jooq.impl.DSL.table; import static org.jooq.impl.DSL.using; import static org.jooq.impl.DSL.val; import static org.jooq.impl.Utils.consumeExceptions; -import static org.jooq.impl.Utils.consumeWarnings; import static org.jooq.impl.Utils.settings; import java.sql.CallableStatement; @@ -352,10 +351,6 @@ public abstract class AbstractRoutine extends AbstractQueryPart implements Ro consumeExceptions(ctx.configuration(), ctx.statement(), e); throw e; } - - finally { - consumeWarnings(ctx, listener); - } } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java index 10429c461b..93d9a4f194 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java @@ -45,7 +45,6 @@ import static org.jooq.conf.RenderNameStyle.LOWER; import static org.jooq.conf.RenderNameStyle.UPPER; import static org.jooq.impl.DSL.select; // ... -import static org.jooq.impl.Utils.consumeWarnings; import static org.jooq.impl.Utils.fieldArray; import static org.jooq.impl.Utils.unqualify; import static org.jooq.util.sqlite.SQLiteDSL.rowid; @@ -327,15 +326,10 @@ abstract class AbstractStoreQuery extends AbstractQuery implem // SQLite can select _rowid_ after the insert case SQLITE: { - try { - listener.executeStart(ctx); - result = ctx.statement().executeUpdate(); - ctx.rows(result); - listener.executeEnd(ctx); - } - finally { - consumeWarnings(ctx, listener); - } + listener.executeStart(ctx); + result = ctx.statement().executeUpdate(); + ctx.rows(result); + listener.executeEnd(ctx); DSLContext create = DSL.using(ctx.configuration()); returned = @@ -358,15 +352,10 @@ abstract class AbstractStoreQuery extends AbstractQuery implem xx [/pro] */ case CUBRID: { - try { - listener.executeStart(ctx); - result = ctx.statement().executeUpdate(); - ctx.rows(result); - listener.executeEnd(ctx); - } - finally { - consumeWarnings(ctx, listener); - } + listener.executeStart(ctx); + result = ctx.statement().executeUpdate(); + ctx.rows(result); + listener.executeEnd(ctx); selectReturning(ctx.configuration(), create(ctx.configuration()).lastID()); return result; @@ -385,15 +374,10 @@ abstract class AbstractStoreQuery extends AbstractQuery implem case H2: case MARIADB: case MYSQL: { - try { - listener.executeStart(ctx); - result = ctx.statement().executeUpdate(); - ctx.rows(result); - listener.executeEnd(ctx); - } - finally { - consumeWarnings(ctx, listener); - } + listener.executeStart(ctx); + result = ctx.statement().executeUpdate(); + ctx.rows(result); + listener.executeEnd(ctx); rs = ctx.statement().getGeneratedKeys(); @@ -426,15 +410,9 @@ abstract class AbstractStoreQuery extends AbstractQuery implem // in the Postgres JDBC driver case FIREBIRD: case POSTGRES: { - try { - listener.executeStart(ctx); - rs = ctx.statement().executeQuery(); - listener.executeEnd(ctx); - } - finally { - consumeWarnings(ctx, listener); - } - + listener.executeStart(ctx); + rs = ctx.statement().executeQuery(); + listener.executeEnd(ctx); break; } @@ -444,15 +422,10 @@ abstract class AbstractStoreQuery extends AbstractQuery implem xx [/pro] */ case HSQLDB: default: { - try { - listener.executeStart(ctx); - result = ctx.statement().executeUpdate(); - ctx.rows(result); - listener.executeEnd(ctx); - } - finally { - consumeWarnings(ctx, listener); - } + listener.executeStart(ctx); + result = ctx.statement().executeUpdate(); + ctx.rows(result); + listener.executeEnd(ctx); rs = ctx.statement().getGeneratedKeys(); break; diff --git a/jOOQ/src/main/java/org/jooq/impl/BatchMultiple.java b/jOOQ/src/main/java/org/jooq/impl/BatchMultiple.java index 01ef6ad68f..f09139d05e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/BatchMultiple.java +++ b/jOOQ/src/main/java/org/jooq/impl/BatchMultiple.java @@ -40,8 +40,6 @@ */ package org.jooq.impl; -import static org.jooq.impl.Utils.consumeWarnings; - import java.sql.Connection; import java.sql.SQLException; @@ -99,20 +97,15 @@ class BatchMultiple implements Batch { ctx.sql(null); } - try { - listener.executeStart(ctx); + listener.executeStart(ctx); - int[] result = ctx.statement().executeBatch(); - int[] batchRows = ctx.batchRows(); - for (int i = 0; i < batchRows.length && i < result.length; i++) - batchRows[i] = result[i]; + int[] result = ctx.statement().executeBatch(); + int[] batchRows = ctx.batchRows(); + for (int i = 0; i < batchRows.length && i < result.length; i++) + batchRows[i] = result[i]; - listener.executeEnd(ctx); - return result; - } - finally { - consumeWarnings(ctx, listener); - } + listener.executeEnd(ctx); + return result; } // [#3427] ControlFlowSignals must not be passed on to ExecuteListners diff --git a/jOOQ/src/main/java/org/jooq/impl/BatchSingle.java b/jOOQ/src/main/java/org/jooq/impl/BatchSingle.java index c5713faad2..1e20175e39 100644 --- a/jOOQ/src/main/java/org/jooq/impl/BatchSingle.java +++ b/jOOQ/src/main/java/org/jooq/impl/BatchSingle.java @@ -42,7 +42,6 @@ package org.jooq.impl; import static org.jooq.conf.ParamType.INLINED; import static org.jooq.conf.SettingsTools.executeStaticStatements; -import static org.jooq.impl.Utils.consumeWarnings; import static org.jooq.impl.Utils.dataTypes; import static org.jooq.impl.Utils.fields; import static org.jooq.impl.Utils.visitAll; @@ -152,20 +151,15 @@ class BatchSingle implements BatchBindStep { ctx.statement().addBatch(); } - try { - listener.executeStart(ctx); - int[] result = ctx.statement().executeBatch(); + listener.executeStart(ctx); + int[] result = ctx.statement().executeBatch(); - int[] batchRows = ctx.batchRows(); - for (int i = 0; i < batchRows.length && i < result.length; i++) - batchRows[i] = result[i]; + int[] batchRows = ctx.batchRows(); + for (int i = 0; i < batchRows.length && i < result.length; i++) + batchRows[i] = result[i]; - listener.executeEnd(ctx); - return result; - } - finally { - consumeWarnings(ctx, listener); - } + listener.executeEnd(ctx); + return result; } // [#3427] ControlFlowSignals must not be passed on to ExecuteListners diff --git a/jOOQ/src/main/java/org/jooq/impl/Utils.java b/jOOQ/src/main/java/org/jooq/impl/Utils.java index de22095ec3..6a0cca63fd 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Utils.java +++ b/jOOQ/src/main/java/org/jooq/impl/Utils.java @@ -75,6 +75,7 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.SQLWarning; import java.sql.Statement; import java.util.ArrayList; import java.util.Arrays; @@ -1569,10 +1570,13 @@ final class Utils { JDBCUtils.safeClose(ctx.resultSet()); ctx.resultSet(null); + PreparedStatement statement = ctx.statement(); + if (statement != null) { + consumeWarnings(ctx, listener); + } + // [#385] Close statements only if not requested to keep open if (!keepStatement) { - PreparedStatement statement = ctx.statement(); - if (statement != null) { JDBCUtils.safeClose(statement); ctx.statement(null); @@ -2266,12 +2270,21 @@ final class Utils { /** * [#3076] Consume warnings from a {@link Statement} and notify listeners. */ - static final void consumeWarnings(ExecuteContext ctx, ExecuteListener listener) throws SQLException { + static final void consumeWarnings(ExecuteContext ctx, ExecuteListener listener) { // [#3558] In some databases (e.g. MySQL), the call to PreparedStatement.getWarnings() issues // a separate SHOW WARNINGS query. Users may want to avoid this query, explicitly - if (!Boolean.FALSE.equals(ctx.settings().isFetchWarnings())) - ctx.sqlWarning(ctx.statement().getWarnings()); + if (!Boolean.FALSE.equals(ctx.settings().isFetchWarnings())) { + try { + ctx.sqlWarning(ctx.statement().getWarnings()); + } + + // [#3741] In MySQL, calling SHOW WARNINGS on open streaming result sets can cause issues. + // while this has been resolved, we should expect the above call to cause other issues, too + catch (SQLException e) { + ctx.sqlWarning(new SQLWarning("Could not fetch SQLWarning", e)); + } + } if (ctx.sqlWarning() != null) listener.warning(ctx); From 821bbeada6365e0633fcf489c66b741bf1bc4a2c Mon Sep 17 00:00:00 2001 From: lukaseder Date: Tue, 11 Nov 2014 13:33:53 +0100 Subject: [PATCH 08/12] Add version number to splash logo --- jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java index b326c190e8..f62a656437 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java @@ -672,10 +672,10 @@ class DefaultRenderContext extends AbstractContext implements Ren JooqLogger l = JooqLogger.getLogger(Constants.class); String message; - message = "Thank you for using jOOQ"; + message = "Thank you for using jOOQ " + Constants.FULL_VERSION; /* [pro] xx - xxxxxxx x xxxxxx xxx xxx xxxxx xxx xx xxx xxxx xxxx xxxxx xxxxxxxxx + xxxxxxx x xxxxxx xxx xxx xxxxx xxx xx xxx xxxx xxxx x x xxxxxxxxxxxxxxxxxxxxxx x x xxxxx xxxxxxxxx xx [/pro] */ From d501e8bd93faa6a9ca1fc002ca6db0784b18dcea Mon Sep 17 00:00:00 2001 From: lukaseder Date: Tue, 11 Nov 2014 14:49:32 +0100 Subject: [PATCH 09/12] [#3756] Regenerate files only if there is a difference --- .../java/org/jooq/util/AbstractGenerator.java | 16 ++++-- .../java/org/jooq/util/GeneratorWriter.java | 42 +++++++++++---- .../java/org/jooq/util/JavaGenerator.java | 54 +++++++++++-------- 3 files changed, 77 insertions(+), 35 deletions(-) diff --git a/jOOQ-codegen/src/main/java/org/jooq/util/AbstractGenerator.java b/jOOQ-codegen/src/main/java/org/jooq/util/AbstractGenerator.java index 5820de9f6b..57e41a9ced 100644 --- a/jOOQ-codegen/src/main/java/org/jooq/util/AbstractGenerator.java +++ b/jOOQ-codegen/src/main/java/org/jooq/util/AbstractGenerator.java @@ -41,6 +41,8 @@ package org.jooq.util; import java.io.File; +import java.util.Collections; +import java.util.Set; /** @@ -261,20 +263,28 @@ abstract class AbstractGenerator implements Generator { /** * If file is a directory, recursively empty its children. - * If file is a file, delete it + * If file is a file, delete it. */ protected void empty(File file, String suffix) { + empty(file, suffix, Collections.emptySet()); + } + + /** + * If file is a directory, recursively empty its children. + * If file is a file, delete it, except if it is in the list of files to keep. + */ + protected void empty(File file, String suffix, Set keep) { if (file != null) { if (file.isDirectory()) { File[] children = file.listFiles(); if (children != null) { for (File child : children) { - empty(child, suffix); + empty(child, suffix, keep); } } } else { - if (file.getName().endsWith(suffix)) { + if (file.getName().endsWith(suffix) && !keep.contains(file)) { file.delete(); } } diff --git a/jOOQ-codegen/src/main/java/org/jooq/util/GeneratorWriter.java b/jOOQ-codegen/src/main/java/org/jooq/util/GeneratorWriter.java index 00abf96476..266128b7f4 100644 --- a/jOOQ-codegen/src/main/java/org/jooq/util/GeneratorWriter.java +++ b/jOOQ-codegen/src/main/java/org/jooq/util/GeneratorWriter.java @@ -45,6 +45,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; +import java.io.RandomAccessFile; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -78,7 +79,6 @@ public abstract class GeneratorWriter> { private final File file; - private final PrintWriter writer; private final StringBuilder sb; private int indentTabs; private boolean newline = true; @@ -87,12 +87,6 @@ public abstract class GeneratorWriter> { file.getParentFile().mkdirs(); this.file = file; - try { - this.writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8")); - } - catch (IOException e) { - throw new GeneratorException("Error writing " + file.getAbsolutePath()); - } this.sb = new StringBuilder(); } @@ -235,11 +229,37 @@ public abstract class GeneratorWriter> { } public final void close() { - String string = beforeClose(sb.toString()); + String newContent = beforeClose(sb.toString()); - writer.append(string); - writer.flush(); - writer.close(); + try { + // [#3756] Regenerate files only if there is a difference + String oldContent = null; + if (file.exists()) { + RandomAccessFile old = null; + + try { + old = new RandomAccessFile(file, "r"); + byte[] oldBytes = new byte[(int) old.length()]; + old.readFully(oldBytes); + oldContent = new String(oldBytes, "UTF-8"); + } + finally { + if (old != null) + old.close(); + } + } + + if (oldContent == null || !oldContent.equals(newContent)) { + PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8")); + + writer.append(newContent); + writer.flush(); + writer.close(); + } + } + catch (IOException e) { + throw new GeneratorException("Error writing " + file.getAbsolutePath()); + } } protected String beforeClose(String string) { diff --git a/jOOQ-codegen/src/main/java/org/jooq/util/JavaGenerator.java b/jOOQ-codegen/src/main/java/org/jooq/util/JavaGenerator.java index 77f7b79041..6b1caf5ab2 100644 --- a/jOOQ-codegen/src/main/java/org/jooq/util/JavaGenerator.java +++ b/jOOQ-codegen/src/main/java/org/jooq/util/JavaGenerator.java @@ -57,6 +57,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -158,6 +159,11 @@ public class JavaGenerator extends AbstractGenerator { */ private Map schemaVersions; + /** + * All files modified by this generator + */ + private Set files = new LinkedHashSet(); + @Override public final void generate(Database db) { this.isoDate = DatatypeConverter.printDateTime(Calendar.getInstance(TimeZone.getTimeZone("UTC"))); @@ -273,9 +279,6 @@ public class JavaGenerator extends AbstractGenerator { // ---------------------------------------------------------------------- // XXX Initialising // ---------------------------------------------------------------------- - log.info("Emptying", targetPackage.getAbsolutePath()); - empty(getStrategy().getFile(schema).getParentFile(), ".java"); - generateSchema(schema); if (generateGlobalObjectReferences() && database.getSequences(schema).size() > 0) { @@ -356,8 +359,12 @@ public class JavaGenerator extends AbstractGenerator { x xx [/pro] */ + log.info("Removing excess files"); + empty(getStrategy().getFile(schema).getParentFile(), ".java", files); + files.clear(); + // XXX [#651] Refactoring-cursor - watch.splitInfo("GENERATION FINISHED!"); + watch.splitInfo("GENERATION FINISHED: " + schema.getQualifiedName()); } private class AvoidAmbiguousClassesFilter implements Database.Filter { @@ -399,7 +406,7 @@ public class JavaGenerator extends AbstractGenerator { xxxxxxxxxxxxxxxxxxxx xxxxxxxxx xxxxxxxxxxxxxx xxxxxxxxxxxxxx x xxxxxxxxxxxxxxxx xxxxxxxxx - xxxxxxxxxx xxx x xxx xxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx + xxxxxxxxxx xxx x xxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxx xxxxxx xx xxx xxxxxx xx x x xxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxx @@ -438,7 +445,7 @@ public class JavaGenerator extends AbstractGenerator { protected void generateRelations(SchemaDefinition schema) { log.info("Generating Keys"); - JavaWriter out = new JavaWriter(new File(getStrategy().getFile(schema).getParentFile(), "Keys.java")); + JavaWriter out = newJavaWriter(new File(getStrategy().getFile(schema).getParentFile(), "Keys.java")); printPackage(out, schema); printClassJavadoc(out, "A class modelling foreign key relationships between tables of the " + schema.getOutputName() + " schema"); @@ -664,7 +671,7 @@ public class JavaGenerator extends AbstractGenerator { final List interfaces = getStrategy().getJavaClassImplements(tableOrUdt, Mode.RECORD); final List> columns = getTypedElements(tableOrUdt); - JavaWriter out = new JavaWriter(getStrategy().getFile(tableOrUdt, Mode.RECORD)); + JavaWriter out = newJavaWriter(getStrategy().getFile(tableOrUdt, Mode.RECORD)); printPackage(out, tableOrUdt, Mode.RECORD); if (tableOrUdt instanceof TableDefinition) @@ -957,7 +964,7 @@ public class JavaGenerator extends AbstractGenerator { final String className = getStrategy().getJavaClassName(tableOrUDT, Mode.INTERFACE); final List interfaces = getStrategy().getJavaClassImplements(tableOrUDT, Mode.INTERFACE); - JavaWriter out = new JavaWriter(getStrategy().getFile(tableOrUDT, Mode.INTERFACE)); + JavaWriter out = newJavaWriter(getStrategy().getFile(tableOrUDT, Mode.INTERFACE)); printPackage(out, tableOrUDT, Mode.INTERFACE); if (tableOrUDT instanceof TableDefinition) generateInterfaceClassJavadoc((TableDefinition) tableOrUDT, out); @@ -1053,7 +1060,7 @@ public class JavaGenerator extends AbstractGenerator { final String schemaId = getStrategy().getFullJavaIdentifier(schema); final String udtId = getStrategy().getJavaIdentifier(udt); - JavaWriter out = new JavaWriter(getStrategy().getFile(udt)); + JavaWriter out = newJavaWriter(getStrategy().getFile(udt)); printPackage(out, udt); generateUDTClassJavadoc(udt, out); printClassAnnotations(out, schema); @@ -1258,7 +1265,7 @@ public class JavaGenerator extends AbstractGenerator { protected void generateUDTReferences(SchemaDefinition schema) { log.info("Generating UDT references"); - JavaWriter out = new JavaWriter(new File(getStrategy().getFile(schema).getParentFile(), "UDTs.java")); + JavaWriter out = newJavaWriter(new File(getStrategy().getFile(schema).getParentFile(), "UDTs.java")); printPackage(out, schema); printClassJavadoc(out, "Convenience access to all UDTs in " + schema.getOutputName()); printClassAnnotations(out, schema); @@ -1305,7 +1312,7 @@ public class JavaGenerator extends AbstractGenerator { xxxxx xxxxxx xxxxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxx xxxxxx xxxxxxxxxxxxxxxxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - xxxxxxxxxx xxx x xxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxx + xxxxxxxxxx xxx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxx xxxxxxxxxxxxxxxxx xxxxxx xxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxx xxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxx @@ -1401,7 +1408,7 @@ public class JavaGenerator extends AbstractGenerator { final String className = getStrategy().getJavaClassName(e, Mode.ENUM); final List interfaces = getStrategy().getJavaClassImplements(e, Mode.ENUM); - JavaWriter out = new JavaWriter(getStrategy().getFile(e, Mode.ENUM)); + JavaWriter out = newJavaWriter(getStrategy().getFile(e, Mode.ENUM)); printPackage(out, e); generateEnumClassJavadoc(e, out); printClassAnnotations(out, e.getSchema()); @@ -1473,7 +1480,7 @@ public class JavaGenerator extends AbstractGenerator { protected void generateRoutines(SchemaDefinition schema) { log.info("Generating routines and table-valued functions"); - JavaWriter out = new JavaWriter(new File(getStrategy().getFile(schema).getParentFile(), "Routines.java")); + JavaWriter out = newJavaWriter(new File(getStrategy().getFile(schema).getParentFile(), "Routines.java")); printPackage(out, schema); printClassJavadoc(out, "Convenience access to all stored procedures and functions in " + schema.getOutputName()); printClassAnnotations(out, schema); @@ -1548,7 +1555,7 @@ public class JavaGenerator extends AbstractGenerator { final List interfaces = getStrategy().getJavaClassImplements(pkg, Mode.DEFAULT); // Static convenience methods - JavaWriter out = new JavaWriter(getStrategy().getFile(pkg)); + JavaWriter out = newJavaWriter(getStrategy().getFile(pkg)); printPackage(out, pkg); generatePackageClassJavadoc(pkg, out); printClassAnnotations(out, schema); @@ -1596,7 +1603,7 @@ public class JavaGenerator extends AbstractGenerator { protected void generateTableReferences(SchemaDefinition schema) { log.info("Generating table references"); - JavaWriter out = new JavaWriter(new File(getStrategy().getFile(schema).getParentFile(), "Tables.java")); + JavaWriter out = newJavaWriter(new File(getStrategy().getFile(schema).getParentFile(), "Tables.java")); printPackage(out, schema); printClassJavadoc(out, "Convenience access to all tables in " + schema.getOutputName()); printClassAnnotations(out, schema); @@ -1672,7 +1679,7 @@ public class JavaGenerator extends AbstractGenerator { log.info("Generating DAO", getStrategy().getFileName(table, Mode.DAO)); - JavaWriter out = new JavaWriter(getStrategy().getFile(table, Mode.DAO)); + JavaWriter out = newJavaWriter(getStrategy().getFile(table, Mode.DAO)); printPackage(out, table, Mode.DAO); generateDaoClassJavadoc(table, out); printClassAnnotations(out, table.getSchema()); @@ -1795,7 +1802,7 @@ public class JavaGenerator extends AbstractGenerator { interfaces.add(getStrategy().getFullJavaClassName(tableOrUDT, Mode.INTERFACE)); } - JavaWriter out = new JavaWriter(getStrategy().getFile(tableOrUDT, Mode.POJO)); + JavaWriter out = newJavaWriter(getStrategy().getFile(tableOrUDT, Mode.POJO)); printPackage(out, tableOrUDT, Mode.POJO); if (tableOrUDT instanceof TableDefinition) @@ -2048,7 +2055,7 @@ public class JavaGenerator extends AbstractGenerator { ", pk=" + (primaryKey != null ? primaryKey.getName() : "N/A") + "]"); - JavaWriter out = new JavaWriter(getStrategy().getFile(table)); + JavaWriter out = newJavaWriter(getStrategy().getFile(table)); printPackage(out, table); generateTableClassJavadoc(table, out); printClassAnnotations(out, schema); @@ -2302,7 +2309,7 @@ public class JavaGenerator extends AbstractGenerator { protected void generateSequences(SchemaDefinition schema) { log.info("Generating sequences"); - JavaWriter out = new JavaWriter(new File(getStrategy().getFile(schema).getParentFile(), "Sequences.java")); + JavaWriter out = newJavaWriter(new File(getStrategy().getFile(schema).getParentFile(), "Sequences.java")); printPackage(out, schema); printClassJavadoc(out, "Convenience access to all sequences in " + schema.getOutputName()); printClassAnnotations(out, schema); @@ -2334,7 +2341,7 @@ public class JavaGenerator extends AbstractGenerator { final String className = getStrategy().getJavaClassName(schema); final List interfaces = getStrategy().getJavaClassImplements(schema, Mode.DEFAULT); - JavaWriter out = new JavaWriter(getStrategy().getFile(schema)); + JavaWriter out = newJavaWriter(getStrategy().getFile(schema)); printPackage(out, schema); generateSchemaClassJavadoc(schema, out); printClassAnnotations(out, schema); @@ -2571,7 +2578,7 @@ public class JavaGenerator extends AbstractGenerator { final String schemaId = getStrategy().getFullJavaIdentifier(schema); final List packageId = getStrategy().getFullJavaIdentifiers(routine.getPackage()); - JavaWriter out = new JavaWriter(getStrategy().getFile(routine)); + JavaWriter out = newJavaWriter(getStrategy().getFile(routine)); printPackage(out, routine); generateRoutineClassJavadoc(routine, out); printClassAnnotations(out, schema); @@ -3379,4 +3386,9 @@ public class JavaGenerator extends AbstractGenerator { return result; } + + private final JavaWriter newJavaWriter(File file) { + files.add(file); + return new JavaWriter(file); + } } From 8fa9878f42f725ba41330ba50d120657e5c6c471 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Tue, 11 Nov 2014 17:27:49 +0100 Subject: [PATCH 10/12] [#3681] Fetch all result sets from executed stored procedures --- jOOQ/src/main/java/org/jooq/Routine.java | 5 ++ .../org/jooq/impl/AbstractResultQuery.java | 51 ++----------- .../java/org/jooq/impl/AbstractRoutine.java | 33 ++++++-- jOOQ/src/main/java/org/jooq/impl/Intern.java | 76 +++++++++++++++++++ jOOQ/src/main/java/org/jooq/impl/Utils.java | 28 +++++++ 5 files changed, 142 insertions(+), 51 deletions(-) create mode 100644 jOOQ/src/main/java/org/jooq/impl/Intern.java diff --git a/jOOQ/src/main/java/org/jooq/Routine.java b/jOOQ/src/main/java/org/jooq/Routine.java index 041247c4a9..d64c26d1fc 100644 --- a/jOOQ/src/main/java/org/jooq/Routine.java +++ b/jOOQ/src/main/java/org/jooq/Routine.java @@ -134,6 +134,11 @@ public interface Routine extends QueryPart { */ T getReturnValue(); + /** + * @return The routine's results (if available) + */ + List> getResults(); + /** * @return A list of parameters passed to the stored object as argument */ diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java index d0824b4a99..dd1e8424e5 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractResultQuery.java @@ -48,11 +48,11 @@ import static java.util.concurrent.Executors.newSingleThreadExecutor; import static org.jooq.SQLDialect.CUBRID; // ... import static org.jooq.impl.Utils.DATA_LOCK_ROWS_FOR_UPDATE; +import static org.jooq.impl.Utils.consumeResultSets; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; -import java.sql.Statement; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -103,9 +103,7 @@ abstract class AbstractResultQuery extends AbstractQuery imple private List> results; // Some temp variables for String interning - private int[] internIndexes; - private Field[] internFields; - private String[] internNames; + private final Intern intern = new Intern(); AbstractResultQuery(Configuration configuration) { super(configuration); @@ -172,36 +170,22 @@ abstract class AbstractResultQuery extends AbstractQuery imple @Override public final ResultQuery intern(Field... fields) { - this.internFields = fields; + intern.internFields = fields; return this; } @Override public final ResultQuery intern(int... fieldIndexes) { - this.internIndexes = fieldIndexes; + intern.internIndexes = fieldIndexes; return this; } @Override public final ResultQuery intern(String... fieldNames) { - this.internNames = fieldNames; + intern.internNames = fieldNames; return this; } - private final int[] internIndexes(Field[] fields) { - if (internIndexes != null) { - return internIndexes; - } - else if (internFields != null) { - return new Fields(fields).indexesOf(internFields); - } - else if (internNames != null) { - return new Fields(fields).indexesOf(internNames); - } - - return null; - } - @Override protected final void prepare(ExecuteContext ctx) throws SQLException { @@ -270,7 +254,7 @@ abstract class AbstractResultQuery extends AbstractQuery imple if (!many) { if (ctx.resultSet() != null) { Field[] fields = getFields(ctx.resultSet().getMetaData()); - cursor = new CursorImpl(ctx, listener, fields, internIndexes(fields), keepStatement(), keepResultSet(), getRecordType()); + cursor = new CursorImpl(ctx, listener, fields, intern.internIndexes(fields), keepStatement(), keepResultSet(), getRecordType()); if (!lazy) { result = cursor.fetch(); @@ -285,28 +269,7 @@ abstract class AbstractResultQuery extends AbstractQuery imple // Fetch several result sets else { results = new ArrayList>(); - boolean anyResults = false; - - while (ctx.resultSet() != null) { - anyResults = true; - - Field[] fields = new MetaDataFieldProvider(ctx.configuration(), ctx.resultSet().getMetaData()).getFields(); - Cursor c = new CursorImpl(ctx, listener, fields, internIndexes(fields), true, false); - results.add(c.fetch()); - - if (ctx.statement().getMoreResults()) { - ctx.resultSet(ctx.statement().getResultSet()); - } - else { - ctx.resultSet(null); - } - } - - // Call this only when there was at least one ResultSet. - // Otherwise, this call is not supported by ojdbc... - if (anyResults) { - ctx.statement().getMoreResults(Statement.CLOSE_ALL_RESULTS); - } + consumeResultSets(ctx, listener, results, intern); } return result != null ? result.size() : 0; diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java index 7bf42f17fa..5d337d5db6 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java @@ -78,6 +78,7 @@ import org.jooq.ExecuteListener; import org.jooq.Field; import org.jooq.Package; import org.jooq.Parameter; +import org.jooq.Record; import org.jooq.RenderContext; import org.jooq.Result; import org.jooq.Routine; @@ -113,6 +114,7 @@ public abstract class AbstractRoutine extends AbstractQueryPart implements Ro private final List> outParameters; private final DataType type; private Parameter returnParameter; + private List> results; private boolean overloaded; private boolean hasDefaultedParameters; @@ -126,7 +128,7 @@ public abstract class AbstractRoutine extends AbstractQueryPart implements Ro private transient Field function; private Configuration configuration; - private final Map, Object> results; + private final Map, Object> outValues; private final Map, Integer> parameterIndexes; // ------------------------------------------------------------------------ @@ -163,10 +165,11 @@ public abstract class AbstractRoutine extends AbstractQueryPart implements Ro this.allParameters = new ArrayList>(); this.inParameters = new ArrayList>(); this.outParameters = new ArrayList>(); + this.results = new ArrayList>(); this.inValues = new HashMap, Field>(); this.inValuesDefaulted = new HashSet>(); this.inValuesNonDefaulted = new HashSet>(); - this.results = new HashMap, Object>(); + this.outValues = new HashMap, Object>(); this.type = converter == null ? (DataType) type : type.asConvertedDataType((Converter) converter); @@ -240,6 +243,9 @@ public abstract class AbstractRoutine extends AbstractQueryPart implements Ro @Override public final int execute() { + results.clear(); + outValues.clear(); + // Procedures (no return value) are always executed as CallableStatement if (type == null) { return executeCallableStatement(); @@ -282,13 +288,13 @@ public abstract class AbstractRoutine extends AbstractQueryPart implements Ro private final int executeSelectFrom() { DSLContext create = create(configuration); Result result = create.selectFrom(table(asField())).fetch(); - results.put(returnParameter, result); + outValues.put(returnParameter, result); return 0; } private final int executeSelect() { final Field field = asField(); - results.put(returnParameter, create(configuration).select(field).fetchOne(field)); + outValues.put(returnParameter, create(configuration).select(field).fetchOne(field)); return 0; } @@ -316,6 +322,11 @@ public abstract class AbstractRoutine extends AbstractQueryPart implements Ro execute0(ctx, listener); + /* [pro] xx + xx xxxxxxx xxx xxxxx xx xxxxxxxxx xxxxxx xxxx xxx xxxx xxxxxxxxx + xx xxx xxxxxxxxxx xx xxxxxxxx xx xxx xxxxxx + xx [/pro] */ + Utils.consumeResultSets(ctx, listener, results, null); fetchOutParameters(ctx); return 0; } @@ -342,7 +353,10 @@ public abstract class AbstractRoutine extends AbstractQueryPart implements Ro private final void execute0(ExecuteContext ctx, ExecuteListener listener) throws SQLException { try { listener.executeStart(ctx); - ctx.statement().execute(); + + if (ctx.statement().execute()) + ctx.resultSet(ctx.statement().getResultSet()); + listener.executeEnd(ctx); } @@ -564,7 +578,7 @@ public abstract class AbstractRoutine extends AbstractQueryPart implements Ro ); parameter.getBinding().get(out); - results.put(parameter, out.value()); + outValues.put(parameter, out.value()); } private final void registerOutParameters(Configuration c, CallableStatement statement) throws SQLException { @@ -597,9 +611,14 @@ public abstract class AbstractRoutine extends AbstractQueryPart implements Ro return null; } + @Override + public final List> getResults() { + return results; + } + @SuppressWarnings("unchecked") protected final Z getValue(Parameter parameter) { - return (Z) results.get(parameter); + return (Z) outValues.get(parameter); } protected final Map, Field> getInValues() { diff --git a/jOOQ/src/main/java/org/jooq/impl/Intern.java b/jOOQ/src/main/java/org/jooq/impl/Intern.java new file mode 100644 index 0000000000..73aefff688 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/Intern.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq.impl; + +import java.io.Serializable; + +import org.jooq.Field; +import org.jooq.Record; + +/** + * @author Lukas Eder + */ +class Intern implements Serializable { + + /** + * Generated UID + */ + private static final long serialVersionUID = 6455756912567274014L; + + // Some temp variables for String interning + int[] internIndexes; + Field[] internFields; + String[] internNames; + + final int[] internIndexes(Field[] fields) { + if (internIndexes != null) { + return internIndexes; + } + else if (internFields != null) { + return new Fields(fields).indexesOf(internFields); + } + else if (internNames != null) { + return new Fields(fields).indexesOf(internNames); + } + + return null; + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/Utils.java b/jOOQ/src/main/java/org/jooq/impl/Utils.java index 6a0cca63fd..c95729cc8c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Utils.java +++ b/jOOQ/src/main/java/org/jooq/impl/Utils.java @@ -2290,6 +2290,34 @@ final class Utils { listener.warning(ctx); } + /** + * [#3681] Consume all {@link ResultSet}s from a JDBC {@link Statement}. + */ + static void consumeResultSets(ExecuteContext ctx, ExecuteListener listener, List> results, Intern intern) throws SQLException { + boolean anyResults = false; + + while (ctx.resultSet() != null) { + anyResults = true; + + Field[] fields = new MetaDataFieldProvider(ctx.configuration(), ctx.resultSet().getMetaData()).getFields(); + Cursor c = new CursorImpl(ctx, listener, fields, intern != null ? intern.internIndexes(fields) : null, true, false); + results.add(c.fetch()); + + if (ctx.statement().getMoreResults()) { + ctx.resultSet(ctx.statement().getResultSet()); + } + else { + ctx.resultSet(null); + } + } + + // Call this only when there was at least one ResultSet. + // Otherwise, this call is not supported by ojdbc... + if (anyResults) { + ctx.statement().getMoreResults(Statement.CLOSE_ALL_RESULTS); + } + } + static List parseTXT(String string, String nullLiteral) { String[] strings = string.split("[\\r\\n]+"); From 83cebee0eba150655e61de6b8946f5b99b398614 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Tue, 11 Nov 2014 18:21:58 +0100 Subject: [PATCH 11/12] [#3760] Add Record.intoList() --- jOOQ/src/main/java/org/jooq/Record.java | 16 ++++++++++++++++ .../main/java/org/jooq/impl/AbstractRecord.java | 6 ++++++ 2 files changed, 22 insertions(+) diff --git a/jOOQ/src/main/java/org/jooq/Record.java b/jOOQ/src/main/java/org/jooq/Record.java index 615901da60..cf30969e6b 100644 --- a/jOOQ/src/main/java/org/jooq/Record.java +++ b/jOOQ/src/main/java/org/jooq/Record.java @@ -46,6 +46,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLData; import java.sql.Statement; +import java.util.List; import java.util.Map; import javax.annotation.Generated; @@ -683,6 +684,21 @@ public interface Record extends Attachable, Comparable { */ Object[] intoArray(); + /** + * Convert this record into a list. + *

        + * The resulting list has the same number of elements as this record has + * fields. The resulting array contains data as such: + *

        + *

        +     * // For arbitrary values of i
        +     * record.getValue(i) == record.intoList().get(i)
        +     * 
        + *

        + * This is the same as calling Arrays.asList(intoArray()) + */ + List intoList(); + /** * Return this record as a name/value map. *

        diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java index 25e5bc6c4b..3ff2c390f8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java @@ -55,6 +55,7 @@ import static org.jooq.impl.Utils.settings; import java.lang.reflect.Method; import java.sql.ResultSet; import java.util.ArrayList; +import java.util.Arrays; import java.util.BitSet; import java.util.Collection; import java.util.LinkedHashMap; @@ -523,6 +524,11 @@ abstract class AbstractRecord extends AbstractStore implements Record { return into(Object[].class); } + @Override + public final List intoList() { + return Arrays.asList(intoArray()); + } + @Override public final Map intoMap() { Map map = new LinkedHashMap(); From 1826f6e632f41944108e0c84148ca08a813e5547 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Tue, 11 Nov 2014 18:34:17 +0100 Subject: [PATCH 12/12] [#3337] Add support for the CREATE TABLE statement --- jOOQ/src/main/java/org/jooq/Clause.java | 11 +++ .../main/java/org/jooq/CreateTableAsStep.java | 12 +++ .../java/org/jooq/CreateTableColumnStep.java | 61 ++++++++++++++ .../java/org/jooq/impl/CreateTableImpl.java | 83 ++++++++++++++++--- 4 files changed, 154 insertions(+), 13 deletions(-) create mode 100644 jOOQ/src/main/java/org/jooq/CreateTableColumnStep.java diff --git a/jOOQ/src/main/java/org/jooq/Clause.java b/jOOQ/src/main/java/org/jooq/Clause.java index ef23fd69d3..889eca2612 100644 --- a/jOOQ/src/main/java/org/jooq/Clause.java +++ b/jOOQ/src/main/java/org/jooq/Clause.java @@ -814,6 +814,17 @@ public enum Clause { */ CREATE_TABLE_AS, + /** + * A column list within a {@link #CREATE_TABLE} statement. + *

        + * This clause surrounds + *

          + *
        • The parentheses
        • + *
        • The column list
        • + *
        + */ + CREATE_TABLE_COLUMNS, + /** * A complete CREATE VIEW statement. */ diff --git a/jOOQ/src/main/java/org/jooq/CreateTableAsStep.java b/jOOQ/src/main/java/org/jooq/CreateTableAsStep.java index 4026482cdc..475631dfc8 100644 --- a/jOOQ/src/main/java/org/jooq/CreateTableAsStep.java +++ b/jOOQ/src/main/java/org/jooq/CreateTableAsStep.java @@ -68,4 +68,16 @@ public interface CreateTableAsStep { */ @Support({ CUBRID, DERBY, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES, SQLITE }) CreateTableFinalStep as(Select select); + + /** + * Add a column to the column list of the CREATE TABLE statement. + */ + @Support + CreateTableColumnStep column(Field field, DataType type); + + /** + * Add a column to the column list of the CREATE TABLE statement. + */ + @Support + CreateTableColumnStep column(String field, DataType type); } diff --git a/jOOQ/src/main/java/org/jooq/CreateTableColumnStep.java b/jOOQ/src/main/java/org/jooq/CreateTableColumnStep.java new file mode 100644 index 0000000000..9133e347c9 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/CreateTableColumnStep.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq; + +/** + * A {@link Query} that can create tables. + * + * @author Lukas Eder + */ +public interface CreateTableColumnStep extends CreateTableFinalStep { + + /** + * Add a column to the column list of the CREATE TABLE statement. + */ + @Support + CreateTableColumnStep column(Field field, DataType type); + + /** + * Add a column to the column list of the CREATE TABLE statement. + */ + @Support + CreateTableColumnStep column(String field, DataType type); +} diff --git a/jOOQ/src/main/java/org/jooq/impl/CreateTableImpl.java b/jOOQ/src/main/java/org/jooq/impl/CreateTableImpl.java index b987f000d9..be4e953d56 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CreateTableImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/CreateTableImpl.java @@ -43,17 +43,25 @@ package org.jooq.impl; import static java.util.Arrays.asList; import static org.jooq.Clause.CREATE_TABLE; import static org.jooq.Clause.CREATE_TABLE_AS; +import static org.jooq.Clause.CREATE_TABLE_COLUMNS; import static org.jooq.Clause.CREATE_TABLE_NAME; // ... // ... // ... +import static org.jooq.impl.DSL.fieldByName; import static org.jooq.impl.Utils.DATA_SELECT_INTO_TABLE; +import java.util.ArrayList; +import java.util.List; + import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.Context; import org.jooq.CreateTableAsStep; +import org.jooq.CreateTableColumnStep; import org.jooq.CreateTableFinalStep; +import org.jooq.DataType; +import org.jooq.Field; import org.jooq.Record; import org.jooq.Select; import org.jooq.Table; @@ -65,20 +73,24 @@ class CreateTableImpl extends AbstractQuery implements // Cascading interface implementations for CREATE TABLE behaviour CreateTableAsStep, - CreateTableFinalStep { + CreateTableColumnStep { /** * Generated UID */ - private static final long serialVersionUID = 8904572826501186329L; + private static final long serialVersionUID = 8904572826501186329L; - private final Table table; - private Select select; + private final Table table; + private Select select; + private final List> columnFields; + private final List> columnTypes; CreateTableImpl(Configuration configuration, Table table) { super(configuration); this.table = table; + this.columnFields = new ArrayList>(); + this.columnTypes = new ArrayList>(); } // ------------------------------------------------------------------------ @@ -91,24 +103,69 @@ class CreateTableImpl extends AbstractQuery implements return this; } + @Override + public final CreateTableColumnStep column(Field field, DataType type) { + columnFields.add(field); + columnTypes.add(type); + return this; + } + + @Override + public final CreateTableColumnStep column(String field, DataType type) { + columnFields.add(fieldByName(type, field)); + columnTypes.add(type); + return this; + } + // ------------------------------------------------------------------------ // XXX: QueryPart API // ------------------------------------------------------------------------ @Override public final void accept(Context ctx) { - /* [pro] xx - xx xxxxxxxxxxxxxxx xxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x - xxxxxxxxxxxxxxxxxxxxxx - x - xxxx - xx [/pro] */ - { - acceptNative(ctx); + + if (select != null) { + + /* [pro] xx + xx xxxxxxxxxxxxxxx xxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x + xxxxxxxxxxxxxxxxxxxxxx + x + xxxx + xx [/pro] */ + { + acceptCreateTableAsSelect(ctx); + } + } + else { + ctx.start(CREATE_TABLE) + .start(CREATE_TABLE_NAME) + .keyword("create table") + .sql(" ") + .visit(table) + .end(CREATE_TABLE_NAME) + .start(CREATE_TABLE_COLUMNS) + .sql("(") + .formatIndentStart() + .formatNewLine(); + + for (int i = 0; i < columnFields.size(); i++) { + ctx.visit(columnFields.get(i)) + .sql(" ") + .sql(columnTypes.get(i).getCastTypeName(ctx.configuration())); + + if (i < columnFields.size() - 1) + ctx.sql(",").formatSeparator(); + } + + ctx.formatIndentEnd() + .formatNewLine() + .sql(")") + .end(CREATE_TABLE_COLUMNS) + .end(CREATE_TABLE); } } - private final void acceptNative(Context ctx) { + private final void acceptCreateTableAsSelect(Context ctx) { ctx.start(CREATE_TABLE) .start(CREATE_TABLE_NAME) .keyword("create table")