package org.postgresql.core.v3;
import org.postgresql.core.Field;
import org.postgresql.core.NativeQuery;
import org.postgresql.core.Oid;
import org.postgresql.core.ParameterList;
import org.postgresql.core.Query;
import org.postgresql.core.SqlCommand;
import org.postgresql.core.Utils;
import org.postgresql.jdbc.PgResultSet;
import java.lang.ref.PhantomReference;
import java.util.BitSet;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
class SimpleQuery implements Query {
private static final Logger LOGGER = Logger.getLogger(SimpleQuery.class.getName());
SimpleQuery(SimpleQuery src) {
this(src.nativeQuery, src.transferModeRegistry, src.sanitiserDisabled);
}
SimpleQuery(NativeQuery query, TypeTransferModeRegistry transferModeRegistry,
boolean sanitiserDisabled) {
this.nativeQuery = query;
this.transferModeRegistry = transferModeRegistry;
this.sanitiserDisabled = sanitiserDisabled;
}
public ParameterList createParameterList() {
if (nativeQuery.bindPositions.length == 0) {
return NO_PARAMETERS;
}
return new SimpleParameterList(getBindCount(), transferModeRegistry);
}
public String toString(ParameterList parameters) {
return nativeQuery.toString(parameters);
}
public String toString() {
return toString(null);
}
public void close() {
unprepare();
}
public SimpleQuery[] getSubqueries() {
return null;
}
public int getMaxResultRowSize() {
if (cachedMaxResultRowSize != null) {
return cachedMaxResultRowSize;
}
if (!this.statementDescribed) {
throw new IllegalStateException(
"Cannot estimate result row size on a statement that is not described");
}
int maxResultRowSize = 0;
if (fields != null) {
for (Field f : fields) {
final int fieldLength = f.getLength();
if (fieldLength < 1 || fieldLength >= 65535) {
maxResultRowSize = -1;
break;
}
maxResultRowSize += fieldLength;
}
}
cachedMaxResultRowSize = maxResultRowSize;
return maxResultRowSize;
}
public String getNativeSql() {
return nativeQuery.nativeSql;
}
void setStatementName(String statementName, short deallocateEpoch) {
assert statementName != null : "statement name should not be null";
this.statementName = statementName;
this.encodedStatementName = Utils.encodeUTF8(statementName);
this.deallocateEpoch = deallocateEpoch;
}
void setPrepareTypes(int[] paramTypes) {
for (int i = 0; i < paramTypes.length; i++) {
int paramType = paramTypes[i];
if (paramType == Oid.UNSPECIFIED) {
if (this.unspecifiedParams == null) {
this.unspecifiedParams = new BitSet();
}
this.unspecifiedParams.set(i);
}
}
if (this.preparedTypes == null) {
this.preparedTypes = paramTypes.clone();
return;
}
System.arraycopy(paramTypes, 0, this.preparedTypes, 0, paramTypes.length);
}
int[] getPrepareTypes() {
return preparedTypes;
}
String getStatementName() {
return statementName;
}
boolean isPreparedFor(int[] paramTypes, short deallocateEpoch) {
if (statementName == null || preparedTypes == null) {
return false;
}
if (this.deallocateEpoch != deallocateEpoch) {
return false;
}
assert paramTypes.length == preparedTypes.length
: String.format("paramTypes:%1$d preparedTypes:%2$d", paramTypes.length,
preparedTypes.length);
BitSet unspecified = this.unspecifiedParams;
for (int i = 0; i < paramTypes.length; ++i) {
int paramType = paramTypes[i];
int preparedType = preparedTypes[i];
if (paramType != preparedType
&& (paramType != Oid.UNSPECIFIED
|| unspecified == null
|| !unspecified.get(i))) {
if (LOGGER.isLoggable(Level.FINER)) {
LOGGER.log(Level.FINER,
"Statement {0} does not match new parameter types. Will have to un-prepare it and parse once again."
+ " To avoid performance issues, use the same data type for the same bind position. Bind index (1-based) is {1},"
+ " preparedType was {2} (after describe {3}), current bind type is {4}",
new Object[]{statementName, i + 1,
Oid.toString(unspecified != null && unspecified.get(i) ? 0 : preparedType),
Oid.toString(preparedType), Oid.toString(paramType)});
}
return false;
}
}
return true;
}
boolean hasUnresolvedTypes() {
if (preparedTypes == null) {
return true;
}
return this.unspecifiedParams != null && !this.unspecifiedParams.isEmpty();
}
byte[] getEncodedStatementName() {
return encodedStatementName;
}
void setFields(Field[] fields) {
this.fields = fields;
this.resultSetColumnNameIndexMap = null;
this.cachedMaxResultRowSize = null;
this.needUpdateFieldFormats = fields != null;
this.hasBinaryFields = false;
}
Field[] getFields() {
return fields;
}
boolean needUpdateFieldFormats() {
if (needUpdateFieldFormats) {
needUpdateFieldFormats = false;
return true;
}
return false;
}
public void resetNeedUpdateFieldFormats() {
needUpdateFieldFormats = fields != null;
}
public boolean hasBinaryFields() {
return hasBinaryFields;
}
public void setHasBinaryFields(boolean hasBinaryFields) {
this.hasBinaryFields = hasBinaryFields;
}
boolean isPortalDescribed() {
return portalDescribed;
}
void setPortalDescribed(boolean portalDescribed) {
this.portalDescribed = portalDescribed;
this.cachedMaxResultRowSize = null;
}
public boolean isStatementDescribed() {
return statementDescribed;
}
void setStatementDescribed(boolean statementDescribed) {
this.statementDescribed = statementDescribed;
this.cachedMaxResultRowSize = null;
}
public boolean isEmpty() {
return getNativeSql().isEmpty();
}
void setCleanupRef(PhantomReference<?> cleanupRef) {
if (this.cleanupRef != null) {
this.cleanupRef.clear();
this.cleanupRef.enqueue();
}
this.cleanupRef = cleanupRef;
}
void unprepare() {
if (cleanupRef != null) {
cleanupRef.clear();
cleanupRef.enqueue();
cleanupRef = null;
}
if (this.unspecifiedParams != null) {
this.unspecifiedParams.clear();
}
statementName = null;
encodedStatementName = null;
fields = null;
this.resultSetColumnNameIndexMap = null;
portalDescribed = false;
statementDescribed = false;
cachedMaxResultRowSize = null;
}
public int getBatchSize() {
return 1;
}
NativeQuery getNativeQuery() {
return nativeQuery;
}
public final int getBindCount() {
return nativeQuery.bindPositions.length * getBatchSize();
}
private Map<String, Integer> resultSetColumnNameIndexMap;
@Override
public Map<String, Integer> getResultSetColumnNameIndexMap() {
Map<String, Integer> columnPositions = this.resultSetColumnNameIndexMap;
if (columnPositions == null && fields != null) {
columnPositions =
PgResultSet.createColumnNameIndexMap(fields, sanitiserDisabled);
if (statementName != null) {
this.resultSetColumnNameIndexMap = columnPositions;
}
}
return columnPositions;
}
@Override
public SqlCommand getSqlCommand() {
return nativeQuery.getCommand();
}
private final NativeQuery nativeQuery;
private final TypeTransferModeRegistry transferModeRegistry;
private String statementName;
private byte[] encodedStatementName;
private Field[] fields;
private boolean needUpdateFieldFormats;
private boolean hasBinaryFields;
private boolean portalDescribed;
private boolean statementDescribed;
private final boolean sanitiserDisabled;
private PhantomReference<?> cleanupRef;
private int[] preparedTypes;
private BitSet unspecifiedParams;
private short deallocateEpoch;
private Integer cachedMaxResultRowSize;
static final SimpleParameterList NO_PARAMETERS = new SimpleParameterList(0, null);
}