small correction to AbstractJdbc2Array

Поиск
Список
Период
Сортировка
От Marek Lewczuk
Тема small correction to AbstractJdbc2Array
Дата
Msg-id 46D27EDA.3050808@lewczuk.com
обсуждение исходный текст
Список pgsql-jdbc
Hi Kris,
you didn't write any comments to my previous post with rewritten
AbstractJdbc2Array class - hope, that you will do it in near feature.
However in the attachment you can find this class with another small
correction (in my previous code NULL elements were not properly recognized).

Regards,
ML
/*-------------------------------------------------------------------------
*
* Copyright (c) 2004-2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
*   $PostgreSQL: pgjdbc/org/postgresql/jdbc2/AbstractJdbc2Array.java,v 1.18 2005/12/04 21:40:33 jurka Exp $
*
*-------------------------------------------------------------------------
*/
package org.postgresql.jdbc2;

import org.postgresql.core.*;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;
import org.postgresql.util.GT;

import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Map;
import java.util.Vector;

/**
 * Array is used collect one column of query result data.
 *
 * <p>Read a field of type Array into either a natively-typed
 * Java array object or a ResultSet.  Accessor methods provide
 * the ability to capture array slices.
 *
 * <p>Other than the constructor all methods are direct implementations
 * of those specified for java.sql.Array.  Please refer to the javadoc
 * for java.sql.Array for detailed descriptions of the functionality
 * and parameters of the methods of this class.
 *
 * @see ResultSet#getArray
 */
public class AbstractJdbc2Array {


    /**
     * Array list implementation specific for storing PG array elements.
     */
    private static class PgArrayList extends ArrayList {

        private static final long serialVersionUID = 2052783752654562677L;

        /**
         * Whether multi-dimensional array.
         */
        boolean isMultiDimensional = false;

    }


    /**
     * A database connection.
     */
    private BaseConnection connection = null;

    /**
     * The ResultSet from which to get the data for this Array.
     */
    private BaseResultSet resultSet;

    /**
     * The Field descriptor for the field to load into this Array.
     */
    private Field field = null;

    /**
     * 1-based index of the query field to load into this Array.
     */
    private int fieldIndex = 0;

    /**
     * Field value as String.
     */
    private String fieldString = null;

    /**
     * Whether Object[] should be used instead primitive arrays. Object[] can
     * contain null elements. It should be set to <Code>true</Code> if
     * {@link BaseConnection#haveMinimumCompatibleVersion(String)} returns
     * <Code>true</Code> for argument "8.2".
     */
    private final boolean useObjects;


    /**
     * Create a new Array.
     *
     * @param connection a database connection
     * @param index 1-based index of the query field to load into this Array
     * @param field the Field descriptor for the field to load into this Array
     * @param result the ResultSet from which to get the data for this Array
     */
    public AbstractJdbc2Array (BaseConnection connection, int index, Field field, BaseResultSet result) throws
SQLException{ 
        this.connection = connection;
        this.field = field;
        this.resultSet = result;
        this.fieldIndex = index;
        this.fieldString = result.getFixedString(index);
        this.useObjects = connection.haveMinimumCompatibleVersion("8.2");
    }

    public Object getArray() throws SQLException {
        return getArrayImpl(1, 0, null);
    }

    public Object getArray(long index, int count) throws SQLException {
        return getArrayImpl(index, count, null);
    }

    public Object getArrayImpl(Map map) throws SQLException {
        return getArrayImpl(1, 0, map);
    }

    public Object getArrayImpl (long index, int count, Map map) throws SQLException {

        // for now maps aren't supported.
        if (map != null && !map.isEmpty()) throw org.postgresql.Driver.notImplemented(this.getClass(),
"getArrayImpl(long,int,Map)");

        // array index is out of range
        if (index < 1) throw new PSQLException(GT.tr("The array index is out of range: {0}", new Long(index)),
PSQLState.DATA_ERROR);

        PgArrayList array = buildArrayList(fieldString);

        if (count == 0) count = array.size();

        // array index out of range
        if ((--index) + count > array.size()) throw new PSQLException(GT.tr("The array index is out of range: {0},
numberof elements: {1}.", new Object[]{new Long(index + count), new Long(array.size())}), PSQLState.DATA_ERROR); 

        return buildArray(array, (int) index, count);
    }

    /**
     * Build {@link ArrayList} from String input.
     *
     * @param String representation of an array
     */
    private PgArrayList buildArrayList (String list) {

        PgArrayList array = new PgArrayList();

        if (list != null) {

            char[] chars = list.toCharArray();
            StringBuffer buffer = new StringBuffer();
            boolean insideString = false;
            boolean wasInsideString = false; // needed for checking if NULL value occured
            Vector dims = new Vector(); // array dimension arrays
            PgArrayList curArray = array; // currently processed array

            // Starting with 8.0 non-standard (beginning index
            // isn't 1) bounds the dimensions are returned in the
            // data formatted like so "[0:3]={0,1,2,3,4}".
            // Older versions simply do not return the bounds.
            //
            // Right now we ignore these bounds, but we could
            // consider allowing these index values to be used
            // even though the JDBC spec says 1 is the first
            // index.  I'm not sure what a client would like
            // to see, so we just retain the old behavior.
            int startOffset = 0;
            {
                if (chars[0] == '[') {
                    while (chars[startOffset] != '=') {
                        startOffset++;
                    }
                    startOffset++; // skip =
                }
            }

            for ( int i = startOffset; i < chars.length; i++ ) {

                //escape character that we need to skip
                if (chars[i] == '\\') i++;

                // subarray start
                else if (!insideString && chars[i] == '{' ) {
                    if (dims.size() == 0) dims.add(array);
                    else {
                        PgArrayList a = new PgArrayList();
                        PgArrayList p = ((PgArrayList) dims.lastElement());
                        {
                            p.add(a);
                            p.isMultiDimensional = true;
                        }
                        dims.add(a);
                    }
                    curArray = (PgArrayList) dims.lastElement();
                    buffer = new StringBuffer();
                    continue;
                }

                // quoted element
                else if (chars[i] == '"') {
                    insideString = !insideString;
                    wasInsideString = true;
                    continue;
                }

                // array end
                else if (!insideString && (chars[i] == ',' || chars[i] == '}') || i == chars.length - 1) {
                    if ( chars[i] != '"' && chars[i] != '}' && chars[i] != ',' && buffer != null)
buffer.append(chars[i]);
                    String b = buffer == null ? null : buffer.toString();
                    if (b != null) curArray.add(useObjects && !wasInsideString && b.equals("NULL") ? null : b);
                    wasInsideString = false;
                    buffer = new StringBuffer();
                    if (chars[i] == '}') {
                        dims.remove(dims.size() - 1);
                        if (dims.size() > 0) curArray = (PgArrayList) dims.lastElement();
                        buffer = null;
                    }
                    continue;
                }

                if (buffer != null) buffer.append( chars[i] );
            }
        }

        return array;
    }

    /**
     * Convert {@link ArrayList} to array.
     *
     * @param input list to be converted into array
     */
    private Object buildArray (PgArrayList input, int index, int count) throws SQLException {

        if (count == -1) count = input.size();

        // array to be returned
        Object ret = null;

        // whether multi-dimensional array
        boolean multi = input.isMultiDimensional;

        // array elements counter
        int length = 0;

        // array type
        final int type = getBaseType();

        if (type == Types.BIT) {
            boolean[] pa = null;
            Object[] oa = null;
            if (multi || useObjects) ret = oa = (multi ? new Object[count] : new Boolean[count]);
            else ret = pa = new boolean[count];
            for ( ; count > 0; count--) {
                Object o = input.get(index++);
                if (multi || useObjects) oa[length++] = o == null ? null : (multi ? buildArray((PgArrayList) o, 0, -1)
:new Boolean(AbstractJdbc2ResultSet.toBoolean((String) o))); 
                else pa[length++] = o == null ? false : AbstractJdbc2ResultSet.toBoolean((String) o);
            }
        }

        else if (type == Types.SMALLINT || type == Types.INTEGER) {
            int[] pa = null;
            Object[] oa = null;
            if (multi || useObjects) ret = oa = (multi ? new Object[count] : new Integer[count]);
            else ret = pa = new int[count];
            for ( ; count > 0; count--) {
                Object o = input.get(index++);
                if (multi || useObjects) oa[length++] = o == null ? null : (multi ? buildArray((PgArrayList) o, 0, -1)
:new Integer(AbstractJdbc2ResultSet.toInt((String) o))); 
                else pa[length++] = o == null ? 0 : AbstractJdbc2ResultSet.toInt((String) o);
            }
        }

        else if (type == Types.BIGINT) {
            long[] pa = null;
            Object[] oa = null;
            if (multi || useObjects) ret = oa = (multi ? new Object[count] : new Long[count]);
            else ret = pa = new long[count];
            for ( ; count > 0; count--) {
                Object o = input.get(index++);
                boolean p = false;
                if (multi || useObjects) oa[length++] = o == null ? null : (multi ? buildArray((PgArrayList) o, 0, -1)
:new Long(AbstractJdbc2ResultSet.toLong((String) o))); 
                else pa[length++] = o == null ? 0l : AbstractJdbc2ResultSet.toLong((String) o);
            }
        }

        else if (type == Types.NUMERIC) {
            Object[] oa = null;
            ret = oa = (multi ? new Object[count] : new BigDecimal[count]);
            for ( ; count > 0; count--) {
                Object v = input.get(index++);
                oa[length++] = multi && v != null ? buildArray((PgArrayList) v, 0, -1) : (v == null ? null :
AbstractJdbc2ResultSet.toBigDecimal((String)v, -1)); 
            }
        }

        else if (type == Types.REAL) {
            float[] pa = null;
            Object[] oa = null;
            if (multi || useObjects) ret = oa = (multi ? new Object[count] : new Float[count]);
            else ret = pa = new float[count];
            for ( ; count > 0; count--) {
                Object o = input.get(index++);
                boolean p = false;
                if (multi || useObjects) oa[length++] = o == null ? null : (multi ? buildArray((PgArrayList) o, 0, -1)
:new Float(AbstractJdbc2ResultSet.toFloat((String) o))); 
                else pa[length++] = o == null ? 0f : AbstractJdbc2ResultSet.toFloat((String) o);
            }
        }

        else if (type == Types.DOUBLE) {
            double[] pa = null;
            Object[] oa = null;
            if (multi || useObjects) ret = oa = (multi ? new Object[count] : new Double[count]);
            else ret = pa = new double[count];
            for ( ; count > 0; count--) {
                Object o = input.get(index++);
                boolean p = false;
                if (multi || useObjects) oa[length++] = o == null ? null : (multi ? buildArray((PgArrayList) o, 0, -1)
:new Double(AbstractJdbc2ResultSet.toDouble((String) o))); 
                else pa[length++] = o == null ? 0d : AbstractJdbc2ResultSet.toDouble((String) o);
            }
        }

        else if (type == Types.CHAR || type == Types.VARCHAR) {
            Object[] oa = null;
            ret = oa = (multi ? new Object[count] : new String[count]);
            for ( ; count > 0; count--) {
                Object v = input.get(index++);
                oa[length++] = multi && v != null ? buildArray((PgArrayList) v, 0, -1) : v;
            }
        }

        else if (type == Types.DATE) {
            Object[] oa = null;
            ret = oa = (multi ? new Object[count] : new java.sql.Date[count]);
            for ( ; count > 0; count--) {
                Object v = input.get(index++);
                oa[length++] = multi && v != null ? buildArray((PgArrayList) v, 0, -1) : (v == null ? null :
connection.getTimestampUtils().toDate(null,(String) v)); 
            }
        }

        else if (type == Types.TIME) {
            Object[] oa = null;
            ret = oa = (multi ? new Object[count] : new java.sql.Time[count]);
            for ( ; count > 0; count--) {
                Object v = input.get(index++);
                oa[length++] = multi && v != null ? buildArray((PgArrayList) v, 0, -1) : (v == null ? null :
connection.getTimestampUtils().toTime(null,(String) v)); 
            }
        }

        else if (type == Types.TIMESTAMP) {
            Object[] oa = null;
            ret = oa = (multi ? new Object[count] : new java.sql.Timestamp[count]);
            for ( ; count > 0; count--) {
                Object v = input.get(index++);
                oa[length++] = multi && v != null ? buildArray((PgArrayList) v, 0, -1) : (v == null ? null :
connection.getTimestampUtils().toTimestamp(null,(String) v)); 
            }
        }

        // other datatypes not currently supported
        else {
            if (connection.getLogger().logDebug()) connection.getLogger().debug("getArrayImpl(long,int,Map) with " +
getBaseTypeName());
            throw org.postgresql.Driver.notImplemented(this.getClass(), "getArrayImpl(long,int,Map)");
        }

        return ret;
    }

    public int getBaseType () throws SQLException {
        return connection.getSQLType(getBaseTypeName());
    }

    public String getBaseTypeName () throws SQLException {
        String t = connection.getPGType(field.getOID());
        if (t.charAt(0) == '_') t = t.substring(1);
        return t;
    }

    public java.sql.ResultSet getResultSet () throws SQLException {
        return getResultSetImpl(1, 0, null);
    }

    public java.sql.ResultSet getResultSet (long index, int count) throws SQLException {
        return getResultSetImpl(index, count, null);
    }

    public java.sql.ResultSet getResultSetImpl (Map map) throws SQLException {
        return getResultSetImpl(1, 0, map);
    }

    public ResultSet getResultSetImpl (long index, int count, Map map) throws SQLException {

        // for now maps aren't supported.
        if (map != null && !map.isEmpty()) throw org.postgresql.Driver.notImplemented(this.getClass(),
"getResultSetImpl(long,int,Map)");

        // array index is out of range
        if (index < 1) throw new PSQLException(GT.tr("The array index is out of range: {0}", new Long(index)),
PSQLState.DATA_ERROR);

        PgArrayList list = buildArrayList(fieldString);

        if (count == 0) count = list.size();

        // array index out of range
        if ((--index) + count > list.size()) throw new PSQLException(GT.tr("The array index is out of range: {0},
numberof elements: {1}.", new Object[]{new Long(index + count), new Long(list.size())}), PSQLState.DATA_ERROR); 

        Vector rows = new Vector();

        // array type
        final int type = getBaseType();

        Field[] fields = new Field[2];
        {
            fields[0] = new Field("INDEX", Oid.INT2);

            if (type == Types.BIT) fields[1] = new Field("VALUE", Oid.BOOL);
            else if (type == Types.SMALLINT) fields[1] = new Field("VALUE", Oid.INT2);
            else if (type == Types.INTEGER) fields[1] = new Field("VALUE", Oid.INT4);
            else if (type == Types.BIGINT) fields[1] = new Field("VALUE", Oid.INT8);
            else if (type == Types.NUMERIC) fields[1] = new Field("VALUE", Oid.NUMERIC);
            else if (type == Types.REAL) fields[1] = new Field("VALUE", Oid.FLOAT4);
            else if (type == Types.DOUBLE) fields[1] = new Field("VALUE", Oid.FLOAT8);
            else if (type == Types.CHAR) fields[1] = new Field("VALUE", Oid.BPCHAR);
            else if (type == Types.VARCHAR) fields[1] = new Field("VALUE", Oid.VARCHAR);
            else if (type == Types.DATE) fields[1] = new Field("VALUE", Oid.DATE);
            else if (type == Types.TIME) fields[1] = new Field("VALUE", Oid.TIME);
            else if (type == Types.TIMESTAMP) fields[1] = new Field("VALUE", Oid.TIMESTAMPTZ);

            // other data types not currently supported
            else {
                if (connection.getLogger().logDebug()) connection.getLogger().debug("getResultSetImpl(long,int,Map)
with" + getBaseTypeName()); 
                throw org.postgresql.Driver.notImplemented(this.getClass(), "getResultSetImpl(long,int,Map)");
            }
        }

        if (list.isMultiDimensional) {
            for (int i = 0; i < list.size(); i++) {
                byte[][] t = new byte[2][0];
                Object v = list.get(i);
                t[0] = connection.encodeString(Integer.toString(i + 1));
                t[1] = connection.encodeString(v == null ? "NULL" : toString((PgArrayList) v));
                rows.add(t);
            }
        }

        else {
            for (int i = 0; i < list.size(); i++) {
                byte[][] t = new byte[2][0];
                String v = (String) list.get(i);
                t[0] = connection.encodeString(Integer.toString(i + 1));
                t[1] = connection.encodeString(v == null ? "NULL" : v);
                rows.add(t);
            }
        }

        BaseStatement stat = (BaseStatement) connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
        return (ResultSet) stat.createDriverResultSet(fields, rows);
    }

    public String toString () {
        return fieldString;
    }


    /**
     * Convert array list to PG String representation (e.g. {0,1,2}).
     */
    private String toString (PgArrayList list) throws SQLException {
        StringBuffer b = new StringBuffer();
        b.append('{');
        for (int i = 0; i < list.size(); i++) {
            Object v = list.get(i);
            if (i > 0) b.append(',');
            if (v == null) b.append("NULL");
            else if (v instanceof PgArrayList) b.append(toString((PgArrayList) v));
            else b.append('"').append(connection.escapeString((String) v)).append('"');
        }
        b.append('}');
        return b.toString();
    }

}


В списке pgsql-jdbc по дате отправления:

Предыдущее
От: Kris Jurka
Дата:
Сообщение: Re: Support for ResultSetMetaData.getTableName
Следующее
От: David Goodenough
Дата:
Сообщение: Re: Support for ResultSetMetaData.getTableName