Skip site navigation (1) Skip section navigation (2)

Peripheral Links

Header And Logo

PostgreSQL
| The world's most advanced open source database.

Site Navigation

Search for
  Advanced Search

AbstractJdbc2Array



Hello Kris and others,
in the attachment you can find rewriten code for AbstractJdbc2Array - it contains working implementations of both getArray and getResultSet methods. I have made all required changes comparing to my previous code, which means that user can set "compatible" parameter to < 8.2 in order to use primitive types and not to parse NULL as Java nulls.

Please test the code and if everything is OK please add it to CSV (or maybe I could do it by myself, then please point me how do log-in etc...).

If you find any problems or have questions please write me an email.

PS. Sorry for the delay sending the code, but I was on vacation :-)

Best regards,
Marek Lewczuk
/*-------------------------------------------------------------------------
*
* 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
     * {(at)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}, number of elements: {1}.", new Object[]{new Long(index + count), new Long(array.size())}), PSQLState.DATA_ERROR);

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

    /**
     * Build {(at)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 {(at)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}, number of 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();
    }

}



Home | Main Index | Thread Index

Privacy Policy | PostgreSQL Archives hosted by Command Prompt, Inc. | Designed by tinysofa
Copyright © 1996 – 2008 PostgreSQL Global Development Group