com.mysql.cj.xdevapi.PreparableStatement Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mysql-connector-j Show documentation
Show all versions of mysql-connector-j Show documentation
JDBC Type 4 driver for MySQL.
/*
* Copyright (c) 2019, 2024, Oracle and/or its affiliates.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by
* the Free Software Foundation.
*
* This program is designed to work with certain software that is licensed under separate terms, as designated in a particular file or component or in
* included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the
* separately licensed software that they have either included with the program or referenced in the documentation.
*
* Without limiting anything contained in the foregoing, this file, which is part of MySQL Connector/J, is also subject to the Universal FOSS Exception,
* version 1.0, a copy of which can be found at http://oss.oracle.com/licenses/universal-foss-exception.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.mysql.cj.xdevapi;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import com.mysql.cj.MysqlxSession;
import com.mysql.cj.protocol.x.XMessage;
import com.mysql.cj.protocol.x.XMessageBuilder;
import com.mysql.cj.protocol.x.XProtocolError;
/**
* Abstract class, common to all X DevAPI statement classes that can be prepared.
*
* @param
* result interface
*/
public abstract class PreparableStatement {
protected enum PreparedState {
UNSUPPORTED, // Preparing statements is completely unsupported in the server currently being used.
UNPREPARED, // Statement is not prepared yet, next execution will run unprepared.
SUSPENDED, // Preparing statements is currently suspended but it is expected to resume sometime later.
PREPARED, // The statement is prepared and ready for execution.
PREPARE, // The statement shall be prepared on next execution.
DEALLOCATE, // The statement shall be deallocated on next execution.
REPREPARE; // The statement shall be deallocated and immediately re-prepared on next execution.
}
protected int preparedStatementId = 0;
protected PreparedState preparedState = PreparedState.UNPREPARED;
protected MysqlxSession mysqlxSession;
/**
* Helper method to return an {@link XMessageBuilder} instance from {@link MysqlxSession} in use.
*
* @return
* the {@link XMessageBuilder} instance from current {@link MysqlxSession}
*/
protected XMessageBuilder getMessageBuilder() {
return (XMessageBuilder) this.mysqlxSession.getMessageBuilder();
}
/**
* Mark this preparable statement to be deallocated on next execution, if it is currently prepared, or cancel the next prepare.
*/
protected void resetPrepareState() {
if (this.preparedState == PreparedState.PREPARED || this.preparedState == PreparedState.REPREPARE) {
this.preparedState = PreparedState.DEALLOCATE;
} else if (this.preparedState == PreparedState.PREPARE) {
this.preparedState = PreparedState.UNPREPARED;
}
}
/**
* Mark this preparable statement to be deallocated and re-prepared on next execution, if it is currently prepared.
*/
protected void setReprepareState() {
if (this.preparedState == PreparedState.PREPARED) {
this.preparedState = PreparedState.REPREPARE;
}
}
/**
* Executes synchronously this statement either directly or using prepared statements if:
* 1. Prepared statements are supported by the server.
* 2. The statement is executed repeatedly without changing its structure.
*
* @return
* the object returned from the low level statement execution
*/
public RES_T execute() {
for (;;) {
switch (this.preparedState) {
case UNSUPPORTED:
// Fall-back to non-prepared statement execution.
return executeStatement();
case UNPREPARED:
// Execute as non-prepared this time but mark as to be prepared on next execution.
RES_T result = executeStatement();
this.preparedState = PreparedState.PREPARE;
return result;
case SUSPENDED:
// An error occurred in some previous prepare. If the server doesn't support prepared statements then mark it as unsupported, otherwise wait
// until the server is ready to accept new prepare attempts, executing non-prepared in the meantime.
if (!this.mysqlxSession.supportsPreparedStatements()) {
this.preparedState = PreparedState.UNSUPPORTED;
} else if (this.mysqlxSession.readyForPreparingStatements()) {
this.preparedState = PreparedState.PREPARE;
} else {
return executeStatement();
}
break;
case PREPARE:
// Prepare this statement. If it succeeds then immediately follow with its execution, otherwise mark it as prepare suspended and let the
// following iteration to decide what to do next.
this.preparedState = prepareStatement() ? PreparedState.PREPARED : PreparedState.SUSPENDED;
break;
case PREPARED:
// The statement is already prepared and can be executed safely.
return executePreparedStatement();
case DEALLOCATE:
// Deallocate this statement and set it as unprepared so that it may be prepared later again.
deallocatePrepared();
this.preparedState = PreparedState.UNPREPARED;
break;
case REPREPARE:
// Deallocate this statement and set it as to prepare so that it gets prepared and executed right away.
deallocatePrepared();
this.preparedState = PreparedState.PREPARE;
break;
}
}
}
/**
* Executes the statement directly (non-prepared). Implementation is dependent on the statement type.
*
* @return
* the object returned from the lower level statement execution
*/
protected abstract RES_T executeStatement();
/**
* Returns the {@link XMessage} needed to prepare this statement. Implementation is dependent on the statement type.
*
* @return
* the {@link XMessage} that prepares this statement
*/
protected abstract XMessage getPrepareStatementXMessage();
/**
* Prepares a statement on the server to be later executed.
*
* @return
* true
if the statement was successfully prepared, false
otherwise
*/
private boolean prepareStatement() {
if (!this.mysqlxSession.supportsPreparedStatements()) {
return false;
}
try {
this.preparedStatementId = this.mysqlxSession.getNewPreparedStatementId(this);
this.mysqlxSession.query(getPrepareStatementXMessage(), new UpdateResultBuilder<>());
} catch (XProtocolError e) {
if (this.mysqlxSession.failedPreparingStatement(this.preparedStatementId, e)) {
this.preparedStatementId = 0;
return false;
}
this.preparedStatementId = 0;
throw e;
} catch (Throwable t) {
this.preparedStatementId = 0;
throw t;
}
return true;
}
/**
* Executes a previously server-prepared statement. Implementation is dependent on the statement type.
*
* @return
* the object returned from the lower level statement execution
*/
protected abstract RES_T executePreparedStatement();
/**
* Deallocate this prepared statement from current {@link MysqlxSession}.
*/
protected void deallocatePrepared() {
if (this.preparedState == PreparedState.PREPARED || this.preparedState == PreparedState.DEALLOCATE || this.preparedState == PreparedState.REPREPARE) {
try {
this.mysqlxSession.query(getMessageBuilder().buildPrepareDeallocate(this.preparedStatementId), new UpdateResultBuilder<>());
} finally {
this.mysqlxSession.freePreparedStatementId(this.preparedStatementId);
this.preparedStatementId = 0;
}
}
}
/**
* {@link PhantomReference} to track prepared statement ids. An instance of this class must be kept until the prepared statement is properly deallocated. If
* proper deallocation does not happen, this is used to identify abandoned prepared statements and proceed with its deallocation after the object is
* destructed by using a {@link ReferenceQueue}.
*/
public static class PreparableStatementFinalizer extends PhantomReference> {
int prepredStatementId;
public PreparableStatementFinalizer(PreparableStatement> referent, ReferenceQueue super PreparableStatement>> q, int preparedStatementId) {
super(referent, q);
this.prepredStatementId = preparedStatementId;
}
public int getPreparedStatementId() {
return this.prepredStatementId;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy