0001: /*
0002: Copyright (C) 2002-2007 MySQL AB
0003:
0004: This program is free software; you can redistribute it and/or modify
0005: it under the terms of version 2 of the GNU General Public License as
0006: published by the Free Software Foundation.
0007:
0008: There are special exceptions to the terms and conditions of the GPL
0009: as it is applied to this software. View the full text of the
0010: exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
0011: software distribution.
0012:
0013: This program is distributed in the hope that it will be useful,
0014: but WITHOUT ANY WARRANTY; without even the implied warranty of
0015: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0016: GNU General Public License for more details.
0017:
0018: You should have received a copy of the GNU General Public License
0019: along with this program; if not, write to the Free Software
0020: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0021:
0022:
0023:
0024: */
0025: package com.mysql.jdbc;
0026:
0027: import com.mysql.jdbc.exceptions.MySQLStatementCancelledException;
0028: import com.mysql.jdbc.exceptions.MySQLTimeoutException;
0029: import com.mysql.jdbc.profiler.ProfileEventSink;
0030: import com.mysql.jdbc.profiler.ProfilerEvent;
0031:
0032: import java.io.IOException;
0033: import java.io.InputStream;
0034: import java.io.Reader;
0035: import java.io.UnsupportedEncodingException;
0036:
0037: import java.lang.reflect.Constructor;
0038: import java.lang.reflect.InvocationTargetException;
0039: import java.math.BigDecimal;
0040:
0041: import java.net.URL;
0042:
0043: import java.sql.Array;
0044: import java.sql.Blob;
0045: import java.sql.Clob;
0046: import java.sql.Date;
0047: import java.sql.ParameterMetaData;
0048: import java.sql.Ref;
0049: import java.sql.ResultSet;
0050: import java.sql.SQLException;
0051: import java.sql.Time;
0052: import java.sql.Timestamp;
0053: import java.sql.Types;
0054:
0055: import java.util.ArrayList;
0056: import java.util.Calendar;
0057: import java.util.Properties;
0058: import java.util.TimeZone;
0059:
0060: /**
0061: * JDBC Interface for MySQL-4.1 and newer server-side PreparedStatements.
0062: *
0063: * @author Mark Matthews
0064: * @version $Id: ServerPreparedStatement.java,v 1.1.2.2 2005/05/17 14:58:56
0065: * mmatthews Exp $
0066: */
0067: public class ServerPreparedStatement extends PreparedStatement {
0068: private static final Constructor JDBC_4_SPS_CTOR;
0069:
0070: static {
0071: if (Util.isJdbc4()) {
0072: try {
0073: JDBC_4_SPS_CTOR = Class.forName(
0074: "com.mysql.jdbc.JDBC4ServerPreparedStatement")
0075: .getConstructor(
0076: new Class[] { ConnectionImpl.class,
0077: String.class, String.class,
0078: Integer.TYPE, Integer.TYPE });
0079: } catch (SecurityException e) {
0080: throw new RuntimeException(e);
0081: } catch (NoSuchMethodException e) {
0082: throw new RuntimeException(e);
0083: } catch (ClassNotFoundException e) {
0084: throw new RuntimeException(e);
0085: }
0086: } else {
0087: JDBC_4_SPS_CTOR = null;
0088: }
0089: }
0090:
0091: protected static final int BLOB_STREAM_READ_BUF_SIZE = 8192;
0092:
0093: static class BatchedBindValues {
0094: BindValue[] batchedParameterValues;
0095:
0096: BatchedBindValues(BindValue[] paramVals) {
0097: int numParams = paramVals.length;
0098:
0099: this .batchedParameterValues = new BindValue[numParams];
0100:
0101: for (int i = 0; i < numParams; i++) {
0102: this .batchedParameterValues[i] = new BindValue(
0103: paramVals[i]);
0104: }
0105: }
0106: }
0107:
0108: public static class BindValue {
0109:
0110: long boundBeforeExecutionNum = 0;
0111:
0112: public long bindLength; /* Default length of data */
0113:
0114: int bufferType; /* buffer type */
0115:
0116: byte byteBinding;
0117:
0118: double doubleBinding;
0119:
0120: float floatBinding;
0121:
0122: int intBinding;
0123:
0124: public boolean isLongData; /* long data indicator */
0125:
0126: public boolean isNull; /* NULL indicator */
0127:
0128: boolean isSet = false; /* has this parameter been set? */
0129:
0130: long longBinding;
0131:
0132: short shortBinding;
0133:
0134: public Object value; /* The value to store */
0135:
0136: BindValue() {
0137: }
0138:
0139: BindValue(BindValue copyMe) {
0140: this .value = copyMe.value;
0141: this .isSet = copyMe.isSet;
0142: this .isLongData = copyMe.isLongData;
0143: this .isNull = copyMe.isNull;
0144: this .bufferType = copyMe.bufferType;
0145: this .bindLength = copyMe.bindLength;
0146: this .byteBinding = copyMe.byteBinding;
0147: this .shortBinding = copyMe.shortBinding;
0148: this .intBinding = copyMe.intBinding;
0149: this .longBinding = copyMe.longBinding;
0150: this .floatBinding = copyMe.floatBinding;
0151: this .doubleBinding = copyMe.doubleBinding;
0152: }
0153:
0154: void reset() {
0155: this .isSet = false;
0156: this .value = null;
0157: this .isLongData = false;
0158:
0159: this .byteBinding = 0;
0160: this .shortBinding = 0;
0161: this .intBinding = 0;
0162: this .longBinding = 0L;
0163: this .floatBinding = 0;
0164: this .doubleBinding = 0D;
0165: }
0166:
0167: public String toString() {
0168: return toString(false);
0169: }
0170:
0171: public String toString(boolean quoteIfNeeded) {
0172: if (this .isLongData) {
0173: return "' STREAM DATA '";
0174: }
0175:
0176: switch (this .bufferType) {
0177: case MysqlDefs.FIELD_TYPE_TINY:
0178: return String.valueOf(byteBinding);
0179: case MysqlDefs.FIELD_TYPE_SHORT:
0180: return String.valueOf(shortBinding);
0181: case MysqlDefs.FIELD_TYPE_LONG:
0182: return String.valueOf(intBinding);
0183: case MysqlDefs.FIELD_TYPE_LONGLONG:
0184: return String.valueOf(longBinding);
0185: case MysqlDefs.FIELD_TYPE_FLOAT:
0186: return String.valueOf(floatBinding);
0187: case MysqlDefs.FIELD_TYPE_DOUBLE:
0188: return String.valueOf(doubleBinding);
0189: case MysqlDefs.FIELD_TYPE_TIME:
0190: case MysqlDefs.FIELD_TYPE_DATE:
0191: case MysqlDefs.FIELD_TYPE_DATETIME:
0192: case MysqlDefs.FIELD_TYPE_TIMESTAMP:
0193: case MysqlDefs.FIELD_TYPE_VAR_STRING:
0194: case MysqlDefs.FIELD_TYPE_STRING:
0195: case MysqlDefs.FIELD_TYPE_VARCHAR:
0196: if (quoteIfNeeded) {
0197: return "'" + String.valueOf(value) + "'";
0198: } else {
0199: return String.valueOf(value);
0200: }
0201: default:
0202: if (value instanceof byte[]) {
0203: return "byte data";
0204:
0205: } else {
0206: if (quoteIfNeeded) {
0207: return "'" + String.valueOf(value) + "'";
0208: } else {
0209: return String.valueOf(value);
0210: }
0211: }
0212: }
0213: }
0214: }
0215:
0216: /* 1 (length) + 2 (year) + 1 (month) + 1 (day) */
0217: private static final byte MAX_DATE_REP_LENGTH = (byte) 5;
0218:
0219: /*
0220: * 1 (length) + 2 (year) + 1 (month) + 1 (day) + 1 (hour) + 1 (minute) + 1
0221: * (second) + 4 (microseconds)
0222: */
0223: private static final byte MAX_DATETIME_REP_LENGTH = 12;
0224:
0225: /*
0226: * 1 (length) + 1 (is negative) + 4 (day count) + 1 (hour) + 1 (minute) + 1
0227: * (seconds) + 4 (microseconds)
0228: */
0229: private static final byte MAX_TIME_REP_LENGTH = 13;
0230:
0231: private void storeTime(Buffer intoBuf, Time tm) throws SQLException {
0232:
0233: intoBuf.ensureCapacity(9);
0234: intoBuf.writeByte((byte) 8); // length
0235: intoBuf.writeByte((byte) 0); // neg flag
0236: intoBuf.writeLong(0); // tm->day, not used
0237:
0238: Calendar sessionCalendar = getCalendarInstanceForSessionOrNew();
0239:
0240: synchronized (sessionCalendar) {
0241: java.util.Date oldTime = sessionCalendar.getTime();
0242: try {
0243: sessionCalendar.setTime(tm);
0244: intoBuf.writeByte((byte) sessionCalendar
0245: .get(Calendar.HOUR_OF_DAY));
0246: intoBuf.writeByte((byte) sessionCalendar
0247: .get(Calendar.MINUTE));
0248: intoBuf.writeByte((byte) sessionCalendar
0249: .get(Calendar.SECOND));
0250:
0251: // intoBuf.writeLongInt(0); // tm-second_part
0252: } finally {
0253: sessionCalendar.setTime(oldTime);
0254: }
0255: }
0256: }
0257:
0258: /**
0259: * Flag indicating whether or not the long parameters have been 'switched'
0260: * back to normal parameters. We can not execute() if clearParameters()
0261: * hasn't been called in this case.
0262: */
0263: private boolean detectedLongParameterSwitch = false;
0264:
0265: /**
0266: * The number of fields in the result set (if any) for this
0267: * PreparedStatement.
0268: */
0269: private int fieldCount;
0270:
0271: /** Has this prepared statement been marked invalid? */
0272: private boolean invalid = false;
0273:
0274: /** If this statement has been marked invalid, what was the reason? */
0275: private SQLException invalidationException;
0276:
0277: /** Does this query modify data? */
0278: private boolean isSelectQuery;
0279:
0280: private Buffer outByteBuffer;
0281:
0282: /** Bind values for individual fields */
0283: private BindValue[] parameterBindings;
0284:
0285: /** Field-level metadata for parameters */
0286: private Field[] parameterFields;
0287:
0288: /** Field-level metadata for result sets. */
0289: private Field[] resultFields;
0290:
0291: /** Do we need to send/resend types to the server? */
0292: private boolean sendTypesToServer = false;
0293:
0294: /** The ID that the server uses to identify this PreparedStatement */
0295: private long serverStatementId;
0296:
0297: /** The type used for string bindings, changes from version-to-version */
0298: private int stringTypeCode = MysqlDefs.FIELD_TYPE_STRING;
0299:
0300: private boolean serverNeedsResetBeforeEachExecution;
0301:
0302: /**
0303: * Creates a prepared statement instance -- We need to provide factory-style
0304: * methods so we can support both JDBC3 (and older) and JDBC4 runtimes,
0305: * otherwise the class verifier complains when it tries to load JDBC4-only
0306: * interface classes that are present in JDBC4 method signatures.
0307: */
0308:
0309: protected static ServerPreparedStatement getInstance(
0310: ConnectionImpl conn, String sql, String catalog,
0311: int resultSetType, int resultSetConcurrency)
0312: throws SQLException {
0313: if (!Util.isJdbc4()) {
0314: return new ServerPreparedStatement(conn, sql, catalog,
0315: resultSetType, resultSetConcurrency);
0316: }
0317:
0318: try {
0319: return (ServerPreparedStatement) JDBC_4_SPS_CTOR
0320: .newInstance(new Object[] {
0321: conn,
0322: sql,
0323: catalog,
0324: Constants.integerValueOf(resultSetType),
0325: Constants
0326: .integerValueOf(resultSetConcurrency) });
0327: } catch (IllegalArgumentException e) {
0328: throw new SQLException(e.toString(),
0329: SQLError.SQL_STATE_GENERAL_ERROR);
0330: } catch (InstantiationException e) {
0331: throw new SQLException(e.toString(),
0332: SQLError.SQL_STATE_GENERAL_ERROR);
0333: } catch (IllegalAccessException e) {
0334: throw new SQLException(e.toString(),
0335: SQLError.SQL_STATE_GENERAL_ERROR);
0336: } catch (InvocationTargetException e) {
0337: Throwable target = e.getTargetException();
0338:
0339: if (target instanceof SQLException) {
0340: throw (SQLException) target;
0341: }
0342:
0343: throw new SQLException(target.toString(),
0344: SQLError.SQL_STATE_GENERAL_ERROR);
0345: }
0346: }
0347:
0348: /**
0349: * Creates a new ServerPreparedStatement object.
0350: *
0351: * @param conn
0352: * the connection creating us.
0353: * @param sql
0354: * the SQL containing the statement to prepare.
0355: * @param catalog
0356: * the catalog in use when we were created.
0357: *
0358: * @throws SQLException
0359: * If an error occurs
0360: */
0361: protected ServerPreparedStatement(ConnectionImpl conn, String sql,
0362: String catalog, int resultSetType, int resultSetConcurrency)
0363: throws SQLException {
0364: super (conn, catalog);
0365:
0366: checkNullOrEmptyQuery(sql);
0367:
0368: this .isSelectQuery = StringUtils.startsWithIgnoreCaseAndWs(sql,
0369: "SELECT"); //$NON-NLS-1$
0370:
0371: if (this .connection.versionMeetsMinimum(5, 0, 0)) {
0372: this .serverNeedsResetBeforeEachExecution = !this .connection
0373: .versionMeetsMinimum(5, 0, 3);
0374: } else {
0375: this .serverNeedsResetBeforeEachExecution = !this .connection
0376: .versionMeetsMinimum(4, 1, 10);
0377: }
0378:
0379: this .useTrueBoolean = this .connection.versionMeetsMinimum(3,
0380: 21, 23);
0381: this .hasLimitClause = (StringUtils.indexOfIgnoreCase(sql,
0382: "LIMIT") != -1); //$NON-NLS-1$
0383: this .firstCharOfStmt = StringUtils.firstNonWsCharUc(sql);
0384: String statementComment = this .connection.getStatementComment();
0385:
0386: this .originalSql = (statementComment == null) ? sql : "/* "
0387: + statementComment + " */ " + sql;
0388:
0389: if (this .connection.versionMeetsMinimum(4, 1, 2)) {
0390: this .stringTypeCode = MysqlDefs.FIELD_TYPE_VAR_STRING;
0391: } else {
0392: this .stringTypeCode = MysqlDefs.FIELD_TYPE_STRING;
0393: }
0394:
0395: try {
0396: serverPrepare(sql);
0397: } catch (SQLException sqlEx) {
0398: realClose(false, true);
0399: // don't wrap SQLExceptions
0400: throw sqlEx;
0401: } catch (Exception ex) {
0402: realClose(false, true);
0403:
0404: throw SQLError.createSQLException(ex.toString(),
0405: SQLError.SQL_STATE_GENERAL_ERROR);
0406: }
0407:
0408: setResultSetType(resultSetType);
0409: setResultSetConcurrency(resultSetConcurrency);
0410:
0411: this .parameterTypes = new int[this .parameterCount];
0412: }
0413:
0414: /**
0415: * JDBC 2.0 Add a set of parameters to the batch.
0416: *
0417: * @exception SQLException
0418: * if a database-access error occurs.
0419: *
0420: * @see StatementImpl#addBatch
0421: */
0422: public synchronized void addBatch() throws SQLException {
0423: checkClosed();
0424:
0425: if (this .batchedArgs == null) {
0426: this .batchedArgs = new ArrayList();
0427: }
0428:
0429: this .batchedArgs.add(new BatchedBindValues(
0430: this .parameterBindings));
0431: }
0432:
0433: protected String asSql(boolean quoteStreamsAndUnknowns)
0434: throws SQLException {
0435:
0436: if (this .isClosed) {
0437: return "statement has been closed, no further internal information available";
0438: }
0439:
0440: PreparedStatement pStmtForSub = null;
0441:
0442: try {
0443: pStmtForSub = PreparedStatement.getInstance(
0444: this .connection, this .originalSql,
0445: this .currentCatalog);
0446:
0447: int numParameters = pStmtForSub.parameterCount;
0448: int ourNumParameters = this .parameterCount;
0449:
0450: for (int i = 0; (i < numParameters)
0451: && (i < ourNumParameters); i++) {
0452: if (this .parameterBindings[i] != null) {
0453: if (this .parameterBindings[i].isNull) {
0454: pStmtForSub.setNull(i + 1, Types.NULL);
0455: } else {
0456: BindValue bindValue = this .parameterBindings[i];
0457:
0458: //
0459: // Handle primitives first
0460: //
0461: switch (bindValue.bufferType) {
0462:
0463: case MysqlDefs.FIELD_TYPE_TINY:
0464: pStmtForSub.setByte(i + 1,
0465: bindValue.byteBinding);
0466: break;
0467: case MysqlDefs.FIELD_TYPE_SHORT:
0468: pStmtForSub.setShort(i + 1,
0469: bindValue.shortBinding);
0470: break;
0471: case MysqlDefs.FIELD_TYPE_LONG:
0472: pStmtForSub.setInt(i + 1,
0473: bindValue.intBinding);
0474: break;
0475: case MysqlDefs.FIELD_TYPE_LONGLONG:
0476: pStmtForSub.setLong(i + 1,
0477: bindValue.longBinding);
0478: break;
0479: case MysqlDefs.FIELD_TYPE_FLOAT:
0480: pStmtForSub.setFloat(i + 1,
0481: bindValue.floatBinding);
0482: break;
0483: case MysqlDefs.FIELD_TYPE_DOUBLE:
0484: pStmtForSub.setDouble(i + 1,
0485: bindValue.doubleBinding);
0486: break;
0487: default:
0488: pStmtForSub.setObject(i + 1,
0489: this .parameterBindings[i].value);
0490: break;
0491: }
0492: }
0493: }
0494: }
0495:
0496: return pStmtForSub.asSql(quoteStreamsAndUnknowns);
0497: } finally {
0498: if (pStmtForSub != null) {
0499: try {
0500: pStmtForSub.close();
0501: } catch (SQLException sqlEx) {
0502: ; // ignore
0503: }
0504: }
0505: }
0506: }
0507:
0508: /*
0509: * (non-Javadoc)
0510: *
0511: * @see com.mysql.jdbc.Statement#checkClosed()
0512: */
0513: protected void checkClosed() throws SQLException {
0514: if (this .invalid) {
0515: throw this .invalidationException;
0516: }
0517:
0518: super .checkClosed();
0519: }
0520:
0521: /**
0522: * @see java.sql.PreparedStatement#clearParameters()
0523: */
0524: public void clearParameters() throws SQLException {
0525: checkClosed();
0526: clearParametersInternal(true);
0527: }
0528:
0529: private void clearParametersInternal(boolean clearServerParameters)
0530: throws SQLException {
0531: boolean hadLongData = false;
0532:
0533: if (this .parameterBindings != null) {
0534: for (int i = 0; i < this .parameterCount; i++) {
0535: if ((this .parameterBindings[i] != null)
0536: && this .parameterBindings[i].isLongData) {
0537: hadLongData = true;
0538: }
0539:
0540: this .parameterBindings[i].reset();
0541: }
0542: }
0543:
0544: if (clearServerParameters && hadLongData) {
0545: serverResetStatement();
0546:
0547: this .detectedLongParameterSwitch = false;
0548: }
0549: }
0550:
0551: protected boolean isCached = false;
0552:
0553: protected void setClosed(boolean flag) {
0554: this .isClosed = flag;
0555: }
0556:
0557: /**
0558: * @see java.sql.Statement#close()
0559: */
0560: public void close() throws SQLException {
0561: if (this .isCached) {
0562: this .isClosed = true;
0563: this .connection.recachePreparedStatement(this );
0564: return;
0565: }
0566:
0567: realClose(true, true);
0568: }
0569:
0570: private void dumpCloseForTestcase() {
0571: StringBuffer buf = new StringBuffer();
0572: this .connection.generateConnectionCommentBlock(buf);
0573: buf.append("DEALLOCATE PREPARE debug_stmt_");
0574: buf.append(this .statementId);
0575: buf.append(";\n");
0576:
0577: this .connection.dumpTestcaseQuery(buf.toString());
0578: }
0579:
0580: private void dumpExecuteForTestcase() throws SQLException {
0581: StringBuffer buf = new StringBuffer();
0582:
0583: for (int i = 0; i < this .parameterCount; i++) {
0584: this .connection.generateConnectionCommentBlock(buf);
0585:
0586: buf.append("SET @debug_stmt_param");
0587: buf.append(this .statementId);
0588: buf.append("_");
0589: buf.append(i);
0590: buf.append("=");
0591:
0592: if (this .parameterBindings[i].isNull) {
0593: buf.append("NULL");
0594: } else {
0595: buf.append(this .parameterBindings[i].toString(true));
0596: }
0597:
0598: buf.append(";\n");
0599: }
0600:
0601: this .connection.generateConnectionCommentBlock(buf);
0602:
0603: buf.append("EXECUTE debug_stmt_");
0604: buf.append(this .statementId);
0605:
0606: if (this .parameterCount > 0) {
0607: buf.append(" USING ");
0608: for (int i = 0; i < this .parameterCount; i++) {
0609: if (i > 0) {
0610: buf.append(", ");
0611: }
0612:
0613: buf.append("@debug_stmt_param");
0614: buf.append(this .statementId);
0615: buf.append("_");
0616: buf.append(i);
0617:
0618: }
0619: }
0620:
0621: buf.append(";\n");
0622:
0623: this .connection.dumpTestcaseQuery(buf.toString());
0624: }
0625:
0626: private void dumpPrepareForTestcase() throws SQLException {
0627:
0628: StringBuffer buf = new StringBuffer(
0629: this .originalSql.length() + 64);
0630:
0631: this .connection.generateConnectionCommentBlock(buf);
0632:
0633: buf.append("PREPARE debug_stmt_");
0634: buf.append(this .statementId);
0635: buf.append(" FROM \"");
0636: buf.append(this .originalSql);
0637: buf.append("\";\n");
0638:
0639: this .connection.dumpTestcaseQuery(buf.toString());
0640: }
0641:
0642: /**
0643: * @see java.sql.Statement#executeBatch()
0644: */
0645: public synchronized int[] executeBatch() throws SQLException {
0646: if (this .connection.isReadOnly()) {
0647: throw SQLError.createSQLException(Messages
0648: .getString("ServerPreparedStatement.2") //$NON-NLS-1$
0649: + Messages.getString("ServerPreparedStatement.3"), //$NON-NLS-1$
0650: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
0651: }
0652:
0653: checkClosed();
0654:
0655: synchronized (this .connection.getMutex()) {
0656: clearWarnings();
0657:
0658: // Store this for later, we're going to 'swap' them out
0659: // as we execute each batched statement...
0660: BindValue[] oldBindValues = this .parameterBindings;
0661:
0662: try {
0663: int[] updateCounts = null;
0664:
0665: if (this .batchedArgs != null) {
0666: int nbrCommands = this .batchedArgs.size();
0667: updateCounts = new int[nbrCommands];
0668:
0669: if (this .retrieveGeneratedKeys) {
0670: this .batchedGeneratedKeys = new ArrayList(
0671: nbrCommands);
0672: }
0673:
0674: for (int i = 0; i < nbrCommands; i++) {
0675: updateCounts[i] = -3;
0676: }
0677:
0678: SQLException sqlEx = null;
0679:
0680: int commandIndex = 0;
0681:
0682: BindValue[] previousBindValuesForBatch = null;
0683:
0684: for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
0685: Object arg = this .batchedArgs.get(commandIndex);
0686:
0687: if (arg instanceof String) {
0688: updateCounts[commandIndex] = executeUpdate((String) arg);
0689: } else {
0690: this .parameterBindings = ((BatchedBindValues) arg).batchedParameterValues;
0691:
0692: try {
0693: // We need to check types each time, as
0694: // the user might have bound different
0695: // types in each addBatch()
0696:
0697: if (previousBindValuesForBatch != null) {
0698: for (int j = 0; j < this .parameterBindings.length; j++) {
0699: if (this .parameterBindings[j].bufferType != previousBindValuesForBatch[j].bufferType) {
0700: this .sendTypesToServer = true;
0701:
0702: break;
0703: }
0704: }
0705: }
0706:
0707: try {
0708: updateCounts[commandIndex] = executeUpdate(
0709: false, true);
0710: } finally {
0711: previousBindValuesForBatch = this .parameterBindings;
0712: }
0713:
0714: if (this .retrieveGeneratedKeys) {
0715: java.sql.ResultSet rs = null;
0716:
0717: try {
0718: // we don't want to use our version,
0719: // because we've altered the behavior of
0720: // ours to support batch updates
0721: // (catch-22)
0722: // Ideally, what we need here is
0723: // super.super.getGeneratedKeys()
0724: // but that construct doesn't exist in
0725: // Java, so that's why there's
0726: // this kludge.
0727: rs = getGeneratedKeysInternal();
0728:
0729: while (rs.next()) {
0730: this .batchedGeneratedKeys
0731: .add(new ByteArrayRow(
0732: new byte[][] { rs
0733: .getBytes(1) }));
0734: }
0735: } finally {
0736: if (rs != null) {
0737: rs.close();
0738: }
0739: }
0740: }
0741: } catch (SQLException ex) {
0742: updateCounts[commandIndex] = EXECUTE_FAILED;
0743:
0744: if (this .continueBatchOnError) {
0745: sqlEx = ex;
0746: } else {
0747: int[] newUpdateCounts = new int[commandIndex];
0748: System.arraycopy(updateCounts, 0,
0749: newUpdateCounts, 0,
0750: commandIndex);
0751:
0752: throw new java.sql.BatchUpdateException(
0753: ex.getMessage(), ex
0754: .getSQLState(), ex
0755: .getErrorCode(),
0756: newUpdateCounts);
0757: }
0758: }
0759: }
0760: }
0761:
0762: if (sqlEx != null) {
0763: throw new java.sql.BatchUpdateException(sqlEx
0764: .getMessage(), sqlEx.getSQLState(),
0765: sqlEx.getErrorCode(), updateCounts);
0766: }
0767: }
0768:
0769: return (updateCounts != null) ? updateCounts
0770: : new int[0];
0771: } finally {
0772: this .parameterBindings = oldBindValues;
0773: this .sendTypesToServer = true;
0774:
0775: clearBatch();
0776: }
0777: }
0778: }
0779:
0780: /**
0781: * @see com.mysql.jdbc.PreparedStatement#executeInternal(int,
0782: * com.mysql.jdbc.Buffer, boolean, boolean)
0783: */
0784: protected com.mysql.jdbc.ResultSetInternalMethods executeInternal(
0785: int maxRowsToRetrieve, Buffer sendPacket,
0786: boolean createStreamingResultSet,
0787: boolean queryIsSelectOnly, Field[] metadataFromCache,
0788: boolean isBatch) throws SQLException {
0789: this .numberOfExecutions++;
0790:
0791: // We defer to server-side execution
0792: try {
0793: return serverExecute(maxRowsToRetrieve,
0794: createStreamingResultSet, metadataFromCache);
0795: } catch (SQLException sqlEx) {
0796: // don't wrap SQLExceptions
0797: if (this .connection.getEnablePacketDebug()) {
0798: this .connection.getIO().dumpPacketRingBuffer();
0799: }
0800:
0801: if (this .connection.getDumpQueriesOnException()) {
0802: String extractedSql = toString();
0803: StringBuffer messageBuf = new StringBuffer(extractedSql
0804: .length() + 32);
0805: messageBuf
0806: .append("\n\nQuery being executed when exception was thrown:\n\n");
0807: messageBuf.append(extractedSql);
0808:
0809: sqlEx = ConnectionImpl.appendMessageToException(sqlEx,
0810: messageBuf.toString());
0811: }
0812:
0813: throw sqlEx;
0814: } catch (Exception ex) {
0815: if (this .connection.getEnablePacketDebug()) {
0816: this .connection.getIO().dumpPacketRingBuffer();
0817: }
0818:
0819: SQLException sqlEx = SQLError.createSQLException(ex
0820: .toString(), SQLError.SQL_STATE_GENERAL_ERROR);
0821:
0822: if (this .connection.getDumpQueriesOnException()) {
0823: String extractedSql = toString();
0824: StringBuffer messageBuf = new StringBuffer(extractedSql
0825: .length() + 32);
0826: messageBuf
0827: .append("\n\nQuery being executed when exception was thrown:\n\n");
0828: messageBuf.append(extractedSql);
0829:
0830: sqlEx = ConnectionImpl.appendMessageToException(sqlEx,
0831: messageBuf.toString());
0832: }
0833:
0834: throw sqlEx;
0835: }
0836: }
0837:
0838: /**
0839: * @see com.mysql.jdbc.PreparedStatement#fillSendPacket()
0840: */
0841: protected Buffer fillSendPacket() throws SQLException {
0842: return null; // we don't use this type of packet
0843: }
0844:
0845: /**
0846: * @see com.mysql.jdbc.PreparedStatement#fillSendPacket(byte,
0847: * java.io.InputStream, boolean, int)
0848: */
0849: protected Buffer fillSendPacket(byte[][] batchedParameterStrings,
0850: InputStream[] batchedParameterStreams,
0851: boolean[] batchedIsStream, int[] batchedStreamLengths)
0852: throws SQLException {
0853: return null; // we don't use this type of packet
0854: }
0855:
0856: /**
0857: * Returns the structure representing the value that (can be)/(is)
0858: * bound at the given parameter index.
0859: *
0860: * @param parameterIndex 1-based
0861: * @param forLongData is this for a stream?
0862: * @return
0863: * @throws SQLException
0864: */
0865: protected BindValue getBinding(int parameterIndex,
0866: boolean forLongData) throws SQLException {
0867: checkClosed();
0868:
0869: if (this .parameterBindings.length == 0) {
0870: throw SQLError.createSQLException(Messages
0871: .getString("ServerPreparedStatement.8"), //$NON-NLS-1$
0872: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
0873: }
0874:
0875: parameterIndex--;
0876:
0877: if ((parameterIndex < 0)
0878: || (parameterIndex >= this .parameterBindings.length)) {
0879: throw SQLError.createSQLException(Messages
0880: .getString("ServerPreparedStatement.9") //$NON-NLS-1$
0881: + (parameterIndex + 1)
0882: + Messages.getString("ServerPreparedStatement.10") //$NON-NLS-1$
0883: + this .parameterBindings.length,
0884: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
0885: }
0886:
0887: if (this .parameterBindings[parameterIndex] == null) {
0888: this .parameterBindings[parameterIndex] = new BindValue();
0889: } else {
0890: if (this .parameterBindings[parameterIndex].isLongData
0891: && !forLongData) {
0892: this .detectedLongParameterSwitch = true;
0893: }
0894: }
0895:
0896: this .parameterBindings[parameterIndex].isSet = true;
0897: this .parameterBindings[parameterIndex].boundBeforeExecutionNum = this .numberOfExecutions;
0898:
0899: return this .parameterBindings[parameterIndex];
0900: }
0901:
0902: /**
0903: * @see com.mysql.jdbc.PreparedStatement#getBytes(int)
0904: */
0905: byte[] getBytes(int parameterIndex) throws SQLException {
0906: BindValue bindValue = getBinding(parameterIndex, false);
0907:
0908: if (bindValue.isNull) {
0909: return null;
0910: } else if (bindValue.isLongData) {
0911: throw new NotImplemented();
0912: } else {
0913: if (this .outByteBuffer == null) {
0914: this .outByteBuffer = new Buffer(this .connection
0915: .getNetBufferLength());
0916: }
0917:
0918: this .outByteBuffer.clear();
0919:
0920: int originalPosition = this .outByteBuffer.getPosition();
0921:
0922: storeBinding(this .outByteBuffer, bindValue, this .connection
0923: .getIO());
0924:
0925: int newPosition = this .outByteBuffer.getPosition();
0926:
0927: int length = newPosition - originalPosition;
0928:
0929: byte[] valueAsBytes = new byte[length];
0930:
0931: System.arraycopy(this .outByteBuffer.getByteBuffer(),
0932: originalPosition, valueAsBytes, 0, length);
0933:
0934: return valueAsBytes;
0935: }
0936: }
0937:
0938: /**
0939: * @see java.sql.PreparedStatement#getMetaData()
0940: */
0941: public java.sql.ResultSetMetaData getMetaData() throws SQLException {
0942: checkClosed();
0943:
0944: if (this .resultFields == null) {
0945: return null;
0946: }
0947:
0948: return new ResultSetMetaData(this .resultFields, this .connection
0949: .getUseOldAliasMetadataBehavior());
0950: }
0951:
0952: /**
0953: * @see java.sql.PreparedStatement#getParameterMetaData()
0954: */
0955: public ParameterMetaData getParameterMetaData() throws SQLException {
0956: checkClosed();
0957:
0958: if (this .parameterMetaData == null) {
0959: this .parameterMetaData = new MysqlParameterMetadata(
0960: this .parameterFields, this .parameterCount);
0961: }
0962:
0963: return this .parameterMetaData;
0964: }
0965:
0966: /**
0967: * @see com.mysql.jdbc.PreparedStatement#isNull(int)
0968: */
0969: boolean isNull(int paramIndex) {
0970: throw new IllegalArgumentException(Messages
0971: .getString("ServerPreparedStatement.7")); //$NON-NLS-1$
0972: }
0973:
0974: /**
0975: * Closes this connection and frees all resources.
0976: *
0977: * @param calledExplicitly
0978: * was this called from close()?
0979: *
0980: * @throws SQLException
0981: * if an error occurs
0982: */
0983: protected void realClose(boolean calledExplicitly,
0984: boolean closeOpenResults) throws SQLException {
0985: if (this .isClosed) {
0986: return;
0987: }
0988:
0989: if (this .connection != null) {
0990: if (this .connection.getAutoGenerateTestcaseScript()) {
0991: dumpCloseForTestcase();
0992: }
0993:
0994: //
0995: // Don't communicate with the server if we're being
0996: // called from the finalizer...
0997: //
0998: // This will leak server resources, but if we don't do this,
0999: // we'll deadlock (potentially, because there's no guarantee
1000: // when, what order, and what concurrency finalizers will be
1001: // called with). Well-behaved programs won't rely on finalizers
1002: // to clean up their statements.
1003: //
1004:
1005: SQLException exceptionDuringClose = null;
1006:
1007: if (calledExplicitly && !this .connection.isClosed()) {
1008: synchronized (this .connection.getMutex()) {
1009: try {
1010:
1011: MysqlIO mysql = this .connection.getIO();
1012:
1013: Buffer packet = mysql.getSharedSendPacket();
1014:
1015: packet
1016: .writeByte((byte) MysqlDefs.COM_CLOSE_STATEMENT);
1017: packet.writeLong(this .serverStatementId);
1018:
1019: mysql.sendCommand(
1020: MysqlDefs.COM_CLOSE_STATEMENT, null,
1021: packet, true, null);
1022: } catch (SQLException sqlEx) {
1023: exceptionDuringClose = sqlEx;
1024: }
1025: }
1026: }
1027:
1028: super .realClose(calledExplicitly, closeOpenResults);
1029:
1030: clearParametersInternal(false);
1031: this .parameterBindings = null;
1032:
1033: this .parameterFields = null;
1034: this .resultFields = null;
1035:
1036: if (exceptionDuringClose != null) {
1037: throw exceptionDuringClose;
1038: }
1039: }
1040: }
1041:
1042: /**
1043: * Used by Connection when auto-reconnecting to retrieve 'lost' prepared
1044: * statements.
1045: *
1046: * @throws SQLException
1047: * if an error occurs.
1048: */
1049: protected void rePrepare() throws SQLException {
1050: this .invalidationException = null;
1051:
1052: try {
1053: serverPrepare(this .originalSql);
1054: } catch (SQLException sqlEx) {
1055: // don't wrap SQLExceptions
1056: this .invalidationException = sqlEx;
1057: } catch (Exception ex) {
1058: this .invalidationException = SQLError.createSQLException(ex
1059: .toString(), SQLError.SQL_STATE_GENERAL_ERROR);
1060: }
1061:
1062: if (this .invalidationException != null) {
1063: this .invalid = true;
1064:
1065: this .parameterBindings = null;
1066:
1067: this .parameterFields = null;
1068: this .resultFields = null;
1069:
1070: if (this .results != null) {
1071: try {
1072: this .results.close();
1073: } catch (Exception ex) {
1074: ;
1075: }
1076: }
1077:
1078: if (this .connection != null) {
1079: if (this .maxRowsChanged) {
1080: this .connection.unsetMaxRows(this );
1081: }
1082:
1083: if (!this .connection.getDontTrackOpenResources()) {
1084: this .connection.unregisterStatement(this );
1085: }
1086: }
1087: }
1088: }
1089:
1090: /**
1091: * Tells the server to execute this prepared statement with the current
1092: * parameter bindings.
1093: *
1094: * <pre>
1095: *
1096: *
1097: * - Server gets the command 'COM_EXECUTE' to execute the
1098: * previously prepared query. If there is any param markers;
1099: * then client will send the data in the following format:
1100: *
1101: * [COM_EXECUTE:1]
1102: * [STMT_ID:4]
1103: * [NULL_BITS:(param_count+7)/8)]
1104: * [TYPES_SUPPLIED_BY_CLIENT(0/1):1]
1105: * [[length]data]
1106: * [[length]data] .. [[length]data].
1107: *
1108: * (Note: Except for string/binary types; all other types will not be
1109: * supplied with length field)
1110: *
1111: *
1112: * </pre>
1113: *
1114: * @param maxRowsToRetrieve
1115: * DOCUMENT ME!
1116: * @param createStreamingResultSet
1117: * DOCUMENT ME!
1118: *
1119: * @return DOCUMENT ME!
1120: *
1121: * @throws SQLException
1122: */
1123: private com.mysql.jdbc.ResultSetInternalMethods serverExecute(
1124: int maxRowsToRetrieve, boolean createStreamingResultSet,
1125: Field[] metadataFromCache) throws SQLException {
1126: synchronized (this .connection.getMutex()) {
1127: if (this .detectedLongParameterSwitch) {
1128: // Check when values were bound
1129: boolean firstFound = false;
1130: long boundTimeToCheck = 0;
1131:
1132: for (int i = 0; i < this .parameterCount - 1; i++) {
1133: if (this .parameterBindings[i].isLongData) {
1134: if (firstFound
1135: && boundTimeToCheck != this .parameterBindings[i].boundBeforeExecutionNum) {
1136: throw SQLError
1137: .createSQLException(
1138: Messages
1139: .getString("ServerPreparedStatement.11") //$NON-NLS-1$
1140: + Messages
1141: .getString("ServerPreparedStatement.12"), //$NON-NLS-1$
1142: SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
1143: } else {
1144: firstFound = true;
1145: boundTimeToCheck = this .parameterBindings[i].boundBeforeExecutionNum;
1146: }
1147: }
1148: }
1149:
1150: // Okay, we've got all "newly"-bound streams, so reset
1151: // server-side state to clear out previous bindings
1152:
1153: serverResetStatement();
1154: }
1155:
1156: // Check bindings
1157: for (int i = 0; i < this .parameterCount; i++) {
1158: if (!this .parameterBindings[i].isSet) {
1159: throw SQLError
1160: .createSQLException(
1161: Messages
1162: .getString("ServerPreparedStatement.13") + (i + 1) //$NON-NLS-1$
1163: + Messages
1164: .getString("ServerPreparedStatement.14"),
1165: SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
1166: }
1167: }
1168:
1169: //
1170: // Send all long data
1171: //
1172: for (int i = 0; i < this .parameterCount; i++) {
1173: if (this .parameterBindings[i].isLongData) {
1174: serverLongData(i, this .parameterBindings[i]);
1175: }
1176: }
1177:
1178: if (this .connection.getAutoGenerateTestcaseScript()) {
1179: dumpExecuteForTestcase();
1180: }
1181:
1182: //
1183: // store the parameter values
1184: //
1185: MysqlIO mysql = this .connection.getIO();
1186:
1187: Buffer packet = mysql.getSharedSendPacket();
1188:
1189: packet.clear();
1190: packet.writeByte((byte) MysqlDefs.COM_EXECUTE);
1191: packet.writeLong(this .serverStatementId);
1192:
1193: boolean usingCursor = false;
1194:
1195: if (this .connection.versionMeetsMinimum(4, 1, 2)) {
1196: // we only create cursor-backed result sets if
1197: // a) The query is a SELECT
1198: // b) The server supports it
1199: // c) We know it is forward-only (note this doesn't
1200: // preclude updatable result sets)
1201: // d) The user has set a fetch size
1202: if (this .resultFields != null
1203: && this .connection.isCursorFetchEnabled()
1204: && getResultSetType() == ResultSet.TYPE_FORWARD_ONLY
1205: && getResultSetConcurrency() == ResultSet.CONCUR_READ_ONLY
1206: && getFetchSize() > 0) {
1207: packet.writeByte(MysqlDefs.OPEN_CURSOR_FLAG);
1208: usingCursor = true;
1209: } else {
1210: packet.writeByte((byte) 0); // placeholder for flags
1211: }
1212:
1213: packet.writeLong(1); // placeholder for parameter
1214: // iterations
1215: }
1216:
1217: /* Reserve place for null-marker bytes */
1218: int nullCount = (this .parameterCount + 7) / 8;
1219:
1220: // if (mysql.versionMeetsMinimum(4, 1, 2)) {
1221: // nullCount = (this.parameterCount + 9) / 8;
1222: // }
1223: int nullBitsPosition = packet.getPosition();
1224:
1225: for (int i = 0; i < nullCount; i++) {
1226: packet.writeByte((byte) 0);
1227: }
1228:
1229: byte[] nullBitsBuffer = new byte[nullCount];
1230:
1231: /* In case if buffers (type) altered, indicate to server */
1232: packet.writeByte(this .sendTypesToServer ? (byte) 1
1233: : (byte) 0);
1234:
1235: if (this .sendTypesToServer) {
1236: /*
1237: * Store types of parameters in first in first package that is
1238: * sent to the server.
1239: */
1240: for (int i = 0; i < this .parameterCount; i++) {
1241: packet
1242: .writeInt(this .parameterBindings[i].bufferType);
1243: }
1244: }
1245:
1246: //
1247: // store the parameter values
1248: //
1249: for (int i = 0; i < this .parameterCount; i++) {
1250: if (!this .parameterBindings[i].isLongData) {
1251: if (!this .parameterBindings[i].isNull) {
1252: storeBinding(packet, this .parameterBindings[i],
1253: mysql);
1254: } else {
1255: nullBitsBuffer[i / 8] |= (1 << (i & 7));
1256: }
1257: }
1258: }
1259:
1260: //
1261: // Go back and write the NULL flags
1262: // to the beginning of the packet
1263: //
1264: int endPosition = packet.getPosition();
1265: packet.setPosition(nullBitsPosition);
1266: packet.writeBytesNoNull(nullBitsBuffer);
1267: packet.setPosition(endPosition);
1268:
1269: long begin = 0;
1270:
1271: boolean logSlowQueries = this .connection
1272: .getLogSlowQueries();
1273: boolean gatherPerformanceMetrics = this .connection
1274: .getGatherPerformanceMetrics();
1275:
1276: if (this .profileSQL || logSlowQueries
1277: || gatherPerformanceMetrics) {
1278: begin = mysql.getCurrentTimeNanosOrMillis();
1279: }
1280:
1281: resetCancelledState();
1282:
1283: CancelTask timeoutTask = null;
1284:
1285: try {
1286: if (this .connection.getEnableQueryTimeouts()
1287: && this .timeoutInMillis != 0
1288: && this .connection.versionMeetsMinimum(5, 0, 0)) {
1289: timeoutTask = new CancelTask(this );
1290: this .connection.getCancelTimer().schedule(
1291: timeoutTask, this .timeoutInMillis);
1292: }
1293:
1294: Buffer resultPacket = mysql.sendCommand(
1295: MysqlDefs.COM_EXECUTE, null, packet, false,
1296: null);
1297:
1298: long queryEndTime = 0L;
1299:
1300: if (logSlowQueries || gatherPerformanceMetrics
1301: || this .profileSQL) {
1302: queryEndTime = mysql.getCurrentTimeNanosOrMillis();
1303: }
1304:
1305: if (timeoutTask != null) {
1306: timeoutTask.cancel();
1307:
1308: if (timeoutTask.caughtWhileCancelling != null) {
1309: throw timeoutTask.caughtWhileCancelling;
1310: }
1311:
1312: timeoutTask = null;
1313: }
1314:
1315: synchronized (this .cancelTimeoutMutex) {
1316: if (this .wasCancelled) {
1317: SQLException cause = null;
1318:
1319: if (this .wasCancelledByTimeout) {
1320: cause = new MySQLTimeoutException();
1321: } else {
1322: cause = new MySQLStatementCancelledException();
1323: }
1324:
1325: resetCancelledState();
1326:
1327: throw cause;
1328: }
1329: }
1330:
1331: boolean queryWasSlow = false;
1332:
1333: if (logSlowQueries || gatherPerformanceMetrics) {
1334: long elapsedTime = queryEndTime - begin;
1335:
1336: if (logSlowQueries
1337: && (elapsedTime >= mysql
1338: .getSlowQueryThreshold())) {
1339: queryWasSlow = true;
1340:
1341: StringBuffer mesgBuf = new StringBuffer(
1342: 48 + this .originalSql.length());
1343: mesgBuf
1344: .append(Messages
1345: .getString("ServerPreparedStatement.15")); //$NON-NLS-1$
1346: mesgBuf.append(mysql.getSlowQueryThreshold());
1347: mesgBuf
1348: .append(Messages
1349: .getString("ServerPreparedStatement.15a")); //$NON-NLS-1$
1350: mesgBuf.append(elapsedTime);
1351: mesgBuf
1352: .append(Messages
1353: .getString("ServerPreparedStatement.16")); //$NON-NLS-1$
1354:
1355: mesgBuf.append("as prepared: ");
1356: mesgBuf.append(this .originalSql);
1357: mesgBuf
1358: .append("\n\n with parameters bound:\n\n");
1359: mesgBuf.append(asSql(true));
1360:
1361: this .eventSink
1362: .consumeEvent(new ProfilerEvent(
1363: ProfilerEvent.TYPE_SLOW_QUERY,
1364: "", this .currentCatalog, this .connection.getId(), //$NON-NLS-1$
1365: getId(), 0, System
1366: .currentTimeMillis(),
1367: elapsedTime, mysql
1368: .getQueryTimingUnits(),
1369: null, new Throwable(), mesgBuf
1370: .toString()));
1371: }
1372:
1373: if (gatherPerformanceMetrics) {
1374: this .connection
1375: .registerQueryExecutionTime(elapsedTime);
1376: }
1377: }
1378:
1379: this .connection.incrementNumberOfPreparedExecutes();
1380:
1381: if (this .profileSQL) {
1382: this .eventSink = ProfileEventSink
1383: .getInstance(this .connection);
1384:
1385: this .eventSink
1386: .consumeEvent(new ProfilerEvent(
1387: ProfilerEvent.TYPE_EXECUTE,
1388: "", this .currentCatalog, //$NON-NLS-1$
1389: this .connectionId,
1390: this .statementId,
1391: -1,
1392: System.currentTimeMillis(),
1393: (int) (mysql
1394: .getCurrentTimeNanosOrMillis() - begin),
1395: mysql.getQueryTimingUnits(), null,
1396: new Throwable(),
1397: truncateQueryToLog(asSql(true))));
1398: }
1399:
1400: com.mysql.jdbc.ResultSetInternalMethods rs = mysql
1401: .readAllResults(this , maxRowsToRetrieve,
1402: this .resultSetType,
1403: this .resultSetConcurrency,
1404: createStreamingResultSet,
1405: this .currentCatalog, resultPacket,
1406: true, this .fieldCount,
1407: metadataFromCache);
1408:
1409: if (this .profileSQL) {
1410: long fetchEndTime = mysql
1411: .getCurrentTimeNanosOrMillis();
1412:
1413: this .eventSink
1414: .consumeEvent(new ProfilerEvent(
1415: ProfilerEvent.TYPE_FETCH,
1416: "", this .currentCatalog, this .connection.getId(), //$NON-NLS-1$
1417: getId(),
1418: 0 /* FIXME rs.resultId */, System
1419: .currentTimeMillis(),
1420: (fetchEndTime - queryEndTime),
1421: mysql.getQueryTimingUnits(), null,
1422: new Throwable(), null));
1423: }
1424:
1425: if (queryWasSlow
1426: && this .connection.getExplainSlowQueries()) {
1427: String queryAsString = asSql(true);
1428:
1429: mysql.explainSlowQuery(queryAsString.getBytes(),
1430: queryAsString);
1431: }
1432:
1433: if (!createStreamingResultSet
1434: && this .serverNeedsResetBeforeEachExecution) {
1435: serverResetStatement(); // clear any long data...
1436: }
1437:
1438: this .sendTypesToServer = false;
1439: this .results = rs;
1440:
1441: if (mysql.hadWarnings()) {
1442: mysql.scanForAndThrowDataTruncation();
1443: }
1444:
1445: return rs;
1446: } finally {
1447: if (timeoutTask != null) {
1448: timeoutTask.cancel();
1449: }
1450: }
1451: }
1452: }
1453:
1454: /**
1455: * Sends stream-type data parameters to the server.
1456: *
1457: * <pre>
1458: *
1459: * Long data handling:
1460: *
1461: * - Server gets the long data in pieces with command type 'COM_LONG_DATA'.
1462: * - The packet recieved will have the format as:
1463: * [COM_LONG_DATA: 1][STMT_ID:4][parameter_number:2][type:2][data]
1464: * - Checks if the type is specified by client, and if yes reads the type,
1465: * and stores the data in that format.
1466: * - It's up to the client to check for read data ended. The server doesn't
1467: * care; and also server doesn't notify to the client that it got the
1468: * data or not; if there is any error; then during execute; the error
1469: * will be returned
1470: *
1471: * </pre>
1472: *
1473: * @param parameterIndex
1474: * DOCUMENT ME!
1475: * @param longData
1476: * DOCUMENT ME!
1477: *
1478: * @throws SQLException
1479: * if an error occurs.
1480: */
1481: private void serverLongData(int parameterIndex, BindValue longData)
1482: throws SQLException {
1483: synchronized (this .connection.getMutex()) {
1484: MysqlIO mysql = this .connection.getIO();
1485:
1486: Buffer packet = mysql.getSharedSendPacket();
1487:
1488: Object value = longData.value;
1489:
1490: if (value instanceof byte[]) {
1491: packet.clear();
1492: packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
1493: packet.writeLong(this .serverStatementId);
1494: packet.writeInt((parameterIndex));
1495:
1496: packet.writeBytesNoNull((byte[]) longData.value);
1497:
1498: mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null,
1499: packet, true, null);
1500: } else if (value instanceof InputStream) {
1501: storeStream(mysql, parameterIndex, packet,
1502: (InputStream) value);
1503: } else if (value instanceof java.sql.Blob) {
1504: storeStream(mysql, parameterIndex, packet,
1505: ((java.sql.Blob) value).getBinaryStream());
1506: } else if (value instanceof Reader) {
1507: storeReader(mysql, parameterIndex, packet,
1508: (Reader) value);
1509: } else {
1510: throw SQLError.createSQLException(Messages
1511: .getString("ServerPreparedStatement.18") //$NON-NLS-1$
1512: + value.getClass().getName() + "'", //$NON-NLS-1$
1513: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1514: }
1515: }
1516: }
1517:
1518: private void serverPrepare(String sql) throws SQLException {
1519: synchronized (this .connection.getMutex()) {
1520: MysqlIO mysql = this .connection.getIO();
1521:
1522: if (this .connection.getAutoGenerateTestcaseScript()) {
1523: dumpPrepareForTestcase();
1524: }
1525:
1526: try {
1527: long begin = 0;
1528:
1529: if (StringUtils.startsWithIgnoreCaseAndWs(sql,
1530: "LOAD DATA")) { //$NON-NLS-1$
1531: this .isLoadDataQuery = true;
1532: } else {
1533: this .isLoadDataQuery = false;
1534: }
1535:
1536: if (this .connection.getProfileSql()) {
1537: begin = System.currentTimeMillis();
1538: }
1539:
1540: String characterEncoding = null;
1541: String connectionEncoding = this .connection
1542: .getEncoding();
1543:
1544: if (!this .isLoadDataQuery
1545: && this .connection.getUseUnicode()
1546: && (connectionEncoding != null)) {
1547: characterEncoding = connectionEncoding;
1548: }
1549:
1550: Buffer prepareResultPacket = mysql.sendCommand(
1551: MysqlDefs.COM_PREPARE, sql, null, false,
1552: characterEncoding);
1553:
1554: if (this .connection.versionMeetsMinimum(4, 1, 1)) {
1555: // 4.1.1 and newer use the first byte
1556: // as an 'ok' or 'error' flag, so move
1557: // the buffer pointer past it to
1558: // start reading the statement id.
1559: prepareResultPacket.setPosition(1);
1560: } else {
1561: // 4.1.0 doesn't use the first byte as an
1562: // 'ok' or 'error' flag
1563: prepareResultPacket.setPosition(0);
1564: }
1565:
1566: this .serverStatementId = prepareResultPacket.readLong();
1567: this .fieldCount = prepareResultPacket.readInt();
1568: this .parameterCount = prepareResultPacket.readInt();
1569: this .parameterBindings = new BindValue[this .parameterCount];
1570:
1571: for (int i = 0; i < this .parameterCount; i++) {
1572: this .parameterBindings[i] = new BindValue();
1573: }
1574:
1575: this .connection.incrementNumberOfPrepares();
1576:
1577: if (this .profileSQL) {
1578: this .eventSink.consumeEvent(new ProfilerEvent(
1579: ProfilerEvent.TYPE_PREPARE,
1580: "", this .currentCatalog, //$NON-NLS-1$
1581: this .connectionId, this .statementId, -1,
1582: System.currentTimeMillis(), mysql
1583: .getCurrentTimeNanosOrMillis()
1584: - begin, mysql
1585: .getQueryTimingUnits(), null,
1586: new Throwable(), truncateQueryToLog(sql)));
1587: }
1588:
1589: if (this .parameterCount > 0) {
1590: if (this .connection.versionMeetsMinimum(4, 1, 2)
1591: && !mysql.isVersion(5, 0, 0)) {
1592: this .parameterFields = new Field[this .parameterCount];
1593:
1594: Buffer metaDataPacket = mysql.readPacket();
1595:
1596: int i = 0;
1597:
1598: while (!metaDataPacket.isLastDataPacket()
1599: && (i < this .parameterCount)) {
1600: this .parameterFields[i++] = mysql
1601: .unpackField(metaDataPacket, false);
1602: metaDataPacket = mysql.readPacket();
1603: }
1604: }
1605: }
1606:
1607: if (this .fieldCount > 0) {
1608: this .resultFields = new Field[this .fieldCount];
1609:
1610: Buffer fieldPacket = mysql.readPacket();
1611:
1612: int i = 0;
1613:
1614: // Read in the result set column information
1615: while (!fieldPacket.isLastDataPacket()
1616: && (i < this .fieldCount)) {
1617: this .resultFields[i++] = mysql.unpackField(
1618: fieldPacket, false);
1619: fieldPacket = mysql.readPacket();
1620: }
1621: }
1622: } catch (SQLException sqlEx) {
1623: if (this .connection.getDumpQueriesOnException()) {
1624: StringBuffer messageBuf = new StringBuffer(
1625: this .originalSql.length() + 32);
1626: messageBuf
1627: .append("\n\nQuery being prepared when exception was thrown:\n\n");
1628: messageBuf.append(this .originalSql);
1629:
1630: sqlEx = ConnectionImpl.appendMessageToException(
1631: sqlEx, messageBuf.toString());
1632: }
1633:
1634: throw sqlEx;
1635: } finally {
1636: // Leave the I/O channel in a known state...there might be
1637: // packets out there
1638: // that we're not interested in
1639: this .connection.getIO().clearInputStream();
1640: }
1641: }
1642: }
1643:
1644: private String truncateQueryToLog(String sql) {
1645: String query = null;
1646:
1647: if (sql.length() > this .connection.getMaxQuerySizeToLog()) {
1648: StringBuffer queryBuf = new StringBuffer(this .connection
1649: .getMaxQuerySizeToLog() + 12);
1650: queryBuf.append(sql.substring(0, this .connection
1651: .getMaxQuerySizeToLog()));
1652: queryBuf.append(Messages.getString("MysqlIO.25"));
1653:
1654: query = queryBuf.toString();
1655: } else {
1656: query = sql;
1657: }
1658:
1659: return query;
1660: }
1661:
1662: private void serverResetStatement() throws SQLException {
1663: synchronized (this .connection.getMutex()) {
1664:
1665: MysqlIO mysql = this .connection.getIO();
1666:
1667: Buffer packet = mysql.getSharedSendPacket();
1668:
1669: packet.clear();
1670: packet.writeByte((byte) MysqlDefs.COM_RESET_STMT);
1671: packet.writeLong(this .serverStatementId);
1672:
1673: try {
1674: mysql.sendCommand(MysqlDefs.COM_RESET_STMT, null,
1675: packet, !this .connection.versionMeetsMinimum(4,
1676: 1, 2), null);
1677: } catch (SQLException sqlEx) {
1678: throw sqlEx;
1679: } catch (Exception ex) {
1680: throw SQLError.createSQLException(ex.toString(),
1681: SQLError.SQL_STATE_GENERAL_ERROR);
1682: } finally {
1683: mysql.clearInputStream();
1684: }
1685: }
1686: }
1687:
1688: /**
1689: * @see java.sql.PreparedStatement#setArray(int, java.sql.Array)
1690: */
1691: public void setArray(int i, Array x) throws SQLException {
1692: throw new NotImplemented();
1693: }
1694:
1695: /**
1696: * @see java.sql.PreparedStatement#setAsciiStream(int, java.io.InputStream,
1697: * int)
1698: */
1699: public void setAsciiStream(int parameterIndex, InputStream x,
1700: int length) throws SQLException {
1701: checkClosed();
1702:
1703: if (x == null) {
1704: setNull(parameterIndex, java.sql.Types.BINARY);
1705: } else {
1706: BindValue binding = getBinding(parameterIndex, true);
1707: setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
1708:
1709: binding.value = x;
1710: binding.isNull = false;
1711: binding.isLongData = true;
1712:
1713: if (this .connection.getUseStreamLengthsInPrepStmts()) {
1714: binding.bindLength = length;
1715: } else {
1716: binding.bindLength = -1;
1717: }
1718: }
1719: }
1720:
1721: /**
1722: * @see java.sql.PreparedStatement#setBigDecimal(int, java.math.BigDecimal)
1723: */
1724: public void setBigDecimal(int parameterIndex, BigDecimal x)
1725: throws SQLException {
1726: checkClosed();
1727:
1728: if (x == null) {
1729: setNull(parameterIndex, java.sql.Types.DECIMAL);
1730: } else {
1731:
1732: BindValue binding = getBinding(parameterIndex, false);
1733:
1734: if (this .connection.versionMeetsMinimum(5, 0, 3)) {
1735: setType(binding, MysqlDefs.FIELD_TYPE_NEW_DECIMAL);
1736: } else {
1737: setType(binding, this .stringTypeCode);
1738: }
1739:
1740: binding.value = StringUtils.fixDecimalExponent(StringUtils
1741: .consistentToString(x));
1742: binding.isNull = false;
1743: binding.isLongData = false;
1744: }
1745: }
1746:
1747: /**
1748: * @see java.sql.PreparedStatement#setBinaryStream(int, java.io.InputStream,
1749: * int)
1750: */
1751: public void setBinaryStream(int parameterIndex, InputStream x,
1752: int length) throws SQLException {
1753: checkClosed();
1754:
1755: if (x == null) {
1756: setNull(parameterIndex, java.sql.Types.BINARY);
1757: } else {
1758: BindValue binding = getBinding(parameterIndex, true);
1759: setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
1760:
1761: binding.value = x;
1762: binding.isNull = false;
1763: binding.isLongData = true;
1764:
1765: if (this .connection.getUseStreamLengthsInPrepStmts()) {
1766: binding.bindLength = length;
1767: } else {
1768: binding.bindLength = -1;
1769: }
1770: }
1771: }
1772:
1773: /**
1774: * @see java.sql.PreparedStatement#setBlob(int, java.sql.Blob)
1775: */
1776: public void setBlob(int parameterIndex, Blob x) throws SQLException {
1777: checkClosed();
1778:
1779: if (x == null) {
1780: setNull(parameterIndex, java.sql.Types.BINARY);
1781: } else {
1782: BindValue binding = getBinding(parameterIndex, true);
1783: setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
1784:
1785: binding.value = x;
1786: binding.isNull = false;
1787: binding.isLongData = true;
1788:
1789: if (this .connection.getUseStreamLengthsInPrepStmts()) {
1790: binding.bindLength = x.length();
1791: } else {
1792: binding.bindLength = -1;
1793: }
1794: }
1795: }
1796:
1797: /**
1798: * @see java.sql.PreparedStatement#setBoolean(int, boolean)
1799: */
1800: public void setBoolean(int parameterIndex, boolean x)
1801: throws SQLException {
1802: setByte(parameterIndex, (x ? (byte) 1 : (byte) 0));
1803: }
1804:
1805: /**
1806: * @see java.sql.PreparedStatement#setByte(int, byte)
1807: */
1808: public void setByte(int parameterIndex, byte x) throws SQLException {
1809: checkClosed();
1810:
1811: BindValue binding = getBinding(parameterIndex, false);
1812: setType(binding, MysqlDefs.FIELD_TYPE_TINY);
1813:
1814: binding.value = null;
1815: binding.byteBinding = x;
1816: binding.isNull = false;
1817: binding.isLongData = false;
1818: }
1819:
1820: /**
1821: * @see java.sql.PreparedStatement#setBytes(int, byte)
1822: */
1823: public void setBytes(int parameterIndex, byte[] x)
1824: throws SQLException {
1825: checkClosed();
1826:
1827: if (x == null) {
1828: setNull(parameterIndex, java.sql.Types.BINARY);
1829: } else {
1830: BindValue binding = getBinding(parameterIndex, false);
1831: setType(binding, MysqlDefs.FIELD_TYPE_VAR_STRING);
1832:
1833: binding.value = x;
1834: binding.isNull = false;
1835: binding.isLongData = false;
1836: }
1837: }
1838:
1839: /**
1840: * @see java.sql.PreparedStatement#setCharacterStream(int, java.io.Reader,
1841: * int)
1842: */
1843: public void setCharacterStream(int parameterIndex, Reader reader,
1844: int length) throws SQLException {
1845: checkClosed();
1846:
1847: if (reader == null) {
1848: setNull(parameterIndex, java.sql.Types.BINARY);
1849: } else {
1850: BindValue binding = getBinding(parameterIndex, true);
1851: setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
1852:
1853: binding.value = reader;
1854: binding.isNull = false;
1855: binding.isLongData = true;
1856:
1857: if (this .connection.getUseStreamLengthsInPrepStmts()) {
1858: binding.bindLength = length;
1859: } else {
1860: binding.bindLength = -1;
1861: }
1862: }
1863: }
1864:
1865: /**
1866: * @see java.sql.PreparedStatement#setClob(int, java.sql.Clob)
1867: */
1868: public void setClob(int parameterIndex, Clob x) throws SQLException {
1869: checkClosed();
1870:
1871: if (x == null) {
1872: setNull(parameterIndex, java.sql.Types.BINARY);
1873: } else {
1874: BindValue binding = getBinding(parameterIndex, true);
1875: setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
1876:
1877: binding.value = x.getCharacterStream();
1878: binding.isNull = false;
1879: binding.isLongData = true;
1880:
1881: if (this .connection.getUseStreamLengthsInPrepStmts()) {
1882: binding.bindLength = x.length();
1883: } else {
1884: binding.bindLength = -1;
1885: }
1886: }
1887: }
1888:
1889: /**
1890: * Set a parameter to a java.sql.Date value. The driver converts this to a
1891: * SQL DATE value when it sends it to the database.
1892: *
1893: * @param parameterIndex
1894: * the first parameter is 1, the second is 2, ...
1895: * @param x
1896: * the parameter value
1897: *
1898: * @exception SQLException
1899: * if a database-access error occurs.
1900: */
1901: public void setDate(int parameterIndex, Date x) throws SQLException {
1902: setDate(parameterIndex, x, null);
1903: }
1904:
1905: /**
1906: * Set a parameter to a java.sql.Date value. The driver converts this to a
1907: * SQL DATE value when it sends it to the database.
1908: *
1909: * @param parameterIndex
1910: * the first parameter is 1, the second is 2, ...
1911: * @param x
1912: * the parameter value
1913: * @param cal
1914: * the calendar to interpret the date with
1915: *
1916: * @exception SQLException
1917: * if a database-access error occurs.
1918: */
1919: public void setDate(int parameterIndex, Date x, Calendar cal)
1920: throws SQLException {
1921: if (x == null) {
1922: setNull(parameterIndex, java.sql.Types.DATE);
1923: } else {
1924: BindValue binding = getBinding(parameterIndex, false);
1925: setType(binding, MysqlDefs.FIELD_TYPE_DATE);
1926:
1927: binding.value = x;
1928: binding.isNull = false;
1929: binding.isLongData = false;
1930: }
1931: }
1932:
1933: /**
1934: * @see java.sql.PreparedStatement#setDouble(int, double)
1935: */
1936: public void setDouble(int parameterIndex, double x)
1937: throws SQLException {
1938: checkClosed();
1939:
1940: if (!this .connection.getAllowNanAndInf()
1941: && (x == Double.POSITIVE_INFINITY
1942: || x == Double.NEGATIVE_INFINITY || Double
1943: .isNaN(x))) {
1944: throw SQLError
1945: .createSQLException(
1946: "'"
1947: + x
1948: + "' is not a valid numeric or approximate numeric value",
1949: SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1950:
1951: }
1952:
1953: BindValue binding = getBinding(parameterIndex, false);
1954: setType(binding, MysqlDefs.FIELD_TYPE_DOUBLE);
1955:
1956: binding.value = null;
1957: binding.doubleBinding = x;
1958: binding.isNull = false;
1959: binding.isLongData = false;
1960: }
1961:
1962: /**
1963: * @see java.sql.PreparedStatement#setFloat(int, float)
1964: */
1965: public void setFloat(int parameterIndex, float x)
1966: throws SQLException {
1967: checkClosed();
1968:
1969: BindValue binding = getBinding(parameterIndex, false);
1970: setType(binding, MysqlDefs.FIELD_TYPE_FLOAT);
1971:
1972: binding.value = null;
1973: binding.floatBinding = x;
1974: binding.isNull = false;
1975: binding.isLongData = false;
1976: }
1977:
1978: /**
1979: * @see java.sql.PreparedStatement#setInt(int, int)
1980: */
1981: public void setInt(int parameterIndex, int x) throws SQLException {
1982: checkClosed();
1983:
1984: BindValue binding = getBinding(parameterIndex, false);
1985: setType(binding, MysqlDefs.FIELD_TYPE_LONG);
1986:
1987: binding.value = null;
1988: binding.intBinding = x;
1989: binding.isNull = false;
1990: binding.isLongData = false;
1991: }
1992:
1993: /**
1994: * @see java.sql.PreparedStatement#setLong(int, long)
1995: */
1996: public void setLong(int parameterIndex, long x) throws SQLException {
1997: checkClosed();
1998:
1999: BindValue binding = getBinding(parameterIndex, false);
2000: setType(binding, MysqlDefs.FIELD_TYPE_LONGLONG);
2001:
2002: binding.value = null;
2003: binding.longBinding = x;
2004: binding.isNull = false;
2005: binding.isLongData = false;
2006: }
2007:
2008: /**
2009: * @see java.sql.PreparedStatement#setNull(int, int)
2010: */
2011: public void setNull(int parameterIndex, int sqlType)
2012: throws SQLException {
2013: checkClosed();
2014:
2015: BindValue binding = getBinding(parameterIndex, false);
2016:
2017: //
2018: // Don't re-set types, but use something if this
2019: // parameter was never specified
2020: //
2021: if (binding.bufferType == 0) {
2022: setType(binding, MysqlDefs.FIELD_TYPE_NULL);
2023: }
2024:
2025: binding.value = null;
2026: binding.isNull = true;
2027: binding.isLongData = false;
2028: }
2029:
2030: /**
2031: * @see java.sql.PreparedStatement#setNull(int, int, java.lang.String)
2032: */
2033: public void setNull(int parameterIndex, int sqlType, String typeName)
2034: throws SQLException {
2035: checkClosed();
2036:
2037: BindValue binding = getBinding(parameterIndex, false);
2038:
2039: //
2040: // Don't re-set types, but use something if this
2041: // parameter was never specified
2042: //
2043: if (binding.bufferType == 0) {
2044: setType(binding, MysqlDefs.FIELD_TYPE_NULL);
2045: }
2046:
2047: binding.value = null;
2048: binding.isNull = true;
2049: binding.isLongData = false;
2050: }
2051:
2052: /**
2053: * @see java.sql.PreparedStatement#setRef(int, java.sql.Ref)
2054: */
2055: public void setRef(int i, Ref x) throws SQLException {
2056: throw new NotImplemented();
2057: }
2058:
2059: /**
2060: * @see java.sql.PreparedStatement#setShort(int, short)
2061: */
2062: public void setShort(int parameterIndex, short x)
2063: throws SQLException {
2064: checkClosed();
2065:
2066: BindValue binding = getBinding(parameterIndex, false);
2067: setType(binding, MysqlDefs.FIELD_TYPE_SHORT);
2068:
2069: binding.value = null;
2070: binding.shortBinding = x;
2071: binding.isNull = false;
2072: binding.isLongData = false;
2073: }
2074:
2075: /**
2076: * @see java.sql.PreparedStatement#setString(int, java.lang.String)
2077: */
2078: public void setString(int parameterIndex, String x)
2079: throws SQLException {
2080: checkClosed();
2081:
2082: if (x == null) {
2083: setNull(parameterIndex, java.sql.Types.CHAR);
2084: } else {
2085: BindValue binding = getBinding(parameterIndex, false);
2086:
2087: setType(binding, this .stringTypeCode);
2088:
2089: binding.value = x;
2090: binding.isNull = false;
2091: binding.isLongData = false;
2092: }
2093: }
2094:
2095: /**
2096: * Set a parameter to a java.sql.Time value.
2097: *
2098: * @param parameterIndex
2099: * the first parameter is 1...));
2100: * @param x
2101: * the parameter value
2102: *
2103: * @throws SQLException
2104: * if a database access error occurs
2105: */
2106: public void setTime(int parameterIndex, java.sql.Time x)
2107: throws SQLException {
2108: setTimeInternal(parameterIndex, x, null, this .connection
2109: .getDefaultTimeZone(), false);
2110: }
2111:
2112: /**
2113: * Set a parameter to a java.sql.Time value. The driver converts this to a
2114: * SQL TIME value when it sends it to the database, using the given
2115: * timezone.
2116: *
2117: * @param parameterIndex
2118: * the first parameter is 1...));
2119: * @param x
2120: * the parameter value
2121: * @param cal
2122: * the timezone to use
2123: *
2124: * @throws SQLException
2125: * if a database access error occurs
2126: */
2127: public void setTime(int parameterIndex, java.sql.Time x,
2128: Calendar cal) throws SQLException {
2129: setTimeInternal(parameterIndex, x, cal, cal.getTimeZone(), true);
2130: }
2131:
2132: /**
2133: * Set a parameter to a java.sql.Time value. The driver converts this to a
2134: * SQL TIME value when it sends it to the database, using the given
2135: * timezone.
2136: *
2137: * @param parameterIndex
2138: * the first parameter is 1...));
2139: * @param x
2140: * the parameter value
2141: * @param tz
2142: * the timezone to use
2143: *
2144: * @throws SQLException
2145: * if a database access error occurs
2146: */
2147: public void setTimeInternal(int parameterIndex, java.sql.Time x,
2148: Calendar targetCalendar, TimeZone tz, boolean rollForward)
2149: throws SQLException {
2150: if (x == null) {
2151: setNull(parameterIndex, java.sql.Types.TIME);
2152: } else {
2153: BindValue binding = getBinding(parameterIndex, false);
2154: setType(binding, MysqlDefs.FIELD_TYPE_TIME);
2155:
2156: Calendar sessionCalendar = getCalendarInstanceForSessionOrNew();
2157:
2158: synchronized (sessionCalendar) {
2159: binding.value = TimeUtil.changeTimezone(
2160: this .connection, sessionCalendar,
2161: targetCalendar, x, tz, this .connection
2162: .getServerTimezoneTZ(), rollForward);
2163: }
2164:
2165: binding.isNull = false;
2166: binding.isLongData = false;
2167: }
2168: }
2169:
2170: /**
2171: * Set a parameter to a java.sql.Timestamp value. The driver converts this
2172: * to a SQL TIMESTAMP value when it sends it to the database.
2173: *
2174: * @param parameterIndex
2175: * the first parameter is 1, the second is 2, ...
2176: * @param x
2177: * the parameter value
2178: *
2179: * @throws SQLException
2180: * if a database-access error occurs.
2181: */
2182: public void setTimestamp(int parameterIndex, java.sql.Timestamp x)
2183: throws SQLException {
2184: setTimestampInternal(parameterIndex, x, null, this .connection
2185: .getDefaultTimeZone(), false);
2186: }
2187:
2188: /**
2189: * Set a parameter to a java.sql.Timestamp value. The driver converts this
2190: * to a SQL TIMESTAMP value when it sends it to the database.
2191: *
2192: * @param parameterIndex
2193: * the first parameter is 1, the second is 2, ...
2194: * @param x
2195: * the parameter value
2196: * @param cal
2197: * the timezone to use
2198: *
2199: * @throws SQLException
2200: * if a database-access error occurs.
2201: */
2202: public void setTimestamp(int parameterIndex, java.sql.Timestamp x,
2203: Calendar cal) throws SQLException {
2204: setTimestampInternal(parameterIndex, x, cal, cal.getTimeZone(),
2205: true);
2206: }
2207:
2208: protected void setTimestampInternal(int parameterIndex,
2209: java.sql.Timestamp x, Calendar targetCalendar, TimeZone tz,
2210: boolean rollForward) throws SQLException {
2211: if (x == null) {
2212: setNull(parameterIndex, java.sql.Types.TIMESTAMP);
2213: } else {
2214: BindValue binding = getBinding(parameterIndex, false);
2215: setType(binding, MysqlDefs.FIELD_TYPE_DATETIME);
2216:
2217: Calendar sessionCalendar = this .connection
2218: .getUseJDBCCompliantTimezoneShift() ? this .connection
2219: .getUtcCalendar()
2220: : getCalendarInstanceForSessionOrNew();
2221:
2222: synchronized (sessionCalendar) {
2223: binding.value = TimeUtil.changeTimezone(
2224: this .connection, sessionCalendar,
2225: targetCalendar, x, tz, this .connection
2226: .getServerTimezoneTZ(), rollForward);
2227: }
2228:
2229: binding.isNull = false;
2230: binding.isLongData = false;
2231: }
2232: }
2233:
2234: protected void setType(BindValue oldValue, int bufferType) {
2235: if (oldValue.bufferType != bufferType) {
2236: this .sendTypesToServer = true;
2237: }
2238:
2239: oldValue.bufferType = bufferType;
2240: }
2241:
2242: /**
2243: * DOCUMENT ME!
2244: *
2245: * @param parameterIndex
2246: * DOCUMENT ME!
2247: * @param x
2248: * DOCUMENT ME!
2249: * @param length
2250: * DOCUMENT ME!
2251: *
2252: * @throws SQLException
2253: * DOCUMENT ME!
2254: * @throws NotImplemented
2255: * DOCUMENT ME!
2256: *
2257: * @see java.sql.PreparedStatement#setUnicodeStream(int,
2258: * java.io.InputStream, int)
2259: * @deprecated
2260: */
2261: public void setUnicodeStream(int parameterIndex, InputStream x,
2262: int length) throws SQLException {
2263: checkClosed();
2264:
2265: throw new NotImplemented();
2266: }
2267:
2268: /**
2269: * @see java.sql.PreparedStatement#setURL(int, java.net.URL)
2270: */
2271: public void setURL(int parameterIndex, URL x) throws SQLException {
2272: checkClosed();
2273:
2274: setString(parameterIndex, x.toString());
2275: }
2276:
2277: /**
2278: * Method storeBinding.
2279: *
2280: * @param packet
2281: * @param bindValue
2282: * @param mysql
2283: * DOCUMENT ME!
2284: *
2285: * @throws SQLException
2286: * DOCUMENT ME!
2287: */
2288: private void storeBinding(Buffer packet, BindValue bindValue,
2289: MysqlIO mysql) throws SQLException {
2290: try {
2291: Object value = bindValue.value;
2292:
2293: //
2294: // Handle primitives first
2295: //
2296: switch (bindValue.bufferType) {
2297:
2298: case MysqlDefs.FIELD_TYPE_TINY:
2299: packet.writeByte(bindValue.byteBinding);
2300: return;
2301: case MysqlDefs.FIELD_TYPE_SHORT:
2302: packet.ensureCapacity(2);
2303: packet.writeInt(bindValue.shortBinding);
2304: return;
2305: case MysqlDefs.FIELD_TYPE_LONG:
2306: packet.ensureCapacity(4);
2307: packet.writeLong(bindValue.intBinding);
2308: return;
2309: case MysqlDefs.FIELD_TYPE_LONGLONG:
2310: packet.ensureCapacity(8);
2311: packet.writeLongLong(bindValue.longBinding);
2312: return;
2313: case MysqlDefs.FIELD_TYPE_FLOAT:
2314: packet.ensureCapacity(4);
2315: packet.writeFloat(bindValue.floatBinding);
2316: return;
2317: case MysqlDefs.FIELD_TYPE_DOUBLE:
2318: packet.ensureCapacity(8);
2319: packet.writeDouble(bindValue.doubleBinding);
2320: return;
2321: case MysqlDefs.FIELD_TYPE_TIME:
2322: storeTime(packet, (Time) value);
2323: return;
2324: case MysqlDefs.FIELD_TYPE_DATE:
2325: case MysqlDefs.FIELD_TYPE_DATETIME:
2326: case MysqlDefs.FIELD_TYPE_TIMESTAMP:
2327: storeDateTime(packet, (java.util.Date) value, mysql);
2328: return;
2329: case MysqlDefs.FIELD_TYPE_VAR_STRING:
2330: case MysqlDefs.FIELD_TYPE_STRING:
2331: case MysqlDefs.FIELD_TYPE_VARCHAR:
2332: case MysqlDefs.FIELD_TYPE_DECIMAL:
2333: case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
2334: if (value instanceof byte[]) {
2335: packet.writeLenBytes((byte[]) value);
2336: } else if (!this .isLoadDataQuery) {
2337: packet.writeLenString((String) value,
2338: this .charEncoding, this .connection
2339: .getServerCharacterEncoding(),
2340: this .charConverter, this .connection
2341: .parserKnowsUnicode(),
2342: this .connection);
2343: } else {
2344: packet.writeLenBytes(((String) value).getBytes());
2345: }
2346:
2347: return;
2348: }
2349:
2350: } catch (UnsupportedEncodingException uEE) {
2351: throw SQLError.createSQLException(Messages
2352: .getString("ServerPreparedStatement.22") //$NON-NLS-1$
2353: + this .connection.getEncoding() + "'", //$NON-NLS-1$
2354: SQLError.SQL_STATE_GENERAL_ERROR);
2355: }
2356: }
2357:
2358: private void storeDataTime412AndOlder(Buffer intoBuf,
2359: java.util.Date dt) throws SQLException {
2360:
2361: Calendar sessionCalendar = getCalendarInstanceForSessionOrNew();
2362:
2363: synchronized (sessionCalendar) {
2364: java.util.Date oldTime = sessionCalendar.getTime();
2365:
2366: try {
2367: intoBuf.ensureCapacity(8);
2368: intoBuf.writeByte((byte) 7); // length
2369:
2370: sessionCalendar.setTime(dt);
2371:
2372: int year = sessionCalendar.get(Calendar.YEAR);
2373: int month = sessionCalendar.get(Calendar.MONTH) + 1;
2374: int date = sessionCalendar.get(Calendar.DATE);
2375:
2376: intoBuf.writeInt(year);
2377: intoBuf.writeByte((byte) month);
2378: intoBuf.writeByte((byte) date);
2379:
2380: if (dt instanceof java.sql.Date) {
2381: intoBuf.writeByte((byte) 0);
2382: intoBuf.writeByte((byte) 0);
2383: intoBuf.writeByte((byte) 0);
2384: } else {
2385: intoBuf.writeByte((byte) sessionCalendar
2386: .get(Calendar.HOUR_OF_DAY));
2387: intoBuf.writeByte((byte) sessionCalendar
2388: .get(Calendar.MINUTE));
2389: intoBuf.writeByte((byte) sessionCalendar
2390: .get(Calendar.SECOND));
2391: }
2392: } finally {
2393: sessionCalendar.setTime(oldTime);
2394: }
2395: }
2396: }
2397:
2398: private void storeDateTime(Buffer intoBuf, java.util.Date dt,
2399: MysqlIO mysql) throws SQLException {
2400: if (this .connection.versionMeetsMinimum(4, 1, 3)) {
2401: storeDateTime413AndNewer(intoBuf, dt);
2402: } else {
2403: storeDataTime412AndOlder(intoBuf, dt);
2404: }
2405: }
2406:
2407: private void storeDateTime413AndNewer(Buffer intoBuf,
2408: java.util.Date dt) throws SQLException {
2409: Calendar sessionCalendar = (dt instanceof Timestamp && this .connection
2410: .getUseJDBCCompliantTimezoneShift()) ? this .connection
2411: .getUtcCalendar()
2412: : getCalendarInstanceForSessionOrNew();
2413:
2414: synchronized (sessionCalendar) {
2415: java.util.Date oldTime = sessionCalendar.getTime();
2416:
2417: try {
2418: sessionCalendar.setTime(dt);
2419:
2420: if (dt instanceof java.sql.Date) {
2421: sessionCalendar.set(Calendar.HOUR_OF_DAY, 0);
2422: sessionCalendar.set(Calendar.MINUTE, 0);
2423: sessionCalendar.set(Calendar.SECOND, 0);
2424: }
2425:
2426: byte length = (byte) 7;
2427:
2428: if (dt instanceof java.sql.Timestamp) {
2429: length = (byte) 11;
2430: }
2431:
2432: intoBuf.ensureCapacity(length);
2433:
2434: intoBuf.writeByte(length); // length
2435:
2436: int year = sessionCalendar.get(Calendar.YEAR);
2437: int month = sessionCalendar.get(Calendar.MONTH) + 1;
2438: int date = sessionCalendar.get(Calendar.DAY_OF_MONTH);
2439:
2440: intoBuf.writeInt(year);
2441: intoBuf.writeByte((byte) month);
2442: intoBuf.writeByte((byte) date);
2443:
2444: if (dt instanceof java.sql.Date) {
2445: intoBuf.writeByte((byte) 0);
2446: intoBuf.writeByte((byte) 0);
2447: intoBuf.writeByte((byte) 0);
2448: } else {
2449: intoBuf.writeByte((byte) sessionCalendar
2450: .get(Calendar.HOUR_OF_DAY));
2451: intoBuf.writeByte((byte) sessionCalendar
2452: .get(Calendar.MINUTE));
2453: intoBuf.writeByte((byte) sessionCalendar
2454: .get(Calendar.SECOND));
2455: }
2456:
2457: if (length == 11) {
2458: // MySQL expects microseconds, not nanos
2459: intoBuf.writeLong(((java.sql.Timestamp) dt)
2460: .getNanos() / 1000);
2461: }
2462:
2463: } finally {
2464: sessionCalendar.setTime(oldTime);
2465: }
2466: }
2467: }
2468:
2469: //
2470: // TO DO: Investigate using NIO to do this faster
2471: //
2472: private void storeReader(MysqlIO mysql, int parameterIndex,
2473: Buffer packet, Reader inStream) throws SQLException {
2474: String forcedEncoding = this .connection
2475: .getClobCharacterEncoding();
2476:
2477: String clobEncoding = (forcedEncoding == null ? this .connection
2478: .getEncoding() : forcedEncoding);
2479:
2480: int maxBytesChar = 2;
2481:
2482: if (clobEncoding != null) {
2483: if (!clobEncoding.equals("UTF-16")) {
2484: maxBytesChar = this .connection
2485: .getMaxBytesPerChar(clobEncoding);
2486:
2487: if (maxBytesChar == 1) {
2488: maxBytesChar = 2; // for safety
2489: }
2490: } else {
2491: maxBytesChar = 4;
2492: }
2493: }
2494:
2495: char[] buf = new char[BLOB_STREAM_READ_BUF_SIZE / maxBytesChar];
2496:
2497: int numRead = 0;
2498:
2499: int bytesInPacket = 0;
2500: int totalBytesRead = 0;
2501: int bytesReadAtLastSend = 0;
2502: int packetIsFullAt = this .connection.getBlobSendChunkSize();
2503:
2504: try {
2505: packet.clear();
2506: packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
2507: packet.writeLong(this .serverStatementId);
2508: packet.writeInt((parameterIndex));
2509:
2510: boolean readAny = false;
2511:
2512: while ((numRead = inStream.read(buf)) != -1) {
2513: readAny = true;
2514:
2515: byte[] valueAsBytes = StringUtils.getBytes(buf, null,
2516: clobEncoding, this .connection
2517: .getServerCharacterEncoding(), 0,
2518: numRead, this .connection.parserKnowsUnicode());
2519:
2520: packet.writeBytesNoNull(valueAsBytes, 0,
2521: valueAsBytes.length);
2522:
2523: bytesInPacket += valueAsBytes.length;
2524: totalBytesRead += valueAsBytes.length;
2525:
2526: if (bytesInPacket >= packetIsFullAt) {
2527: bytesReadAtLastSend = totalBytesRead;
2528:
2529: mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null,
2530: packet, true, null);
2531:
2532: bytesInPacket = 0;
2533: packet.clear();
2534: packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
2535: packet.writeLong(this .serverStatementId);
2536: packet.writeInt((parameterIndex));
2537: }
2538: }
2539:
2540: if (totalBytesRead != bytesReadAtLastSend) {
2541: mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null,
2542: packet, true, null);
2543: }
2544:
2545: if (!readAny) {
2546: mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null,
2547: packet, true, null);
2548: }
2549: } catch (IOException ioEx) {
2550: throw SQLError
2551: .createSQLException(Messages
2552: .getString("ServerPreparedStatement.24") //$NON-NLS-1$
2553: + ioEx.toString(),
2554: SQLError.SQL_STATE_GENERAL_ERROR);
2555: } finally {
2556: if (this .connection.getAutoClosePStmtStreams()) {
2557: if (inStream != null) {
2558: try {
2559: inStream.close();
2560: } catch (IOException ioEx) {
2561: ; // ignore
2562: }
2563: }
2564: }
2565: }
2566: }
2567:
2568: private void storeStream(MysqlIO mysql, int parameterIndex,
2569: Buffer packet, InputStream inStream) throws SQLException {
2570: byte[] buf = new byte[BLOB_STREAM_READ_BUF_SIZE];
2571:
2572: int numRead = 0;
2573:
2574: try {
2575: int bytesInPacket = 0;
2576: int totalBytesRead = 0;
2577: int bytesReadAtLastSend = 0;
2578: int packetIsFullAt = this .connection.getBlobSendChunkSize();
2579:
2580: packet.clear();
2581: packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
2582: packet.writeLong(this .serverStatementId);
2583: packet.writeInt((parameterIndex));
2584:
2585: boolean readAny = false;
2586:
2587: while ((numRead = inStream.read(buf)) != -1) {
2588:
2589: readAny = true;
2590:
2591: packet.writeBytesNoNull(buf, 0, numRead);
2592: bytesInPacket += numRead;
2593: totalBytesRead += numRead;
2594:
2595: if (bytesInPacket >= packetIsFullAt) {
2596: bytesReadAtLastSend = totalBytesRead;
2597:
2598: mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null,
2599: packet, true, null);
2600:
2601: bytesInPacket = 0;
2602: packet.clear();
2603: packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
2604: packet.writeLong(this .serverStatementId);
2605: packet.writeInt((parameterIndex));
2606: }
2607: }
2608:
2609: if (totalBytesRead != bytesReadAtLastSend) {
2610: mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null,
2611: packet, true, null);
2612: }
2613:
2614: if (!readAny) {
2615: mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null,
2616: packet, true, null);
2617: }
2618: } catch (IOException ioEx) {
2619: throw SQLError
2620: .createSQLException(Messages
2621: .getString("ServerPreparedStatement.25") //$NON-NLS-1$
2622: + ioEx.toString(),
2623: SQLError.SQL_STATE_GENERAL_ERROR);
2624: } finally {
2625: if (this .connection.getAutoClosePStmtStreams()) {
2626: if (inStream != null) {
2627: try {
2628: inStream.close();
2629: } catch (IOException ioEx) {
2630: ; // ignore
2631: }
2632: }
2633: }
2634: }
2635: }
2636:
2637: /**
2638: * @see java.lang.Object#toString()
2639: */
2640: public String toString() {
2641: StringBuffer toStringBuf = new StringBuffer();
2642:
2643: toStringBuf.append("com.mysql.jdbc.ServerPreparedStatement["); //$NON-NLS-1$
2644: toStringBuf.append(this .serverStatementId);
2645: toStringBuf.append("] - "); //$NON-NLS-1$
2646:
2647: try {
2648: toStringBuf.append(asSql());
2649: } catch (SQLException sqlEx) {
2650: toStringBuf.append(Messages
2651: .getString("ServerPreparedStatement.6")); //$NON-NLS-1$
2652: toStringBuf.append(sqlEx);
2653: }
2654:
2655: return toStringBuf.toString();
2656: }
2657:
2658: protected long getServerStatementId() {
2659: return serverStatementId;
2660: }
2661:
2662: }
|