package org.postgresql.core.v3;
import org.postgresql.core.Oid;
import org.postgresql.core.PGStream;
import org.postgresql.core.ParameterList;
import org.postgresql.core.Utils;
import org.postgresql.geometric.PGbox;
import org.postgresql.geometric.PGpoint;
import org.postgresql.jdbc.UUIDArrayAssistant;
import org.postgresql.util.ByteConverter;
import org.postgresql.util.GT;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;
import org.postgresql.util.StreamWrapper;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Arrays;
class SimpleParameterList implements V3ParameterList {
private static final byte IN = 1;
private static final byte OUT = 2;
private static final byte INOUT = IN | OUT;
private static final byte TEXT = 0;
private static final byte BINARY = 4;
SimpleParameterList(int paramCount, TypeTransferModeRegistry transferModeRegistry) {
this.paramValues = new Object[paramCount];
this.paramTypes = new int[paramCount];
this.encoded = new byte[paramCount][];
this.flags = new byte[paramCount];
this.transferModeRegistry = transferModeRegistry;
}
@Override
public void registerOutParameter(int index, int sqlType) throws SQLException {
if (index < 1 || index > paramValues.length) {
throw new PSQLException(
GT.tr("The column index is out of range: {0}, number of columns: {1}.",
index, paramValues.length),
PSQLState.INVALID_PARAMETER_VALUE);
}
flags[index - 1] |= OUT;
}
private void bind(int index, Object value, int oid, byte binary) throws SQLException {
if (index < 1 || index > paramValues.length) {
throw new PSQLException(
GT.tr("The column index is out of range: {0}, number of columns: {1}.",
index, paramValues.length),
PSQLState.INVALID_PARAMETER_VALUE);
}
--index;
encoded[index] = null;
paramValues[index] = value;
flags[index] = (byte) (direction(index) | IN | binary);
if (oid == Oid.UNSPECIFIED && paramTypes[index] != Oid.UNSPECIFIED && value == NULL_OBJECT) {
return;
}
paramTypes[index] = oid;
pos = index + 1;
}
public int getParameterCount() {
return paramValues.length;
}
public int getOutParameterCount() {
int count = 0;
for (int i = 0; i < paramTypes.length; i++) {
if ((direction(i) & OUT) == OUT) {
count++;
}
}
if (count == 0) {
count = 1;
}
return count;
}
public int getInParameterCount() {
int count = 0;
for (int i = 0; i < paramTypes.length; i++) {
if (direction(i) != OUT) {
count++;
}
}
return count;
}
public void setIntParameter(int index, int value) throws SQLException {
byte[] data = new byte[4];
ByteConverter.int4(data, 0, value);
bind(index, data, Oid.INT4, BINARY);
}
public void setLiteralParameter(int index, String value, int oid) throws SQLException {
bind(index, value, oid, TEXT);
}
public void setStringParameter(int index, String value, int oid) throws SQLException {
bind(index, value, oid, TEXT);
}
public void setBinaryParameter(int index, byte[] value, int oid) throws SQLException {
bind(index, value, oid, BINARY);
}
@Override
public void setBytea(int index, byte[] data, int offset, int length) throws SQLException {
bind(index, new StreamWrapper(data, offset, length), Oid.BYTEA, BINARY);
}
@Override
public void setBytea(int index, InputStream stream, int length) throws SQLException {
bind(index, new StreamWrapper(stream, length), Oid.BYTEA, BINARY);
}
@Override
public void setBytea(int index, InputStream stream) throws SQLException {
bind(index, new StreamWrapper(stream), Oid.BYTEA, BINARY);
}
@Override
public void setText(int index, InputStream stream) throws SQLException {
bind(index, new StreamWrapper(stream), Oid.TEXT, TEXT);
}
@Override
public void setNull(int index, int oid) throws SQLException {
byte binaryTransfer = TEXT;
if (transferModeRegistry.useBinaryForReceive(oid)) {
binaryTransfer = BINARY;
}
bind(index, NULL_OBJECT, oid, binaryTransfer);
}
@Override
public String toString(int index, boolean standardConformingStrings) {
--index;
if (paramValues[index] == null) {
return "?";
} else if (paramValues[index] == NULL_OBJECT) {
return "NULL";
} else if ((flags[index] & BINARY) == BINARY) {
switch (paramTypes[index]) {
case Oid.INT2:
short s = ByteConverter.int2((byte[]) paramValues[index], 0);
return Short.toString(s);
case Oid.INT4:
int i = ByteConverter.int4((byte[]) paramValues[index], 0);
return Integer.toString(i);
case Oid.INT8:
long l = ByteConverter.int8((byte[]) paramValues[index], 0);
return Long.toString(l);
case Oid.FLOAT4:
float f = ByteConverter.float4((byte[]) paramValues[index], 0);
if (Float.isNaN(f)) {
return "'NaN'::real";
}
return Float.toString(f);
case Oid.FLOAT8:
double d = ByteConverter.float8((byte[]) paramValues[index], 0);
if (Double.isNaN(d)) {
return "'NaN'::double precision";
}
return Double.toString(d);
case Oid.UUID:
String uuid =
new UUIDArrayAssistant().buildElement((byte[]) paramValues[index], 0, 16).toString();
return "'" + uuid + "'::uuid";
case Oid.POINT:
PGpoint pgPoint = new PGpoint();
pgPoint.setByteValue((byte[]) paramValues[index], 0);
return "'" + pgPoint.toString() + "'::point";
case Oid.BOX:
PGbox pgBox = new PGbox();
pgBox.setByteValue((byte[]) paramValues[index], 0);
return "'" + pgBox.toString() + "'::box";
}
return "?";
} else {
String param = paramValues[index].toString();
StringBuilder p = new StringBuilder(3 + (param.length() + 10) / 10 * 11);
p.append('\'');
try {
p = Utils.escapeLiteral(p, param, standardConformingStrings);
} catch (SQLException sqle) {
p.append(param);
}
p.append('\'');
int paramType = paramTypes[index];
if (paramType == Oid.TIMESTAMP) {
p.append("::timestamp");
} else if (paramType == Oid.TIMESTAMPTZ) {
p.append("::timestamp with time zone");
} else if (paramType == Oid.TIME) {
p.append("::time");
} else if (paramType == Oid.TIMETZ) {
p.append("::time with time zone");
} else if (paramType == Oid.DATE) {
p.append("::date");
} else if (paramType == Oid.INTERVAL) {
p.append("::interval");
} else if (paramType == Oid.NUMERIC) {
p.append("::numeric");
}
return p.toString();
}
}
@Override
public void () throws SQLException {
for (int i = 0; i < paramTypes.length; ++i) {
if (direction(i) != OUT && paramValues[i] == null) {
throw new PSQLException(GT.tr("No value specified for parameter {0}.", i + 1),
PSQLState.INVALID_PARAMETER_VALUE);
}
}
}
@Override
public void convertFunctionOutParameters() {
for (int i = 0; i < paramTypes.length; ++i) {
if (direction(i) == OUT) {
paramTypes[i] = Oid.VOID;
paramValues[i] = "null";
}
}
}
private static void streamBytea(PGStream pgStream, StreamWrapper wrapper) throws IOException {
byte[] rawData = wrapper.getBytes();
if (rawData != null) {
pgStream.send(rawData, wrapper.getOffset(), wrapper.getLength());
return;
}
pgStream.sendStream(wrapper.getStream(), wrapper.getLength());
}
public int[] getTypeOIDs() {
return paramTypes;
}
int getTypeOID(int index) {
return paramTypes[index - 1];
}
boolean hasUnresolvedTypes() {
for (int paramType : paramTypes) {
if (paramType == Oid.UNSPECIFIED) {
return true;
}
}
return false;
}
void setResolvedType(int index, int oid) {
if (paramTypes[index - 1] == Oid.UNSPECIFIED) {
paramTypes[index - 1] = oid;
} else if (paramTypes[index - 1] != oid) {
throw new IllegalArgumentException("Can't change resolved type for param: " + index + " from "
+ paramTypes[index - 1] + " to " + oid);
}
}
boolean isNull(int index) {
return (paramValues[index - 1] == NULL_OBJECT);
}
boolean isBinary(int index) {
return (flags[index - 1] & BINARY) != 0;
}
private byte direction(int index) {
return (byte) (flags[index] & INOUT);
}
int getV3Length(int index) {
--index;
if (paramValues[index] == NULL_OBJECT) {
throw new IllegalArgumentException("can't getV3Length() on a null parameter");
}
if (paramValues[index] instanceof byte[]) {
return ((byte[]) paramValues[index]).length;
}
if (paramValues[index] instanceof StreamWrapper) {
return ((StreamWrapper) paramValues[index]).getLength();
}
if (encoded[index] == null) {
encoded[index] = Utils.encodeUTF8(paramValues[index].toString());
}
return encoded[index].length;
}
void writeV3Value(int index, PGStream pgStream) throws IOException {
--index;
if (paramValues[index] == NULL_OBJECT) {
throw new IllegalArgumentException("can't writeV3Value() on a null parameter");
}
if (paramValues[index] instanceof byte[]) {
pgStream.send((byte[]) paramValues[index]);
return;
}
if (paramValues[index] instanceof StreamWrapper) {
streamBytea(pgStream, (StreamWrapper) paramValues[index]);
return;
}
if (encoded[index] == null) {
encoded[index] = Utils.encodeUTF8((String) paramValues[index]);
}
pgStream.send(encoded[index]);
}
public ParameterList copy() {
SimpleParameterList newCopy = new SimpleParameterList(paramValues.length, transferModeRegistry);
System.arraycopy(paramValues, 0, newCopy.paramValues, 0, paramValues.length);
System.arraycopy(paramTypes, 0, newCopy.paramTypes, 0, paramTypes.length);
System.arraycopy(flags, 0, newCopy.flags, 0, flags.length);
newCopy.pos = pos;
return newCopy;
}
public void clear() {
Arrays.fill(paramValues, null);
Arrays.fill(paramTypes, 0);
Arrays.fill(encoded, null);
Arrays.fill(flags, (byte) 0);
pos = 0;
}
public SimpleParameterList[] getSubparams() {
return null;
}
public Object[] getValues() {
return paramValues;
}
public int[] getParamTypes() {
return paramTypes;
}
public byte[] getFlags() {
return flags;
}
public byte[][] getEncoding() {
return encoded;
}
@Override
public void appendAll(ParameterList list) throws SQLException {
if (list instanceof org.postgresql.core.v3.SimpleParameterList ) {
SimpleParameterList spl = (SimpleParameterList) list;
int inParamCount = spl.getInParameterCount();
if ((pos + inParamCount) > paramValues.length) {
throw new PSQLException(
GT.tr("Added parameters index out of range: {0}, number of columns: {1}.",
(pos + inParamCount), paramValues.length),
PSQLState.INVALID_PARAMETER_VALUE);
}
System.arraycopy(spl.getValues(), 0, this.paramValues, pos, inParamCount);
System.arraycopy(spl.getParamTypes(), 0, this.paramTypes, pos, inParamCount);
System.arraycopy(spl.getFlags(), 0, this.flags, pos, inParamCount);
System.arraycopy(spl.getEncoding(), 0, this.encoded, pos, inParamCount);
pos += inParamCount;
}
}
@Override
public String toString() {
StringBuilder ts = new StringBuilder("<[");
if (paramValues.length > 0) {
ts.append(toString(1, true));
for (int c = 2; c <= paramValues.length; c++) {
ts.append(" ,").append(toString(c, true));
}
}
ts.append("]>");
return ts.toString();
}
private final Object[] paramValues;
private final int[] paramTypes;
private final byte[] flags;
private final byte[][] encoded;
private final TypeTransferModeRegistry transferModeRegistry;
private static final Object NULL_OBJECT = new Object();
private int pos = 0;
}