1
/*****************************************************************************
2
* Copyright (C) 2008 EnterpriseDB Corporation.
3
* Copyright (C) 2011 Stado Global Development Group.
5
* This file is part of Stado.
7
* Stado is free software: you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation, either version 3 of the License, or
10
* (at your option) any later version.
12
* Stado is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with Stado. If not, see <http://www.gnu.org/licenses/>.
20
* You can find Stado at http://www.stado.us
22
****************************************************************************/
23
package org.postgresql.driver.core.v3;
25
import java.io.InputStream;
26
import java.io.IOException;
27
import java.sql.SQLException;
28
import java.util.Arrays;
30
import org.postgresql.driver.core.*;
31
import org.postgresql.driver.util.GT;
32
import org.postgresql.driver.util.PSQLException;
33
import org.postgresql.driver.util.PSQLState;
34
import org.postgresql.driver.util.StreamWrapper;
37
* Parameter list for a single-statement V3 query.
39
* @author Oliver Jowett (oliver@opencloud.com)
41
class SimpleParameterList implements V3ParameterList {
43
private final static int IN = 1;
44
private final static int OUT = 2;
45
private final static int INOUT = IN|OUT;
47
SimpleParameterList(int paramCount, ProtocolConnectionImpl protoConnection) {
48
this.paramValues = new Object[paramCount];
49
this.paramTypes = new int[paramCount];
50
this.encoded = new byte[paramCount][];
51
this.direction = new int[paramCount];
52
this.protoConnection = protoConnection;
55
public void registerOutParameter( int index, int sqlType ) throws SQLException
57
if (index < 1 || index > paramValues.length)
58
throw new PSQLException(GT.tr("The column index is out of range: {0}, number of columns: {1}.", new Object[]{new Integer(index), new Integer(paramValues.length)}), PSQLState.INVALID_PARAMETER_VALUE );
60
direction[index-1] |= OUT;
63
private void bind(int index, Object value, int oid) throws SQLException {
64
if (index < 1 || index > paramValues.length)
65
throw new PSQLException(GT.tr("The column index is out of range: {0}, number of columns: {1}.", new Object[]{new Integer(index), new Integer(paramValues.length)}), PSQLState.INVALID_PARAMETER_VALUE );
69
encoded[index] = null;
70
paramValues[index] = value ;
71
direction[index] |= IN;
73
// If we are setting something to an UNSPECIFIED NULL, don't overwrite
74
// our existing type for it. We don't need the correct type info to
75
// send this value, and we don't want to overwrite and require a
77
if (oid == Oid.UNSPECIFIED && paramTypes[index] != Oid.UNSPECIFIED && value == NULL_OBJECT)
80
paramTypes[index] = oid;
83
public int getParameterCount()
85
return paramValues.length;
87
public int getOutParameterCount()
90
for( int i=paramTypes.length; --i >= 0;)
92
if ((direction[i] & OUT) == OUT )
97
// Every function has at least one output.
103
public int getInParameterCount()
106
for( int i=0; i< paramTypes.length;i++)
108
if (direction[i] != OUT )
116
public void setIntParameter(int index, int value) throws SQLException {
117
byte[] data = new byte[4];
118
data[3] = (byte)value;
119
data[2] = (byte)(value >> 8);
120
data[1] = (byte)(value >> 16);
121
data[0] = (byte)(value >> 24);
122
bind(index, data, Oid.INT4);
125
public void setLiteralParameter(int index, String value, int oid) throws SQLException {
126
bind(index, value, oid);
129
public void setStringParameter(int index, String value, int oid) throws SQLException {
130
bind(index, value, oid);
133
public void setBytea(int index, byte[] data, int offset, int length) throws SQLException {
134
bind(index, new StreamWrapper(data, offset, length), Oid.BYTEA);
137
public void setBytea(int index, InputStream stream, int length) throws SQLException {
138
bind(index, new StreamWrapper(stream, length), Oid.BYTEA);
141
public void setNull(int index, int oid) throws SQLException {
142
bind(index, NULL_OBJECT, oid);
145
public String toString(int index) {
147
if (paramValues[index] == null)
149
else if (paramValues[index] == NULL_OBJECT)
152
String param = paramValues[index].toString();
153
boolean hasBackslash = param.indexOf('\\') != -1;
155
// add room for quotes + potential escaping.
156
StringBuffer p = new StringBuffer(3 + param.length() * 11 / 10);
158
boolean standardConformingStrings = false;
159
boolean supportsEStringSyntax = false;
160
if (protoConnection != null) {
161
standardConformingStrings = protoConnection.getStandardConformingStrings();
162
supportsEStringSyntax = protoConnection.getServerVersion().compareTo("8.1") >= 0;
165
if (hasBackslash && !standardConformingStrings && supportsEStringSyntax)
170
p = Utils.appendEscapedLiteral(p, param, standardConformingStrings);
171
} catch (SQLException sqle) {
172
// This should only happen if we have an embedded null
173
// and there's not much we can do if we do hit one.
175
// The goal of toString isn't to be sent to the server,
176
// so we aren't 100% accurate (see StreamWrapper), put
177
// the unescaped version of the data.
186
public void checkAllParametersSet() throws SQLException {
187
for (int i = 0; i < paramTypes.length; ++i)
189
if (direction[i] != OUT && paramValues[i] == null)
190
throw new PSQLException(GT.tr("No value specified for parameter {0}.", new Integer(i + 1)), PSQLState.INVALID_PARAMETER_VALUE);
194
public void convertFunctionOutParameters()
196
for (int i=0; i<paramTypes.length; ++i)
198
if (direction[i] == OUT)
200
paramTypes[i] = Oid.VOID;
201
paramValues[i] = "null";
210
private static void streamBytea(PGStream pgStream, StreamWrapper wrapper) throws IOException {
211
byte[] rawData = wrapper.getBytes();
214
pgStream.Send(rawData, wrapper.getOffset(), wrapper.getLength());
218
pgStream.SendStream(wrapper.getStream(), wrapper.getLength());
221
public int[] getTypeOIDs() {
226
// Package-private V3 accessors
229
int getTypeOID(int index) {
230
return paramTypes[index-1];
233
boolean hasUnresolvedTypes() {
234
for (int i=0; i< paramTypes.length; i++) {
235
if (paramTypes[i] == Oid.UNSPECIFIED)
241
void setResolvedType(int index, int oid) {
242
// only allow overwriting an unknown value
243
if (paramTypes[index-1] == Oid.UNSPECIFIED) {
244
paramTypes[index-1] = oid;
245
} else if (paramTypes[index-1] != oid) {
246
throw new IllegalArgumentException("Can't change resolved type for param: " + index + " from " + paramTypes[index-1] + " to " + oid);
250
boolean isNull(int index) {
251
return (paramValues[index-1] == NULL_OBJECT);
254
boolean isBinary(int index) {
255
// Currently, only StreamWrapper uses the binary parameter form.
256
return (paramValues[index-1] instanceof StreamWrapper);
259
int getV3Length(int index) {
263
if (paramValues[index] == NULL_OBJECT)
264
throw new IllegalArgumentException("can't getV3Length() on a null parameter");
267
if (paramValues[index] instanceof byte[])
268
return ((byte[])paramValues[index]).length;
270
// Binary-format bytea?
271
if (paramValues[index] instanceof StreamWrapper)
272
return ((StreamWrapper)paramValues[index]).getLength();
275
if (encoded[index] == null)
277
// Encode value and compute actual length using UTF-8.
278
encoded[index] = Utils.encodeUTF8(paramValues[index].toString());
281
return encoded[index].length;
284
void writeV3Value(int index, PGStream pgStream) throws IOException {
288
if (paramValues[index] == NULL_OBJECT)
289
throw new IllegalArgumentException("can't writeV3Value() on a null parameter");
292
if (paramValues[index] instanceof byte[])
294
pgStream.Send((byte[])paramValues[index]);
298
// Binary-format bytea?
299
if (paramValues[index] instanceof StreamWrapper)
301
streamBytea(pgStream, (StreamWrapper)paramValues[index]);
306
if (encoded[index] == null)
307
encoded[index] = Utils.encodeUTF8((String)paramValues[index]);
308
pgStream.Send(encoded[index]);
313
public ParameterList copy() {
314
SimpleParameterList newCopy = new SimpleParameterList(paramValues.length, protoConnection);
315
System.arraycopy(paramValues, 0, newCopy.paramValues, 0, paramValues.length);
316
System.arraycopy(paramTypes, 0, newCopy.paramTypes, 0, paramTypes.length);
317
System.arraycopy(direction, 0, newCopy.direction, 0, direction.length);
321
public void clear() {
322
Arrays.fill(paramValues, null);
323
Arrays.fill(paramTypes, 0);
324
Arrays.fill(encoded, null);
325
Arrays.fill(direction, 0);
327
public SimpleParameterList[] getSubparams() {
331
private final Object[] paramValues;
332
private final int[] paramTypes;
333
private final int[] direction;
334
private final byte[][] encoded;
335
private final ProtocolConnectionImpl protoConnection;
338
* Marker object representing NULL; this distinguishes
339
* "parameter never set" from "parameter set to null".
341
private final static Object NULL_OBJECT = new Object();