From 622b3a526cb1b92aaca1a58239ca32a73352203a Mon Sep 17 00:00:00 2001 From: lukaseder Date: Tue, 23 Jan 2018 18:01:58 +0100 Subject: [PATCH] [#5960] Add a DiagnosticsListener SPI --- .../src/main/java/org/jooq/Configuration.java | 54 +++++++ jOOQ/src/main/java/org/jooq/DSLContext.java | 8 + .../java/org/jooq/DiagnosticsContext.java | 76 +++++++++ .../java/org/jooq/DiagnosticsListener.java | 62 ++++++++ .../org/jooq/DiagnosticsListenerProvider.java | 76 +++++++++ .../org/jooq/impl/DefaultConfiguration.java | 122 +++++++++++++++ .../java/org/jooq/impl/DefaultDSLContext.java | 13 +- .../jooq/impl/DefaultDiagnosticsContext.java | 78 ++++++++++ .../jooq/impl/DefaultDiagnosticsListener.java | 56 +++++++ .../DefaultDiagnosticsListenerProvider.java | 103 +++++++++++++ .../org/jooq/impl/DiagnosticsConnection.java | 127 +++++++++++++++ .../org/jooq/impl/DiagnosticsListeners.java | 69 +++++++++ .../org/jooq/impl/DiagnosticsResultSet.java | 145 ++++++++++++++++++ .../org/jooq/impl/DiagnosticsStatement.java | 138 +++++++++++++++++ 14 files changed, 1123 insertions(+), 4 deletions(-) create mode 100644 jOOQ/src/main/java/org/jooq/DiagnosticsContext.java create mode 100644 jOOQ/src/main/java/org/jooq/DiagnosticsListener.java create mode 100644 jOOQ/src/main/java/org/jooq/DiagnosticsListenerProvider.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DefaultDiagnosticsContext.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DefaultDiagnosticsListener.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DefaultDiagnosticsListenerProvider.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DiagnosticsConnection.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DiagnosticsListeners.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DiagnosticsResultSet.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DiagnosticsStatement.java diff --git a/jOOQ/src/main/java/org/jooq/Configuration.java b/jOOQ/src/main/java/org/jooq/Configuration.java index dde8902133..5c7594205b 100644 --- a/jOOQ/src/main/java/org/jooq/Configuration.java +++ b/jOOQ/src/main/java/org/jooq/Configuration.java @@ -50,6 +50,7 @@ import javax.sql.DataSource; import org.jooq.conf.Settings; import org.jooq.impl.DataSourceConnectionProvider; import org.jooq.impl.DefaultConnectionProvider; +import org.jooq.impl.DefaultDiagnosticsListenerProvider; import org.jooq.impl.DefaultExecuteListenerProvider; import org.jooq.impl.DefaultExecutorProvider; import org.jooq.impl.DefaultRecordListenerProvider; @@ -293,6 +294,12 @@ public interface Configuration extends Serializable { */ TransactionListenerProvider[] transactionListenerProviders(); + /** + * Get the configured DiagnosticsListenerProviders from this + * configuration. + */ + DiagnosticsListenerProvider[] diagnosticsListenerProviders(); + /** * Get this configuration's underlying record mapper provider. */ @@ -651,6 +658,33 @@ public interface Configuration extends Serializable { */ Configuration set(TransactionListenerProvider... newTransactionListenerProviders); + /** + * Change this configuration to hold a new diagnostics listeners. + *

+ * This will wrap the argument {@link DiagnosticsListener} in a + * {@link DefaultDiagnosticsListenerProvider} for convenience. + *

+ * This method is not thread-safe and should not be used in globally + * available Configuration objects. + * + * @param newDiagnosticsListeners The new diagnostics listeners to be + * contained in the changed configuration. + * @return The changed configuration. + */ + Configuration set(DiagnosticsListener... newDiagnosticsListeners); + + /** + * Change this configuration to hold a new diagnostics listener providers. + *

+ * This method is not thread-safe and should not be used in globally + * available Configuration objects. + * + * @param newDiagnosticsListenerProviders The new diagnostics listener + * providers to be contained in the changed configuration. + * @return The changed configuration. + */ + Configuration set(DiagnosticsListenerProvider... newDiagnosticsListenerProviders); + /** * Change this configuration to hold a new converter provider. *

@@ -907,6 +941,26 @@ public interface Configuration extends Serializable { */ Configuration derive(TransactionListenerProvider... newTransactionListenerProviders); + /** + * Create a derived configuration from this one, with new diagnostics + * listeners. + * + * @param newDiagnosticsListeners The new diagnostics listeners to be + * contained in the derived configuration. + * @return The derived configuration. + */ + Configuration derive(DiagnosticsListener... newDiagnosticsListeners); + + /** + * Create a derived configuration from this one, with new diagnostics + * listener providers. + * + * @param newDiagnosticsListenerProviders The new diagnostics listener + * providers to be contained in the derived configuration. + * @return The derived configuration. + */ + Configuration derive(DiagnosticsListenerProvider... newDiagnosticsListenerProviders); + /** * Create a derived configuration from this one, with new converter * provider. diff --git a/jOOQ/src/main/java/org/jooq/DSLContext.java b/jOOQ/src/main/java/org/jooq/DSLContext.java index 88feb87d58..aaf8655162 100644 --- a/jOOQ/src/main/java/org/jooq/DSLContext.java +++ b/jOOQ/src/main/java/org/jooq/DSLContext.java @@ -216,6 +216,14 @@ public interface DSLContext extends Scope , AutoCloseable { */ Connection parsingConnection(); + /** + * A JDBC connection that proxies the underlying connection to run the jOOQ + * Diagnostics Pack on executed queries. + *

+ * This is experimental functionality. + */ + Connection diagnosticsConnection(); + /** * Access the database meta data. *

diff --git a/jOOQ/src/main/java/org/jooq/DiagnosticsContext.java b/jOOQ/src/main/java/org/jooq/DiagnosticsContext.java new file mode 100644 index 0000000000..d53379d456 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/DiagnosticsContext.java @@ -0,0 +1,76 @@ +/* + * 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. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq; + +import java.sql.ResultSet; + +/** + * A parameter object that is passed to {@link DiagnosticsListener} methods. + * + * @author Lukas Eder + */ +public interface DiagnosticsContext { + + /** + * The {@link ResultSet} available in this context, or null, if + * there was no result set. + */ + ResultSet resultSet(); + + /** + * The number of rows that were fetched from {@link #resultSet()}, or + * -1 if there was no result set. + */ + int resultSetFetchedRows(); + + /** + * The number of rows that were actually available from + * {@link #resultSet()}, or -1 if there was no result set. + *

+ * Calling this method will try to scroll to the end of the + * {@link #resultSet()}, in order to count the number of rows, which incurs + * overhead! + *

+ * If the result set is still being consumed (i.e. prior to the + * {@link ResultSet#close()} call), and scrolling back to the current row + * after scrolling to the end of {@link #resultSet()} is not possible (e.g. + * because the driver supports only {@link ResultSet#TYPE_FORWARD_ONLY}), + * then this will return the same value as {@link #resultSetFetchedRows()}. + */ + int resultSetActualRows(); +} diff --git a/jOOQ/src/main/java/org/jooq/DiagnosticsListener.java b/jOOQ/src/main/java/org/jooq/DiagnosticsListener.java new file mode 100644 index 0000000000..89287aba9c --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/DiagnosticsListener.java @@ -0,0 +1,62 @@ +/* + * 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. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq; + +import java.sql.ResultSet; + +/** + * A diagnostics listener. + * + * @author Lukas Eder + */ +public interface DiagnosticsListener { + + /** + * The fetched JDBC {@link ResultSet} was too large. + *

+ * An event indicating that a JDBC {@link ResultSet} was fetched with + * A rows, but only B rows (B < A) + * were consumed. + *

+ * Typically, this problem can be remedied by applying the appropriate + * LIMIT clause in SQL, or {@link SelectLimitStep#limit(int)} + * clause in jOOQ. + */ + void resultSetTooLarge(DiagnosticsContext ctx); + +} diff --git a/jOOQ/src/main/java/org/jooq/DiagnosticsListenerProvider.java b/jOOQ/src/main/java/org/jooq/DiagnosticsListenerProvider.java new file mode 100644 index 0000000000..9d1f9dc63f --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/DiagnosticsListenerProvider.java @@ -0,0 +1,76 @@ +/* + * 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. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq; + +import org.jooq.impl.DefaultDiagnosticsListenerProvider; + +/** + * A provider for {@link TransactionListener} instances. + *

+ * In order to facilitate the lifecycle management of + * TransactionListener instances that are provided to a jOOQ + * {@link Configuration}, clients can implement this API. To jOOQ, it is thus + * irrelevant, if transaction listeners are stateful or stateless, local to an + * execution, or global to an application. + * + * @author Lukas Eder + * @see TransactionListener + * @see Configuration + */ + +@FunctionalInterface + +public interface DiagnosticsListenerProvider { + + /** + * Provide an DiagnosticsListener instance. + *

+ * Implementations are free to choose whether this method returns new + * instances at every call or whether the same instance is returned + * repetitively. + *

+ * The lifecycle of the DiagnosticsListener is not specified. + * New instances can be provided any time clients want to reset their + * diagnostics. + * + * @return A DiagnosticsListener instance. + * @see DiagnosticsListener + * @see DefaultDiagnosticsListenerProvider + */ + DiagnosticsListener provide(); +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java b/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java index 3caccff44e..e85288a36b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java @@ -59,6 +59,8 @@ import org.jooq.Configuration; import org.jooq.ConnectionProvider; import org.jooq.ConverterProvider; import org.jooq.DSLContext; +import org.jooq.DiagnosticsListener; +import org.jooq.DiagnosticsListenerProvider; import org.jooq.ExecuteListener; import org.jooq.ExecuteListenerProvider; import org.jooq.ExecutorProvider; @@ -115,6 +117,7 @@ public class DefaultConfiguration implements Configuration { private transient ExecuteListenerProvider[] executeListenerProviders; private transient VisitListenerProvider[] visitListenerProviders; private transient TransactionListenerProvider[] transactionListenerProviders; + private transient DiagnosticsListenerProvider[] diagnosticsListenerProviders; private transient ConverterProvider converterProvider; // [#7062] Apart from the possibility of containing user defined objects, the data @@ -462,6 +465,7 @@ public class DefaultConfiguration implements Configuration { executeListenerProviders, visitListenerProviders, transactionListenerProviders, + null, converterProvider, null, @@ -472,6 +476,60 @@ public class DefaultConfiguration implements Configuration { ); } + /** + * Create the actual configuration object. + *

+ * This constructor has been made package-private to allow for adding new + * configuration properties in the future, without breaking client code. + * Consider creating a configuration by chaining calls to various + * derive() methods. + * + * @deprecated Use + * {@link #DefaultConfiguration(ConnectionProvider, ExecutorProvider, TransactionProvider, RecordMapperProvider, RecordUnmapperProvider, RecordListenerProvider[], ExecuteListenerProvider[], VisitListenerProvider[], TransactionListenerProvider[], DiagnosticsListenerProvider[], ConverterProvider, SQLDialect, Settings, Map)} + * instead. This constructor is maintained to provide jOOQ 3.2, + * 3.3, 3.7, 3.8, 3.9, 3.10 backwards-compatibility if called with + * reflection from Spring configurations. + */ + @Deprecated + DefaultConfiguration( + ConnectionProvider connectionProvider, + ExecutorProvider executorProvider, + TransactionProvider transactionProvider, + RecordMapperProvider recordMapperProvider, + RecordUnmapperProvider recordUnmapperProvider, + RecordListenerProvider[] recordListenerProviders, + ExecuteListenerProvider[] executeListenerProviders, + VisitListenerProvider[] visitListenerProviders, + TransactionListenerProvider[] transactionListenerProviders, + ConverterProvider converterProvider, + + Clock clock, + + SQLDialect dialect, + Settings settings, + Map data) + { + this( + connectionProvider, + executorProvider, + transactionProvider, + recordMapperProvider, + recordUnmapperProvider, + recordListenerProviders, + executeListenerProviders, + visitListenerProviders, + transactionListenerProviders, + null, + converterProvider, + + clock, + + dialect, + settings, + data + ); + } + /** * Create the actual configuration object. *

@@ -490,6 +548,7 @@ public class DefaultConfiguration implements Configuration { ExecuteListenerProvider[] executeListenerProviders, VisitListenerProvider[] visitListenerProviders, TransactionListenerProvider[] transactionListenerProviders, + DiagnosticsListenerProvider[] diagnosticsListenerProviders, ConverterProvider converterProvider, Clock clock, @@ -507,6 +566,7 @@ public class DefaultConfiguration implements Configuration { set(executeListenerProviders); set(visitListenerProviders); set(transactionListenerProviders); + set(diagnosticsListenerProviders); set(converterProvider); set(clock); @@ -559,6 +619,7 @@ public class DefaultConfiguration implements Configuration { executeListenerProviders, visitListenerProviders, transactionListenerProviders, + diagnosticsListenerProviders, converterProvider, clock, @@ -586,6 +647,7 @@ public class DefaultConfiguration implements Configuration { executeListenerProviders, visitListenerProviders, transactionListenerProviders, + diagnosticsListenerProviders, converterProvider, clock, @@ -608,6 +670,7 @@ public class DefaultConfiguration implements Configuration { executeListenerProviders, visitListenerProviders, transactionListenerProviders, + diagnosticsListenerProviders, converterProvider, clock, @@ -635,6 +698,7 @@ public class DefaultConfiguration implements Configuration { executeListenerProviders, visitListenerProviders, transactionListenerProviders, + diagnosticsListenerProviders, converterProvider, clock, @@ -662,6 +726,7 @@ public class DefaultConfiguration implements Configuration { executeListenerProviders, visitListenerProviders, transactionListenerProviders, + diagnosticsListenerProviders, converterProvider, clock, @@ -689,6 +754,7 @@ public class DefaultConfiguration implements Configuration { executeListenerProviders, visitListenerProviders, transactionListenerProviders, + diagnosticsListenerProviders, converterProvider, clock, @@ -716,6 +782,7 @@ public class DefaultConfiguration implements Configuration { newExecuteListenerProviders, visitListenerProviders, transactionListenerProviders, + diagnosticsListenerProviders, converterProvider, clock, @@ -743,6 +810,7 @@ public class DefaultConfiguration implements Configuration { executeListenerProviders, newVisitListenerProviders, transactionListenerProviders, + diagnosticsListenerProviders, converterProvider, clock, @@ -770,6 +838,35 @@ public class DefaultConfiguration implements Configuration { executeListenerProviders, visitListenerProviders, newTransactionListenerProviders, + diagnosticsListenerProviders, + converterProvider, + + clock, + + dialect, + settings, + data + ); + } + + @Override + public final Configuration derive(DiagnosticsListener... newDiagnosticsListeners) { + return derive(DefaultDiagnosticsListenerProvider.providers(newDiagnosticsListeners)); + } + + @Override + public final Configuration derive(DiagnosticsListenerProvider... newDiagnosticsListenerProviders) { + return new DefaultConfiguration( + connectionProvider, + executorProvider, + transactionProvider, + recordMapperProvider, + recordUnmapperProvider, + recordListenerProviders, + executeListenerProviders, + visitListenerProviders, + transactionListenerProviders, + newDiagnosticsListenerProviders, converterProvider, clock, @@ -792,6 +889,7 @@ public class DefaultConfiguration implements Configuration { executeListenerProviders, visitListenerProviders, transactionListenerProviders, + diagnosticsListenerProviders, newConverterProvider, clock, @@ -815,6 +913,7 @@ public class DefaultConfiguration implements Configuration { executeListenerProviders, visitListenerProviders, transactionListenerProviders, + diagnosticsListenerProviders, converterProvider, newClock, dialect, @@ -836,6 +935,7 @@ public class DefaultConfiguration implements Configuration { executeListenerProviders, visitListenerProviders, transactionListenerProviders, + diagnosticsListenerProviders, converterProvider, clock, @@ -858,6 +958,7 @@ public class DefaultConfiguration implements Configuration { executeListenerProviders, visitListenerProviders, transactionListenerProviders, + diagnosticsListenerProviders, converterProvider, clock, @@ -1004,6 +1105,20 @@ public class DefaultConfiguration implements Configuration { return this; } + @Override + public final Configuration set(DiagnosticsListener... newDiagnosticsListeners) { + return set(DefaultDiagnosticsListenerProvider.providers(newDiagnosticsListeners)); + } + + @Override + public final Configuration set(DiagnosticsListenerProvider... newDiagnosticsListenerProviders) { + this.diagnosticsListenerProviders = newDiagnosticsListenerProviders != null + ? newDiagnosticsListenerProviders + : new DiagnosticsListenerProvider[0]; + + return this; + } + @Override public final Configuration set(ConverterProvider newConverterProvider) { this.converterProvider = newConverterProvider != null @@ -1212,6 +1327,11 @@ public class DefaultConfiguration implements Configuration { return transactionListenerProviders; } + @Override + public final DiagnosticsListenerProvider[] diagnosticsListenerProviders() { + return diagnosticsListenerProviders; + } + @Override public final ConverterProvider converterProvider() { return converterProvider; @@ -1299,6 +1419,7 @@ public class DefaultConfiguration implements Configuration { oos.writeObject(cloneSerializables(recordListenerProviders)); oos.writeObject(cloneSerializables(visitListenerProviders)); oos.writeObject(cloneSerializables(transactionListenerProviders)); + oos.writeObject(cloneSerializables(diagnosticsListenerProviders)); oos.writeObject(converterProvider instanceof Serializable ? converterProvider @@ -1342,6 +1463,7 @@ public class DefaultConfiguration implements Configuration { recordListenerProviders = (RecordListenerProvider[]) ois.readObject(); visitListenerProviders = (VisitListenerProvider[]) ois.readObject(); transactionListenerProviders = (TransactionListenerProvider[]) ois.readObject(); + diagnosticsListenerProviders = (DiagnosticsListenerProvider[]) ois.readObject(); converterProvider = (ConverterProvider) ois.readObject(); data = new ConcurrentHashMap(); diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java index 37e9a46d3f..7a84641248 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java @@ -304,7 +304,7 @@ public class DefaultDSLContext extends AbstractScope implements DSLContext, Seri } public DefaultDSLContext(SQLDialect dialect, Settings settings) { - this(new DefaultConfiguration(new NoConnectionProvider(), null, null, null, null, null, null, null, null, null, null, dialect, settings, null)); + this(new DefaultConfiguration(new NoConnectionProvider(), null, null, null, null, null, null, null, null, null, null, null, dialect, settings, null)); } public DefaultDSLContext(Connection connection, SQLDialect dialect) { @@ -312,7 +312,7 @@ public class DefaultDSLContext extends AbstractScope implements DSLContext, Seri } public DefaultDSLContext(Connection connection, SQLDialect dialect, Settings settings) { - this(new DefaultConfiguration(new DefaultConnectionProvider(connection), null, null, null, null, null, null, null, null, null, null, dialect, settings, null)); + this(new DefaultConfiguration(new DefaultConnectionProvider(connection), null, null, null, null, null, null, null, null, null, null, null, dialect, settings, null)); } public DefaultDSLContext(DataSource datasource, SQLDialect dialect) { @@ -320,7 +320,7 @@ public class DefaultDSLContext extends AbstractScope implements DSLContext, Seri } public DefaultDSLContext(DataSource datasource, SQLDialect dialect, Settings settings) { - this(new DefaultConfiguration(new DataSourceConnectionProvider(datasource), null, null, null, null, null, null, null, null, null, null, dialect, settings, null)); + this(new DefaultConfiguration(new DataSourceConnectionProvider(datasource), null, null, null, null, null, null, null, null, null, null, null, dialect, settings, null)); } public DefaultDSLContext(ConnectionProvider connectionProvider, SQLDialect dialect) { @@ -328,7 +328,7 @@ public class DefaultDSLContext extends AbstractScope implements DSLContext, Seri } public DefaultDSLContext(ConnectionProvider connectionProvider, SQLDialect dialect, Settings settings) { - this(new DefaultConfiguration(connectionProvider, null, null, null, null, null, null, null, null, null, null, dialect, settings, null)); + this(new DefaultConfiguration(connectionProvider, null, null, null, null, null, null, null, null, null, null, null, dialect, settings, null)); } public DefaultDSLContext(Configuration configuration) { @@ -381,6 +381,11 @@ public class DefaultDSLContext extends AbstractScope implements DSLContext, Seri return new ParsingConnection(configuration()); } + @Override + public Connection diagnosticsConnection() { + return new DiagnosticsConnection(configuration()); + } + @Override public Meta meta() { return new MetaImpl(configuration()); diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultDiagnosticsContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultDiagnosticsContext.java new file mode 100644 index 0000000000..97fc6c3fb0 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultDiagnosticsContext.java @@ -0,0 +1,78 @@ +/* + * 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. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq.impl; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.jooq.DiagnosticsContext; + +/** + * @author Lukas Eder + */ +final class DefaultDiagnosticsContext implements DiagnosticsContext { + + ResultSet resultSet; + int resultSetFetchedRows; + int resultSetActualRows; + + @Override + public final ResultSet resultSet() { + return resultSet; + } + + @Override + public final int resultSetFetchedRows() { + return resultSet == null ? -1 : resultSetFetchedRows; + } + + @Override + public final int resultSetActualRows() { + if (resultSet == null) + return -1; + + try { + while (resultSet.next()) + resultSetActualRows++; + + resultSet.absolute(resultSetFetchedRows); + } + catch (SQLException ignore) {} + return resultSetActualRows; + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultDiagnosticsListener.java b/jOOQ/src/main/java/org/jooq/impl/DefaultDiagnosticsListener.java new file mode 100644 index 0000000000..8a48777438 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultDiagnosticsListener.java @@ -0,0 +1,56 @@ +/* + * 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. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq.impl; + +import org.jooq.DiagnosticsContext; +import org.jooq.DiagnosticsListener; + +/** + * A publicly available default implementation of {@link DiagnosticsListener}. + *

+ * Use this to stay compatible with future API changes (i.e. added methods to + * DiagnosticsListener) + * + * @author Lukas Eder + */ +public class DefaultDiagnosticsListener implements DiagnosticsListener { + + @Override + public void resultSetTooLarge(DiagnosticsContext ctx) {} + +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultDiagnosticsListenerProvider.java b/jOOQ/src/main/java/org/jooq/impl/DefaultDiagnosticsListenerProvider.java new file mode 100644 index 0000000000..d64a69a69f --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultDiagnosticsListenerProvider.java @@ -0,0 +1,103 @@ +/* + * 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. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq.impl; + +import java.io.Serializable; + +import org.jooq.DiagnosticsListener; +import org.jooq.DiagnosticsListenerProvider; + +/** + * A default implementation for {@link DiagnosticsListenerProvider}. + *

+ * This implementation just wraps an instance of {@link DiagnosticsListener}, + * always providing the same. + * + * @author Lukas Eder + */ +public class DefaultDiagnosticsListenerProvider implements DiagnosticsListenerProvider, Serializable { + + /** + * Generated UID. + */ + private static final long serialVersionUID = -2122007794302549679L; + + /** + * The delegate listener. + */ + private final DiagnosticsListener listener; + + /** + * Convenience method to construct an array of + * DefaultDiagnosticsListenerProvider from an array of + * DiagnosticsListener instances. + */ + public static DiagnosticsListenerProvider[] providers(DiagnosticsListener... listeners) { + DiagnosticsListenerProvider[] result = new DiagnosticsListenerProvider[listeners.length]; + + for (int i = 0; i < listeners.length; i++) + result[i] = new DefaultDiagnosticsListenerProvider(listeners[i]); + + return result; + } + + /** + * Create a new provider instance from an argument listener. + * + * @param listener The argument listener. + */ + public DefaultDiagnosticsListenerProvider(DiagnosticsListener listener) { + this.listener = listener; + } + + /** + * {@inheritDoc} + */ + @Override + public final DiagnosticsListener provide() { + return listener; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return listener.toString(); + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DiagnosticsConnection.java b/jOOQ/src/main/java/org/jooq/impl/DiagnosticsConnection.java new file mode 100644 index 0000000000..ebf3df574b --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DiagnosticsConnection.java @@ -0,0 +1,127 @@ +/* + * 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. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq.impl; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Statement; + +import org.jooq.Configuration; +import org.jooq.tools.jdbc.DefaultConnection; + +/** + * @author Lukas Eder + */ +final class DiagnosticsConnection extends DefaultConnection { + + final Configuration configuration; + final DiagnosticsListeners listeners; + + DiagnosticsConnection(Configuration configuration) { + super(configuration.connectionProvider().acquire()); + + this.configuration = configuration; + this.listeners = DiagnosticsListeners.get(configuration); + } + + @Override + public final Statement createStatement() throws SQLException { + return new DiagnosticsStatement(this, getDelegate().createStatement()); + } + + @Override + public final Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + return new DiagnosticsStatement(this, getDelegate().createStatement(resultSetType, resultSetConcurrency)); + } + + @Override + public final Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return new DiagnosticsStatement(this, getDelegate().createStatement(resultSetType, resultSetConcurrency, resultSetHoldability)); + } + + @Override + public final PreparedStatement prepareStatement(String sql) throws SQLException { + return new DiagnosticsStatement(this, getDelegate().prepareStatement(sql)); + } + + @Override + public final PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return new DiagnosticsStatement(this, getDelegate().prepareStatement(sql, resultSetType, resultSetConcurrency)); + } + + @Override + public final PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return new DiagnosticsStatement(this, getDelegate().prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); + } + + @Override + public final PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { + return new DiagnosticsStatement(this, getDelegate().prepareStatement(sql, autoGeneratedKeys)); + } + + @Override + public final PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { + return new DiagnosticsStatement(this, getDelegate().prepareStatement(sql, columnIndexes)); + } + + @Override + public final PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { + return new DiagnosticsStatement(this, getDelegate().prepareStatement(sql, columnNames)); + } + + @Override + public final CallableStatement prepareCall(String sql) throws SQLException { + return new DiagnosticsStatement(this, getDelegate().prepareCall(sql)); + } + + @Override + public final CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return new DiagnosticsStatement(this, getDelegate().prepareCall(sql, resultSetType, resultSetConcurrency)); + } + + @Override + public final CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return new DiagnosticsStatement(this, getDelegate().prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); + } + + @Override + public final void close() throws SQLException { + configuration.connectionProvider().release(getDelegate()); + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DiagnosticsListeners.java b/jOOQ/src/main/java/org/jooq/impl/DiagnosticsListeners.java new file mode 100644 index 0000000000..d164f5df4b --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DiagnosticsListeners.java @@ -0,0 +1,69 @@ +/* + * 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. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq.impl; + +import org.jooq.Configuration; +import org.jooq.DiagnosticsContext; +import org.jooq.DiagnosticsListener; +import org.jooq.DiagnosticsListenerProvider; + +/** + * @author Lukas Eder + */ +final class DiagnosticsListeners implements DiagnosticsListener { + + final DiagnosticsListener[] listeners; + + DiagnosticsListeners(DiagnosticsListenerProvider[] providers) { + listeners = new DiagnosticsListener[providers.length]; + + for (int i = 0; i < listeners.length; i++) + listeners[i] = providers[i].provide(); + + } + + static final DiagnosticsListeners get(Configuration configuration) { + return new DiagnosticsListeners(configuration.diagnosticsListenerProviders()); + } + + @Override + public final void resultSetTooLarge(DiagnosticsContext ctx) { + for (DiagnosticsListener listener : listeners) + listener.resultSetTooLarge(ctx); + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DiagnosticsResultSet.java b/jOOQ/src/main/java/org/jooq/impl/DiagnosticsResultSet.java new file mode 100644 index 0000000000..6b850c6fe2 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DiagnosticsResultSet.java @@ -0,0 +1,145 @@ +/* + * 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. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq.impl; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.jooq.tools.jdbc.DefaultResultSet; + +/** + * @author Lukas Eder + */ +final class DiagnosticsResultSet extends DefaultResultSet { + + final DiagnosticsConnection connection; + int current; + int rows; + + public DiagnosticsResultSet(ResultSet delegate, Statement creator, DiagnosticsConnection connection) { + super(delegate, creator); + + this.connection = connection; + } + + // ------------------------------------------------------------------------ + // XXX Navigational methods + // ------------------------------------------------------------------------ + + @Override + public void beforeFirst() throws SQLException { + super.beforeFirst(); + moveAbsolute(true, super.getRow()); + } + + @Override + public void afterLast() throws SQLException { + super.afterLast(); + moveAbsolute(true, super.getRow()); + } + + @Override + public boolean first() throws SQLException { + return moveAbsolute(super.first(), super.getRow()); + } + + @Override + public boolean last() throws SQLException { + return moveAbsolute(super.last(), super.getRow()); + } + + @Override + public boolean absolute(int row) throws SQLException { + return moveAbsolute(super.absolute(row), super.getRow()); + } + + @Override + public boolean relative(int relative) throws SQLException { + return moveRelative(super.relative(relative), relative); + } + + @Override + public boolean next() throws SQLException { + return moveRelative(super.next(), 1); + } + + @Override + public boolean previous() throws SQLException { + return moveRelative(super.previous(), -1); + } + + private final boolean moveRelative(boolean success, int relative) { + if (success) { + current = current + relative; + rows = Math.max(rows, current); + } + + return success; + } + + private final boolean moveAbsolute(boolean success, int absolute) { + if (success) { + current = absolute; + rows = Math.max(rows, current); + } + + return success; + } + + @Override + public void close() throws SQLException { + try { + if (current < rows) + super.absolute(current = rows); + + if (super.next()) { + DefaultDiagnosticsContext ctx = new DefaultDiagnosticsContext(); + + ctx.resultSet = super.getDelegate(); + ctx.resultSetFetchedRows = current; + ctx.resultSetActualRows = current + 1; + + connection.listeners.resultSetTooLarge(ctx); + } + } + catch (SQLException ignore) {} + + super.close(); + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DiagnosticsStatement.java b/jOOQ/src/main/java/org/jooq/impl/DiagnosticsStatement.java new file mode 100644 index 0000000000..409fb3b847 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DiagnosticsStatement.java @@ -0,0 +1,138 @@ +/* + * 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. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq.impl; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.jooq.tools.jdbc.DefaultCallableStatement; + +/** + * @author Lukas Eder + */ +final class DiagnosticsStatement extends DefaultCallableStatement { + + private final DiagnosticsConnection connection; + + DiagnosticsStatement(DiagnosticsConnection connection, Statement statement) { + super(statement); + + this.connection = connection; + } + + @Override + public final ResultSet executeQuery(String sql) throws SQLException { + return new DiagnosticsResultSet(super.executeQuery(sql), this, connection); + } + + @Override + public final int executeUpdate(String sql) throws SQLException { + return super.executeUpdate(sql); + } + + @Override + public final int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + return super.executeUpdate(sql, autoGeneratedKeys); + } + + @Override + public final int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + return super.executeUpdate(sql, columnIndexes); + } + + @Override + public final int executeUpdate(String sql, String[] columnNames) throws SQLException { + return super.executeUpdate(sql, columnNames); + } + + @Override + public final boolean execute(String sql) throws SQLException { + return super.execute(sql); + } + + @Override + public final boolean execute(String sql, int autoGeneratedKeys) throws SQLException { + return super.execute(sql, autoGeneratedKeys); + } + + @Override + public final boolean execute(String sql, int[] columnIndexes) throws SQLException { + return super.execute(sql, columnIndexes); + } + + @Override + public final boolean execute(String sql, String[] columnNames) throws SQLException { + return super.execute(sql, columnNames); + } + + + + @Override + public final long executeLargeUpdate(String sql) throws SQLException { + return super.executeLargeUpdate(sql); + } + + @Override + public final long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + return super.executeLargeUpdate(sql, autoGeneratedKeys); + } + + @Override + public final long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException { + return super.executeLargeUpdate(sql, columnIndexes); + } + + @Override + public final long executeLargeUpdate(String sql, String[] columnNames) throws SQLException { + return super.executeLargeUpdate(sql, columnNames); + } + + + + @Override + public final void addBatch(String sql) throws SQLException { + super.addBatch(sql); + } + + @Override + public final Connection getConnection() throws SQLException { + return connection; + } +}