diff --git a/src/jvmMain/kotlin/ru/langrafhomyak/db/jdbc_resources_manager/PreparedStatementWrapper.kt b/src/jvmMain/kotlin/ru/langrafhomyak/db/jdbc_resources_manager/PreparedStatementWrapper.kt new file mode 100644 index 0000000..3b90140 --- /dev/null +++ b/src/jvmMain/kotlin/ru/langrafhomyak/db/jdbc_resources_manager/PreparedStatementWrapper.kt @@ -0,0 +1,357 @@ +package ru.langrafhomyak.db.jdbc_resources_manager + +import java.io.InputStream +import java.io.Reader +import java.math.BigDecimal +import java.net.URL +import java.sql.Array +import java.sql.Blob +import java.sql.Clob +import java.sql.Connection +import java.sql.Date +import java.sql.NClob +import java.sql.ParameterMetaData +import java.sql.PreparedStatement +import java.sql.Ref +import java.sql.ResultSet +import java.sql.ResultSetMetaData +import java.sql.RowId +import java.sql.SQLException +import java.sql.SQLWarning +import java.sql.SQLXML +import java.sql.Statement +import java.sql.Time +import java.sql.Timestamp +import java.util.Calendar +import ru.landrafhomyak.utility.reference_counter.CloseableReferenceCounter + +@Suppress("UsePropertyAccessSyntax") +internal abstract class PreparedStatementWrapper(protected val _orig: PreparedStatement) : PreparedStatement { + private val _refcnt = CloseableReferenceCounter("This prepared statement was returned to pool to be used in future") + private var _currentQuery: ResultSetWrapper? = null + private var _closeOnCompletion: Boolean = false + + protected abstract fun _onClose() + + override fun close() { + this._refcnt.close("Can't close prepared statement while it's in use") + this._onClose() + } + + override fun closeOnCompletion() = + this._refcnt.withRef { this._closeOnCompletion = true } + + + override fun isCloseOnCompletion(): Boolean = + this._refcnt.withRef { this._closeOnCompletion } + + private inner class _BoundResultSetWrapper(orig: ResultSet) : ResultSetWrapper(orig) { + override fun close() { + if (this._orig.isClosed) + return + this._orig.close() + this@PreparedStatementWrapper._currentQuery = null + if (this@PreparedStatementWrapper._closeOnCompletion) + this@PreparedStatementWrapper.close() + } + + override fun getStatement(): Statement? = this@PreparedStatementWrapper + } + + private fun _prepareRS(rs: ResultSet): ResultSet { + val wrapped = this._BoundResultSetWrapper(rs) + this._currentQuery = wrapped + return wrapped + } + + override fun executeQuery(): ResultSet? = + this._refcnt.withRef { this._orig.executeQuery()?.let(this::_prepareRS) } + + override fun getResultSet(): ResultSet? = + this._refcnt.withRef { this._orig.getResultSet()?.let(this::_prepareRS) } + + override fun getGeneratedKeys(): ResultSet? = + this._refcnt.withRef { this._orig.getGeneratedKeys()?.let(this::_prepareRS) } + + override fun unwrap(iface: Class): T? = + iface.cast(this) ?: this._orig.unwrap(iface) + + override fun isWrapperFor(iface: Class<*>): Boolean = + iface.isInstance(this) || this._orig.isWrapperFor(iface) + + override fun cancel() = + this._refcnt.withRef(this._orig::cancel) + + override fun executeUpdate(): Int = + this._refcnt.withRef(this._orig::executeUpdate) + + override fun setNull(parameterIndex: Int, sqlType: Int) = + this._refcnt.withRef { this._orig.setNull(parameterIndex, sqlType) } + + override fun setBoolean(parameterIndex: Int, x: Boolean) = + this._refcnt.withRef { this._orig.setBoolean(parameterIndex, x) } + + override fun setByte(parameterIndex: Int, x: Byte) = + this._refcnt.withRef { this._orig.setByte(parameterIndex, x) } + + override fun setShort(parameterIndex: Int, x: Short) = + this._refcnt.withRef { this._orig.setShort(parameterIndex, x) } + + override fun setInt(parameterIndex: Int, x: Int) = + this._refcnt.withRef { this._orig.setInt(parameterIndex, x) } + + override fun setLong(parameterIndex: Int, x: Long) = + this._refcnt.withRef { this._orig.setLong(parameterIndex, x) } + + override fun setFloat(parameterIndex: Int, x: Float) = + this._refcnt.withRef { this._orig.setFloat(parameterIndex, x) } + + override fun setDouble(parameterIndex: Int, x: Double) = + this._refcnt.withRef { this._orig.setDouble(parameterIndex, x) } + + override fun setBigDecimal(parameterIndex: Int, x: BigDecimal?) = + this._refcnt.withRef { this._orig.setBigDecimal(parameterIndex, x) } + + override fun setString(parameterIndex: Int, x: String?) = + this._refcnt.withRef { this._orig.setString(parameterIndex, x) } + + override fun setBytes(parameterIndex: Int, x: ByteArray?) = + this._refcnt.withRef { this._orig.setBytes(parameterIndex, x) } + + override fun setDate(parameterIndex: Int, x: Date?) = + this._refcnt.withRef { this._orig.setDate(parameterIndex, x) } + + override fun setTime(parameterIndex: Int, x: Time?) = + this._refcnt.withRef { this._orig.setTime(parameterIndex, x) } + + override fun setTimestamp(parameterIndex: Int, x: Timestamp?) = + this._refcnt.withRef { this._orig.setTimestamp(parameterIndex, x) } + + override fun setAsciiStream(parameterIndex: Int, x: InputStream?, length: Int) = + this._refcnt.withRef { this._orig.setAsciiStream(parameterIndex, x, length) } + + @Deprecated("Deprecated in Java") + override fun setUnicodeStream(parameterIndex: Int, x: InputStream?, length: Int) = + this._refcnt.withRef { this._orig.setUnicodeStream(parameterIndex, x, length) } + + override fun setBinaryStream(parameterIndex: Int, x: InputStream?, length: Int) = + this._refcnt.withRef { this._orig.setBinaryStream(parameterIndex, x) } + + override fun clearParameters() = + this._refcnt.withRef(this._orig::clearParameters) + + override fun setObject(parameterIndex: Int, x: Any?, targetSqlType: Int) = + this._refcnt.withRef { this._orig.setObject(parameterIndex, x, targetSqlType) } + + override fun setObject(parameterIndex: Int, x: Any?) = + this._refcnt.withRef { this._orig.setObject(parameterIndex, x) } + + override fun execute(): Boolean = + this._refcnt.withRef(this._orig::execute) + + override fun addBatch() = + this._refcnt.withRef(this._orig::addBatch) + + override fun setCharacterStream(parameterIndex: Int, reader: Reader?, length: Int) = + this._refcnt.withRef { this._orig.setCharacterStream(parameterIndex, reader, length) } + + override fun setRef(parameterIndex: Int, x: Ref?) = + this._refcnt.withRef { this._orig.setRef(parameterIndex, x) } + + override fun setBlob(parameterIndex: Int, x: Blob?) = + this._refcnt.withRef { this._orig.setBlob(parameterIndex, x) } + + override fun setClob(parameterIndex: Int, x: Clob?) = + this._refcnt.withRef { this._orig.setClob(parameterIndex, x) } + + override fun setArray(parameterIndex: Int, x: Array?) = + this._refcnt.withRef { this._orig.setArray(parameterIndex, x) } + + override fun getMetaData(): ResultSetMetaData? = + this._refcnt.withRef(this._orig::getMetaData) + + override fun setDate(parameterIndex: Int, x: Date?, cal: Calendar?) = + this._refcnt.withRef { this._orig.setDate(parameterIndex, x) } + + override fun setTime(parameterIndex: Int, x: Time?, cal: Calendar?) = + this._refcnt.withRef { this._orig.setTime(parameterIndex, x) } + + override fun setTimestamp(parameterIndex: Int, x: Timestamp?, cal: Calendar?) = + this._refcnt.withRef { this._orig.setTimestamp(parameterIndex, x) } + + override fun setNull(parameterIndex: Int, sqlType: Int, typeName: String?) = + this._refcnt.withRef { this._orig.setNull(parameterIndex, sqlType, typeName) } + + override fun setURL(parameterIndex: Int, x: URL?) = + this._refcnt.withRef { this._orig.setURL(parameterIndex, x) } + + override fun getParameterMetaData(): ParameterMetaData? = + this._refcnt.withRef(this._orig::getParameterMetaData) + + override fun setRowId(parameterIndex: Int, x: RowId?) = + this._refcnt.withRef { this._orig.setRowId(parameterIndex, x) } + + override fun setNString(parameterIndex: Int, value: String?) = + this._refcnt.withRef { this._orig.setNString(parameterIndex, value) } + + override fun setNCharacterStream(parameterIndex: Int, value: Reader?, length: Long) = + this._refcnt.withRef { this._orig.setNCharacterStream(parameterIndex, value, length) } + + override fun setNClob(parameterIndex: Int, value: NClob?) = + this._refcnt.withRef { this._orig.setNClob(parameterIndex, value) } + + override fun setClob(parameterIndex: Int, reader: Reader?, length: Long) = + this._refcnt.withRef { this._orig.setClob(parameterIndex, reader, length) } + + override fun setBlob(parameterIndex: Int, inputStream: InputStream?, length: Long) = + this._refcnt.withRef { this._orig.setBlob(parameterIndex, inputStream, length) } + + override fun setNClob(parameterIndex: Int, reader: Reader?, length: Long) = + this._refcnt.withRef { this._orig.setNClob(parameterIndex, reader, length) } + + override fun setSQLXML(parameterIndex: Int, xmlObject: SQLXML?) = + this._refcnt.withRef { this._orig.setSQLXML(parameterIndex, xmlObject) } + + override fun setObject(parameterIndex: Int, x: Any?, targetSqlType: Int, scaleOrLength: Int) = + this._refcnt.withRef { this._orig.setObject(parameterIndex, x, targetSqlType, scaleOrLength) } + + override fun setAsciiStream(parameterIndex: Int, x: InputStream?, length: Long) = + this._refcnt.withRef { this._orig.setAsciiStream(parameterIndex, x, length) } + + override fun setBinaryStream(parameterIndex: Int, x: InputStream?, length: Long) = + this._refcnt.withRef { this._orig.setBinaryStream(parameterIndex, x, length) } + + override fun setCharacterStream(parameterIndex: Int, reader: Reader?, length: Long) = + this._refcnt.withRef { this._orig.setCharacterStream(parameterIndex, reader, length) } + + override fun setAsciiStream(parameterIndex: Int, x: InputStream?) = + this._refcnt.withRef { this._orig.setAsciiStream(parameterIndex, x) } + + override fun setBinaryStream(parameterIndex: Int, x: InputStream?) = + this._refcnt.withRef { this._orig.setBinaryStream(parameterIndex, x) } + + override fun setCharacterStream(parameterIndex: Int, reader: Reader?) = + this._refcnt.withRef { this._orig.setCharacterStream(parameterIndex, reader) } + + override fun setNCharacterStream(parameterIndex: Int, value: Reader?) = + this._refcnt.withRef { this._orig.setNCharacterStream(parameterIndex, value) } + + override fun setClob(parameterIndex: Int, reader: Reader?) = + this._refcnt.withRef { this._orig.setClob(parameterIndex, reader) } + + override fun setBlob(parameterIndex: Int, inputStream: InputStream?) = + this._refcnt.withRef { this._orig.setBlob(parameterIndex, inputStream) } + + override fun setNClob(parameterIndex: Int, reader: Reader?) = + this._refcnt.withRef { this._orig.setNClob(parameterIndex, reader) } + + override fun executeQuery(sql: String?): ResultSet? = + throw SQLException("executeQuery(String) not allowed on PreparedStatement") + + override fun executeUpdate(sql: String?): Int = + throw SQLException("executeUpdate(String) not allowed on PreparedStatement") + + override fun getMaxFieldSize(): Int = + this._refcnt.withRef(this._orig::getMaxFieldSize) + + override fun setMaxFieldSize(max: Int) = + this._refcnt.withRef { this._orig.setMaxFieldSize(max) } + + override fun getMaxRows(): Int = + this._refcnt.withRef(this._orig::getMaxRows) + + override fun setMaxRows(max: Int) = + this._refcnt.withRef { this._orig.setMaxRows(max) } + + override fun setEscapeProcessing(enable: Boolean) = + this._refcnt.withRef { this._orig.setEscapeProcessing(enable) } + + override fun getQueryTimeout(): Int = + this._refcnt.withRef(this._orig::getQueryTimeout) + + override fun setQueryTimeout(seconds: Int) = + this._refcnt.withRef { this._orig.setQueryTimeout(seconds) } + + override fun getWarnings(): SQLWarning? = + this._refcnt.withRef(this._orig::getWarnings) + + override fun clearWarnings() = + this._refcnt.withRef(this._orig::clearWarnings) + + override fun setCursorName(name: String?) = + this._refcnt.withRef { this._orig.setCursorName(name) } + + override fun execute(sql: String?): Boolean = + throw SQLException("execute(String) not allowed on PreparedStatement") + + override fun getUpdateCount(): Int = + this._refcnt.withRef(this._orig::getUpdateCount) + + override fun getMoreResults() = + this._refcnt.withRef(this._orig::getMoreResults) + + override fun setFetchDirection(direction: Int) = + this._refcnt.withRef { this._orig.setFetchDirection(direction) } + + override fun getFetchDirection() = + this._refcnt.withRef(this._orig::getFetchDirection) + + override fun setFetchSize(rows: Int) = + this._refcnt.withRef { this._orig.setFetchSize(rows) } + + override fun getFetchSize() = + this._refcnt.withRef(this._orig::getFetchSize) + + override fun getResultSetConcurrency() = + this._refcnt.withRef(this._orig::getFetchSize) + + override fun getResultSetType() = + this._refcnt.withRef(this._orig::getFetchSize) + + override fun addBatch(sql: String?) = + throw SQLException("addBatch(String) not allowed on PreparedStatement") + + override fun clearBatch() = + this._refcnt.withRef(this._orig::clearBatch) + + override fun executeBatch(): IntArray? = + this._refcnt.withRef(this._orig::executeBatch) + + override fun getConnection(): Connection? = + this._orig.getConnection() + + override fun getMoreResults(current: Int): Boolean = + this._refcnt.withRef { this._orig.getMoreResults(current) } + + override fun executeUpdate(sql: String?, autoGeneratedKeys: Int): Int = + throw SQLException("executeUpdate(String, int) not allowed on PreparedStatement") + + override fun executeUpdate(sql: String?, columnIndexes: IntArray?): Int = + throw SQLException("executeUpdate(String, int[]) not allowed on PreparedStatement") + + override fun executeUpdate(sql: String?, columnNames: kotlin.Array?): Int = + throw SQLException("executeUpdate(String, String[]) not allowed on PreparedStatement") + + override fun execute(sql: String?, autoGeneratedKeys: Int): Boolean = + throw SQLException("execute(String, int) not allowed on PreparedStatement") + + override fun execute(sql: String?, columnIndexes: IntArray?): Boolean = + throw SQLException("executeUpdate(String, int[]) not allowed on PreparedStatement") + + override fun execute(sql: String?, columnNames: kotlin.Array?): Boolean = + throw SQLException("execute(String, String[]) not allowed on PreparedStatement") + + override fun getResultSetHoldability(): Int = + this._refcnt.withRef(this._orig::getResultSetHoldability) + + override fun isClosed(): Boolean = + this._refcnt.withRef(this._orig::isClosed) + + override fun setPoolable(poolable: Boolean) { + if (poolable) + return + throw SQLException("Disabling pooling not allowed on prepared statements driven by PreparedStatementsCompilationCache") + } + + override fun isPoolable(): Boolean = true +} \ No newline at end of file diff --git a/src/jvmMain/kotlin/ru/langrafhomyak/db/jdbc_resources_manager/ResultSetWrapper.kt b/src/jvmMain/kotlin/ru/langrafhomyak/db/jdbc_resources_manager/ResultSetWrapper.kt new file mode 100644 index 0000000..ce462c5 --- /dev/null +++ b/src/jvmMain/kotlin/ru/langrafhomyak/db/jdbc_resources_manager/ResultSetWrapper.kt @@ -0,0 +1,29 @@ +package ru.langrafhomyak.db.jdbc_resources_manager + +import java.sql.Clob +import java.sql.ResultSet +import java.sql.ResultSetMetaData +import java.sql.SQLType +import java.sql.Statement + +internal abstract class ResultSetWrapper(protected val _orig: ResultSet) : ResultSet by _orig { + abstract override fun close() + + abstract override fun getStatement(): Statement? + + override fun updateObject(columnIndex: Int, x: Any?, targetSqlType: SQLType?, scaleOrLength: Int) { + this._orig.updateObject(columnIndex, x, targetSqlType, scaleOrLength) + } + + override fun updateObject(columnLabel: String?, x: Any?, targetSqlType: SQLType?, scaleOrLength: Int) { + this._orig.updateObject(columnLabel, x, targetSqlType, scaleOrLength) + } + + override fun updateObject(columnIndex: Int, x: Any?, targetSqlType: SQLType?) { + this._orig.updateObject(columnIndex, x, targetSqlType) + } + + override fun updateObject(columnLabel: String?, x: Any?, targetSqlType: SQLType?) { + this._orig.updateObject(columnLabel, x, targetSqlType) + } +} \ No newline at end of file