0001: /*-------------------------------------------------------------------------
0002: *
0003: * Copyright (c) 2004-2005, PostgreSQL Global Development Group
0004: *
0005: * IDENTIFICATION
0006: * $PostgreSQL: pgjdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java,v 1.104 2007/07/27 10:15:32 jurka Exp $
0007: *
0008: *-------------------------------------------------------------------------
0009: */
0010: package org.postgresql.jdbc2;
0011:
0012: import java.io.*;
0013: import java.lang.reflect.InvocationTargetException;
0014: import java.lang.reflect.Method;
0015: import java.math.*;
0016: import java.sql.*;
0017: import java.util.ArrayList;
0018: import java.util.Vector;
0019: import java.util.Calendar;
0020:
0021: import org.postgresql.Driver;
0022: import org.postgresql.largeobject.*;
0023: import org.postgresql.core.*;
0024: import org.postgresql.core.types.*;
0025: import org.postgresql.util.PSQLException;
0026: import org.postgresql.util.PSQLState;
0027: import org.postgresql.util.PGobject;
0028: import org.postgresql.util.GT;
0029:
0030: /**
0031: * This class defines methods of the jdbc2 specification.
0032: * The real Statement class (for jdbc2) is org.postgresql.jdbc2.Jdbc2Statement
0033: */
0034: public abstract class AbstractJdbc2Statement implements BaseStatement {
0035: protected ArrayList batchStatements = null;
0036: protected ArrayList batchParameters = null;
0037: protected final int resultsettype; // the resultset type to return (ResultSet.TYPE_xxx)
0038: protected final int concurrency; // is it updateable or not? (ResultSet.CONCUR_xxx)
0039: protected int fetchdirection = ResultSet.FETCH_FORWARD; // fetch direction hint (currently ignored)
0040:
0041: // The connection who created us
0042: protected BaseConnection connection;
0043:
0044: /** The warnings chain. */
0045: protected SQLWarning warnings = null;
0046:
0047: /** Maximum number of rows to return, 0 = unlimited */
0048: protected int maxrows = 0;
0049:
0050: /** Number of rows to get in a batch. */
0051: protected int fetchSize = 0;
0052:
0053: /** Timeout (in seconds) for a query (not used) */
0054: protected int timeout = 0;
0055:
0056: protected boolean replaceProcessingEnabled = true;
0057:
0058: /** The current results. */
0059: protected ResultWrapper result = null;
0060:
0061: /** The first unclosed result. */
0062: protected ResultWrapper firstUnclosedResult = null;
0063:
0064: /** used to differentiate between new function call
0065: * logic and old function call logic
0066: * will be set to true if the server is < 8.1 or
0067: * if we are using v2 protocol
0068: * There is an exception to this where we are using v3, and the
0069: * call does not have an out parameter before the call
0070: */
0071: protected boolean adjustIndex = false;
0072:
0073: /*
0074: * Used to set adjustIndex above
0075: */
0076: protected boolean outParmBeforeFunc = false;
0077:
0078: // Static variables for parsing SQL when replaceProcessing is true.
0079: private static final short IN_SQLCODE = 0;
0080: private static final short IN_STRING = 1;
0081: private static final short IN_IDENTIFIER = 6;
0082: private static final short BACKSLASH = 2;
0083: private static final short ESC_TIMEDATE = 3;
0084: private static final short ESC_FUNCTION = 4;
0085: private static final short ESC_OUTERJOIN = 5;
0086: private static final short ESC_ESCAPECHAR = 7;
0087:
0088: protected final Query preparedQuery; // Query fragments for prepared statement.
0089: protected final ParameterList preparedParameters; // Parameter values for prepared statement.
0090: protected Query lastSimpleQuery;
0091:
0092: protected int m_prepareThreshold; // Reuse threshold to enable use of PREPARE
0093: protected int m_useCount = 0; // Number of times this statement has been used
0094:
0095: //Used by the callablestatement style methods
0096: private boolean isFunction;
0097: // functionReturnType contains the user supplied value to check
0098: // testReturn contains a modified version to make it easier to
0099: // check the getXXX methods..
0100: private int[] functionReturnType;
0101: private int[] testReturn;
0102: // returnTypeSet is true when a proper call to registerOutParameter has been made
0103: private boolean returnTypeSet;
0104: protected Object[] callResult;
0105: protected int maxfieldSize = 0;
0106:
0107: public ResultSet createDriverResultSet(Field[] fields, Vector tuples)
0108: throws SQLException {
0109: return createResultSet(null, fields, tuples, null);
0110: }
0111:
0112: public AbstractJdbc2Statement(AbstractJdbc2Connection c,
0113: int rsType, int rsConcurrency) throws SQLException {
0114: this .connection = c;
0115: this .preparedQuery = null;
0116: this .preparedParameters = null;
0117: this .lastSimpleQuery = null;
0118: resultsettype = rsType;
0119: concurrency = rsConcurrency;
0120: }
0121:
0122: public AbstractJdbc2Statement(AbstractJdbc2Connection connection,
0123: String sql, boolean isCallable, int rsType,
0124: int rsConcurrency) throws SQLException {
0125: this .connection = connection;
0126: this .lastSimpleQuery = null;
0127:
0128: String parsed_sql = replaceProcessing(sql);
0129: if (isCallable)
0130: parsed_sql = modifyJdbcCall(parsed_sql);
0131:
0132: this .preparedQuery = connection.getQueryExecutor()
0133: .createParameterizedQuery(parsed_sql);
0134: this .preparedParameters = preparedQuery.createParameterList();
0135:
0136: int inParamCount = preparedParameters.getInParameterCount() + 1;
0137: this .testReturn = new int[inParamCount];
0138: this .functionReturnType = new int[inParamCount];
0139:
0140: resultsettype = rsType;
0141: concurrency = rsConcurrency;
0142: }
0143:
0144: public abstract ResultSet createResultSet(Query originalQuery,
0145: Field[] fields, Vector tuples, ResultCursor cursor)
0146: throws SQLException;
0147:
0148: public BaseConnection getPGConnection() {
0149: return connection;
0150: }
0151:
0152: public String getFetchingCursorName() {
0153: return null;
0154: }
0155:
0156: public int getFetchSize() {
0157: return fetchSize;
0158: }
0159:
0160: protected boolean wantsScrollableResultSet() {
0161: return resultsettype != ResultSet.TYPE_FORWARD_ONLY;
0162: }
0163:
0164: protected boolean wantsHoldableResultSet() {
0165: return false;
0166: }
0167:
0168: //
0169: // ResultHandler implementations for updates, queries, and either-or.
0170: //
0171:
0172: public class StatementResultHandler implements ResultHandler {
0173: private SQLException error;
0174: private ResultWrapper results;
0175:
0176: ResultWrapper getResults() {
0177: return results;
0178: }
0179:
0180: private void append(ResultWrapper newResult) {
0181: if (results == null)
0182: results = newResult;
0183: else
0184: results.append(newResult);
0185: }
0186:
0187: public void handleResultRows(Query fromQuery, Field[] fields,
0188: Vector tuples, ResultCursor cursor) {
0189: try {
0190: ResultSet rs = AbstractJdbc2Statement.this
0191: .createResultSet(fromQuery, fields, tuples,
0192: cursor);
0193: append(new ResultWrapper(rs));
0194: } catch (SQLException e) {
0195: handleError(e);
0196: }
0197: }
0198:
0199: public void handleCommandStatus(String status, int updateCount,
0200: long insertOID) {
0201: append(new ResultWrapper(updateCount, insertOID));
0202: }
0203:
0204: public void handleWarning(SQLWarning warning) {
0205: AbstractJdbc2Statement.this .addWarning(warning);
0206: }
0207:
0208: public void handleError(SQLException newError) {
0209: if (error == null)
0210: error = newError;
0211: else
0212: error.setNextException(newError);
0213: }
0214:
0215: public void handleCompletion() throws SQLException {
0216: if (error != null)
0217: throw error;
0218: }
0219: }
0220:
0221: /*
0222: * Execute a SQL statement that retruns a single ResultSet
0223: *
0224: * @param sql typically a static SQL SELECT statement
0225: * @return a ResulSet that contains the data produced by the query
0226: * @exception SQLException if a database access error occurs
0227: */
0228: public java.sql.ResultSet executeQuery(String p_sql)
0229: throws SQLException {
0230: if (preparedQuery != null)
0231: throw new PSQLException(
0232: GT
0233: .tr("Can''t use query methods that take a query string on a PreparedStatement."),
0234: PSQLState.WRONG_OBJECT_TYPE);
0235:
0236: if (!executeWithFlags(p_sql, 0))
0237: throw new PSQLException(GT
0238: .tr("No results were returned by the query."),
0239: PSQLState.NO_DATA);
0240:
0241: if (result.getNext() != null)
0242: throw new PSQLException(
0243: GT
0244: .tr("Multiple ResultSets were returned by the query."),
0245: PSQLState.TOO_MANY_RESULTS);
0246:
0247: return (ResultSet) result.getResultSet();
0248: }
0249:
0250: /*
0251: * A Prepared SQL query is executed and its ResultSet is returned
0252: *
0253: * @return a ResultSet that contains the data produced by the
0254: * * query - never null
0255: * @exception SQLException if a database access error occurs
0256: */
0257: public java.sql.ResultSet executeQuery() throws SQLException {
0258: if (!executeWithFlags(0))
0259: throw new PSQLException(GT
0260: .tr("No results were returned by the query."),
0261: PSQLState.NO_DATA);
0262:
0263: if (result.getNext() != null)
0264: throw new PSQLException(
0265: GT
0266: .tr("Multiple ResultSets were returned by the query."),
0267: PSQLState.TOO_MANY_RESULTS);
0268:
0269: return (ResultSet) result.getResultSet();
0270: }
0271:
0272: /*
0273: * Execute a SQL INSERT, UPDATE or DELETE statement. In addition
0274: * SQL statements that return nothing such as SQL DDL statements
0275: * can be executed
0276: *
0277: * @param sql a SQL statement
0278: * @return either a row count, or 0 for SQL commands
0279: * @exception SQLException if a database access error occurs
0280: */
0281: public int executeUpdate(String p_sql) throws SQLException {
0282: if (preparedQuery != null)
0283: throw new PSQLException(
0284: GT
0285: .tr("Can''t use query methods that take a query string on a PreparedStatement."),
0286: PSQLState.WRONG_OBJECT_TYPE);
0287: if (isFunction) {
0288: executeWithFlags(p_sql, 0);
0289: return 0;
0290: }
0291: if (executeWithFlags(p_sql, QueryExecutor.QUERY_NO_RESULTS))
0292: throw new PSQLException(
0293: GT
0294: .tr("A result was returned when none was expected."),
0295: PSQLState.TOO_MANY_RESULTS);
0296: return getUpdateCount();
0297: }
0298:
0299: /*
0300: * Execute a SQL INSERT, UPDATE or DELETE statement. In addition,
0301: * SQL statements that return nothing such as SQL DDL statements can
0302: * be executed.
0303: *
0304: * @return either the row count for INSERT, UPDATE or DELETE; or
0305: * * 0 for SQL statements that return nothing.
0306: * @exception SQLException if a database access error occurs
0307: */
0308: public int executeUpdate() throws SQLException {
0309: if (isFunction) {
0310: executeWithFlags(0);
0311: return 0;
0312: }
0313: if (executeWithFlags(QueryExecutor.QUERY_NO_RESULTS))
0314: throw new PSQLException(
0315: GT
0316: .tr("A result was returned when none was expected."),
0317: PSQLState.TOO_MANY_RESULTS);
0318:
0319: return getUpdateCount();
0320: }
0321:
0322: /*
0323: * Execute a SQL statement that may return multiple results. We
0324: * don't have to worry about this since we do not support multiple
0325: * ResultSets. You can use getResultSet or getUpdateCount to
0326: * retrieve the result.
0327: *
0328: * @param sql any SQL statement
0329: * @return true if the next result is a ResulSet, false if it is
0330: * an update count or there are no more results
0331: * @exception SQLException if a database access error occurs
0332: */
0333: public boolean execute(String p_sql) throws SQLException {
0334: if (preparedQuery != null)
0335: throw new PSQLException(
0336: GT
0337: .tr("Can''t use query methods that take a query string on a PreparedStatement."),
0338: PSQLState.WRONG_OBJECT_TYPE);
0339:
0340: return executeWithFlags(p_sql, 0);
0341: }
0342:
0343: public boolean executeWithFlags(String p_sql, int flags)
0344: throws SQLException {
0345: checkClosed();
0346: p_sql = replaceProcessing(p_sql);
0347: Query simpleQuery = connection.getQueryExecutor()
0348: .createSimpleQuery(p_sql);
0349: execute(simpleQuery, null, QueryExecutor.QUERY_ONESHOT | flags);
0350: this .lastSimpleQuery = simpleQuery;
0351: return (result != null && result.getResultSet() != null);
0352: }
0353:
0354: public boolean execute() throws SQLException {
0355: return executeWithFlags(0);
0356: }
0357:
0358: public boolean executeWithFlags(int flags) throws SQLException {
0359: checkClosed();
0360:
0361: execute(preparedQuery, preparedParameters, flags);
0362:
0363: // If we are executing and there are out parameters
0364: // callable statement function set the return data
0365:
0366: if (isFunction && returnTypeSet) {
0367: if (result == null || result.getResultSet() == null)
0368: throw new PSQLException(
0369: GT
0370: .tr("A CallableStatement was executed with nothing returned."),
0371: PSQLState.NO_DATA);
0372:
0373: ResultSet rs = result.getResultSet();
0374: if (!rs.next())
0375: throw new PSQLException(
0376: GT
0377: .tr("A CallableStatement was executed with nothing returned."),
0378: PSQLState.NO_DATA);
0379:
0380: // figure out how many columns
0381: int cols = rs.getMetaData().getColumnCount();
0382:
0383: int outParameterCount = preparedParameters
0384: .getOutParameterCount();
0385:
0386: if (cols != outParameterCount)
0387: throw new PSQLException(
0388: GT
0389: .tr("A CallableStatement was excecuted with an invalid number of parameters"),
0390: PSQLState.SYNTAX_ERROR);
0391:
0392: // reset last result fetched (for wasNull)
0393: lastIndex = 0;
0394:
0395: // allocate enough space for all possible parameters without regard to in/out
0396: callResult = new Object[preparedParameters
0397: .getParameterCount() + 1];
0398:
0399: // move them into the result set
0400: for (int i = 0, j = 0; i < cols; i++, j++) {
0401: // find the next out parameter, the assumption is that the functionReturnType
0402: // array will be initialized with 0 and only out parameters will have values
0403: // other than 0. 0 is the value for java.sql.Types.NULL, which should not
0404: // conflict
0405: while (j < functionReturnType.length
0406: && functionReturnType[j] == 0)
0407: j++;
0408:
0409: callResult[j] = rs.getObject(i + 1);
0410: int columnType = rs.getMetaData().getColumnType(i + 1);
0411:
0412: if (columnType != functionReturnType[j]) {
0413: // this is here for the sole purpose of passing the cts
0414: if (columnType == Types.DOUBLE
0415: && functionReturnType[j] == Types.REAL) {
0416: // return it as a float
0417: if (callResult[j] != null)
0418: callResult[j] = new Float(
0419: ((Double) callResult[j])
0420: .floatValue());
0421: } else {
0422: throw new PSQLException(
0423: GT
0424: .tr(
0425: "A CallableStatement function was executed and the out parameter {0} was of type {1} however type {2} was registered.",
0426: new Object[] {
0427: new Integer(
0428: i + 1),
0429: "java.sql.Types="
0430: + columnType,
0431: "java.sql.Types="
0432: + functionReturnType[j] }),
0433: PSQLState.DATA_TYPE_MISMATCH);
0434: }
0435: }
0436:
0437: }
0438: rs.close();
0439: result = null;
0440: return false;
0441: }
0442:
0443: return (result != null && result.getResultSet() != null);
0444: }
0445:
0446: protected void execute(Query queryToExecute,
0447: ParameterList queryParameters, int flags)
0448: throws SQLException {
0449: // Every statement execution clears any previous warnings.
0450: clearWarnings();
0451:
0452: // Close any existing resultsets associated with this statement.
0453: while (firstUnclosedResult != null) {
0454: if (firstUnclosedResult.getResultSet() != null)
0455: firstUnclosedResult.getResultSet().close();
0456: firstUnclosedResult = firstUnclosedResult.getNext();
0457: }
0458:
0459: if (lastSimpleQuery != null) {
0460: lastSimpleQuery.close();
0461: lastSimpleQuery = null;
0462: }
0463:
0464: // Enable cursor-based resultset if possible.
0465: if (fetchSize > 0 && !wantsScrollableResultSet()
0466: && !connection.getAutoCommit()
0467: && !wantsHoldableResultSet())
0468: flags |= QueryExecutor.QUERY_FORWARD_CURSOR;
0469:
0470: // Only use named statements after we hit the threshold
0471: if (preparedQuery != null) {
0472: ++m_useCount; // We used this statement once more.
0473: if (m_prepareThreshold == 0
0474: || m_useCount < m_prepareThreshold)
0475: flags |= QueryExecutor.QUERY_ONESHOT;
0476: }
0477:
0478: if (connection.getAutoCommit())
0479: flags |= QueryExecutor.QUERY_SUPPRESS_BEGIN;
0480:
0481: StatementResultHandler handler = new StatementResultHandler();
0482: result = null;
0483: connection.getQueryExecutor().execute(queryToExecute,
0484: queryParameters, handler, maxrows, fetchSize, flags);
0485: result = firstUnclosedResult = handler.getResults();
0486: }
0487:
0488: /*
0489: * setCursorName defines the SQL cursor name that will be used by
0490: * subsequent execute methods. This name can then be used in SQL
0491: * positioned update/delete statements to identify the current row
0492: * in the ResultSet generated by this statement. If a database
0493: * doesn't support positioned update/delete, this method is a
0494: * no-op.
0495: *
0496: * <p><B>Note:</B> By definition, positioned update/delete execution
0497: * must be done by a different Statement than the one which
0498: * generated the ResultSet being used for positioning. Also, cursor
0499: * names must be unique within a Connection.
0500: *
0501: * @param name the new cursor name
0502: * @exception SQLException if a database access error occurs
0503: */
0504: public void setCursorName(String name) throws SQLException {
0505: checkClosed();
0506: // No-op.
0507: }
0508:
0509: protected boolean isClosed = false;
0510: private int lastIndex = 0;
0511:
0512: /*
0513: * getUpdateCount returns the current result as an update count,
0514: * if the result is a ResultSet or there are no more results, -1
0515: * is returned. It should only be called once per result.
0516: *
0517: * @return the current result as an update count.
0518: * @exception SQLException if a database access error occurs
0519: */
0520: public int getUpdateCount() throws SQLException {
0521: checkClosed();
0522: if (result == null)
0523: return -1;
0524:
0525: if (isFunction)
0526: return 1;
0527:
0528: if (result.getResultSet() != null)
0529: return -1;
0530:
0531: return result.getUpdateCount();
0532: }
0533:
0534: /*
0535: * getMoreResults moves to a Statement's next result. If it returns
0536: * true, this result is a ResulSet.
0537: *
0538: * @return true if the next ResultSet is valid
0539: * @exception SQLException if a database access error occurs
0540: */
0541: public boolean getMoreResults() throws SQLException {
0542: if (result == null)
0543: return false;
0544:
0545: result = result.getNext();
0546:
0547: // Close preceding resultsets.
0548: while (firstUnclosedResult != result) {
0549: if (firstUnclosedResult.getResultSet() != null)
0550: firstUnclosedResult.getResultSet().close();
0551: firstUnclosedResult = firstUnclosedResult.getNext();
0552: }
0553:
0554: return (result != null && result.getResultSet() != null);
0555: }
0556:
0557: /*
0558: * The maxRows limit is set to limit the number of rows that
0559: * any ResultSet can contain. If the limit is exceeded, the
0560: * excess rows are silently dropped.
0561: *
0562: * @return the current maximum row limit; zero means unlimited
0563: * @exception SQLException if a database access error occurs
0564: */
0565: public int getMaxRows() throws SQLException {
0566: checkClosed();
0567: return maxrows;
0568: }
0569:
0570: /*
0571: * Set the maximum number of rows
0572: *
0573: * @param max the new max rows limit; zero means unlimited
0574: * @exception SQLException if a database access error occurs
0575: * @see getMaxRows
0576: */
0577: public void setMaxRows(int max) throws SQLException {
0578: checkClosed();
0579: if (max < 0)
0580: throw new PSQLException(
0581: GT
0582: .tr("Maximum number of rows must be a value grater than or equal to 0."),
0583: PSQLState.INVALID_PARAMETER_VALUE);
0584: maxrows = max;
0585: }
0586:
0587: /*
0588: * If escape scanning is on (the default), the driver will do escape
0589: * substitution before sending the SQL to the database.
0590: *
0591: * @param enable true to enable; false to disable
0592: * @exception SQLException if a database access error occurs
0593: */
0594: public void setEscapeProcessing(boolean enable) throws SQLException {
0595: checkClosed();
0596: replaceProcessingEnabled = enable;
0597: }
0598:
0599: /*
0600: * The queryTimeout limit is the number of seconds the driver
0601: * will wait for a Statement to execute. If the limit is
0602: * exceeded, a SQLException is thrown.
0603: *
0604: * @return the current query timeout limit in seconds; 0 = unlimited
0605: * @exception SQLException if a database access error occurs
0606: */
0607: public int getQueryTimeout() throws SQLException {
0608: checkClosed();
0609: return timeout;
0610: }
0611:
0612: /*
0613: * Sets the queryTimeout limit
0614: *
0615: * @param seconds - the new query timeout limit in seconds
0616: * @exception SQLException if a database access error occurs
0617: */
0618: public void setQueryTimeout(int seconds) throws SQLException {
0619: checkClosed();
0620: if (seconds < 0)
0621: throw new PSQLException(
0622: GT
0623: .tr("Query timeout must be a value greater than or equals to 0."),
0624: PSQLState.INVALID_PARAMETER_VALUE);
0625: timeout = seconds;
0626: }
0627:
0628: /**
0629: * This adds a warning to the warning chain.
0630: * @param warn warning to add
0631: */
0632: public void addWarning(SQLWarning warn) {
0633: if (warnings != null)
0634: warnings.setNextWarning(warn);
0635: else
0636: warnings = warn;
0637: }
0638:
0639: /*
0640: * The first warning reported by calls on this Statement is
0641: * returned. A Statement's execute methods clear its SQLWarning
0642: * chain. Subsequent Statement warnings will be chained to this
0643: * SQLWarning.
0644: *
0645: * <p>The Warning chain is automatically cleared each time a statement
0646: * is (re)executed.
0647: *
0648: * <p><B>Note:</B> If you are processing a ResultSet then any warnings
0649: * associated with ResultSet reads will be chained on the ResultSet
0650: * object.
0651: *
0652: * @return the first SQLWarning on null
0653: * @exception SQLException if a database access error occurs
0654: */
0655: public SQLWarning getWarnings() throws SQLException {
0656: checkClosed();
0657: return warnings;
0658: }
0659:
0660: /*
0661: * The maxFieldSize limit (in bytes) is the maximum amount of
0662: * data returned for any column value; it only applies to
0663: * BINARY, VARBINARY, LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR
0664: * columns. If the limit is exceeded, the excess data is silently
0665: * discarded.
0666: *
0667: * @return the current max column size limit; zero means unlimited
0668: * @exception SQLException if a database access error occurs
0669: */
0670: public int getMaxFieldSize() throws SQLException {
0671: return maxfieldSize;
0672: }
0673:
0674: /*
0675: * Sets the maxFieldSize
0676: *
0677: * @param max the new max column size limit; zero means unlimited
0678: * @exception SQLException if a database access error occurs
0679: */
0680: public void setMaxFieldSize(int max) throws SQLException {
0681: checkClosed();
0682: if (max < 0)
0683: throw new PSQLException(
0684: GT
0685: .tr("The maximum field size must be a value greater than or equal to 0."),
0686: PSQLState.INVALID_PARAMETER_VALUE);
0687: maxfieldSize = max;
0688: }
0689:
0690: /*
0691: * After this call, getWarnings returns null until a new warning
0692: * is reported for this Statement.
0693: *
0694: * @exception SQLException if a database access error occurs
0695: */
0696: public void clearWarnings() throws SQLException {
0697: warnings = null;
0698: }
0699:
0700: /*
0701: * getResultSet returns the current result as a ResultSet. It
0702: * should only be called once per result.
0703: *
0704: * @return the current result set; null if there are no more
0705: * @exception SQLException if a database access error occurs (why?)
0706: */
0707: public java.sql.ResultSet getResultSet() throws SQLException {
0708: checkClosed();
0709:
0710: if (result == null)
0711: return null;
0712:
0713: return (ResultSet) result.getResultSet();
0714: }
0715:
0716: /*
0717: * In many cases, it is desirable to immediately release a
0718: * Statement's database and JDBC resources instead of waiting
0719: * for this to happen when it is automatically closed. The
0720: * close method provides this immediate release.
0721: *
0722: * <p><B>Note:</B> A Statement is automatically closed when it is
0723: * garbage collected. When a Statement is closed, its current
0724: * ResultSet, if one exists, is also closed.
0725: *
0726: * @exception SQLException if a database access error occurs (why?)
0727: */
0728: public void close() throws SQLException {
0729: // closing an already closed Statement is a no-op.
0730: if (isClosed)
0731: return;
0732:
0733: // Force the ResultSet(s) to close
0734: while (firstUnclosedResult != null) {
0735: if (firstUnclosedResult.getResultSet() != null)
0736: firstUnclosedResult.getResultSet().close();
0737: firstUnclosedResult = firstUnclosedResult.getNext();
0738: }
0739:
0740: if (lastSimpleQuery != null)
0741: lastSimpleQuery.close();
0742:
0743: if (preparedQuery != null)
0744: preparedQuery.close();
0745:
0746: // Disasociate it from us
0747: result = firstUnclosedResult = null;
0748: isClosed = true;
0749: }
0750:
0751: /**
0752: * This finalizer ensures that statements that have allocated server-side
0753: * resources free them when they become unreferenced.
0754: */
0755: protected void finalize() {
0756: try {
0757: close();
0758: } catch (SQLException e) {
0759: }
0760: }
0761:
0762: /*
0763: * Filter the SQL string of Java SQL Escape clauses.
0764: *
0765: * Currently implemented Escape clauses are those mentioned in 11.3
0766: * in the specification. Basically we look through the sql string for
0767: * {d xxx}, {t xxx}, {ts xxx}, {oj xxx} or {fn xxx} in non-string sql
0768: * code. When we find them, we just strip the escape part leaving only
0769: * the xxx part.
0770: * So, something like "select * from x where d={d '2001-10-09'}" would
0771: * return "select * from x where d= '2001-10-09'".
0772: */
0773: protected String replaceProcessing(String p_sql)
0774: throws SQLException {
0775: if (replaceProcessingEnabled) {
0776: // Since escape codes can only appear in SQL CODE, we keep track
0777: // of if we enter a string or not.
0778: int len = p_sql.length();
0779: StringBuffer newsql = new StringBuffer(len);
0780: int i = 0;
0781: while (i < len) {
0782: i = parseSql(p_sql, i, newsql, false, connection
0783: .getStandardConformingStrings());
0784: // We need to loop here in case we encounter invalid
0785: // SQL, consider: SELECT a FROM t WHERE (1 > 0)) ORDER BY a
0786: // We can't ending replacing after the extra closing paren
0787: // because that changes a syntax error to a valid query
0788: // that isn't what the user specified.
0789: if (i < len) {
0790: newsql.append(p_sql.charAt(i));
0791: i++;
0792: }
0793: }
0794: return newsql.toString();
0795: } else {
0796: return p_sql;
0797: }
0798: }
0799:
0800: /**
0801: * parse the given sql from index i, appending it to the gven buffer
0802: * until we hit an unmatched right parentheses or end of string. When
0803: * the stopOnComma flag is set we also stop processing when a comma is
0804: * found in sql text that isn't inside nested parenthesis.
0805: *
0806: * @param p_sql the original query text
0807: * @param i starting position for replacing
0808: * @param newsql where to write the replaced output
0809: * @param stopOnComma should we stop after hitting the first comma in sql text?
0810: * @param stdStrings whether standard_conforming_strings is on
0811: * @return the position we stopped processing at
0812: */
0813: protected static int parseSql(String p_sql, int i,
0814: StringBuffer newsql, boolean stopOnComma, boolean stdStrings)
0815: throws SQLException {
0816: short state = IN_SQLCODE;
0817: int len = p_sql.length();
0818: int nestedParenthesis = 0;
0819: boolean endOfNested = false;
0820:
0821: // because of the ++i loop
0822: i--;
0823: while (!endOfNested && ++i < len) {
0824: char c = p_sql.charAt(i);
0825: switch (state) {
0826: case IN_SQLCODE:
0827: if (c == '\'') // start of a string?
0828: state = IN_STRING;
0829: else if (c == '"') // start of a identifer?
0830: state = IN_IDENTIFIER;
0831: else if (c == '(') { // begin nested sql
0832: nestedParenthesis++;
0833: } else if (c == ')') { // end of nested sql
0834: nestedParenthesis--;
0835: if (nestedParenthesis < 0) {
0836: endOfNested = true;
0837: break;
0838: }
0839: } else if (stopOnComma && c == ','
0840: && nestedParenthesis == 0) {
0841: endOfNested = true;
0842: break;
0843: } else if (c == '{') { // start of an escape code?
0844: if (i + 1 < len) {
0845: char next = p_sql.charAt(i + 1);
0846: char nextnext = (i + 2 < len) ? p_sql
0847: .charAt(i + 2) : '\0';
0848: if (next == 'd' || next == 'D') {
0849: state = ESC_TIMEDATE;
0850: i++;
0851: newsql.append("DATE ");
0852: break;
0853: } else if (next == 't' || next == 'T') {
0854: state = ESC_TIMEDATE;
0855: if (nextnext == 's' || nextnext == 'S') {
0856: // timestamp constant
0857: i += 2;
0858: newsql.append("TIMESTAMP ");
0859: } else {
0860: // time constant
0861: i++;
0862: newsql.append("TIME ");
0863: }
0864: break;
0865: } else if (next == 'f' || next == 'F') {
0866: state = ESC_FUNCTION;
0867: i += (nextnext == 'n' || nextnext == 'N') ? 2
0868: : 1;
0869: break;
0870: } else if (next == 'o' || next == 'O') {
0871: state = ESC_OUTERJOIN;
0872: i += (nextnext == 'j' || nextnext == 'J') ? 2
0873: : 1;
0874: break;
0875: } else if (next == 'e' || next == 'E') { // we assume that escape is the only escape sequence beginning with e
0876: state = ESC_ESCAPECHAR;
0877: break;
0878: }
0879: }
0880: }
0881: newsql.append(c);
0882: break;
0883:
0884: case IN_STRING:
0885: if (c == '\'') // end of string?
0886: state = IN_SQLCODE;
0887: else if (c == '\\' && !stdStrings) // a backslash?
0888: state = BACKSLASH;
0889:
0890: newsql.append(c);
0891: break;
0892:
0893: case IN_IDENTIFIER:
0894: if (c == '"') // end of identifier
0895: state = IN_SQLCODE;
0896: newsql.append(c);
0897: break;
0898:
0899: case BACKSLASH:
0900: state = IN_STRING;
0901:
0902: newsql.append(c);
0903: break;
0904:
0905: case ESC_FUNCTION:
0906: // extract function name
0907: String functionName;
0908: int posArgs = p_sql.indexOf('(', i);
0909: if (posArgs != -1) {
0910: functionName = p_sql.substring(i, posArgs).trim();
0911: // extract arguments
0912: i = posArgs + 1;// we start the scan after the first (
0913: StringBuffer args = new StringBuffer();
0914: i = parseSql(p_sql, i, args, false, stdStrings);
0915: // translate the function and parse arguments
0916: newsql.append(escapeFunction(functionName, args
0917: .toString(), stdStrings));
0918: }
0919: // go to the end of the function copying anything found
0920: i++;
0921: while (i < len && p_sql.charAt(i) != '}')
0922: newsql.append(p_sql.charAt(i++));
0923: state = IN_SQLCODE; // end of escaped function (or query)
0924: break;
0925: case ESC_TIMEDATE:
0926: case ESC_OUTERJOIN:
0927: case ESC_ESCAPECHAR:
0928: if (c == '}')
0929: state = IN_SQLCODE; // end of escape code.
0930: else
0931: newsql.append(c);
0932: break;
0933: } // end switch
0934: }
0935: return i;
0936: }
0937:
0938: /**
0939: * generate sql for escaped functions
0940: * @param functionName the escaped function name
0941: * @param args the arguments for this functin
0942: * @param stdStrings whether standard_conforming_strings is on
0943: * @return the right postgreSql sql
0944: */
0945: protected static String escapeFunction(String functionName,
0946: String args, boolean stdStrings) throws SQLException {
0947: // parse function arguments
0948: int len = args.length();
0949: int i = 0;
0950: ArrayList parsedArgs = new ArrayList();
0951: while (i < len) {
0952: StringBuffer arg = new StringBuffer();
0953: int lastPos = i;
0954: i = parseSql(args, i, arg, true, stdStrings);
0955: if (lastPos != i) {
0956: parsedArgs.add(arg);
0957: }
0958: i++;
0959: }
0960: // we can now tranlate escape functions
0961: try {
0962: Method escapeMethod = EscapedFunctions
0963: .getFunction(functionName);
0964: return (String) escapeMethod.invoke(null,
0965: new Object[] { parsedArgs });
0966: } catch (InvocationTargetException e) {
0967: if (e.getTargetException() instanceof SQLException)
0968: throw (SQLException) e.getTargetException();
0969: else
0970: throw new PSQLException(e.getTargetException()
0971: .getMessage(), PSQLState.SYSTEM_ERROR);
0972: } catch (Exception e) {
0973: // by default the function name is kept unchanged
0974: StringBuffer buf = new StringBuffer();
0975: buf.append(functionName).append('(');
0976: for (int iArg = 0; iArg < parsedArgs.size(); iArg++) {
0977: buf.append(parsedArgs.get(iArg));
0978: if (iArg != (parsedArgs.size() - 1))
0979: buf.append(',');
0980: }
0981: buf.append(')');
0982: return buf.toString();
0983: }
0984: }
0985:
0986: /*
0987: *
0988: * The following methods are postgres extensions and are defined
0989: * in the interface BaseStatement
0990: *
0991: */
0992:
0993: /*
0994: * Returns the Last inserted/updated oid. Deprecated in 7.2 because
0995: * range of OID values is greater than a java signed int.
0996: * @deprecated Replaced by getLastOID in 7.2
0997: */
0998: public int getInsertedOID() throws SQLException {
0999: checkClosed();
1000: if (result == null)
1001: return 0;
1002: return (int) result.getInsertOID();
1003: }
1004:
1005: /*
1006: * Returns the Last inserted/updated oid.
1007: * @return OID of last insert
1008: * @since 7.2
1009: */
1010: public long getLastOID() throws SQLException {
1011: checkClosed();
1012: if (result == null)
1013: return 0;
1014: return result.getInsertOID();
1015: }
1016:
1017: /*
1018: * Set a parameter to SQL NULL
1019: *
1020: * <p><B>Note:</B> You must specify the parameter's SQL type.
1021: *
1022: * @param parameterIndex the first parameter is 1, etc...
1023: * @param sqlType the SQL type code defined in java.sql.Types
1024: * @exception SQLException if a database access error occurs
1025: */
1026: public void setNull(int parameterIndex, int sqlType)
1027: throws SQLException {
1028: checkClosed();
1029:
1030: int oid;
1031: switch (sqlType) {
1032: case Types.INTEGER:
1033: oid = Oid.INT4;
1034: break;
1035: case Types.TINYINT:
1036: case Types.SMALLINT:
1037: oid = Oid.INT2;
1038: break;
1039: case Types.BIGINT:
1040: oid = Oid.INT8;
1041: break;
1042: case Types.REAL:
1043: oid = Oid.FLOAT4;
1044: break;
1045: case Types.DOUBLE:
1046: case Types.FLOAT:
1047: oid = Oid.FLOAT8;
1048: break;
1049: case Types.DECIMAL:
1050: case Types.NUMERIC:
1051: oid = Oid.NUMERIC;
1052: break;
1053: case Types.CHAR:
1054: oid = Oid.BPCHAR;
1055: break;
1056: case Types.VARCHAR:
1057: case Types.LONGVARCHAR:
1058: oid = Oid.VARCHAR;
1059: break;
1060: case Types.DATE:
1061: oid = Oid.DATE;
1062: break;
1063: case Types.TIME:
1064: oid = Oid.TIME;
1065: break;
1066: case Types.TIMESTAMP:
1067: oid = Oid.TIMESTAMPTZ;
1068: break;
1069: case Types.BIT:
1070: oid = Oid.BOOL;
1071: break;
1072: case Types.BINARY:
1073: case Types.VARBINARY:
1074: case Types.LONGVARBINARY:
1075: if (connection.haveMinimumCompatibleVersion("7.2")) {
1076: oid = Oid.BYTEA;
1077: } else {
1078: oid = Oid.OID;
1079: }
1080: break;
1081: case Types.BLOB:
1082: case Types.CLOB:
1083: oid = Oid.OID;
1084: break;
1085: case Types.ARRAY:
1086: case Types.DISTINCT:
1087: case Types.STRUCT:
1088: case Types.NULL:
1089: case Types.OTHER:
1090: oid = Oid.UNSPECIFIED;
1091: break;
1092: default:
1093: // Bad Types value.
1094: throw new PSQLException(GT.tr("Unknown Types value."),
1095: PSQLState.INVALID_PARAMETER_TYPE);
1096: }
1097: if (adjustIndex)
1098: parameterIndex--;
1099: preparedParameters.setNull(parameterIndex, oid);
1100: }
1101:
1102: /*
1103: * Set a parameter to a Java boolean value. The driver converts this
1104: * to a SQL BIT value when it sends it to the database.
1105: *
1106: * @param parameterIndex the first parameter is 1...
1107: * @param x the parameter value
1108: * @exception SQLException if a database access error occurs
1109: */
1110: public void setBoolean(int parameterIndex, boolean x)
1111: throws SQLException {
1112: checkClosed();
1113: bindString(parameterIndex, x ? "1" : "0", Oid.BOOL);
1114: }
1115:
1116: /*
1117: * Set a parameter to a Java byte value. The driver converts this to
1118: * a SQL TINYINT value when it sends it to the database.
1119: *
1120: * @param parameterIndex the first parameter is 1...
1121: * @param x the parameter value
1122: * @exception SQLException if a database access error occurs
1123: */
1124: public void setByte(int parameterIndex, byte x) throws SQLException {
1125: checkClosed();
1126: bindLiteral(parameterIndex, Integer.toString(x), Oid.INT2);
1127: }
1128:
1129: /*
1130: * Set a parameter to a Java short value. The driver converts this
1131: * to a SQL SMALLINT value when it sends it to the database.
1132: *
1133: * @param parameterIndex the first parameter is 1...
1134: * @param x the parameter value
1135: * @exception SQLException if a database access error occurs
1136: */
1137: public void setShort(int parameterIndex, short x)
1138: throws SQLException {
1139: checkClosed();
1140: bindLiteral(parameterIndex, Integer.toString(x), Oid.INT2);
1141: }
1142:
1143: /*
1144: * Set a parameter to a Java int value. The driver converts this to
1145: * a SQL INTEGER value when it sends it to the database.
1146: *
1147: * @param parameterIndex the first parameter is 1...
1148: * @param x the parameter value
1149: * @exception SQLException if a database access error occurs
1150: */
1151: public void setInt(int parameterIndex, int x) throws SQLException {
1152: checkClosed();
1153: bindLiteral(parameterIndex, Integer.toString(x), Oid.INT4);
1154: }
1155:
1156: /*
1157: * Set a parameter to a Java long value. The driver converts this to
1158: * a SQL BIGINT value when it sends it to the database.
1159: *
1160: * @param parameterIndex the first parameter is 1...
1161: * @param x the parameter value
1162: * @exception SQLException if a database access error occurs
1163: */
1164: public void setLong(int parameterIndex, long x) throws SQLException {
1165: checkClosed();
1166: bindLiteral(parameterIndex, Long.toString(x), Oid.INT8);
1167: }
1168:
1169: /*
1170: * Set a parameter to a Java float value. The driver converts this
1171: * to a SQL FLOAT value when it sends it to the database.
1172: *
1173: * @param parameterIndex the first parameter is 1...
1174: * @param x the parameter value
1175: * @exception SQLException if a database access error occurs
1176: */
1177: public void setFloat(int parameterIndex, float x)
1178: throws SQLException {
1179: checkClosed();
1180: bindLiteral(parameterIndex, Float.toString(x), Oid.FLOAT8);
1181: }
1182:
1183: /*
1184: * Set a parameter to a Java double value. The driver converts this
1185: * to a SQL DOUBLE value when it sends it to the database
1186: *
1187: * @param parameterIndex the first parameter is 1...
1188: * @param x the parameter value
1189: * @exception SQLException if a database access error occurs
1190: */
1191: public void setDouble(int parameterIndex, double x)
1192: throws SQLException {
1193: checkClosed();
1194: bindLiteral(parameterIndex, Double.toString(x), Oid.FLOAT8);
1195: }
1196:
1197: /*
1198: * Set a parameter to a java.lang.BigDecimal value. The driver
1199: * converts this to a SQL NUMERIC value when it sends it to the
1200: * database.
1201: *
1202: * @param parameterIndex the first parameter is 1...
1203: * @param x the parameter value
1204: * @exception SQLException if a database access error occurs
1205: */
1206: public void setBigDecimal(int parameterIndex, BigDecimal x)
1207: throws SQLException {
1208: checkClosed();
1209: if (x == null)
1210: setNull(parameterIndex, Types.DECIMAL);
1211: else
1212: bindLiteral(parameterIndex, x.toString(), Oid.NUMERIC);
1213: }
1214:
1215: /*
1216: * Set a parameter to a Java String value. The driver converts this
1217: * to a SQL VARCHAR or LONGVARCHAR value (depending on the arguments
1218: * size relative to the driver's limits on VARCHARs) when it sends it
1219: * to the database.
1220: *
1221: * @param parameterIndex the first parameter is 1...
1222: * @param x the parameter value
1223: * @exception SQLException if a database access error occurs
1224: */
1225: public void setString(int parameterIndex, String x)
1226: throws SQLException {
1227: checkClosed();
1228: setString(parameterIndex, x,
1229: (connection.getStringVarcharFlag() ? Oid.VARCHAR
1230: : Oid.UNSPECIFIED));
1231: }
1232:
1233: protected void setString(int parameterIndex, String x, int oid)
1234: throws SQLException {
1235: // if the passed string is null, then set this column to null
1236: checkClosed();
1237: if (x == null) {
1238: if (adjustIndex)
1239: parameterIndex--;
1240: preparedParameters.setNull(parameterIndex, oid);
1241: } else
1242: bindString(parameterIndex, x, oid);
1243: }
1244:
1245: /*
1246: * Set a parameter to a Java array of bytes. The driver converts this
1247: * to a SQL VARBINARY or LONGVARBINARY (depending on the argument's
1248: * size relative to the driver's limits on VARBINARYs) when it sends
1249: * it to the database.
1250: *
1251: * <p>Implementation note:
1252: * <br>With org.postgresql, this creates a large object, and stores the
1253: * objects oid in this column.
1254: *
1255: * @param parameterIndex the first parameter is 1...
1256: * @param x the parameter value
1257: * @exception SQLException if a database access error occurs
1258: */
1259: public void setBytes(int parameterIndex, byte[] x)
1260: throws SQLException {
1261: checkClosed();
1262:
1263: if (null == x) {
1264: setNull(parameterIndex, Types.VARBINARY);
1265: return;
1266: }
1267:
1268: if (connection.haveMinimumCompatibleVersion("7.2")) {
1269: //Version 7.2 supports the bytea datatype for byte arrays
1270: byte[] copy = new byte[x.length];
1271: System.arraycopy(x, 0, copy, 0, x.length);
1272: preparedParameters.setBytea(parameterIndex, copy, 0,
1273: x.length);
1274: } else {
1275: //Version 7.1 and earlier support done as LargeObjects
1276: LargeObjectManager lom = connection.getLargeObjectAPI();
1277: long oid = lom.createLO();
1278: LargeObject lob = lom.open(oid);
1279: lob.write(x);
1280: lob.close();
1281: setLong(parameterIndex, oid);
1282: }
1283: }
1284:
1285: /*
1286: * Set a parameter to a java.sql.Date value. The driver converts this
1287: * to a SQL DATE value when it sends it to the database.
1288: *
1289: * @param parameterIndex the first parameter is 1...
1290: * @param x the parameter value
1291: * @exception SQLException if a database access error occurs
1292: */
1293: public void setDate(int parameterIndex, java.sql.Date x)
1294: throws SQLException {
1295: setDate(parameterIndex, x, null);
1296: }
1297:
1298: /*
1299: * Set a parameter to a java.sql.Time value. The driver converts
1300: * this to a SQL TIME value when it sends it to the database.
1301: *
1302: * @param parameterIndex the first parameter is 1...));
1303: * @param x the parameter value
1304: * @exception SQLException if a database access error occurs
1305: */
1306: public void setTime(int parameterIndex, Time x) throws SQLException {
1307: setTime(parameterIndex, x, null);
1308: }
1309:
1310: /*
1311: * Set a parameter to a java.sql.Timestamp value. The driver converts
1312: * this to a SQL TIMESTAMP value when it sends it to the database.
1313: *
1314: * @param parameterIndex the first parameter is 1...
1315: * @param x the parameter value
1316: * @exception SQLException if a database access error occurs
1317: */
1318: public void setTimestamp(int parameterIndex, Timestamp x)
1319: throws SQLException {
1320: setTimestamp(parameterIndex, x, null);
1321: }
1322:
1323: private void setCharacterStreamPost71(int parameterIndex,
1324: InputStream x, int length, String encoding)
1325: throws SQLException {
1326:
1327: if (x == null) {
1328: setNull(parameterIndex, Types.VARCHAR);
1329: return;
1330: }
1331: if (length < 0)
1332: throw new PSQLException(GT.tr("Invalid stream length {0}.",
1333: new Integer(length)),
1334: PSQLState.INVALID_PARAMETER_VALUE);
1335:
1336: //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text)
1337: //As the spec/javadoc for this method indicate this is to be used for
1338: //large String values (i.e. LONGVARCHAR) PG doesn't have a separate
1339: //long varchar datatype, but with toast all text datatypes are capable of
1340: //handling very large values. Thus the implementation ends up calling
1341: //setString() since there is no current way to stream the value to the server
1342: try {
1343: InputStreamReader l_inStream = new InputStreamReader(x,
1344: encoding);
1345: char[] l_chars = new char[length];
1346: int l_charsRead = 0;
1347: while (true) {
1348: int n = l_inStream.read(l_chars, l_charsRead, length
1349: - l_charsRead);
1350: if (n == -1)
1351: break;
1352:
1353: l_charsRead += n;
1354:
1355: if (l_charsRead == length)
1356: break;
1357: }
1358:
1359: setString(parameterIndex, new String(l_chars, 0,
1360: l_charsRead), Oid.VARCHAR);
1361: } catch (UnsupportedEncodingException l_uee) {
1362: throw new PSQLException(GT.tr(
1363: "The JVM claims not to support the {0} encoding.",
1364: encoding), PSQLState.UNEXPECTED_ERROR, l_uee);
1365: } catch (IOException l_ioe) {
1366: throw new PSQLException(GT
1367: .tr("Provided InputStream failed."),
1368: PSQLState.UNEXPECTED_ERROR, l_ioe);
1369: }
1370: }
1371:
1372: /*
1373: * When a very large ASCII value is input to a LONGVARCHAR parameter,
1374: * it may be more practical to send it via a java.io.InputStream.
1375: * JDBC will read the data from the stream as needed, until it reaches
1376: * end-of-file. The JDBC driver will do any necessary conversion from
1377: * ASCII to the database char format.
1378: *
1379: * <P><B>Note:</B> This stream object can either be a standard Java
1380: * stream object or your own subclass that implements the standard
1381: * interface.
1382: *
1383: * @param parameterIndex the first parameter is 1...
1384: * @param x the parameter value
1385: * @param length the number of bytes in the stream
1386: * @exception SQLException if a database access error occurs
1387: */
1388: public void setAsciiStream(int parameterIndex, InputStream x,
1389: int length) throws SQLException {
1390: checkClosed();
1391: if (connection.haveMinimumCompatibleVersion("7.2")) {
1392: setCharacterStreamPost71(parameterIndex, x, length, "ASCII");
1393: } else {
1394: //Version 7.1 supported only LargeObjects by treating everything
1395: //as binary data
1396: setBinaryStream(parameterIndex, x, length);
1397: }
1398: }
1399:
1400: /*
1401: * When a very large Unicode value is input to a LONGVARCHAR parameter,
1402: * it may be more practical to send it via a java.io.InputStream.
1403: * JDBC will read the data from the stream as needed, until it reaches
1404: * end-of-file. The JDBC driver will do any necessary conversion from
1405: * UNICODE to the database char format.
1406: *
1407: * <P><B>Note:</B> This stream object can either be a standard Java
1408: * stream object or your own subclass that implements the standard
1409: * interface.
1410: *
1411: * @param parameterIndex the first parameter is 1...
1412: * @param x the parameter value
1413: * @exception SQLException if a database access error occurs
1414: */
1415: public void setUnicodeStream(int parameterIndex, InputStream x,
1416: int length) throws SQLException {
1417: checkClosed();
1418: if (connection.haveMinimumCompatibleVersion("7.2")) {
1419: setCharacterStreamPost71(parameterIndex, x, length, "UTF-8");
1420: } else {
1421: //Version 7.1 supported only LargeObjects by treating everything
1422: //as binary data
1423: setBinaryStream(parameterIndex, x, length);
1424: }
1425: }
1426:
1427: /*
1428: * When a very large binary value is input to a LONGVARBINARY parameter,
1429: * it may be more practical to send it via a java.io.InputStream.
1430: * JDBC will read the data from the stream as needed, until it reaches
1431: * end-of-file.
1432: *
1433: * <P><B>Note:</B> This stream object can either be a standard Java
1434: * stream object or your own subclass that implements the standard
1435: * interface.
1436: *
1437: * @param parameterIndex the first parameter is 1...
1438: * @param x the parameter value
1439: * @exception SQLException if a database access error occurs
1440: */
1441: public void setBinaryStream(int parameterIndex, InputStream x,
1442: int length) throws SQLException {
1443: checkClosed();
1444:
1445: if (x == null) {
1446: setNull(parameterIndex, Types.VARBINARY);
1447: return;
1448: }
1449:
1450: if (length < 0)
1451: throw new PSQLException(GT.tr("Invalid stream length {0}.",
1452: new Integer(length)),
1453: PSQLState.INVALID_PARAMETER_VALUE);
1454:
1455: if (connection.haveMinimumCompatibleVersion("7.2")) {
1456: //Version 7.2 supports BinaryStream for for the PG bytea type
1457: //As the spec/javadoc for this method indicate this is to be used for
1458: //large binary values (i.e. LONGVARBINARY) PG doesn't have a separate
1459: //long binary datatype, but with toast the bytea datatype is capable of
1460: //handling very large values.
1461:
1462: preparedParameters.setBytea(parameterIndex, x, length);
1463: } else {
1464: //Version 7.1 only supported streams for LargeObjects
1465: //but the jdbc spec indicates that streams should be
1466: //available for LONGVARBINARY instead
1467: LargeObjectManager lom = connection.getLargeObjectAPI();
1468: long oid = lom.createLO();
1469: LargeObject lob = lom.open(oid);
1470: OutputStream los = lob.getOutputStream();
1471: try {
1472: // could be buffered, but then the OutputStream returned by LargeObject
1473: // is buffered internally anyhow, so there would be no performance
1474: // boost gained, if anything it would be worse!
1475: int c = x.read();
1476: int p = 0;
1477: while (c > -1 && p < length) {
1478: los.write(c);
1479: c = x.read();
1480: p++;
1481: }
1482: los.close();
1483: } catch (IOException se) {
1484: throw new PSQLException(GT
1485: .tr("Provided InputStream failed."),
1486: PSQLState.UNEXPECTED_ERROR, se);
1487: }
1488: // lob is closed by the stream so don't call lob.close()
1489: setLong(parameterIndex, oid);
1490: }
1491: }
1492:
1493: /*
1494: * In general, parameter values remain in force for repeated used of a
1495: * Statement. Setting a parameter value automatically clears its
1496: * previous value. However, in coms cases, it is useful to immediately
1497: * release the resources used by the current parameter values; this
1498: * can be done by calling clearParameters
1499: *
1500: * @exception SQLException if a database access error occurs
1501: */
1502: public void clearParameters() throws SQLException {
1503: preparedParameters.clear();
1504: }
1505:
1506: private PGType createInternalType(Object x, int targetType)
1507: throws PSQLException {
1508: if (x instanceof Byte)
1509: return PGByte.castToServerType((Byte) x, targetType);
1510: if (x instanceof Short)
1511: return PGShort.castToServerType((Short) x, targetType);
1512: if (x instanceof Integer)
1513: return PGInteger.castToServerType((Integer) x, targetType);
1514: if (x instanceof Long)
1515: return PGLong.castToServerType((Long) x, targetType);
1516: if (x instanceof Double)
1517: return PGDouble.castToServerType((Double) x, targetType);
1518: if (x instanceof Float)
1519: return PGFloat.castToServerType((Float) x, targetType);
1520: if (x instanceof BigDecimal)
1521: return PGBigDecimal.castToServerType((BigDecimal) x,
1522: targetType);
1523: // since all of the above are instances of Number make sure this is after them
1524: if (x instanceof Number)
1525: return PGNumber.castToServerType((Number) x, targetType);
1526: if (x instanceof Boolean)
1527: return PGBoolean.castToServerType((Boolean) x, targetType);
1528: return new PGUnknown(x);
1529:
1530: }
1531:
1532: // Helper method for setting parameters to PGobject subclasses.
1533: private void setPGobject(int parameterIndex, PGobject x)
1534: throws SQLException {
1535: String typename = x.getType();
1536: int oid = connection.getPGType(typename);
1537: if (oid == Oid.UNSPECIFIED)
1538: throw new PSQLException(GT
1539: .tr("Unknown type {0}.", typename),
1540: PSQLState.INVALID_PARAMETER_TYPE);
1541:
1542: setString(parameterIndex, x.getValue(), oid);
1543: }
1544:
1545: /*
1546: * Set the value of a parameter using an object; use the java.lang
1547: * equivalent objects for integral values.
1548: *
1549: * <P>The given Java object will be converted to the targetSqlType before
1550: * being sent to the database.
1551: *
1552: * <P>note that this method may be used to pass database-specific
1553: * abstract data types. This is done by using a Driver-specific
1554: * Java type and using a targetSqlType of java.sql.Types.OTHER
1555: *
1556: * @param parameterIndex the first parameter is 1...
1557: * @param x the object containing the input parameter value
1558: * @param targetSqlType The SQL type to be send to the database
1559: * @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC
1560: * * types this is the number of digits after the decimal. For
1561: * * all other types this value will be ignored.
1562: * @exception SQLException if a database access error occurs
1563: */
1564: public void setObject(int parameterIndex, Object in,
1565: int targetSqlType, int scale) throws SQLException {
1566: checkClosed();
1567:
1568: if (in == null) {
1569: setNull(parameterIndex, targetSqlType);
1570: return;
1571: }
1572:
1573: Object pgType = createInternalType(in, targetSqlType);
1574: switch (targetSqlType) {
1575: case Types.INTEGER:
1576: bindLiteral(parameterIndex, pgType.toString(), Oid.INT4);
1577: break;
1578: case Types.TINYINT:
1579: case Types.SMALLINT:
1580: bindLiteral(parameterIndex, pgType.toString(), Oid.INT2);
1581: break;
1582: case Types.BIGINT:
1583: bindLiteral(parameterIndex, pgType.toString(), Oid.INT8);
1584: break;
1585: case Types.REAL:
1586: //TODO: is this really necessary ?
1587: //bindLiteral(parameterIndex, new Float(pgType.toString()).toString(), Oid.FLOAT4);
1588: bindLiteral(parameterIndex, pgType.toString(), Oid.FLOAT4);
1589: break;
1590: case Types.DOUBLE:
1591: case Types.FLOAT:
1592: bindLiteral(parameterIndex, pgType.toString(), Oid.FLOAT8);
1593: break;
1594: case Types.DECIMAL:
1595: case Types.NUMERIC:
1596: bindLiteral(parameterIndex, pgType.toString(), Oid.NUMERIC);
1597: break;
1598: case Types.CHAR:
1599: setString(parameterIndex, pgType.toString(), Oid.BPCHAR);
1600: break;
1601: case Types.VARCHAR:
1602: case Types.LONGVARCHAR:
1603: setString(parameterIndex, pgType.toString(), Oid.VARCHAR);
1604: break;
1605: case Types.DATE:
1606: if (in instanceof java.sql.Date)
1607: setDate(parameterIndex, (java.sql.Date) in);
1608: else {
1609: java.sql.Date tmpd;
1610: if (in instanceof java.util.Date) {
1611: tmpd = new java.sql.Date(((java.util.Date) in)
1612: .getTime());
1613: } else {
1614: tmpd = connection.getTimestampUtils().toDate(null,
1615: in.toString());
1616: }
1617: setDate(parameterIndex, tmpd);
1618: }
1619: break;
1620: case Types.TIME:
1621: if (in instanceof java.sql.Time)
1622: setTime(parameterIndex, (java.sql.Time) in);
1623: else {
1624: java.sql.Time tmpt;
1625: if (in instanceof java.util.Date) {
1626: tmpt = new java.sql.Time(((java.util.Date) in)
1627: .getTime());
1628: } else {
1629: tmpt = connection.getTimestampUtils().toTime(null,
1630: in.toString());
1631: }
1632: setTime(parameterIndex, tmpt);
1633: }
1634: break;
1635: case Types.TIMESTAMP:
1636: if (in instanceof java.sql.Timestamp)
1637: setTimestamp(parameterIndex, (java.sql.Timestamp) in);
1638: else {
1639: java.sql.Timestamp tmpts;
1640: if (in instanceof java.util.Date) {
1641: tmpts = new java.sql.Timestamp(
1642: ((java.util.Date) in).getTime());
1643: } else {
1644: tmpts = connection.getTimestampUtils().toTimestamp(
1645: null, in.toString());
1646: }
1647: setTimestamp(parameterIndex, tmpts);
1648: }
1649: break;
1650: case Types.BIT:
1651: bindLiteral(parameterIndex, pgType.toString(), Oid.BOOL);
1652: break;
1653: case Types.BINARY:
1654: case Types.VARBINARY:
1655: case Types.LONGVARBINARY:
1656: setObject(parameterIndex, in);
1657: break;
1658: case Types.BLOB:
1659: if (in instanceof Blob)
1660: setBlob(parameterIndex, (Blob) in);
1661: else
1662: throw new PSQLException(GT.tr(
1663: "Cannot cast an instance of {0} to type {1}",
1664: new Object[] { in.getClass().getName(),
1665: "Types.BLOB" }),
1666: PSQLState.INVALID_PARAMETER_TYPE);
1667: break;
1668: case Types.CLOB:
1669: if (in instanceof Clob)
1670: setClob(parameterIndex, (Clob) in);
1671: else
1672: throw new PSQLException(GT.tr(
1673: "Cannot cast an instance of {0} to type {1}",
1674: new Object[] { in.getClass().getName(),
1675: "Types.CLOB" }),
1676: PSQLState.INVALID_PARAMETER_TYPE);
1677: break;
1678: case Types.ARRAY:
1679: if (in instanceof Array)
1680: setArray(parameterIndex, (Array) in);
1681: else
1682: throw new PSQLException(GT.tr(
1683: "Cannot cast an instance of {0} to type {1}",
1684: new Object[] { in.getClass().getName(),
1685: "Types.ARRAY" }),
1686: PSQLState.INVALID_PARAMETER_TYPE);
1687: break;
1688: case Types.OTHER:
1689: if (in instanceof PGobject)
1690: setPGobject(parameterIndex, (PGobject) in);
1691: else
1692: throw new PSQLException(GT.tr(
1693: "Cannot cast an instance of {0} to type {1}",
1694: new Object[] { in.getClass().getName(),
1695: "Types.OTHER" }),
1696: PSQLState.INVALID_PARAMETER_TYPE);
1697: break;
1698: default:
1699: throw new PSQLException(GT.tr(
1700: "Unsupported Types value: {0}", new Integer(
1701: targetSqlType)),
1702: PSQLState.INVALID_PARAMETER_TYPE);
1703: }
1704: }
1705:
1706: public void setObject(int parameterIndex, Object x,
1707: int targetSqlType) throws SQLException {
1708: setObject(parameterIndex, x, targetSqlType, 0);
1709: }
1710:
1711: /*
1712: * This stores an Object into a parameter.
1713: */
1714: public void setObject(int parameterIndex, Object x)
1715: throws SQLException {
1716: checkClosed();
1717: if (x == null)
1718: setNull(parameterIndex, Types.OTHER);
1719: else if (x instanceof String)
1720: setString(parameterIndex, (String) x);
1721: else if (x instanceof BigDecimal)
1722: setBigDecimal(parameterIndex, (BigDecimal) x);
1723: else if (x instanceof Short)
1724: setShort(parameterIndex, ((Short) x).shortValue());
1725: else if (x instanceof Integer)
1726: setInt(parameterIndex, ((Integer) x).intValue());
1727: else if (x instanceof Long)
1728: setLong(parameterIndex, ((Long) x).longValue());
1729: else if (x instanceof Float)
1730: setFloat(parameterIndex, ((Float) x).floatValue());
1731: else if (x instanceof Double)
1732: setDouble(parameterIndex, ((Double) x).doubleValue());
1733: else if (x instanceof byte[])
1734: setBytes(parameterIndex, (byte[]) x);
1735: else if (x instanceof java.sql.Date)
1736: setDate(parameterIndex, (java.sql.Date) x);
1737: else if (x instanceof Time)
1738: setTime(parameterIndex, (Time) x);
1739: else if (x instanceof Timestamp)
1740: setTimestamp(parameterIndex, (Timestamp) x);
1741: else if (x instanceof Boolean)
1742: setBoolean(parameterIndex, ((Boolean) x).booleanValue());
1743: else if (x instanceof Byte)
1744: setByte(parameterIndex, ((Byte) x).byteValue());
1745: else if (x instanceof Blob)
1746: setBlob(parameterIndex, (Blob) x);
1747: else if (x instanceof Clob)
1748: setClob(parameterIndex, (Clob) x);
1749: else if (x instanceof Array)
1750: setArray(parameterIndex, (Array) x);
1751: else if (x instanceof PGobject)
1752: setPGobject(parameterIndex, (PGobject) x);
1753: else {
1754: // Can't infer a type.
1755: throw new PSQLException(
1756: GT
1757: .tr(
1758: "Can''t infer the SQL type to use for an instance of {0}. Use setObject() with an explicit Types value to specify the type to use.",
1759: x.getClass().getName()),
1760: PSQLState.INVALID_PARAMETER_TYPE);
1761: }
1762: }
1763:
1764: /*
1765: * Before executing a stored procedure call you must explicitly
1766: * call registerOutParameter to register the java.sql.Type of each
1767: * out parameter.
1768: *
1769: * <p>Note: When reading the value of an out parameter, you must use
1770: * the getXXX method whose Java type XXX corresponds to the
1771: * parameter's registered SQL type.
1772: *
1773: * ONLY 1 RETURN PARAMETER if {?= call ..} syntax is used
1774: *
1775: * @param parameterIndex the first parameter is 1, the second is 2,...
1776: * @param sqlType SQL type code defined by java.sql.Types; for
1777: * parameters of type Numeric or Decimal use the version of
1778: * registerOutParameter that accepts a scale value
1779: * @exception SQLException if a database-access error occurs.
1780: */
1781: public void registerOutParameter(int parameterIndex, int sqlType,
1782: boolean setPreparedParameters) throws SQLException {
1783: checkClosed();
1784: switch (sqlType) {
1785: case Types.TINYINT:
1786: // we don't have a TINYINT type use SMALLINT
1787: sqlType = Types.SMALLINT;
1788: break;
1789: case Types.LONGVARCHAR:
1790: sqlType = Types.VARCHAR;
1791: break;
1792: case Types.DECIMAL:
1793: sqlType = Types.NUMERIC;
1794: break;
1795: case Types.FLOAT:
1796: // float is the same as double
1797: sqlType = Types.DOUBLE;
1798: break;
1799: case Types.VARBINARY:
1800: case Types.LONGVARBINARY:
1801: sqlType = Types.BINARY;
1802: break;
1803: default:
1804: break;
1805: }
1806: if (!isFunction)
1807: throw new PSQLException(
1808: GT
1809: .tr("This statement does not declare an OUT parameter. Use '{' ?= call ... '}' to declare one."),
1810: PSQLState.STATEMENT_NOT_ALLOWED_IN_FUNCTION_CALL);
1811: checkIndex(parameterIndex, false);
1812:
1813: if (setPreparedParameters)
1814: preparedParameters.registerOutParameter(parameterIndex,
1815: sqlType);
1816: // functionReturnType contains the user supplied value to check
1817: // testReturn contains a modified version to make it easier to
1818: // check the getXXX methods..
1819: functionReturnType[parameterIndex - 1] = sqlType;
1820: testReturn[parameterIndex - 1] = sqlType;
1821:
1822: if (functionReturnType[parameterIndex - 1] == Types.CHAR
1823: || functionReturnType[parameterIndex - 1] == Types.LONGVARCHAR)
1824: testReturn[parameterIndex - 1] = Types.VARCHAR;
1825: else if (functionReturnType[parameterIndex - 1] == Types.FLOAT)
1826: testReturn[parameterIndex - 1] = Types.REAL; // changes to streamline later error checking
1827: returnTypeSet = true;
1828: }
1829:
1830: /*
1831: * You must also specify the scale for numeric/decimal types:
1832: *
1833: * <p>Note: When reading the value of an out parameter, you must use
1834: * the getXXX method whose Java type XXX corresponds to the
1835: * parameter's registered SQL type.
1836: *
1837: * @param parameterIndex the first parameter is 1, the second is 2,...
1838: * @param sqlType use either java.sql.Type.NUMERIC or java.sql.Type.DECIMAL
1839: * @param scale a value greater than or equal to zero representing the
1840: * desired number of digits to the right of the decimal point
1841: * @exception SQLException if a database-access error occurs.
1842: */
1843: public void registerOutParameter(int parameterIndex, int sqlType,
1844: int scale, boolean setPreparedParameters)
1845: throws SQLException {
1846: registerOutParameter(parameterIndex, sqlType,
1847: setPreparedParameters); // ignore for now..
1848: }
1849:
1850: /*
1851: * An OUT parameter may have the value of SQL NULL; wasNull
1852: * reports whether the last value read has this special value.
1853: *
1854: * <p>Note: You must first call getXXX on a parameter to read its
1855: * value and then call wasNull() to see if the value was SQL NULL.
1856: * @return true if the last parameter read was SQL NULL
1857: * @exception SQLException if a database-access error occurs.
1858: */
1859: public boolean wasNull() throws SQLException {
1860: if (lastIndex == 0)
1861: throw new PSQLException(
1862: GT
1863: .tr("wasNull cannot be call before fetching a result."),
1864: PSQLState.OBJECT_NOT_IN_STATE);
1865:
1866: // check to see if the last access threw an exception
1867: return (callResult[lastIndex - 1] == null);
1868: }
1869:
1870: /*
1871: * Get the value of a CHAR, VARCHAR, or LONGVARCHAR parameter as a
1872: * Java String.
1873: *
1874: * @param parameterIndex the first parameter is 1, the second is 2,...
1875: * @return the parameter value; if the value is SQL NULL, the result is null
1876: * @exception SQLException if a database-access error occurs.
1877: */
1878: public String getString(int parameterIndex) throws SQLException {
1879: checkClosed();
1880: checkIndex(parameterIndex, Types.VARCHAR, "String");
1881: return (String) callResult[parameterIndex - 1];
1882: }
1883:
1884: /*
1885: * Get the value of a BIT parameter as a Java boolean.
1886: *
1887: * @param parameterIndex the first parameter is 1, the second is 2,...
1888: * @return the parameter value; if the value is SQL NULL, the result is false
1889: * @exception SQLException if a database-access error occurs.
1890: */
1891: public boolean getBoolean(int parameterIndex) throws SQLException {
1892: checkClosed();
1893: checkIndex(parameterIndex, Types.BIT, "Boolean");
1894: if (callResult[parameterIndex - 1] == null)
1895: return false;
1896:
1897: return ((Boolean) callResult[parameterIndex - 1])
1898: .booleanValue();
1899: }
1900:
1901: /*
1902: * Get the value of a TINYINT parameter as a Java byte.
1903: *
1904: * @param parameterIndex the first parameter is 1, the second is 2,...
1905: * @return the parameter value; if the value is SQL NULL, the result is 0
1906: * @exception SQLException if a database-access error occurs.
1907: */
1908: public byte getByte(int parameterIndex) throws SQLException {
1909: checkClosed();
1910: // fake tiny int with smallint
1911: checkIndex(parameterIndex, Types.SMALLINT, "Byte");
1912:
1913: if (callResult[parameterIndex - 1] == null)
1914: return 0;
1915:
1916: return ((Integer) callResult[parameterIndex - 1]).byteValue();
1917:
1918: }
1919:
1920: /*
1921: * Get the value of a SMALLINT parameter as a Java short.
1922: *
1923: * @param parameterIndex the first parameter is 1, the second is 2,...
1924: * @return the parameter value; if the value is SQL NULL, the result is 0
1925: * @exception SQLException if a database-access error occurs.
1926: */
1927: public short getShort(int parameterIndex) throws SQLException {
1928: checkClosed();
1929: checkIndex(parameterIndex, Types.SMALLINT, "Short");
1930: if (callResult[parameterIndex - 1] == null)
1931: return 0;
1932: return ((Integer) callResult[parameterIndex - 1]).shortValue();
1933: }
1934:
1935: /*
1936: * Get the value of an INTEGER parameter as a Java int.
1937: *
1938: * @param parameterIndex the first parameter is 1, the second is 2,...
1939: * @return the parameter value; if the value is SQL NULL, the result is 0
1940: * @exception SQLException if a database-access error occurs.
1941: */
1942: public int getInt(int parameterIndex) throws SQLException {
1943: checkClosed();
1944: checkIndex(parameterIndex, Types.INTEGER, "Int");
1945: if (callResult[parameterIndex - 1] == null)
1946: return 0;
1947:
1948: return ((Integer) callResult[parameterIndex - 1]).intValue();
1949: }
1950:
1951: /*
1952: * Get the value of a BIGINT parameter as a Java long.
1953: *
1954: * @param parameterIndex the first parameter is 1, the second is 2,...
1955: * @return the parameter value; if the value is SQL NULL, the result is 0
1956: * @exception SQLException if a database-access error occurs.
1957: */
1958: public long getLong(int parameterIndex) throws SQLException {
1959: checkClosed();
1960: checkIndex(parameterIndex, Types.BIGINT, "Long");
1961: if (callResult[parameterIndex - 1] == null)
1962: return 0;
1963:
1964: return ((Long) callResult[parameterIndex - 1]).longValue();
1965: }
1966:
1967: /*
1968: * Get the value of a FLOAT parameter as a Java float.
1969: *
1970: * @param parameterIndex the first parameter is 1, the second is 2,...
1971: * @return the parameter value; if the value is SQL NULL, the result is 0
1972: * @exception SQLException if a database-access error occurs.
1973: */
1974: public float getFloat(int parameterIndex) throws SQLException {
1975: checkClosed();
1976: checkIndex(parameterIndex, Types.REAL, "Float");
1977: if (callResult[parameterIndex - 1] == null)
1978: return 0;
1979:
1980: return ((Float) callResult[parameterIndex - 1]).floatValue();
1981: }
1982:
1983: /*
1984: * Get the value of a DOUBLE parameter as a Java double.
1985: *
1986: * @param parameterIndex the first parameter is 1, the second is 2,...
1987: * @return the parameter value; if the value is SQL NULL, the result is 0
1988: * @exception SQLException if a database-access error occurs.
1989: */
1990: public double getDouble(int parameterIndex) throws SQLException {
1991: checkClosed();
1992: checkIndex(parameterIndex, Types.DOUBLE, "Double");
1993: if (callResult[parameterIndex - 1] == null)
1994: return 0;
1995:
1996: return ((Double) callResult[parameterIndex - 1]).doubleValue();
1997: }
1998:
1999: /*
2000: * Get the value of a NUMERIC parameter as a java.math.BigDecimal
2001: * object.
2002: *
2003: * @param parameterIndex the first parameter is 1, the second is 2,...
2004: * @param scale a value greater than or equal to zero representing the
2005: * desired number of digits to the right of the decimal point
2006: * @return the parameter value; if the value is SQL NULL, the result is null
2007: * @exception SQLException if a database-access error occurs.
2008: * @deprecated in Java2.0
2009: */
2010: public BigDecimal getBigDecimal(int parameterIndex, int scale)
2011: throws SQLException {
2012: checkClosed();
2013: checkIndex(parameterIndex, Types.NUMERIC, "BigDecimal");
2014: return ((BigDecimal) callResult[parameterIndex - 1]);
2015: }
2016:
2017: /*
2018: * Get the value of a SQL BINARY or VARBINARY parameter as a Java
2019: * byte[]
2020: *
2021: * @param parameterIndex the first parameter is 1, the second is 2,...
2022: * @return the parameter value; if the value is SQL NULL, the result is null
2023: * @exception SQLException if a database-access error occurs.
2024: */
2025: public byte[] getBytes(int parameterIndex) throws SQLException {
2026: checkClosed();
2027: checkIndex(parameterIndex, Types.VARBINARY, Types.BINARY,
2028: "Bytes");
2029: return ((byte[]) callResult[parameterIndex - 1]);
2030: }
2031:
2032: /*
2033: * Get the value of a SQL DATE parameter as a java.sql.Date object
2034: *
2035: * @param parameterIndex the first parameter is 1, the second is 2,...
2036: * @return the parameter value; if the value is SQL NULL, the result is null
2037: * @exception SQLException if a database-access error occurs.
2038: */
2039: public java.sql.Date getDate(int parameterIndex)
2040: throws SQLException {
2041: checkClosed();
2042: checkIndex(parameterIndex, Types.DATE, "Date");
2043: return (java.sql.Date) callResult[parameterIndex - 1];
2044: }
2045:
2046: /*
2047: * Get the value of a SQL TIME parameter as a java.sql.Time object.
2048: *
2049: * @param parameterIndex the first parameter is 1, the second is 2,...
2050: * @return the parameter value; if the value is SQL NULL, the result is null
2051: * @exception SQLException if a database-access error occurs.
2052: */
2053: public java.sql.Time getTime(int parameterIndex)
2054: throws SQLException {
2055: checkClosed();
2056: checkIndex(parameterIndex, Types.TIME, "Time");
2057: return (java.sql.Time) callResult[parameterIndex - 1];
2058: }
2059:
2060: /*
2061: * Get the value of a SQL TIMESTAMP parameter as a java.sql.Timestamp object.
2062: *
2063: * @param parameterIndex the first parameter is 1, the second is 2,...
2064: * @return the parameter value; if the value is SQL NULL, the result is null
2065: * @exception SQLException if a database-access error occurs.
2066: */
2067: public java.sql.Timestamp getTimestamp(int parameterIndex)
2068: throws SQLException {
2069: checkClosed();
2070: checkIndex(parameterIndex, Types.TIMESTAMP, "Timestamp");
2071: return (java.sql.Timestamp) callResult[parameterIndex - 1];
2072: }
2073:
2074: // getObject returns a Java object for the parameter.
2075: // See the JDBC spec's "Dynamic Programming" chapter for details.
2076: /*
2077: * Get the value of a parameter as a Java object.
2078: *
2079: * <p>This method returns a Java object whose type coresponds to the
2080: * SQL type that was registered for this parameter using
2081: * registerOutParameter.
2082: *
2083: * <P>Note that this method may be used to read datatabase-specific,
2084: * abstract data types. This is done by specifying a targetSqlType
2085: * of java.sql.types.OTHER, which allows the driver to return a
2086: * database-specific Java type.
2087: *
2088: * <p>See the JDBC spec's "Dynamic Programming" chapter for details.
2089: *
2090: * @param parameterIndex the first parameter is 1, the second is 2,...
2091: * @return A java.lang.Object holding the OUT parameter value.
2092: * @exception SQLException if a database-access error occurs.
2093: */
2094: public Object getObject(int parameterIndex) throws SQLException {
2095: checkClosed();
2096: checkIndex(parameterIndex);
2097: return callResult[parameterIndex - 1];
2098: }
2099:
2100: /*
2101: * Returns the SQL statement with the current template values
2102: * substituted.
2103: */
2104: public String toString() {
2105: if (preparedQuery == null)
2106: return super .toString();
2107:
2108: return preparedQuery.toString(preparedParameters);
2109: }
2110:
2111: /*
2112: * Note if s is a String it should be escaped by the caller to avoid SQL
2113: * injection attacks. It is not done here for efficency reasons as
2114: * most calls to this method do not require escaping as the source
2115: * of the string is known safe (i.e. Integer.toString())
2116: */
2117: private void bindLiteral(int paramIndex, String s, int oid)
2118: throws SQLException {
2119: if (adjustIndex)
2120: paramIndex--;
2121: preparedParameters.setLiteralParameter(paramIndex, s, oid);
2122: }
2123:
2124: /*
2125: * This version is for values that should turn into strings
2126: * e.g. setString directly calls bindString with no escaping;
2127: * the per-protocol ParameterList does escaping as needed.
2128: */
2129: private void bindString(int paramIndex, String s, int oid)
2130: throws SQLException {
2131: if (adjustIndex)
2132: paramIndex--;
2133: preparedParameters.setStringParameter(paramIndex, s, oid);
2134: }
2135:
2136: /**
2137: * this method will turn a string of the form
2138: * { [? =] call <some_function> [(?, [?,..])] }
2139: * into the PostgreSQL format which is
2140: * select <some_function> (?, [?, ...]) as result
2141: * or select * from <some_function> (?, [?, ...]) as result (7.3)
2142: */
2143: private String modifyJdbcCall(String p_sql) throws SQLException {
2144: checkClosed();
2145:
2146: // Mini-parser for JDBC function-call syntax (only)
2147: // TODO: Merge with escape processing (and parameter parsing?)
2148: // so we only parse each query once.
2149:
2150: isFunction = false;
2151:
2152: boolean stdStrings = connection.getStandardConformingStrings();
2153:
2154: int len = p_sql.length();
2155: int state = 1;
2156: boolean inQuotes = false, inEscape = false;
2157: outParmBeforeFunc = false;
2158: int startIndex = -1, endIndex = -1;
2159: boolean syntaxError = false;
2160: int i = 0;
2161:
2162: while (i < len && !syntaxError) {
2163: char ch = p_sql.charAt(i);
2164:
2165: switch (state) {
2166: case 1: // Looking for { at start of query
2167: if (ch == '{') {
2168: ++i;
2169: ++state;
2170: } else if (Character.isWhitespace(ch)) {
2171: ++i;
2172: } else {
2173: // Not function-call syntax. Skip the rest of the string.
2174: i = len;
2175: }
2176: break;
2177:
2178: case 2: // After {, looking for ? or =, skipping whitespace
2179: if (ch == '?') {
2180: outParmBeforeFunc = isFunction = true; // { ? = call ... } -- function with one out parameter
2181: ++i;
2182: ++state;
2183: } else if (ch == 'c') { // { call ... } -- proc with no out parameters
2184: state += 3; // Don't increase 'i'
2185: } else if (Character.isWhitespace(ch)) {
2186: ++i;
2187: } else {
2188: // "{ foo ...", doesn't make sense, complain.
2189: syntaxError = true;
2190: }
2191: break;
2192:
2193: case 3: // Looking for = after ?, skipping whitespace
2194: if (ch == '=') {
2195: ++i;
2196: ++state;
2197: } else if (Character.isWhitespace(ch)) {
2198: ++i;
2199: } else {
2200: syntaxError = true;
2201: }
2202: break;
2203:
2204: case 4: // Looking for 'call' after '? =' skipping whitespace
2205: if (ch == 'c' || ch == 'C') {
2206: ++state; // Don't increase 'i'.
2207: } else if (Character.isWhitespace(ch)) {
2208: ++i;
2209: } else {
2210: syntaxError = true;
2211: }
2212: break;
2213:
2214: case 5: // Should be at 'call ' either at start of string or after ?=
2215: if ((ch == 'c' || ch == 'C')
2216: && i + 4 <= len
2217: && p_sql.substring(i, i + 4).equalsIgnoreCase(
2218: "call")) {
2219: isFunction = true;
2220: i += 4;
2221: ++state;
2222: } else if (Character.isWhitespace(ch)) {
2223: ++i;
2224: } else {
2225: syntaxError = true;
2226: }
2227: break;
2228:
2229: case 6: // Looking for whitespace char after 'call'
2230: if (Character.isWhitespace(ch)) {
2231: // Ok, we found the start of the real call.
2232: ++i;
2233: ++state;
2234: startIndex = i;
2235: } else {
2236: syntaxError = true;
2237: }
2238: break;
2239:
2240: case 7: // In "body" of the query (after "{ [? =] call ")
2241: if (ch == '\'') {
2242: inQuotes = !inQuotes;
2243: ++i;
2244: } else if (inQuotes && ch == '\\' && !stdStrings) {
2245: // Backslash in string constant, skip next character.
2246: i += 2;
2247: } else if (!inQuotes && ch == '{') {
2248: inEscape = !inEscape;
2249: ++i;
2250: } else if (!inQuotes && ch == '}') {
2251: if (!inEscape) {
2252: // Should be end of string.
2253: endIndex = i;
2254: ++i;
2255: ++state;
2256: } else {
2257: inEscape = false;
2258: }
2259: } else if (!inQuotes && ch == ';') {
2260: syntaxError = true;
2261: } else {
2262: // Everything else is ok.
2263: ++i;
2264: }
2265: break;
2266:
2267: case 8: // At trailing end of query, eating whitespace
2268: if (Character.isWhitespace(ch)) {
2269: ++i;
2270: } else {
2271: syntaxError = true;
2272: }
2273: break;
2274:
2275: default:
2276: throw new IllegalStateException(
2277: "somehow got into bad state " + state);
2278: }
2279: }
2280:
2281: // We can only legally end in a couple of states here.
2282: if (i == len && !syntaxError) {
2283: if (state == 1)
2284: return p_sql; // Not an escaped syntax.
2285: if (state != 8)
2286: syntaxError = true; // Ran out of query while still parsing
2287: }
2288:
2289: if (syntaxError)
2290: throw new PSQLException(
2291: GT
2292: .tr(
2293: "Malformed function or procedure escape syntax at offset {0}.",
2294: new Integer(i)),
2295: PSQLState.STATEMENT_NOT_ALLOWED_IN_FUNCTION_CALL);
2296:
2297: if (connection.haveMinimumServerVersion("8.1")
2298: && ((AbstractJdbc2Connection) connection)
2299: .getProtocolVersion() == 3) {
2300: String s = p_sql.substring(startIndex, endIndex);
2301: StringBuffer sb = new StringBuffer(s);
2302: if (outParmBeforeFunc) {
2303: // move the single out parameter into the function call
2304: // so that it can be treated like all other parameters
2305: boolean needComma = false;
2306:
2307: // have to use String.indexOf for java 2
2308: int opening = s.indexOf('(') + 1;
2309: int closing = s.indexOf(')');
2310: for (int j = opening; j < closing; j++) {
2311: if (!Character.isWhitespace(sb.charAt(j))) {
2312: needComma = true;
2313: break;
2314: }
2315: }
2316: if (needComma) {
2317: sb.insert(opening, "?,");
2318: } else {
2319: sb.insert(opening, "?");
2320: }
2321:
2322: }
2323: return "select * from " + sb.toString() + " as result";
2324: } else {
2325: return "select " + p_sql.substring(startIndex, endIndex)
2326: + " as result";
2327: }
2328: }
2329:
2330: /** helperfunction for the getXXX calls to check isFunction and index == 1
2331: * Compare BOTH type fields against the return type.
2332: */
2333: protected void checkIndex(int parameterIndex, int type1, int type2,
2334: String getName) throws SQLException {
2335: checkIndex(parameterIndex);
2336: if (type1 != this .testReturn[parameterIndex - 1]
2337: && type2 != this .testReturn[parameterIndex - 1])
2338: throw new PSQLException(
2339: GT
2340: .tr(
2341: "Parameter of type {0} was registered, but call to get{1} (sqltype={2}) was made.",
2342: new Object[] {
2343: "java.sql.Types="
2344: + testReturn[parameterIndex - 1],
2345: getName,
2346: "java.sql.Types=" + type1 }),
2347: PSQLState.MOST_SPECIFIC_TYPE_DOES_NOT_MATCH);
2348: }
2349:
2350: /** helperfunction for the getXXX calls to check isFunction and index == 1
2351: */
2352: protected void checkIndex(int parameterIndex, int type,
2353: String getName) throws SQLException {
2354: checkIndex(parameterIndex);
2355: if (type != this .testReturn[parameterIndex - 1])
2356: throw new PSQLException(
2357: GT
2358: .tr(
2359: "Parameter of type {0} was registered, but call to get{1} (sqltype={2}) was made.",
2360: new Object[] {
2361: "java.sql.Types="
2362: + testReturn[parameterIndex - 1],
2363: getName,
2364: "java.sql.Types=" + type }),
2365: PSQLState.MOST_SPECIFIC_TYPE_DOES_NOT_MATCH);
2366: }
2367:
2368: private void checkIndex(int parameterIndex) throws SQLException {
2369: checkIndex(parameterIndex, true);
2370: }
2371:
2372: /** helperfunction for the getXXX calls to check isFunction and index == 1
2373: * @param parameterIndex index of getXXX (index)
2374: * check to make sure is a function and index == 1
2375: */
2376: private void checkIndex(int parameterIndex, boolean fetchingData)
2377: throws SQLException {
2378: if (!isFunction)
2379: throw new PSQLException(
2380: GT
2381: .tr("A CallableStatement was declared, but no call to registerOutParameter(1, <some type>) was made."),
2382: PSQLState.STATEMENT_NOT_ALLOWED_IN_FUNCTION_CALL);
2383:
2384: if (fetchingData) {
2385: if (!returnTypeSet)
2386: throw new PSQLException(GT
2387: .tr("No function outputs were registered."),
2388: PSQLState.OBJECT_NOT_IN_STATE);
2389:
2390: if (callResult == null)
2391: throw new PSQLException(
2392: GT
2393: .tr("Results cannot be retrieved from a CallableStatement before it is executed."),
2394: PSQLState.NO_DATA);
2395:
2396: lastIndex = parameterIndex;
2397: }
2398: }
2399:
2400: public void setPrepareThreshold(int newThreshold)
2401: throws SQLException {
2402: checkClosed();
2403:
2404: if (newThreshold < 0)
2405: newThreshold = 0;
2406:
2407: this .m_prepareThreshold = newThreshold;
2408: }
2409:
2410: public int getPrepareThreshold() {
2411: return m_prepareThreshold;
2412: }
2413:
2414: public void setUseServerPrepare(boolean flag) throws SQLException {
2415: setPrepareThreshold(flag ? 1 : 0);
2416: }
2417:
2418: public boolean isUseServerPrepare() {
2419: return (preparedQuery != null && m_prepareThreshold != 0 && m_useCount + 1 >= m_prepareThreshold);
2420: }
2421:
2422: protected void checkClosed() throws SQLException {
2423: if (isClosed)
2424: throw new PSQLException(GT
2425: .tr("This statement has been closed."),
2426: PSQLState.OBJECT_NOT_IN_STATE);
2427: }
2428:
2429: // ** JDBC 2 Extensions **
2430:
2431: public void addBatch(String p_sql) throws SQLException {
2432: checkClosed();
2433:
2434: if (preparedQuery != null)
2435: throw new PSQLException(
2436: GT
2437: .tr("Can''t use query methods that take a query string on a PreparedStatement."),
2438: PSQLState.WRONG_OBJECT_TYPE);
2439:
2440: if (batchStatements == null) {
2441: batchStatements = new ArrayList();
2442: batchParameters = new ArrayList();
2443: }
2444:
2445: p_sql = replaceProcessing(p_sql);
2446:
2447: batchStatements.add(connection.getQueryExecutor()
2448: .createSimpleQuery(p_sql));
2449: batchParameters.add(null);
2450: }
2451:
2452: public void clearBatch() throws SQLException {
2453: if (batchStatements != null) {
2454: batchStatements.clear();
2455: batchParameters.clear();
2456: }
2457: }
2458:
2459: //
2460: // ResultHandler for batch queries.
2461: //
2462:
2463: private class BatchResultHandler implements ResultHandler {
2464: private BatchUpdateException batchException = null;
2465: private int resultIndex = 0;
2466:
2467: private final Query[] queries;
2468: private final ParameterList[] parameterLists;
2469: private final int[] updateCounts;
2470:
2471: BatchResultHandler(Query[] queries,
2472: ParameterList[] parameterLists, int[] updateCounts) {
2473: this .queries = queries;
2474: this .parameterLists = parameterLists;
2475: this .updateCounts = updateCounts;
2476: }
2477:
2478: public void handleResultRows(Query fromQuery, Field[] fields,
2479: Vector tuples, ResultCursor cursor) {
2480: handleError(new PSQLException(
2481: GT
2482: .tr("A result was returned when none was expected."),
2483: PSQLState.TOO_MANY_RESULTS));
2484: }
2485:
2486: public void handleCommandStatus(String status, int updateCount,
2487: long insertOID) {
2488: if (resultIndex >= updateCounts.length) {
2489: handleError(new PSQLException(GT
2490: .tr("Too many update results were returned."),
2491: PSQLState.TOO_MANY_RESULTS));
2492: return;
2493: }
2494:
2495: updateCounts[resultIndex++] = updateCount;
2496: }
2497:
2498: public void handleWarning(SQLWarning warning) {
2499: AbstractJdbc2Statement.this .addWarning(warning);
2500: }
2501:
2502: public void handleError(SQLException newError) {
2503: if (batchException == null) {
2504: int[] successCounts;
2505:
2506: if (resultIndex >= updateCounts.length)
2507: successCounts = updateCounts;
2508: else {
2509: successCounts = new int[resultIndex];
2510: System.arraycopy(updateCounts, 0, successCounts, 0,
2511: resultIndex);
2512: }
2513:
2514: String queryString = "<unknown>";
2515: if (resultIndex < queries.length)
2516: queryString = queries[resultIndex]
2517: .toString(parameterLists[resultIndex]);
2518:
2519: batchException = new BatchUpdateException(
2520: GT
2521: .tr(
2522: "Batch entry {0} {1} was aborted. Call getNextException to see the cause.",
2523: new Object[] {
2524: new Integer(resultIndex),
2525: queryString }),
2526: successCounts);
2527: }
2528:
2529: batchException.setNextException(newError);
2530: }
2531:
2532: public void handleCompletion() throws SQLException {
2533: if (batchException != null)
2534: throw batchException;
2535: }
2536: }
2537:
2538: private class CallableBatchResultHandler implements ResultHandler {
2539: private BatchUpdateException batchException = null;
2540: private int resultIndex = 0;
2541:
2542: private final Query[] queries;
2543: private final ParameterList[] parameterLists;
2544: private final int[] updateCounts;
2545:
2546: CallableBatchResultHandler(Query[] queries,
2547: ParameterList[] parameterLists, int[] updateCounts) {
2548: this .queries = queries;
2549: this .parameterLists = parameterLists;
2550: this .updateCounts = updateCounts;
2551: }
2552:
2553: public void handleResultRows(Query fromQuery, Field[] fields,
2554: Vector tuples, ResultCursor cursor) {
2555:
2556: }
2557:
2558: public void handleCommandStatus(String status, int updateCount,
2559: long insertOID) {
2560: if (resultIndex >= updateCounts.length) {
2561: handleError(new PSQLException(GT
2562: .tr("Too many update results were returned."),
2563: PSQLState.TOO_MANY_RESULTS));
2564: return;
2565: }
2566:
2567: updateCounts[resultIndex++] = updateCount;
2568: }
2569:
2570: public void handleWarning(SQLWarning warning) {
2571: AbstractJdbc2Statement.this .addWarning(warning);
2572: }
2573:
2574: public void handleError(SQLException newError) {
2575: if (batchException == null) {
2576: int[] successCounts;
2577:
2578: if (resultIndex >= updateCounts.length)
2579: successCounts = updateCounts;
2580: else {
2581: successCounts = new int[resultIndex];
2582: System.arraycopy(updateCounts, 0, successCounts, 0,
2583: resultIndex);
2584: }
2585:
2586: String queryString = "<unknown>";
2587: if (resultIndex < queries.length)
2588: queryString = queries[resultIndex]
2589: .toString(parameterLists[resultIndex]);
2590:
2591: batchException = new BatchUpdateException(
2592: GT
2593: .tr(
2594: "Batch entry {0} {1} was aborted. Call getNextException to see the cause.",
2595: new Object[] {
2596: new Integer(resultIndex),
2597: queryString }),
2598: successCounts);
2599: }
2600:
2601: batchException.setNextException(newError);
2602: }
2603:
2604: public void handleCompletion() throws SQLException {
2605: if (batchException != null)
2606: throw batchException;
2607: }
2608: }
2609:
2610: public int[] executeBatch() throws SQLException {
2611: checkClosed();
2612:
2613: // Every statement execution clears any previous warnings.
2614: clearWarnings();
2615:
2616: if (batchStatements == null || batchStatements.isEmpty())
2617: return new int[0];
2618:
2619: int size = batchStatements.size();
2620: int[] updateCounts = new int[size];
2621:
2622: // Construct query/parameter arrays.
2623: Query[] queries = (Query[]) batchStatements
2624: .toArray(new Query[batchStatements.size()]);
2625: ParameterList[] parameterLists = (ParameterList[]) batchParameters
2626: .toArray(new ParameterList[batchParameters.size()]);
2627: batchStatements.clear();
2628: batchParameters.clear();
2629:
2630: // Close any existing resultsets associated with this statement.
2631: while (firstUnclosedResult != null) {
2632: if (firstUnclosedResult.getResultSet() != null) {
2633: firstUnclosedResult.getResultSet().close();
2634: }
2635: firstUnclosedResult = firstUnclosedResult.getNext();
2636: }
2637:
2638: if (lastSimpleQuery != null) {
2639: lastSimpleQuery.close();
2640: lastSimpleQuery = null;
2641: }
2642:
2643: int flags = QueryExecutor.QUERY_NO_RESULTS;
2644:
2645: // Only use named statements after we hit the threshold
2646: if (preparedQuery != null) {
2647: m_useCount += queries.length;
2648: }
2649: if (m_prepareThreshold == 0 || m_useCount < m_prepareThreshold)
2650: flags |= QueryExecutor.QUERY_ONESHOT;
2651:
2652: if (connection.getAutoCommit())
2653: flags |= QueryExecutor.QUERY_SUPPRESS_BEGIN;
2654:
2655: result = null;
2656:
2657: ResultHandler handler;
2658: if (isFunction) {
2659: handler = new CallableBatchResultHandler(queries,
2660: parameterLists, updateCounts);
2661: } else {
2662: handler = new BatchResultHandler(queries, parameterLists,
2663: updateCounts);
2664: }
2665:
2666: connection.getQueryExecutor().execute(queries, parameterLists,
2667: handler, maxrows, fetchSize, flags);
2668:
2669: return updateCounts;
2670: }
2671:
2672: /*
2673: * Cancel can be used by one thread to cancel a statement that
2674: * is being executed by another thread.
2675: * <p>
2676: *
2677: * @exception SQLException only because thats the spec.
2678: */
2679: public void cancel() throws SQLException {
2680: connection.cancelQuery();
2681: }
2682:
2683: public Connection getConnection() throws SQLException {
2684: return (Connection) connection;
2685: }
2686:
2687: public int getFetchDirection() {
2688: return fetchdirection;
2689: }
2690:
2691: public int getResultSetConcurrency() {
2692: return concurrency;
2693: }
2694:
2695: public int getResultSetType() {
2696: return resultsettype;
2697: }
2698:
2699: public void setFetchDirection(int direction) throws SQLException {
2700: switch (direction) {
2701: case ResultSet.FETCH_FORWARD:
2702: case ResultSet.FETCH_REVERSE:
2703: case ResultSet.FETCH_UNKNOWN:
2704: fetchdirection = direction;
2705: break;
2706: default:
2707: throw new PSQLException(GT.tr(
2708: "Invalid fetch direction constant: {0}.",
2709: new Integer(direction)),
2710: PSQLState.INVALID_PARAMETER_VALUE);
2711: }
2712: }
2713:
2714: public void setFetchSize(int rows) throws SQLException {
2715: checkClosed();
2716: if (rows < 0)
2717: throw new PSQLException(
2718: GT
2719: .tr("Fetch size must be a value greater to or equal to 0."),
2720: PSQLState.INVALID_PARAMETER_VALUE);
2721: fetchSize = rows;
2722: }
2723:
2724: public void addBatch() throws SQLException {
2725: checkClosed();
2726:
2727: if (batchStatements == null) {
2728: batchStatements = new ArrayList();
2729: batchParameters = new ArrayList();
2730: }
2731:
2732: // we need to create copies of our parameters, otherwise the values can be changed
2733: batchStatements.add(preparedQuery);
2734: batchParameters.add(preparedParameters.copy());
2735: }
2736:
2737: public ResultSetMetaData getMetaData() throws SQLException {
2738: checkClosed();
2739: ResultSet rs = getResultSet();
2740:
2741: if (rs == null) {
2742: // OK, we haven't executed it yet, we've got to go to the backend
2743: // for more info. We send the full query, but just don't
2744: // execute it.
2745:
2746: int flags = QueryExecutor.QUERY_ONESHOT
2747: | QueryExecutor.QUERY_DESCRIBE_ONLY
2748: | QueryExecutor.QUERY_SUPPRESS_BEGIN;
2749: StatementResultHandler handler = new StatementResultHandler();
2750: connection.getQueryExecutor().execute(preparedQuery,
2751: preparedParameters, handler, 0, 0, flags);
2752: ResultWrapper wrapper = handler.getResults();
2753: if (wrapper != null) {
2754: rs = wrapper.getResultSet();
2755: }
2756: }
2757:
2758: if (rs != null)
2759: return rs.getMetaData();
2760:
2761: return null;
2762: }
2763:
2764: public void setArray(int i, java.sql.Array x) throws SQLException {
2765: checkClosed();
2766:
2767: if (null == x) {
2768: setNull(i, Types.ARRAY);
2769: return;
2770: }
2771:
2772: // This only works for Array implementations that return a valid array
2773: // literal from Array.toString(), such as the implementation we return
2774: // from ResultSet.getArray(). Eventually we need a proper implementation
2775: // here that works for any Array implementation.
2776:
2777: // Use a typename that is "_" plus the base type; this matches how the
2778: // backend looks for array types.
2779: String typename = "_" + x.getBaseTypeName();
2780: int oid = connection.getPGType(typename);
2781: if (oid == Oid.UNSPECIFIED)
2782: throw new PSQLException(GT
2783: .tr("Unknown type {0}.", typename),
2784: PSQLState.INVALID_PARAMETER_TYPE);
2785:
2786: setString(i, x.toString(), oid);
2787: }
2788:
2789: public void setBlob(int i, Blob x) throws SQLException {
2790: checkClosed();
2791:
2792: if (x == null) {
2793: setNull(i, Types.BLOB);
2794: return;
2795: }
2796:
2797: InputStream l_inStream = x.getBinaryStream();
2798: LargeObjectManager lom = connection.getLargeObjectAPI();
2799: long oid = lom.createLO();
2800: LargeObject lob = lom.open(oid);
2801: OutputStream los = lob.getOutputStream();
2802: byte[] buf = new byte[4096];
2803: try {
2804: // could be buffered, but then the OutputStream returned by LargeObject
2805: // is buffered internally anyhow, so there would be no performance
2806: // boost gained, if anything it would be worse!
2807: int bytesRemaining = (int) x.length();
2808: int numRead = l_inStream.read(buf, 0, Math.min(buf.length,
2809: bytesRemaining));
2810: while (numRead != -1 && bytesRemaining > 0) {
2811: bytesRemaining -= numRead;
2812: if (numRead == buf.length)
2813: los.write(buf); // saves a buffer creation and copy in LargeObject since it's full
2814: else
2815: los.write(buf, 0, numRead);
2816: numRead = l_inStream.read(buf, 0, Math.min(buf.length,
2817: bytesRemaining));
2818: }
2819: } catch (IOException se) {
2820: throw new PSQLException(
2821: GT
2822: .tr("Unexpected error writing large object to database."),
2823: PSQLState.UNEXPECTED_ERROR, se);
2824: } finally {
2825: try {
2826: los.close();
2827: l_inStream.close();
2828: } catch (Exception e) {
2829: }
2830: }
2831: setLong(i, oid);
2832: }
2833:
2834: public void setCharacterStream(int i, java.io.Reader x, int length)
2835: throws SQLException {
2836: checkClosed();
2837:
2838: if (x == null) {
2839: if (connection.haveMinimumServerVersion("7.2")) {
2840: setNull(i, Types.VARCHAR);
2841: } else {
2842: setNull(i, Types.CLOB);
2843: }
2844: return;
2845: }
2846:
2847: if (length < 0)
2848: throw new PSQLException(GT.tr("Invalid stream length {0}.",
2849: new Integer(length)),
2850: PSQLState.INVALID_PARAMETER_VALUE);
2851:
2852: if (connection.haveMinimumCompatibleVersion("7.2")) {
2853: //Version 7.2 supports CharacterStream for for the PG text types
2854: //As the spec/javadoc for this method indicate this is to be used for
2855: //large text values (i.e. LONGVARCHAR) PG doesn't have a separate
2856: //long varchar datatype, but with toast all the text datatypes are capable of
2857: //handling very large values. Thus the implementation ends up calling
2858: //setString() since there is no current way to stream the value to the server
2859: char[] l_chars = new char[length];
2860: int l_charsRead = 0;
2861: try {
2862: while (true) {
2863: int n = x.read(l_chars, l_charsRead, length
2864: - l_charsRead);
2865: if (n == -1)
2866: break;
2867:
2868: l_charsRead += n;
2869:
2870: if (l_charsRead == length)
2871: break;
2872: }
2873: } catch (IOException l_ioe) {
2874: throw new PSQLException(GT
2875: .tr("Provided Reader failed."),
2876: PSQLState.UNEXPECTED_ERROR, l_ioe);
2877: }
2878: setString(i, new String(l_chars, 0, l_charsRead));
2879: } else {
2880: //Version 7.1 only supported streams for LargeObjects
2881: //but the jdbc spec indicates that streams should be
2882: //available for LONGVARCHAR instead
2883: LargeObjectManager lom = connection.getLargeObjectAPI();
2884: long oid = lom.createLO();
2885: LargeObject lob = lom.open(oid);
2886: OutputStream los = lob.getOutputStream();
2887: try {
2888: // could be buffered, but then the OutputStream returned by LargeObject
2889: // is buffered internally anyhow, so there would be no performance
2890: // boost gained, if anything it would be worse!
2891: int c = x.read();
2892: int p = 0;
2893: while (c > -1 && p < length) {
2894: los.write(c);
2895: c = x.read();
2896: p++;
2897: }
2898: los.close();
2899: } catch (IOException se) {
2900: throw new PSQLException(
2901: GT
2902: .tr("Unexpected error writing large object to database."),
2903: PSQLState.UNEXPECTED_ERROR, se);
2904: }
2905: // lob is closed by the stream so don't call lob.close()
2906: setLong(i, oid);
2907: }
2908: }
2909:
2910: public void setClob(int i, Clob x) throws SQLException {
2911: checkClosed();
2912:
2913: if (x == null) {
2914: setNull(i, Types.CLOB);
2915: return;
2916: }
2917:
2918: InputStream l_inStream = x.getAsciiStream();
2919: int l_length = (int) x.length();
2920: LargeObjectManager lom = connection.getLargeObjectAPI();
2921: long oid = lom.createLO();
2922: LargeObject lob = lom.open(oid);
2923: OutputStream los = lob.getOutputStream();
2924: try {
2925: // could be buffered, but then the OutputStream returned by LargeObject
2926: // is buffered internally anyhow, so there would be no performance
2927: // boost gained, if anything it would be worse!
2928: int c = l_inStream.read();
2929: int p = 0;
2930: while (c > -1 && p < l_length) {
2931: los.write(c);
2932: c = l_inStream.read();
2933: p++;
2934: }
2935: los.close();
2936: } catch (IOException se) {
2937: throw new PSQLException(
2938: GT
2939: .tr("Unexpected error writing large object to database."),
2940: PSQLState.UNEXPECTED_ERROR, se);
2941: }
2942: // lob is closed by the stream so don't call lob.close()
2943: setLong(i, oid);
2944: }
2945:
2946: public void setNull(int i, int t, String s) throws SQLException {
2947: checkClosed();
2948: setNull(i, t);
2949: }
2950:
2951: public void setRef(int i, Ref x) throws SQLException {
2952: throw Driver.notImplemented(this .getClass(), "setRef(int,Ref)");
2953: }
2954:
2955: public void setDate(int i, java.sql.Date d, java.util.Calendar cal)
2956: throws SQLException {
2957: checkClosed();
2958:
2959: if (d == null) {
2960: setNull(i, Types.DATE);
2961: return;
2962: }
2963:
2964: if (cal != null)
2965: cal = (Calendar) cal.clone();
2966:
2967: // We must use UNSPECIFIED here, or inserting a Date-with-timezone into a
2968: // timestamptz field does an unexpected rotation by the server's TimeZone:
2969: //
2970: // We want to interpret 2005/01/01 with calendar +0100 as
2971: // "local midnight in +0100", but if we go via date it interprets it
2972: // as local midnight in the server's timezone:
2973:
2974: // template1=# select '2005-01-01+0100'::timestamptz;
2975: // timestamptz
2976: // ------------------------
2977: // 2005-01-01 02:00:00+03
2978: // (1 row)
2979:
2980: // template1=# select '2005-01-01+0100'::date::timestamptz;
2981: // timestamptz
2982: // ------------------------
2983: // 2005-01-01 00:00:00+03
2984: // (1 row)
2985:
2986: bindString(i, connection.getTimestampUtils().toString(cal, d),
2987: Oid.UNSPECIFIED);
2988: }
2989:
2990: public void setTime(int i, Time t, java.util.Calendar cal)
2991: throws SQLException {
2992: checkClosed();
2993:
2994: if (t == null) {
2995: setNull(i, Types.TIME);
2996: return;
2997: }
2998:
2999: if (cal != null)
3000: cal = (Calendar) cal.clone();
3001:
3002: bindString(i, connection.getTimestampUtils().toString(cal, t),
3003: Oid.UNSPECIFIED);
3004: }
3005:
3006: public void setTimestamp(int i, Timestamp t, java.util.Calendar cal)
3007: throws SQLException {
3008: checkClosed();
3009:
3010: if (t == null) {
3011: setNull(i, Types.TIMESTAMP);
3012: return;
3013: }
3014:
3015: if (cal != null)
3016: cal = (Calendar) cal.clone();
3017:
3018: // Use UNSPECIFIED as a compromise to get both TIMESTAMP and TIMESTAMPTZ working.
3019: // This is because you get this in a +1300 timezone:
3020: //
3021: // template1=# select '2005-01-01 15:00:00 +1000'::timestamptz;
3022: // timestamptz
3023: // ------------------------
3024: // 2005-01-01 18:00:00+13
3025: // (1 row)
3026:
3027: // template1=# select '2005-01-01 15:00:00 +1000'::timestamp;
3028: // timestamp
3029: // ---------------------
3030: // 2005-01-01 15:00:00
3031: // (1 row)
3032:
3033: // template1=# select '2005-01-01 15:00:00 +1000'::timestamptz::timestamp;
3034: // timestamp
3035: // ---------------------
3036: // 2005-01-01 18:00:00
3037: // (1 row)
3038:
3039: // So we want to avoid doing a timestamptz -> timestamp conversion, as that
3040: // will first convert the timestamptz to an equivalent time in the server's
3041: // timezone (+1300, above), then turn it into a timestamp with the "wrong"
3042: // time compared to the string we originally provided. But going straight
3043: // to timestamp is OK as the input parser for timestamp just throws away
3044: // the timezone part entirely. Since we don't know ahead of time what type
3045: // we're actually dealing with, UNSPECIFIED seems the lesser evil, even if it
3046: // does give more scope for type-mismatch errors being silently hidden.
3047:
3048: bindString(i, connection.getTimestampUtils().toString(cal, t),
3049: Oid.UNSPECIFIED); // Let the server infer the right type.
3050: }
3051:
3052: // ** JDBC 2 Extensions for CallableStatement**
3053:
3054: public java.sql.Array getArray(int i) throws SQLException {
3055: checkClosed();
3056: checkIndex(i, Types.ARRAY, "Array");
3057: return (Array) callResult[i - 1];
3058: }
3059:
3060: public java.math.BigDecimal getBigDecimal(int parameterIndex)
3061: throws SQLException {
3062: checkClosed();
3063: checkIndex(parameterIndex, Types.NUMERIC, "BigDecimal");
3064: return ((BigDecimal) callResult[parameterIndex - 1]);
3065: }
3066:
3067: public Blob getBlob(int i) throws SQLException {
3068: throw Driver.notImplemented(this .getClass(), "getBlob(int)");
3069: }
3070:
3071: public Clob getClob(int i) throws SQLException {
3072: throw Driver.notImplemented(this .getClass(), "getClob(int)");
3073: }
3074:
3075: public Object getObjectImpl(int i, java.util.Map map)
3076: throws SQLException {
3077: if (map == null || map.isEmpty()) {
3078: return getObject(i);
3079: }
3080: throw Driver.notImplemented(this .getClass(),
3081: "getObjectImpl(int,Map)");
3082: }
3083:
3084: public Ref getRef(int i) throws SQLException {
3085: throw Driver.notImplemented(this .getClass(), "getRef(int)");
3086: }
3087:
3088: public java.sql.Date getDate(int i, java.util.Calendar cal)
3089: throws SQLException {
3090: checkClosed();
3091: checkIndex(i, Types.DATE, "Date");
3092:
3093: if (callResult[i - 1] == null)
3094: return null;
3095:
3096: if (cal != null)
3097: cal = (Calendar) cal.clone();
3098:
3099: String value = callResult[i - 1].toString();
3100: return connection.getTimestampUtils().toDate(cal, value);
3101: }
3102:
3103: public Time getTime(int i, java.util.Calendar cal)
3104: throws SQLException {
3105: checkClosed();
3106: checkIndex(i, Types.TIME, "Time");
3107:
3108: if (callResult[i - 1] == null)
3109: return null;
3110:
3111: if (cal != null)
3112: cal = (Calendar) cal.clone();
3113:
3114: String value = callResult[i - 1].toString();
3115: return connection.getTimestampUtils().toTime(cal, value);
3116: }
3117:
3118: public Timestamp getTimestamp(int i, java.util.Calendar cal)
3119: throws SQLException {
3120: checkClosed();
3121: checkIndex(i, Types.TIMESTAMP, "Timestamp");
3122:
3123: if (callResult[i - 1] == null)
3124: return null;
3125:
3126: if (cal != null)
3127: cal = (Calendar) cal.clone();
3128:
3129: String value = callResult[i - 1].toString();
3130: return connection.getTimestampUtils().toTimestamp(cal, value);
3131: }
3132:
3133: // no custom types allowed yet..
3134: public void registerOutParameter(int parameterIndex, int sqlType,
3135: String typeName) throws SQLException {
3136: throw Driver.notImplemented(this .getClass(),
3137: "registerOutParameter(int,int,String)");
3138: }
3139:
3140: }
|