PreparedStatement.java 30.9 KB
Newer Older
P
Peter Mount 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
package org.postgresql.jdbc2;

// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver.
// If you make any modifications to this file, you must make sure that the
// changes are also made (if relevent) to the related JDBC 1 class in the
// org.postgresql.jdbc1 package.

import java.io.*;
import java.math.*;
import java.sql.*;
import java.text.*;
import java.util.*;
import org.postgresql.largeobject.*;
import org.postgresql.util.*;

B
Bruce Momjian 已提交
16
/*
P
Peter Mount 已提交
17 18 19 20 21 22 23 24 25
 * A SQL Statement is pre-compiled and stored in a PreparedStatement object.
 * This object can then be used to efficiently execute this statement multiple
 * times.
 *
 * <p><B>Note:</B> The setXXX methods for setting IN parameter values must
 * specify types that are compatible with the defined SQL type of the input
 * parameter.  For instance, if the IN parameter has SQL type Integer, then
 * setInt should be used.
 *
26
 * <p>If arbitrary parameter type conversions are required, then the setObject
P
Peter Mount 已提交
27 28 29 30 31
 * method should be used with a target SQL type.
 *
 * @see ResultSet
 * @see java.sql.PreparedStatement
 */
32
public class PreparedStatement extends Statement implements java.sql.PreparedStatement
P
Peter Mount 已提交
33 34 35 36 37 38
{
	String sql;
	String[] templateStrings;
	String[] inStrings;
	Connection connection;

39 40
	// Some performance caches
	private StringBuffer sbuf = new StringBuffer();
P
Peter Mount 已提交
41

42 43 44 45
	// We use ThreadLocal for SimpleDateFormat's because they are not that
	// thread safe, so each calling thread has its own object.
	private static ThreadLocal tl_df = new ThreadLocal(); // setDate() SimpleDateFormat
	private static ThreadLocal tl_tsdf = new ThreadLocal(); // setTimestamp() SimpleDateFormat
P
Peter Mount 已提交
46

B
Bruce Momjian 已提交
47
	/*
P
Peter Mount 已提交
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
	 * Constructor for the PreparedStatement class.
	 * Split the SQL statement into segments - separated by the arguments.
	 * When we rebuild the thing with the arguments, we can substitute the
	 * args and join the whole thing together.
	 *
	 * @param conn the instanatiating connection
	 * @param sql the SQL statement with ? for IN markers
	 * @exception SQLException if something bad occurs
	 */
	public PreparedStatement(Connection connection, String sql) throws SQLException
	{
		super(connection);

		Vector v = new Vector();
		boolean inQuotes = false;
		int lastParmEnd = 0, i;

		this.sql = sql;
		this.connection = connection;
67

P
Peter Mount 已提交
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
		for (i = 0; i < sql.length(); ++i)
		{
			int c = sql.charAt(i);

			if (c == '\'')
				inQuotes = !inQuotes;
			if (c == '?' && !inQuotes)
			{
				v.addElement(sql.substring (lastParmEnd, i));
				lastParmEnd = i + 1;
			}
		}
		v.addElement(sql.substring (lastParmEnd, sql.length()));

		templateStrings = new String[v.size()];
		inStrings = new String[v.size() - 1];
		clearParameters();

		for (i = 0 ; i < templateStrings.length; ++i)
			templateStrings[i] = (String)v.elementAt(i);
	}

B
Bruce Momjian 已提交
90
	/*
P
Peter Mount 已提交
91 92 93
	 * A Prepared SQL query is executed and its ResultSet is returned
	 *
	 * @return a ResultSet that contains the data produced by the
94
			*			  *			query - never null
P
Peter Mount 已提交
95 96 97 98
	 * @exception SQLException if a database access error occurs
	 */
	public java.sql.ResultSet executeQuery() throws SQLException
	{
99
		return super.executeQuery(compileQuery());	// in Statement class
P
Peter Mount 已提交
100 101
	}

B
Bruce Momjian 已提交
102
	/*
P
Peter Mount 已提交
103 104 105 106 107
	 * Execute a SQL INSERT, UPDATE or DELETE statement.  In addition,
	 * SQL statements that return nothing such as SQL DDL statements can
	 * be executed.
	 *
	 * @return either the row count for INSERT, UPDATE or DELETE; or
108
			*			  *			0 for SQL statements that return nothing.
P
Peter Mount 已提交
109 110 111 112
	 * @exception SQLException if a database access error occurs
	 */
	public int executeUpdate() throws SQLException
	{
113
		return super.executeUpdate(compileQuery());		// in Statement class
P
Peter Mount 已提交
114 115
	}

B
Bruce Momjian 已提交
116
	/*
117 118 119 120 121 122 123 124
	 * Helper - this compiles the SQL query from the various parameters
	 * This is identical to toString() except it throws an exception if a
	 * parameter is unused.
	 */
	private synchronized String compileQuery()
	throws SQLException
	{
		sbuf.setLength(0);
P
Peter Mount 已提交
125 126 127 128 129
		int i;

		for (i = 0 ; i < inStrings.length ; ++i)
		{
			if (inStrings[i] == null)
130
				throw new PSQLException("postgresql.prep.param", new Integer(i + 1));
P
Peter Mount 已提交
131
			sbuf.append (templateStrings[i]).append (inStrings[i]);
P
Peter Mount 已提交
132
		}
P
Peter Mount 已提交
133
		sbuf.append(templateStrings[inStrings.length]);
134 135
		return sbuf.toString();
	}
P
Peter Mount 已提交
136

B
Bruce Momjian 已提交
137
	/*
P
Peter Mount 已提交
138 139 140 141 142 143 144 145 146 147 148 149 150 151
	 * Set a parameter to SQL NULL
	 *
	 * <p><B>Note:</B> You must specify the parameters SQL type (although
	 * PostgreSQL ignores it)
	 *
	 * @param parameterIndex the first parameter is 1, etc...
	 * @param sqlType the SQL type code defined in java.sql.Types
	 * @exception SQLException if a database access error occurs
	 */
	public void setNull(int parameterIndex, int sqlType) throws SQLException
	{
		set(parameterIndex, "null");
	}

B
Bruce Momjian 已提交
152
	/*
P
Peter Mount 已提交
153 154 155 156 157 158 159 160 161 162 163 164
	 * Set a parameter to a Java boolean value.  The driver converts this
	 * to a SQL BIT value when it sends it to the database.
	 *
	 * @param parameterIndex the first parameter is 1...
	 * @param x the parameter value
	 * @exception SQLException if a database access error occurs
	 */
	public void setBoolean(int parameterIndex, boolean x) throws SQLException
	{
		set(parameterIndex, x ? "'t'" : "'f'");
	}

B
Bruce Momjian 已提交
165
	/*
P
Peter Mount 已提交
166 167 168 169 170 171 172 173 174
	 * Set a parameter to a Java byte value.  The driver converts this to
	 * a SQL TINYINT value when it sends it to the database.
	 *
	 * @param parameterIndex the first parameter is 1...
	 * @param x the parameter value
	 * @exception SQLException if a database access error occurs
	 */
	public void setByte(int parameterIndex, byte x) throws SQLException
	{
175
		set(parameterIndex, Integer.toString(x));
P
Peter Mount 已提交
176 177
	}

B
Bruce Momjian 已提交
178
	/*
P
Peter Mount 已提交
179 180 181 182 183 184 185 186 187
	 * Set a parameter to a Java short value.  The driver converts this
	 * to a SQL SMALLINT value when it sends it to the database.
	 *
	 * @param parameterIndex the first parameter is 1...
	 * @param x the parameter value
	 * @exception SQLException if a database access error occurs
	 */
	public void setShort(int parameterIndex, short x) throws SQLException
	{
188
		set(parameterIndex, Integer.toString(x));
P
Peter Mount 已提交
189 190
	}

B
Bruce Momjian 已提交
191
	/*
P
Peter Mount 已提交
192 193 194 195 196 197 198 199 200
	 * Set a parameter to a Java int value.  The driver converts this to
	 * a SQL INTEGER value when it sends it to the database.
	 *
	 * @param parameterIndex the first parameter is 1...
	 * @param x the parameter value
	 * @exception SQLException if a database access error occurs
	 */
	public void setInt(int parameterIndex, int x) throws SQLException
	{
201
		set(parameterIndex, Integer.toString(x));
P
Peter Mount 已提交
202 203
	}

B
Bruce Momjian 已提交
204
	/*
P
Peter Mount 已提交
205 206 207 208 209 210 211 212 213
	 * Set a parameter to a Java long value.  The driver converts this to
	 * a SQL BIGINT value when it sends it to the database.
	 *
	 * @param parameterIndex the first parameter is 1...
	 * @param x the parameter value
	 * @exception SQLException if a database access error occurs
	 */
	public void setLong(int parameterIndex, long x) throws SQLException
	{
214
		set(parameterIndex, Long.toString(x));
P
Peter Mount 已提交
215 216
	}

B
Bruce Momjian 已提交
217
	/*
P
Peter Mount 已提交
218 219 220 221 222 223 224 225 226
	 * Set a parameter to a Java float value.  The driver converts this
	 * to a SQL FLOAT value when it sends it to the database.
	 *
	 * @param parameterIndex the first parameter is 1...
	 * @param x the parameter value
	 * @exception SQLException if a database access error occurs
	 */
	public void setFloat(int parameterIndex, float x) throws SQLException
	{
227
		set(parameterIndex, Float.toString(x));
P
Peter Mount 已提交
228 229
	}

B
Bruce Momjian 已提交
230
	/*
231
	 * Set a parameter to a Java double value.	The driver converts this
P
Peter Mount 已提交
232 233 234 235 236 237 238 239
	 * to a SQL DOUBLE value when it sends it to the database
	 *
	 * @param parameterIndex the first parameter is 1...
	 * @param x the parameter value
	 * @exception SQLException if a database access error occurs
	 */
	public void setDouble(int parameterIndex, double x) throws SQLException
	{
240
		set(parameterIndex, Double.toString(x));
P
Peter Mount 已提交
241 242
	}

B
Bruce Momjian 已提交
243
	/*
P
Peter Mount 已提交
244 245 246 247 248 249 250 251 252 253 254 255 256
	 * Set a parameter to a java.lang.BigDecimal value.  The driver
	 * converts this to a SQL NUMERIC value when it sends it to the
	 * database.
	 *
	 * @param parameterIndex the first parameter is 1...
	 * @param x the parameter value
	 * @exception SQLException if a database access error occurs
	 */
	public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException
	{
		set(parameterIndex, x.toString());
	}

B
Bruce Momjian 已提交
257
	/*
258
	 * Set a parameter to a Java String value.	The driver converts this
P
Peter Mount 已提交
259 260 261 262 263 264 265 266 267 268
	 * to a SQL VARCHAR or LONGVARCHAR value (depending on the arguments
	 * size relative to the driver's limits on VARCHARs) when it sends it
	 * to the database.
	 *
	 * @param parameterIndex the first parameter is 1...
	 * @param x the parameter value
	 * @exception SQLException if a database access error occurs
	 */
	public void setString(int parameterIndex, String x) throws SQLException
	{
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
		// if the passed string is null, then set this column to null
		if (x == null)
			setNull(parameterIndex, Types.OTHER);
		else
		{
			// use the shared buffer object. Should never clash but this makes
			// us thread safe!
			synchronized (sbuf)
			{
				sbuf.setLength(0);
				int i;

				sbuf.append('\'');
				for (i = 0 ; i < x.length() ; ++i)
				{
					char c = x.charAt(i);
					if (c == '\\' || c == '\'')
						sbuf.append((char)'\\');
					sbuf.append(c);
				}
				sbuf.append('\'');
				set(parameterIndex, sbuf.toString());
			}
		}
	}

B
Bruce Momjian 已提交
295
	/*
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
	 * Set a parameter to a Java array of bytes.  The driver converts this
	 * to a SQL VARBINARY or LONGVARBINARY (depending on the argument's
	 * size relative to the driver's limits on VARBINARYs) when it sends
	 * it to the database.
	 *
	 * <p>Implementation note:
	 * <br>With org.postgresql, this creates a large object, and stores the
	 * objects oid in this column.
	 *
	 * @param parameterIndex the first parameter is 1...
	 * @param x the parameter value
	 * @exception SQLException if a database access error occurs
	 */
	public void setBytes(int parameterIndex, byte x[]) throws SQLException
	{
		if (connection.haveMinimumCompatibleVersion("7.2"))
		{
			//Version 7.2 supports the bytea datatype for byte arrays
			if (null == x)
			{
				setNull(parameterIndex, Types.OTHER);
			}
			else
			{
				setString(parameterIndex, PGbytea.toPGString(x));
			}
		}
		else
		{
			//Version 7.1 and earlier support done as LargeObjects
			LargeObjectManager lom = connection.getLargeObjectAPI();
			int oid = lom.create();
			LargeObject lob = lom.open(oid);
			lob.write(x);
			lob.close();
			setInt(parameterIndex, oid);
		}
	}
P
Peter Mount 已提交
334

B
Bruce Momjian 已提交
335
	/*
P
Peter Mount 已提交
336 337 338 339 340 341 342 343 344
	 * Set a parameter to a java.sql.Date value.  The driver converts this
	 * to a SQL DATE value when it sends it to the database.
	 *
	 * @param parameterIndex the first parameter is 1...
	 * @param x the parameter value
	 * @exception SQLException if a database access error occurs
	 */
	public void setDate(int parameterIndex, java.sql.Date x) throws SQLException
	{
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
		if (null == x)
		{
			setNull(parameterIndex, Types.OTHER);
		}
		else
		{
			SimpleDateFormat df = (SimpleDateFormat) tl_df.get();
			if (df == null)
			{
				df = new SimpleDateFormat("''yyyy-MM-dd''");
				tl_df.set(df);
			}
			set(parameterIndex, df.format(x));
		}
		// The above is how the date should be handled.
		//
		// However, in JDK's prior to 1.1.6 (confirmed with the
		// Linux jdk1.1.3 and the Win95 JRE1.1.5), SimpleDateFormat seems
		// to format a date to the previous day. So the fix is to add a day
		// before formatting.
		//
		// PS: 86400000 is one day
		//
		//set(parameterIndex, df.format(new java.util.Date(x.getTime()+86400000)));
P
Peter Mount 已提交
369
	}
370

B
Bruce Momjian 已提交
371
	/*
P
Peter Mount 已提交
372 373 374 375 376 377 378 379 380
	 * Set a parameter to a java.sql.Time value.  The driver converts
	 * this to a SQL TIME value when it sends it to the database.
	 *
	 * @param parameterIndex the first parameter is 1...));
	 * @param x the parameter value
	 * @exception SQLException if a database access error occurs
	 */
	public void setTime(int parameterIndex, Time x) throws SQLException
	{
381 382 383 384 385 386 387 388
		if (null == x)
		{
			setNull(parameterIndex, Types.OTHER);
		}
		else
		{
			set(parameterIndex, "'" + x.toString() + "'");
		}
P
Peter Mount 已提交
389 390
	}

B
Bruce Momjian 已提交
391
	/*
P
Peter Mount 已提交
392 393 394 395 396 397 398 399
	 * Set a parameter to a java.sql.Timestamp value.  The driver converts
	 * this to a SQL TIMESTAMP value when it sends it to the database.
	 *
	 * @param parameterIndex the first parameter is 1...
	 * @param x the parameter value
	 * @exception SQLException if a database access error occurs
	 */
	public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
	{
		if (null == x)
		{
			setNull(parameterIndex, Types.OTHER);
		}
		else
		{
			SimpleDateFormat df = (SimpleDateFormat) tl_tsdf.get();
			if (df == null)
			{
				df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				df.setTimeZone(TimeZone.getTimeZone("GMT"));
				tl_tsdf.set(df);
			}

			// Use the shared StringBuffer
			synchronized (sbuf)
			{
				sbuf.setLength(0);
				sbuf.append("'").append(df.format(x)).append('.').append(x.getNanos() / 10000000).append("+00'");
				set(parameterIndex, sbuf.toString());
			}

			// The above works, but so does the following. I'm leaving the above in, but this seems
			// to be identical. Pays to read the docs ;-)
			//set(parameterIndex,"'"+x.toString()+"'");
		}
P
Peter Mount 已提交
427 428
	}

B
Bruce Momjian 已提交
429
	/*
P
Peter Mount 已提交
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
	 * When a very large ASCII value is input to a LONGVARCHAR parameter,
	 * it may be more practical to send it via a java.io.InputStream.
	 * JDBC will read the data from the stream as needed, until it reaches
	 * end-of-file.  The JDBC driver will do any necessary conversion from
	 * ASCII to the database char format.
	 *
	 * <P><B>Note:</B> This stream object can either be a standard Java
	 * stream object or your own subclass that implements the standard
	 * interface.
	 *
	 * @param parameterIndex the first parameter is 1...
	 * @param x the parameter value
	 * @param length the number of bytes in the stream
	 * @exception SQLException if a database access error occurs
	 */
	public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException
	{
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
		if (connection.haveMinimumCompatibleVersion("7.2"))
		{
			//Version 7.2 supports AsciiStream for all PG text types (char, varchar, text)
			//As the spec/javadoc for this method indicate this is to be used for
			//large String values (i.e. LONGVARCHAR)  PG doesn't have a separate
			//long varchar datatype, but with toast all text datatypes are capable of
			//handling very large values.  Thus the implementation ends up calling
			//setString() since there is no current way to stream the value to the server
			try
			{
				InputStreamReader l_inStream = new InputStreamReader(x, "ASCII");
				char[] l_chars = new char[length];
				int l_charsRead = l_inStream.read(l_chars, 0, length);
				setString(parameterIndex, new String(l_chars, 0, l_charsRead));
			}
			catch (UnsupportedEncodingException l_uee)
			{
				throw new PSQLException("postgresql.unusual", l_uee);
			}
			catch (IOException l_ioe)
			{
				throw new PSQLException("postgresql.unusual", l_ioe);
			}
		}
		else
		{
			//Version 7.1 supported only LargeObjects by treating everything
			//as binary data
			setBinaryStream(parameterIndex, x, length);
		}
	}
P
Peter Mount 已提交
478

B
Bruce Momjian 已提交
479
	/*
P
Peter Mount 已提交
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
	 * When a very large Unicode value is input to a LONGVARCHAR parameter,
	 * it may be more practical to send it via a java.io.InputStream.
	 * JDBC will read the data from the stream as needed, until it reaches
	 * end-of-file.  The JDBC driver will do any necessary conversion from
	 * UNICODE to the database char format.
	 *
	 * ** DEPRECIATED IN JDBC 2 **
	 *
	 * <P><B>Note:</B> This stream object can either be a standard Java
	 * stream object or your own subclass that implements the standard
	 * interface.
	 *
	 * @param parameterIndex the first parameter is 1...
	 * @param x the parameter value
	 * @exception SQLException if a database access error occurs
	 * @deprecated
	 */
	public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException
	{
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
		if (connection.haveMinimumCompatibleVersion("7.2"))
		{
			//Version 7.2 supports AsciiStream for all PG text types (char, varchar, text)
			//As the spec/javadoc for this method indicate this is to be used for
			//large String values (i.e. LONGVARCHAR)  PG doesn't have a separate
			//long varchar datatype, but with toast all text datatypes are capable of
			//handling very large values.  Thus the implementation ends up calling
			//setString() since there is no current way to stream the value to the server
			try
			{
				InputStreamReader l_inStream = new InputStreamReader(x, "UTF-8");
				char[] l_chars = new char[length];
				int l_charsRead = l_inStream.read(l_chars, 0, length);
				setString(parameterIndex, new String(l_chars, 0, l_charsRead));
			}
			catch (UnsupportedEncodingException l_uee)
			{
				throw new PSQLException("postgresql.unusual", l_uee);
			}
			catch (IOException l_ioe)
			{
				throw new PSQLException("postgresql.unusual", l_ioe);
			}
		}
		else
		{
			//Version 7.1 supported only LargeObjects by treating everything
			//as binary data
			setBinaryStream(parameterIndex, x, length);
		}
	}
P
Peter Mount 已提交
530

B
Bruce Momjian 已提交
531
	/*
P
Peter Mount 已提交
532 533 534
	 * When a very large binary value is input to a LONGVARBINARY parameter,
	 * it may be more practical to send it via a java.io.InputStream.
	 * JDBC will read the data from the stream as needed, until it reaches
535
	 * end-of-file.
P
Peter Mount 已提交
536 537 538 539 540 541 542 543 544 545 546
	 *
	 * <P><B>Note:</B> This stream object can either be a standard Java
	 * stream object or your own subclass that implements the standard
	 * interface.
	 *
	 * @param parameterIndex the first parameter is 1...
	 * @param x the parameter value
	 * @exception SQLException if a database access error occurs
	 */
	public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException
	{
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
		if (connection.haveMinimumCompatibleVersion("7.2"))
		{
			//Version 7.2 supports BinaryStream for for the PG bytea type
			//As the spec/javadoc for this method indicate this is to be used for
			//large binary values (i.e. LONGVARBINARY)	PG doesn't have a separate
			//long binary datatype, but with toast the bytea datatype is capable of
			//handling very large values.  Thus the implementation ends up calling
			//setBytes() since there is no current way to stream the value to the server
			byte[] l_bytes = new byte[length];
			int l_bytesRead;
			try
			{
				l_bytesRead = x.read(l_bytes, 0, length);
			}
			catch (IOException l_ioe)
			{
				throw new PSQLException("postgresql.unusual", l_ioe);
			}
			if (l_bytesRead == length)
			{
				setBytes(parameterIndex, l_bytes);
			}
			else
			{
				//the stream contained less data than they said
				byte[] l_bytes2 = new byte[l_bytesRead];
				System.arraycopy(l_bytes, 0, l_bytes2, 0, l_bytesRead);
				setBytes(parameterIndex, l_bytes2);
			}
		}
		else
		{
			//Version 7.1 only supported streams for LargeObjects
			//but the jdbc spec indicates that streams should be
			//available for LONGVARBINARY instead
			LargeObjectManager lom = connection.getLargeObjectAPI();
			int oid = lom.create();
			LargeObject lob = lom.open(oid);
			OutputStream los = lob.getOutputStream();
			try
			{
				// could be buffered, but then the OutputStream returned by LargeObject
				// is buffered internally anyhow, so there would be no performance
				// boost gained, if anything it would be worse!
				int c = x.read();
				int p = 0;
				while (c > -1 && p < length)
				{
					los.write(c);
					c = x.read();
					p++;
				}
				los.close();
			}
			catch (IOException se)
			{
				throw new PSQLException("postgresql.unusual", se);
			}
			// lob is closed by the stream so don't call lob.close()
			setInt(parameterIndex, oid);
		}
	}
P
Peter Mount 已提交
609

B
Bruce Momjian 已提交
610
	/*
P
Peter Mount 已提交
611 612
	 * In general, parameter values remain in force for repeated used of a
	 * Statement.  Setting a parameter value automatically clears its
613
	 * previous value.	However, in coms cases, it is useful to immediately
P
Peter Mount 已提交
614 615 616 617 618 619 620 621 622 623 624 625 626
	 * release the resources used by the current parameter values; this
	 * can be done by calling clearParameters
	 *
	 * @exception SQLException if a database access error occurs
	 */
	public void clearParameters() throws SQLException
	{
		int i;

		for (i = 0 ; i < inStrings.length ; i++)
			inStrings[i] = null;
	}

B
Bruce Momjian 已提交
627
	/*
P
Peter Mount 已提交
628 629 630 631 632 633 634 635 636 637 638 639 640 641
	 * Set the value of a parameter using an object; use the java.lang
	 * equivalent objects for integral values.
	 *
	 * <P>The given Java object will be converted to the targetSqlType before
	 * being sent to the database.
	 *
	 * <P>note that this method may be used to pass database-specific
	 * abstract data types.  This is done by using a Driver-specific
	 * Java type and using a targetSqlType of java.sql.Types.OTHER
	 *
	 * @param parameterIndex the first parameter is 1...
	 * @param x the object containing the input parameter value
	 * @param targetSqlType The SQL type to be send to the database
	 * @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC
642
	 *	types this is the number of digits after the decimal.  For
P
Peter Mount 已提交
643 644 645 646 647
	 *	all other types this value will be ignored.
	 * @exception SQLException if a database access error occurs
	 */
	public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException
	{
648 649 650 651
		if (x == null)
		{
			setNull(parameterIndex, Types.OTHER);
			return ;
652
		}
P
Peter Mount 已提交
653 654
		switch (targetSqlType)
		{
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
			case Types.TINYINT:
			case Types.SMALLINT:
			case Types.INTEGER:
			case Types.BIGINT:
			case Types.REAL:
			case Types.FLOAT:
			case Types.DOUBLE:
			case Types.DECIMAL:
			case Types.NUMERIC:
				if (x instanceof Boolean)
					set(parameterIndex, ((Boolean)x).booleanValue() ? "1" : "0");
				else
					set(parameterIndex, x.toString());
				break;
			case Types.CHAR:
			case Types.VARCHAR:
			case Types.LONGVARCHAR:
				setString(parameterIndex, x.toString());
				break;
			case Types.DATE:
				setDate(parameterIndex, (java.sql.Date)x);
				break;
			case Types.TIME:
				setTime(parameterIndex, (Time)x);
				break;
			case Types.TIMESTAMP:
				setTimestamp(parameterIndex, (Timestamp)x);
				break;
			case Types.BIT:
				if (x instanceof Boolean)
				{
					set(parameterIndex, ((Boolean)x).booleanValue() ? "TRUE" : "FALSE");
				}
				else
				{
					throw new PSQLException("postgresql.prep.type");
				}
				break;
			case Types.BINARY:
			case Types.VARBINARY:
				setObject(parameterIndex, x);
				break;
			case Types.OTHER:
				setString(parameterIndex, ((PGobject)x).getValue());
				break;
			default:
P
Peter Mount 已提交
701 702 703 704 705 706 707 708
				throw new PSQLException("postgresql.prep.type");
		}
	}

	public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException
	{
		setObject(parameterIndex, x, targetSqlType, 0);
	}
709

B
Bruce Momjian 已提交
710
	/*
711 712 713 714 715
	 * This stores an Object into a parameter.
	 * <p>New for 6.4, if the object is not recognised, but it is
	 * Serializable, then the object is serialised using the
	 * org.postgresql.util.Serialize class.
	 */
P
Peter Mount 已提交
716 717
	public void setObject(int parameterIndex, Object x) throws SQLException
	{
718 719 720 721
		if (x == null)
		{
			setNull(parameterIndex, Types.OTHER);
			return ;
722
		}
P
Peter Mount 已提交
723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
		if (x instanceof String)
			setString(parameterIndex, (String)x);
		else if (x instanceof BigDecimal)
			setBigDecimal(parameterIndex, (BigDecimal)x);
		else if (x instanceof Short)
			setShort(parameterIndex, ((Short)x).shortValue());
		else if (x instanceof Integer)
			setInt(parameterIndex, ((Integer)x).intValue());
		else if (x instanceof Long)
			setLong(parameterIndex, ((Long)x).longValue());
		else if (x instanceof Float)
			setFloat(parameterIndex, ((Float)x).floatValue());
		else if (x instanceof Double)
			setDouble(parameterIndex, ((Double)x).doubleValue());
		else if (x instanceof byte[])
			setBytes(parameterIndex, (byte[])x);
		else if (x instanceof java.sql.Date)
			setDate(parameterIndex, (java.sql.Date)x);
		else if (x instanceof Time)
			setTime(parameterIndex, (Time)x);
		else if (x instanceof Timestamp)
			setTimestamp(parameterIndex, (Timestamp)x);
		else if (x instanceof Boolean)
			setBoolean(parameterIndex, ((Boolean)x).booleanValue());
		else if (x instanceof PGobject)
			setString(parameterIndex, ((PGobject)x).getValue());
		else
750 751
			// Try to store java object in database
			setSerialize(parameterIndex, connection.putObject(x), x.getClass().getName() );
P
Peter Mount 已提交
752 753
	}

B
Bruce Momjian 已提交
754
	/*
P
Peter Mount 已提交
755
	 * Some prepared statements return multiple results; the execute method
756
	 * handles these complex statements as well as the simpler form of
P
Peter Mount 已提交
757 758 759 760 761 762 763 764
	 * statements handled by executeQuery and executeUpdate
	 *
	 * @return true if the next result is a ResultSet; false if it is an
	 *	update count or there are no more results
	 * @exception SQLException if a database access error occurs
	 */
	public boolean execute() throws SQLException
	{
765
		return super.execute(compileQuery());	// in Statement class
P
Peter Mount 已提交
766 767
	}

B
Bruce Momjian 已提交
768
	/*
P
Peter Mount 已提交
769 770
	 * Returns the SQL statement with the current template values
	 * substituted.
771 772
			* NB: This is identical to compileQuery() except instead of throwing
			* SQLException if a parameter is null, it places ? instead.
P
Peter Mount 已提交
773
	 */
774 775 776
	public String toString()
	{
		synchronized (sbuf)
P
Peter Mount 已提交
777
		{
778 779 780 781 782 783 784 785 786 787 788 789 790
			sbuf.setLength(0);
			int i;

			for (i = 0 ; i < inStrings.length ; ++i)
			{
				if (inStrings[i] == null)
					sbuf.append( '?' );
				else
					sbuf.append (templateStrings[i]);
				sbuf.append (inStrings[i]);
			}
			sbuf.append(templateStrings[inStrings.length]);
			return sbuf.toString();
P
Peter Mount 已提交
791 792
		}
	}
793

P
Peter Mount 已提交
794
	// **************************************************************
795
	//	END OF PUBLIC INTERFACE
P
Peter Mount 已提交
796
	// **************************************************************
797

B
Bruce Momjian 已提交
798
	/*
P
Peter Mount 已提交
799
	 * There are a lot of setXXX classes which all basically do
800
	 * the same thing.	We need a method which actually does the
P
Peter Mount 已提交
801 802 803 804 805 806 807 808 809 810 811 812
	 * set for us.
	 *
	 * @param paramIndex the index into the inString
	 * @param s a string to be stored
	 * @exception SQLException if something goes wrong
	 */
	private void set(int paramIndex, String s) throws SQLException
	{
		if (paramIndex < 1 || paramIndex > inStrings.length)
			throw new PSQLException("postgresql.prep.range");
		inStrings[paramIndex - 1] = s;
	}
813

B
Bruce Momjian 已提交
814
	/*
815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
	 * Set a parameter to a tablerow-type oid reference.
	 *
	 * @param parameterIndex the first parameter is 1...
	 * @param x the oid of the object from org.postgresql.util.Serialize.store
	 * @param classname the classname of the java object x
	 * @exception SQLException if a database access error occurs
	 */
	private void setSerialize(int parameterIndex, long x, String classname) throws SQLException
	{
		// converts . to _, toLowerCase, and ensures length<32
		String tablename = Serialize.toPostgreSQL( classname );
		DriverManager.println("setSerialize: setting " + x + "::" + tablename );

		// OID reference to tablerow-type must be cast like:  <oid>::<tablename>
		// Note that postgres support for tablerow data types is incomplete/broken.
		// This cannot be just a plain OID because then there would be ambiguity
		// between when you want the oid itself and when you want the object
		// an oid references.
		set(parameterIndex, Long.toString(x) + "::" + tablename );
	}


837 838
	// ** JDBC 2 Extensions **

B
Bruce Momjian 已提交
839
	/*
840 841 842 843 844 845 846
	 * This parses the query and adds it to the current batch
	 */
	public void addBatch() throws SQLException
	{
		super.addBatch(compileQuery());
	}

B
Bruce Momjian 已提交
847
	/*
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865
	 * Not sure what this one does, so I'm saying this returns the MetaData for
	 * the last ResultSet returned!
	 */
	public java.sql.ResultSetMetaData getMetaData() throws SQLException
	{
		java.sql.ResultSet rs = getResultSet();
		if (rs != null)
			return rs.getMetaData();

		// Does anyone really know what this method does?
		return null;
	}

	public void setArray(int i, java.sql.Array x) throws SQLException
	{
		setString(i, x.toString());
	}

B
Bruce Momjian 已提交
866
	/*
867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899
	 * Sets a Blob
	 */
	public void setBlob(int i, Blob x) throws SQLException
	{
		InputStream l_inStream = x.getBinaryStream();
		int l_length = (int) x.length();
		LargeObjectManager lom = connection.getLargeObjectAPI();
		int oid = lom.create();
		LargeObject lob = lom.open(oid);
		OutputStream los = lob.getOutputStream();
		try
		{
			// could be buffered, but then the OutputStream returned by LargeObject
			// is buffered internally anyhow, so there would be no performance
			// boost gained, if anything it would be worse!
			int c = l_inStream.read();
			int p = 0;
			while (c > -1 && p < l_length)
			{
				los.write(c);
				c = l_inStream.read();
				p++;
			}
			los.close();
		}
		catch (IOException se)
		{
			throw new PSQLException("postgresql.unusual", se);
		}
		// lob is closed by the stream so don't call lob.close()
		setInt(i, oid);
	}

B
Bruce Momjian 已提交
900
	/*
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958
	 * This is similar to setBinaryStream except it uses a Reader instead of
	 * InputStream.
	 */
	public void setCharacterStream(int i, java.io.Reader x, int length) throws SQLException
	{
		if (connection.haveMinimumCompatibleVersion("7.2"))
		{
			//Version 7.2 supports CharacterStream for for the PG text types
			//As the spec/javadoc for this method indicate this is to be used for
			//large text values (i.e. LONGVARCHAR)	PG doesn't have a separate
			//long varchar datatype, but with toast all the text datatypes are capable of
			//handling very large values.  Thus the implementation ends up calling
			//setString() since there is no current way to stream the value to the server
			char[] l_chars = new char[length];
			int l_charsRead;
			try
			{
				l_charsRead = x.read(l_chars, 0, length);
			}
			catch (IOException l_ioe)
			{
				throw new PSQLException("postgresql.unusual", l_ioe);
			}
			setString(i, new String(l_chars, 0, l_charsRead));
		}
		else
		{
			//Version 7.1 only supported streams for LargeObjects
			//but the jdbc spec indicates that streams should be
			//available for LONGVARCHAR instead
			LargeObjectManager lom = connection.getLargeObjectAPI();
			int oid = lom.create();
			LargeObject lob = lom.open(oid);
			OutputStream los = lob.getOutputStream();
			try
			{
				// could be buffered, but then the OutputStream returned by LargeObject
				// is buffered internally anyhow, so there would be no performance
				// boost gained, if anything it would be worse!
				int c = x.read();
				int p = 0;
				while (c > -1 && p < length)
				{
					los.write(c);
					c = x.read();
					p++;
				}
				los.close();
			}
			catch (IOException se)
			{
				throw new PSQLException("postgresql.unusual", se);
			}
			// lob is closed by the stream so don't call lob.close()
			setInt(i, oid);
		}
	}

B
Bruce Momjian 已提交
959
	/*
960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992
	 * New in 7.1
	 */
	public void setClob(int i, Clob x) throws SQLException
	{
		InputStream l_inStream = x.getAsciiStream();
		int l_length = (int) x.length();
		LargeObjectManager lom = connection.getLargeObjectAPI();
		int oid = lom.create();
		LargeObject lob = lom.open(oid);
		OutputStream los = lob.getOutputStream();
		try
		{
			// could be buffered, but then the OutputStream returned by LargeObject
			// is buffered internally anyhow, so there would be no performance
			// boost gained, if anything it would be worse!
			int c = l_inStream.read();
			int p = 0;
			while (c > -1 && p < l_length)
			{
				los.write(c);
				c = l_inStream.read();
				p++;
			}
			los.close();
		}
		catch (IOException se)
		{
			throw new PSQLException("postgresql.unusual", se);
		}
		// lob is closed by the stream so don't call lob.close()
		setInt(i, oid);
	}

B
Bruce Momjian 已提交
993
	/*
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
	 * At least this works as in PostgreSQL null represents anything null ;-)
	 *
	 * New in 7,1
	 */
	public void setNull(int i, int t, String s) throws SQLException
	{
		setNull(i, t);
	}

	public void setRef(int i, Ref x) throws SQLException
	{
		throw org.postgresql.Driver.notImplemented();
	}

B
Bruce Momjian 已提交
1008
	/*
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
	 * New in 7,1
	 */
	public void setDate(int i, java.sql.Date d, java.util.Calendar cal) throws SQLException
	{
		if (cal == null)
			setDate(i, d);
		else
		{
			cal.setTime(d);
			setDate(i, new java.sql.Date(cal.getTime().getTime()));
		}
	}

B
Bruce Momjian 已提交
1022
	/*
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
	 * New in 7,1
	 */
	public void setTime(int i, Time t, java.util.Calendar cal) throws SQLException
	{
		if (cal == null)
			setTime(i, t);
		else
		{
			cal.setTime(t);
			setTime(i, new java.sql.Time(cal.getTime().getTime()));
		}
	}

B
Bruce Momjian 已提交
1036
	/*
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048
	 * New in 7,1
	 */
	public void setTimestamp(int i, Timestamp t, java.util.Calendar cal) throws SQLException
	{
		if (cal == null)
			setTimestamp(i, t);
		else
		{
			cal.setTime(t);
			setTimestamp(i, new java.sql.Timestamp(cal.getTime().getTime()));
		}
	}
1049

P
Peter Mount 已提交
1050
}
1051