All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mysql.cj.AbstractQueryBindings Maven / Gradle / Ivy

There is a newer version: 8.0.33
Show newest version
/*
 * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, version 2.0, as published by the
 * Free Software Foundation.
 *
 * This program is also distributed with certain software (including but not
 * limited to OpenSSL) that is licensed under separate terms, as designated in a
 * particular file or component or in included license documentation. The
 * authors of MySQL hereby grant you an additional permission to link the
 * program and your derivative works with the separately licensed software that
 * they have included with MySQL.
 *
 * Without limiting anything contained in the foregoing, this file, which is
 * part of MySQL Connector/J, is also subject to the Universal FOSS Exception,
 * version 1.0, a copy of which can be found at
 * http://oss.oracle.com/licenses/universal-foss-exception.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
 */

package com.mysql.cj;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.ParsePosition;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Calendar;
import java.util.Locale;

import com.mysql.cj.conf.PropertyKey;
import com.mysql.cj.conf.RuntimeProperty;
import com.mysql.cj.exceptions.ExceptionFactory;
import com.mysql.cj.exceptions.WrongArgumentException;
import com.mysql.cj.protocol.ColumnDefinition;
import com.mysql.cj.protocol.a.NativeConstants.IntegerDataType;
import com.mysql.cj.protocol.a.NativePacketPayload;
import com.mysql.cj.util.StringUtils;
import com.mysql.cj.util.TimeUtil;
import com.mysql.cj.util.Util;

//TODO should not be protocol-specific
public abstract class AbstractQueryBindings implements QueryBindings {

    protected final static byte[] HEX_DIGITS = new byte[] { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
            (byte) '8', (byte) '9', (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F' };

    protected Session session;

    /** Bind values for individual fields */
    protected T[] bindValues;

    protected String charEncoding;

    protected int numberOfExecutions = 0;

    protected RuntimeProperty useStreamLengthsInPrepStmts;
    protected RuntimeProperty sendFractionalSeconds;
    private RuntimeProperty treatUtilDateAsTimestamp;

    /** Is this query a LOAD DATA query? */
    protected boolean isLoadDataQuery = false;

    protected ColumnDefinition columnDefinition;

    public AbstractQueryBindings(int parameterCount, Session sess) {
        this.session = sess;
        this.charEncoding = this.session.getPropertySet().getStringProperty(PropertyKey.characterEncoding).getValue();
        this.sendFractionalSeconds = this.session.getPropertySet().getBooleanProperty(PropertyKey.sendFractionalSeconds);
        this.treatUtilDateAsTimestamp = this.session.getPropertySet().getBooleanProperty(PropertyKey.treatUtilDateAsTimestamp);
        this.useStreamLengthsInPrepStmts = this.session.getPropertySet().getBooleanProperty(PropertyKey.useStreamLengthsInPrepStmts);

        initBindValues(parameterCount);
    }

    protected abstract void initBindValues(int parameterCount);

    @Override
    public abstract AbstractQueryBindings clone();

    @Override
    public void setColumnDefinition(ColumnDefinition colDef) {
        this.columnDefinition = colDef;
    }

    @Override
    public boolean isLoadDataQuery() {
        return this.isLoadDataQuery;
    }

    @Override
    public void setLoadDataQuery(boolean isLoadDataQuery) {
        this.isLoadDataQuery = isLoadDataQuery;
    }

    @Override
    public T[] getBindValues() {
        return this.bindValues;
    }

    @Override
    public void setBindValues(T[] bindValues) {
        this.bindValues = bindValues;
    }

    @Override
    public boolean clearBindValues() {
        boolean hadLongData = false;

        if (this.bindValues != null) {
            for (int i = 0; i < this.bindValues.length; i++) {
                if ((this.bindValues[i] != null) && this.bindValues[i].isStream()) {
                    hadLongData = true;
                }
                this.bindValues[i].reset();
            }
        }

        return hadLongData;
    }

    public abstract void checkParameterSet(int columnIndex);

    public void checkAllParametersSet() {
        for (int i = 0; i < this.bindValues.length; i++) {
            checkParameterSet(i);
        }
    }

    public int getNumberOfExecutions() {
        return this.numberOfExecutions;
    }

    public void setNumberOfExecutions(int numberOfExecutions) {
        this.numberOfExecutions = numberOfExecutions;
    }

    public synchronized final void setValue(int paramIndex, byte[] val, MysqlType type) {
        this.bindValues[paramIndex].setByteValue(val);
        this.bindValues[paramIndex].setMysqlType(type);
    }

    public synchronized final void setOrigValue(int paramIndex, byte[] val) {
        this.bindValues[paramIndex].setOrigByteValue(val);
    }

    @Override
    public synchronized byte[] getOrigBytes(int parameterIndex) {
        return this.bindValues[parameterIndex].getOrigByteValue();
    }

    public synchronized final void setValue(int paramIndex, String val, MysqlType type) {
        byte[] parameterAsBytes = StringUtils.getBytes(val, this.charEncoding);
        setValue(paramIndex, parameterAsBytes, type);
    }

    /**
     * Used to escape binary data with hex for mb charsets
     * 
     * @param buf
     *            source bytes
     * @param packet
     *            write to this packet
     * @param size
     *            number of bytes to read
     */
    public final void hexEscapeBlock(byte[] buf, NativePacketPayload packet, int size) {
        for (int i = 0; i < size; i++) {
            byte b = buf[i];
            int lowBits = (b & 0xff) / 16;
            int highBits = (b & 0xff) % 16;

            packet.writeInteger(IntegerDataType.INT1, HEX_DIGITS[lowBits]);
            packet.writeInteger(IntegerDataType.INT1, HEX_DIGITS[highBits]);
        }
    }

    @Override
    public void setObject(int parameterIndex, Object parameterObj) {
        if (parameterObj == null) {
            setNull(parameterIndex);
        } else {
            if (parameterObj instanceof Byte) {
                setInt(parameterIndex, ((Byte) parameterObj).intValue());

            } else if (parameterObj instanceof String) {
                setString(parameterIndex, (String) parameterObj);

            } else if (parameterObj instanceof BigDecimal) {
                setBigDecimal(parameterIndex, (BigDecimal) parameterObj);

            } else if (parameterObj instanceof Short) {
                setShort(parameterIndex, ((Short) parameterObj).shortValue());

            } else if (parameterObj instanceof Integer) {
                setInt(parameterIndex, ((Integer) parameterObj).intValue());

            } else if (parameterObj instanceof Long) {
                setLong(parameterIndex, ((Long) parameterObj).longValue());

            } else if (parameterObj instanceof Float) {
                setFloat(parameterIndex, ((Float) parameterObj).floatValue());

            } else if (parameterObj instanceof Double) {
                setDouble(parameterIndex, ((Double) parameterObj).doubleValue());

            } else if (parameterObj instanceof byte[]) {
                setBytes(parameterIndex, (byte[]) parameterObj);

            } else if (parameterObj instanceof java.sql.Date) {
                setDate(parameterIndex, (java.sql.Date) parameterObj);

            } else if (parameterObj instanceof Time) {
                setTime(parameterIndex, (Time) parameterObj);

            } else if (parameterObj instanceof Timestamp) {
                setTimestamp(parameterIndex, (Timestamp) parameterObj);

            } else if (parameterObj instanceof Boolean) {
                setBoolean(parameterIndex, ((Boolean) parameterObj).booleanValue());

            } else if (parameterObj instanceof InputStream) {
                setBinaryStream(parameterIndex, (InputStream) parameterObj, -1);

            } else if (parameterObj instanceof java.sql.Blob) {
                setBlob(parameterIndex, (java.sql.Blob) parameterObj);

            } else if (parameterObj instanceof java.sql.Clob) {
                setClob(parameterIndex, (java.sql.Clob) parameterObj);

            } else if (this.treatUtilDateAsTimestamp.getValue() && parameterObj instanceof java.util.Date) {
                setTimestamp(parameterIndex, new Timestamp(((java.util.Date) parameterObj).getTime()));

            } else if (parameterObj instanceof BigInteger) {
                setString(parameterIndex, parameterObj.toString());

            } else if (parameterObj instanceof LocalDate) {
                setDate(parameterIndex, Date.valueOf((LocalDate) parameterObj));

            } else if (parameterObj instanceof LocalDateTime) {
                setTimestamp(parameterIndex, Timestamp.valueOf((LocalDateTime) parameterObj));

            } else if (parameterObj instanceof LocalTime) {
                setTime(parameterIndex, Time.valueOf((LocalTime) parameterObj));

            } else {
                setSerializableObject(parameterIndex, parameterObj);
            }
        }
    }

    @Override
    public void setObject(int parameterIndex, Object parameterObj, MysqlType targetMysqlType) {
        setObject(parameterIndex, parameterObj, targetMysqlType, parameterObj instanceof BigDecimal ? ((BigDecimal) parameterObj).scale() : 0);
    }

    /**
     * Set the value of a parameter using an object; use the java.lang equivalent objects for integral values.
     * 
     * 

* The given Java object will be converted to the targetMysqlType before being sent to the database. * * @param parameterIndex * the first parameter is 1... * @param parameterObj * the object containing the input parameter value * @param targetMysqlType * The MysqlType to be send to the database * @param scaleOrLength * For Types.DECIMAL or Types.NUMERIC types * this is the number of digits after the decimal. For all other * types this value will be ignored. */ public void setObject(int parameterIndex, Object parameterObj, MysqlType targetMysqlType, int scaleOrLength) { if (parameterObj == null) { setNull(parameterIndex); } else { if (parameterObj instanceof LocalDate) { parameterObj = Date.valueOf((LocalDate) parameterObj); } else if (parameterObj instanceof LocalDateTime) { parameterObj = Timestamp.valueOf((LocalDateTime) parameterObj); } else if (parameterObj instanceof LocalTime) { parameterObj = Time.valueOf((LocalTime) parameterObj); } try { /* * From Table-B5 in the JDBC Spec */ switch (targetMysqlType) { case BOOLEAN: if (parameterObj instanceof Boolean) { setBoolean(parameterIndex, ((Boolean) parameterObj).booleanValue()); break; } else if (parameterObj instanceof String) { setBoolean(parameterIndex, "true".equalsIgnoreCase((String) parameterObj) || !"0".equalsIgnoreCase((String) parameterObj)); break; } else if (parameterObj instanceof Number) { int intValue = ((Number) parameterObj).intValue(); setBoolean(parameterIndex, intValue != 0); break; } else { throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("PreparedStatement.66", new Object[] { parameterObj.getClass().getName() }), this.session.getExceptionInterceptor()); } case BIT: case TINYINT: case TINYINT_UNSIGNED: case SMALLINT: case SMALLINT_UNSIGNED: case INT: case INT_UNSIGNED: case MEDIUMINT: case MEDIUMINT_UNSIGNED: case BIGINT: case BIGINT_UNSIGNED: case FLOAT: case FLOAT_UNSIGNED: case DOUBLE: case DOUBLE_UNSIGNED: case DECIMAL: case DECIMAL_UNSIGNED: setNumericObject(parameterIndex, parameterObj, targetMysqlType, scaleOrLength); break; case CHAR: case ENUM: case SET: case VARCHAR: case TINYTEXT: case TEXT: case MEDIUMTEXT: case LONGTEXT: case JSON: if (parameterObj instanceof BigDecimal) { setString(parameterIndex, (StringUtils.fixDecimalExponent(((BigDecimal) parameterObj).toPlainString()))); } else if (parameterObj instanceof java.sql.Clob) { setClob(parameterIndex, (java.sql.Clob) parameterObj); } else { setString(parameterIndex, parameterObj.toString()); } break; case BINARY: case GEOMETRY: case VARBINARY: case TINYBLOB: case BLOB: case MEDIUMBLOB: case LONGBLOB: if (parameterObj instanceof byte[]) { setBytes(parameterIndex, (byte[]) parameterObj); } else if (parameterObj instanceof java.sql.Blob) { setBlob(parameterIndex, (java.sql.Blob) parameterObj); } else { setBytes(parameterIndex, StringUtils.getBytes(parameterObj.toString(), this.charEncoding)); } break; case DATE: case DATETIME: case TIMESTAMP: case YEAR: java.util.Date parameterAsDate; if (parameterObj instanceof String) { ParsePosition pp = new ParsePosition(0); java.text.DateFormat sdf = new java.text.SimpleDateFormat(TimeUtil.getDateTimePattern((String) parameterObj, false), Locale.US); parameterAsDate = sdf.parse((String) parameterObj, pp); } else { parameterAsDate = (java.util.Date) parameterObj; } switch (targetMysqlType) { case DATE: if (parameterAsDate instanceof java.sql.Date) { setDate(parameterIndex, (java.sql.Date) parameterAsDate); } else { setDate(parameterIndex, new java.sql.Date(parameterAsDate.getTime())); } break; case DATETIME: case TIMESTAMP: if (parameterAsDate instanceof java.sql.Timestamp) { setTimestamp(parameterIndex, (java.sql.Timestamp) parameterAsDate); } else { setTimestamp(parameterIndex, new java.sql.Timestamp(parameterAsDate.getTime())); } break; case YEAR: Calendar cal = Calendar.getInstance(); cal.setTime(parameterAsDate); setNumericObject(parameterIndex, cal.get(Calendar.YEAR), targetMysqlType, scaleOrLength); break; default: break; } break; case TIME: if (parameterObj instanceof String) { java.text.DateFormat sdf = new java.text.SimpleDateFormat(TimeUtil.getDateTimePattern((String) parameterObj, true), Locale.US); setTime(parameterIndex, new java.sql.Time(sdf.parse((String) parameterObj).getTime())); } else if (parameterObj instanceof Timestamp) { Timestamp xT = (Timestamp) parameterObj; setTime(parameterIndex, new java.sql.Time(xT.getTime())); } else { setTime(parameterIndex, (java.sql.Time) parameterObj); } break; case UNKNOWN: setSerializableObject(parameterIndex, parameterObj); break; default: throw ExceptionFactory.createException(Messages.getString("PreparedStatement.16"), this.session.getExceptionInterceptor()); } } catch (Exception ex) { throw ExceptionFactory.createException( Messages.getString("PreparedStatement.17") + parameterObj.getClass().toString() + Messages.getString("PreparedStatement.18") + ex.getClass().getName() + Messages.getString("PreparedStatement.19") + ex.getMessage(), ex, this.session.getExceptionInterceptor()); } } } private void setNumericObject(int parameterIndex, Object parameterObj, MysqlType targetMysqlType, int scale) { Number parameterAsNum; if (parameterObj instanceof Boolean) { parameterAsNum = ((Boolean) parameterObj).booleanValue() ? Integer.valueOf(1) : Integer.valueOf(0); } else if (parameterObj instanceof String) { switch (targetMysqlType) { case BIT: if ("1".equals(parameterObj) || "0".equals(parameterObj)) { parameterAsNum = Integer.valueOf((String) parameterObj); } else { boolean parameterAsBoolean = "true".equalsIgnoreCase((String) parameterObj); parameterAsNum = parameterAsBoolean ? Integer.valueOf(1) : Integer.valueOf(0); } break; case TINYINT: case TINYINT_UNSIGNED: case SMALLINT: case SMALLINT_UNSIGNED: case MEDIUMINT: case MEDIUMINT_UNSIGNED: case INT: case INT_UNSIGNED: case YEAR: parameterAsNum = Integer.valueOf((String) parameterObj); break; case BIGINT: parameterAsNum = Long.valueOf((String) parameterObj); break; case BIGINT_UNSIGNED: parameterAsNum = new BigInteger((String) parameterObj); break; case FLOAT: case FLOAT_UNSIGNED: parameterAsNum = Float.valueOf((String) parameterObj); break; case DOUBLE: case DOUBLE_UNSIGNED: parameterAsNum = Double.valueOf((String) parameterObj); break; case DECIMAL: case DECIMAL_UNSIGNED: default: parameterAsNum = new java.math.BigDecimal((String) parameterObj); } } else { parameterAsNum = (Number) parameterObj; } switch (targetMysqlType) { case BIT: case TINYINT: case TINYINT_UNSIGNED: case SMALLINT: case SMALLINT_UNSIGNED: case MEDIUMINT: case MEDIUMINT_UNSIGNED: case INT: case INT_UNSIGNED: case YEAR: setInt(parameterIndex, parameterAsNum.intValue()); break; case BIGINT: case BIGINT_UNSIGNED: setLong(parameterIndex, parameterAsNum.longValue()); break; case FLOAT: case FLOAT_UNSIGNED: setFloat(parameterIndex, parameterAsNum.floatValue()); break; case DOUBLE: case DOUBLE_UNSIGNED: setDouble(parameterIndex, parameterAsNum.doubleValue()); break; case DECIMAL: case DECIMAL_UNSIGNED: if (parameterAsNum instanceof java.math.BigDecimal) { BigDecimal scaledBigDecimal = null; try { scaledBigDecimal = ((java.math.BigDecimal) parameterAsNum).setScale(scale); } catch (ArithmeticException ex) { try { scaledBigDecimal = ((java.math.BigDecimal) parameterAsNum).setScale(scale, BigDecimal.ROUND_HALF_UP); } catch (ArithmeticException arEx) { throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("PreparedStatement.65", new Object[] { scale, parameterAsNum }), this.session.getExceptionInterceptor()); } } setBigDecimal(parameterIndex, scaledBigDecimal); } else if (parameterAsNum instanceof java.math.BigInteger) { setBigDecimal(parameterIndex, new java.math.BigDecimal((java.math.BigInteger) parameterAsNum, scale)); } else { setBigDecimal(parameterIndex, new java.math.BigDecimal(parameterAsNum.doubleValue())); } break; default: break; } } /** * Sets the value for the placeholder as a serialized Java object (used by various forms of setObject() * * @param parameterIndex * parameter index * @param parameterObj * value */ protected final void setSerializableObject(int parameterIndex, Object parameterObj) { try { ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); ObjectOutputStream objectOut = new ObjectOutputStream(bytesOut); objectOut.writeObject(parameterObj); objectOut.flush(); objectOut.close(); bytesOut.flush(); bytesOut.close(); byte[] buf = bytesOut.toByteArray(); ByteArrayInputStream bytesIn = new ByteArrayInputStream(buf); setBinaryStream(parameterIndex, bytesIn, buf.length); this.bindValues[parameterIndex].setMysqlType(MysqlType.BINARY); } catch (Exception ex) { throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("PreparedStatement.54") + ex.getClass().getName(), ex, this.session.getExceptionInterceptor()); } } @Override public boolean isNull(int parameterIndex) { return this.bindValues[parameterIndex].isNull(); } /** * @return bytes */ public byte[] getBytesRepresentation(int parameterIndex) { if (this.bindValues[parameterIndex].isStream()) { return streamToBytes(parameterIndex, this.session.getPropertySet().getBooleanProperty(PropertyKey.useStreamLengthsInPrepStmts).getValue()); } byte[] parameterVal = this.bindValues[parameterIndex].getByteValue(); if (parameterVal == null) { return null; } return StringUtils.unquoteBytes(parameterVal); } private byte[] streamConvertBuf = null; private final byte[] streamToBytes(int parameterIndex, boolean useLength) { InputStream in = this.bindValues[parameterIndex].getStreamValue(); in.mark(Integer.MAX_VALUE); // we may need to read this same stream several times, so we need to reset it at the end. try { if (this.streamConvertBuf == null) { this.streamConvertBuf = new byte[4096]; } if (this.bindValues[parameterIndex].getStreamLength() == -1) { useLength = false; } ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); int bc = useLength ? Util.readBlock(in, this.streamConvertBuf, (int) this.bindValues[parameterIndex].getStreamLength(), null) : Util.readBlock(in, this.streamConvertBuf, null); int lengthLeftToRead = (int) this.bindValues[parameterIndex].getStreamLength() - bc; while (bc > 0) { bytesOut.write(this.streamConvertBuf, 0, bc); if (useLength) { bc = Util.readBlock(in, this.streamConvertBuf, lengthLeftToRead, null); if (bc > 0) { lengthLeftToRead -= bc; } } else { bc = Util.readBlock(in, this.streamConvertBuf, null); } } return bytesOut.toByteArray(); } finally { try { in.reset(); } catch (IOException e) { } if (this.session.getPropertySet().getBooleanProperty(PropertyKey.autoClosePStmtStreams).getValue()) { try { in.close(); } catch (IOException ioEx) { } in = null; } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy