/*-------------------------------------------------------------------------
 *
 * arrayfuncs.c--
 *    Special functions for arrays.
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *    $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.4 1996/08/27 07:32:27 scrappy Exp $
 *
 *-------------------------------------------------------------------------
 */

#include "config.h"

#include <ctype.h>
#include <stdio.h>

#include "postgres.h"

#include "catalog/catalog.h"
#include "catalog/pg_type.h"

#include "utils/syscache.h"
#include "utils/palloc.h"
#include "utils/memutils.h"
#include "storage/fd.h"		/* for SEEK_ */
#include "fmgr.h"
#include "utils/elog.h"
#include "utils/array.h"

#include "libpq/libpq-fs.h"
#include "libpq/be-fsstubs.h"

#define ASSGN    "="

/* An array has the following internal structure:
 *    <nbytes>            - total number of bytes 
 *     <ndim>                - number of dimensions of the array 
 *    <flags>                - bit mask of flags
 *    <dim>                - size of each array axis 
 *    <dim_lower>            - lower boundary of each dimension 
 *    <actual data>        - whatever is the stored data
*/

/*-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-*/
static int _ArrayCount(char *str, int dim[], int typdelim);
static char *_ReadArrayStr(char *arrayStr, int nitems, int ndim, int dim[],
			   func_ptr inputproc, Oid typelem, char typdelim,
			   int typlen, bool typbyval, char typalign,
			   int *nbytes);
static char *_ReadLOArray(char *str, int *nbytes, int *fd, bool *chunkFlag,
			  int ndim, int dim[], int baseSize);
static void _CopyArrayEls(char **values, char *p, int nitems, int typlen,
			  char typalign, bool typbyval);
static void system_cache_lookup(Oid element_type, bool input, int *typlen,
		bool *typbyval, char *typdelim, Oid *typelem, Oid *proc,
		char *typalign);
static Datum _ArrayCast(char *value, bool byval, int len);
static char *_AdvanceBy1word(char *str, char **word);
static void _ArrayRange(int st[], int endp[], int bsize, char *destPtr,
			ArrayType *array, int from);
static int _ArrayClipCount(int stI[], int endpI[], ArrayType *array);
static void _LOArrayRange(int st[], int endp[], int bsize, int srcfd,
	  int destfd, ArrayType *array, int isSrcLO, bool *isNull);
static void _ReadArray (int st[], int endp[], int bsize, int srcfd, int destfd,
		       ArrayType *array, int isDestLO, bool *isNull);
static char *_array_set(ArrayType *array, struct varlena *indx_str,
			struct varlena *dataPtr);
static ArrayCastAndSet(char *src, bool typbyval, int typlen, char *dest);


/*---------------------------------------------------------------------
 * array_in : 
 *        converts an array from the external format in "string" to
 *        it internal format.
 * return value :
 *        the internal representation of the input array
 *--------------------------------------------------------------------
 */
char *
array_in(char *string,		/* input array in external form */
	 Oid element_type)	/* type OID of an array element */
{
    int typlen;
    bool typbyval, done;
    char typdelim;
    Oid typinput;
    Oid typelem;
    char *string_save, *p, *q, *r;
    func_ptr inputproc;
    int i, nitems, dummy;
    int32 nbytes;
    char *dataPtr;
    ArrayType *retval;
    int ndim, dim[MAXDIM], lBound[MAXDIM];
    char typalign;
    
    system_cache_lookup(element_type, true, &typlen, &typbyval, &typdelim, 
			&typelem, &typinput, &typalign);
    
    fmgr_info(typinput, &inputproc, &dummy);
    
    string_save = (char *) palloc(strlen(string) + 3);
    strcpy(string_save, string);
    
    /* --- read array dimensions  ---------- */
    p = q = string_save; done = false;
    for ( ndim = 0; !done; ) {
        while (isspace(*p)) p++;
        if (*p == '[' ) {
	    p++;
	    if ((r = (char *)strchr(p, ':')) == (char *)NULL)
		lBound[ndim] = 1;
	    else { 
		*r = '\0'; 
		lBound[ndim] = atoi(p); 
		p = r + 1;
	    }
	    for (q = p; isdigit(*q); q++);
	    if (*q != ']')
		elog(WARN, "array_in: missing ']' in array declaration");
	    *q = '\0';
	    dim[ndim] = atoi(p);
	    if ((dim[ndim] < 0) || (lBound[ndim] < 0))
		elog(WARN,"array_in: array dimensions need to be positive");
	    dim[ndim] = dim[ndim] - lBound[ndim] + 1;
	    if (dim[ndim] < 0)
		elog(WARN, "array_in: upper_bound cannot be < lower_bound");
	    p = q + 1; ndim++;
        } else {
            done = true;
        }
    }
    
    if (ndim == 0) {
        if (*p == '{') {
            ndim = _ArrayCount(p, dim, typdelim);
            for (i = 0; i < ndim; lBound[i++] = 1);
        } else { 
	    elog(WARN,"array_in: Need to specify dimension");
        }
    } else {
        while (isspace(*p)) p++;
        if (strncmp(p, ASSGN, strlen(ASSGN)))
            elog(WARN, "array_in: missing assignment operator");
        p+= strlen(ASSGN);
        while (isspace(*p)) p++;
    }        
    
    nitems = getNitems( ndim, dim);
    if (nitems == 0) {
        char *emptyArray = palloc(sizeof(ArrayType));
        memset(emptyArray, 0, sizeof(ArrayType));
        * (int32 *) emptyArray = sizeof(ArrayType);
        return emptyArray;
    }
    
    if (*p == '{') {
        /* array not a large object */
        dataPtr =
	    (char *) _ReadArrayStr(p, nitems,  ndim, dim, inputproc, typelem,
				   typdelim, typlen, typbyval, typalign,
				   &nbytes );
        nbytes += ARR_OVERHEAD(ndim);
        retval = (ArrayType *) palloc(nbytes);
	memset(retval,0, nbytes);
        memmove(retval, (char *)&nbytes, sizeof(int)); 
        memmove((char*)ARR_NDIM_PTR(retval), (char *)&ndim, sizeof(int)); 
        SET_LO_FLAG (false, retval);
        memmove((char *)ARR_DIMS(retval), (char *)dim, ndim*sizeof(int)); 
        memmove((char *)ARR_LBOUND(retval), (char *)lBound, 
		ndim*sizeof(int));
	/* dataPtr is an array of arbitraystuff even though its type is char*
	   cast to char** to pass to _CopyArrayEls for now  - jolly */
        _CopyArrayEls((char**)dataPtr,
		      ARR_DATA_PTR(retval), nitems,
		      typlen, typalign, typbyval);
    } else  {
#ifdef LOARRAY
        int dummy, bytes;
        bool chunked = false;
	
        dataPtr = _ReadLOArray(p, &bytes, &dummy, &chunked, ndim, 
			       dim, typlen );
        nbytes = bytes + ARR_OVERHEAD(ndim);
        retval = (ArrayType *) palloc(nbytes);
        memset(retval, 0,nbytes); 
        memmove(retval, (char *)&nbytes, sizeof(int));
        memmove((char *)ARR_NDIM_PTR(retval), (char *)&ndim, sizeof(int));
        SET_LO_FLAG (true, retval);
        SET_CHUNK_FLAG (chunked, retval);
        memmove((char *)ARR_DIMS(retval), (char *)dim, ndim*sizeof(int));
        memmove((char *)ARR_LBOUND(retval),(char *)lBound, ndim*sizeof(int));
        memmove(ARR_DATA_PTR(retval), dataPtr, bytes);
#endif
	elog(WARN, "large object arrays not supported");
    }
    pfree(string_save);
    return((char *)retval);
}

/*-----------------------------------------------------------------------------
 * _ArrayCount --
 *   Counts the number of dimensions and the dim[] array for an array string. 
 *	 The syntax for array input is C-like nested curly braces 
 *-----------------------------------------------------------------------------
 */
static int
_ArrayCount(char *str, int dim[], int typdelim)
{
    int nest_level = 0, i; 
    int ndim = 0, temp[MAXDIM];
    bool scanning_string = false;
    bool eoArray = false;
    char *q;
    
    for (i = 0; i < MAXDIM; ++i) {
	temp[i] = dim[i] = 0;
    }
    
    if (strncmp (str, "{}", 2) == 0) return(0); 
    
    q = str;
    while (eoArray != true) {
        bool done = false;
        while (!done) {
            switch (*q) {
#ifdef ESCAPE_PATCH
            case '\\':
                /* skip escaped characters (\ and ") inside strings */
                if (scanning_string && *(q+1)) {
                    q++;
                }
                break;
#endif
	    case '\"':
		scanning_string = ! scanning_string;
		break;
	    case '{':
		if (!scanning_string)  { 
		    temp[nest_level] = 0;
		    nest_level++;
		}
		break;
	    case '}':
		if (!scanning_string) {
		    if (!ndim) ndim = nest_level;
		    nest_level--;
		    if (nest_level) temp[nest_level-1]++;
		    if (nest_level == 0) eoArray = done = true;
		}
		break;
	    default:
		if (!ndim) ndim = nest_level;
		if (*q == typdelim && !scanning_string )
		    done = true;
		break;
            }
            if (!done) q++;
        }
        temp[ndim-1]++;
        q++;
        if (!eoArray) 
            while (isspace(*q)) q++;
    }
    for (i = 0; i < ndim; ++i) {
	dim[i] = temp[i];
    }
    
    return(ndim);
}

/*---------------------------------------------------------------------------
 * _ReadArrayStr :
 *   parses the array string pointed by "arrayStr" and converts it in the 
 *   internal format. The external format expected is like C array
 *   declaration. Unspecified elements are initialized to zero for fixed length
 *   base types and to empty varlena structures for variable length base
 *   types.
 * result :
 *   returns the internal representation of the array elements
 *   nbytes is set to the size of the array in its internal representation.
 *---------------------------------------------------------------------------
 */
static char *
_ReadArrayStr(char *arrayStr,
	      int nitems,
	      int ndim,
	      int dim[],
	      func_ptr inputproc, /* function used for the conversion */
	      Oid typelem,
	      char typdelim,
	      int typlen,
	      bool typbyval,
	      char typalign,
	      int *nbytes)
{
    int i, nest_level = 0;
    char *p, *q, *r, **values;
    bool scanning_string = false;
    int indx[MAXDIM], prod[MAXDIM];
    bool eoArray = false;
    
    mda_get_prod(ndim, dim, prod);
    for (i = 0; i < ndim; indx[i++] = 0);
    /* read array enclosed within {} */    
    values = (char **) palloc(nitems * sizeof(char *));
    memset(values, 0, nitems * sizeof(char *));
    q = p = arrayStr;
    
    while ( ! eoArray ) {
        bool done = false;
        int i = -1;
	
        while (!done) {
            switch (*q) {
	    case '\\':
		/* Crunch the string on top of the backslash. */
		for (r = q; *r != '\0'; r++) *r = *(r+1);
		break;
	    case '\"':
		if (!scanning_string ) {
		    while (p != q) p++;
		    p++; /* get p past first doublequote */
		} else 
		    *q = '\0';
		scanning_string = ! scanning_string;
		break;
	    case '{':
		if (!scanning_string) { 
		    p++; 
		    nest_level++;
		    if (nest_level > ndim)
			elog(WARN, "array_in: illformed array constant");
		    indx[nest_level - 1] = 0;
		    indx[ndim - 1] = 0;
		}
		break;
	    case '}':
		if (!scanning_string) {
		    if (i == -1)
			i = tuple2linear(ndim, indx, prod); 
		    nest_level--;
		    if (nest_level == 0)
			eoArray = done = true;
		    else { 
			*q = '\0';
			indx[nest_level - 1]++;
		    }
		}
		break;
	    default:
		if (*q == typdelim && !scanning_string ) {
		    if (i == -1) 
			i = tuple2linear(ndim, indx, prod); 
		    done = true;
		    indx[ndim - 1]++;
		}
		break;
            }
            if (!done) 
                q++;
        }
        *q = '\0';                    
        if (i >= nitems)
            elog(WARN, "array_in: illformed array constant");
        values[i] = (*inputproc) (p, typelem);
        p = ++q;
        if (!eoArray)    
            /* 
             * if not at the end of the array skip white space 
             */
            while (isspace(*q)) {
                p++;
                q++;
            }
    }
    if (typlen > 0) {
        *nbytes = nitems * typlen;
        if (!typbyval)
            for (i = 0; i < nitems; i++)
                if (!values[i]) {
                    values[i] = palloc(typlen);
                    memset(values[i], 0, typlen);
                }
    } else {
        for (i = 0, *nbytes = 0; i < nitems; i++) {
            if (values[i]) {
		if (typalign=='d') {
		    *nbytes += DOUBLEALIGN(* (int32 *) values[i]);
		} else {
		    *nbytes += INTALIGN(* (int32 *) values[i]);
		}
	    } else {
                *nbytes += sizeof(int32);
                values[i] = palloc(sizeof(int32));
                *(int32 *)values[i] = sizeof(int32); 
            }
        }
    }
    return((char *)values);
}


/*----------------------------------------------------------------------------
 * Read data about an array to be stored as a large object                    
 *----------------------------------------------------------------------------
 */
static char *
_ReadLOArray(char *str,
	     int *nbytes,
	     int *fd,
	     bool *chunkFlag,
	     int ndim,
	     int dim[],
	     int baseSize)
{
    char *inputfile, *accessfile = NULL, *chunkfile = NULL;
    char *retStr, *_AdvanceBy1word();
    Oid lobjId;
    
    str = _AdvanceBy1word(str, &inputfile); 
    
    while (str != NULL) {
        char *word;
        
        str = _AdvanceBy1word(str, &word); 
	
        if (!strcmp (word, "-chunk")) {
            if (str == NULL)
                elog(WARN, "array_in: access pattern file required");
            str = _AdvanceBy1word(str, &accessfile);
        }
        else if (!strcmp (word, "-noreorg"))  {
            if (str == NULL)
                elog(WARN, "array_in: chunk file required");
            str = _AdvanceBy1word(str, &chunkfile);
        } else {
            elog(WARN, "usage: <input file> -chunk DEFAULT/<access pattern file> -invert/-native [-noreorg <chunk file>]");
	}
    }
    
    if (inputfile == NULL) 
        elog(WARN, "array_in: missing file name");
    lobjId = lo_creat(0);
    *fd = lo_open(lobjId, INV_READ);
    if ( *fd < 0 )
	elog(WARN, "Large object create failed");
    retStr = inputfile;
    *nbytes = strlen(retStr) + 2;
    
    if ( accessfile ) {
        FILE *afd;
        if ((afd = fopen (accessfile, "r")) == NULL)
            elog(WARN, "unable to open access pattern file");
        *chunkFlag = true;
        retStr = _ChunkArray(*fd, afd, ndim, dim, baseSize, nbytes,
			     chunkfile);
    }    
    return(retStr);
}

static void
_CopyArrayEls(char **values, 
	      char *p, 
	      int nitems, 
	      int typlen,
	      char typalign,
	      bool typbyval)
{
    int i;
    
    for (i = 0; i < nitems; i++) {
        int inc;
        inc = ArrayCastAndSet(values[i], typbyval, typlen, p);
        p += inc;
        if (!typbyval) 
            pfree(values[i]);
    }
    pfree(values);
}

/*-------------------------------------------------------------------------
 * array_out : 
 *         takes the internal representation of an array and returns a string
 *        containing the array in its external format.
 *-------------------------------------------------------------------------
 */
char *
array_out(ArrayType *v, Oid element_type)
{
    int typlen;
    bool typbyval;
    char typdelim;
    Oid typoutput, typelem;
    func_ptr outputproc;
    char typalign;
    
    char *p, *retval, **values, delim[2];
    int nitems, overall_length, i, j, k, indx[MAXDIM];
    bool dummy_bool;
    int dummy_int;
    int ndim, *dim;
    
    if (v == (ArrayType *) NULL)
        return ((char *) NULL);
    
    if (ARR_IS_LO(v) == true)  {
        char *p, *save_p;
        int nbytes;
	
        /* get a wide string to print to */
        p = array_dims(v, &dummy_bool);
        nbytes = strlen(ARR_DATA_PTR(v)) + 4 + *(int *)p;
	
        save_p = (char *) palloc(nbytes);
	
        strcpy(save_p, p + sizeof(int));
        strcat(save_p, ASSGN);
        strcat(save_p, ARR_DATA_PTR(v));
        pfree(p);
        return (save_p);
    }
    
    system_cache_lookup(element_type, false, &typlen, &typbyval,
                        &typdelim, &typelem, &typoutput, &typalign);
    fmgr_info(typoutput, & outputproc, &dummy_int);
    sprintf(delim, "%c", typdelim);
    ndim = ARR_NDIM(v);
    dim = ARR_DIMS(v);
    nitems = getNitems(ndim, dim);
    
    if (nitems == 0) {
        char *emptyArray = palloc(3);
        emptyArray[0] = '{';
        emptyArray[1] = '}';
        emptyArray[2] = '\0';
        return emptyArray;
    }
    
    p = ARR_DATA_PTR(v);
    overall_length = 1; /* [TRH] don't forget to count \0 at end. */
    values = (char **) palloc(nitems * sizeof (char *));
    for (i = 0; i < nitems; i++) {
        if (typbyval) {
            switch(typlen) {
	    case 1:
		values[i] = (*outputproc) (*p, typelem);
		break;
	    case 2:
		values[i] = (*outputproc) (* (int16 *) p, typelem);
		break;
	    case 3:
	    case 4:
		values[i] = (*outputproc) (* (int32 *) p, typelem);
		break;
            }
            p += typlen;
        } else {
            values[i] = (*outputproc) (p, typelem);
            if (typlen > 0)
                p += typlen;
            else
                p += INTALIGN(* (int32 *) p);
            /*
	     * For the pair of double quotes
	     */
            overall_length += 2;
        }
        overall_length += (strlen(values[i]) + 1);
    }
    
    /* 
     * count total number of curly braces in output string 
     */
    for (i = j = 0, k = 1; i < ndim; k *= dim[i++], j += k);
    
    p = (char *) palloc(overall_length + 2*j);        
    retval = p;
    
    strcpy(p, "{");
    for (i = 0; i < ndim; indx[i++] = 0);
    j = 0; k = 0;
    do {
        for (i = j; i < ndim - 1; i++)
            strcat(p, "{");
        /*
	 * Surround anything that is not passed by value in double quotes.
	 * See above for more details.
	 */
        if (!typbyval) {
	    strcat(p, "\"");
	    strcat(p, values[k]);
	    strcat(p, "\"");
        } else
	    strcat(p, values[k]);
        pfree(values[k++]); 
	
        for (i = ndim - 1; i >= 0; i--) {
            indx[i] = (indx[i] + 1)%dim[i];
            if (indx[i]) {
                strcat (p, delim);
                break;
            } else  
                strcat (p, "}");
        }
        j = i;
    } while (j  != -1);
    
    pfree(values);
    return(retval);
}

/*-----------------------------------------------------------------------------
 * array_dims :
 *        returns the dimension of the array pointed to by "v"
 *---------------------------------------------------------------------------- 
 */
char *
array_dims(ArrayType *v, bool *isNull)
{
    char *p, *save_p;
    int nbytes, i;
    int *dimv, *lb;
    
    if (v == (ArrayType *) NULL) RETURN_NULL;
    nbytes = ARR_NDIM(v)*33;    
    /* 
     * 33 since we assume 15 digits per number + ':' +'[]' 
     */         
    save_p = p =  (char *) palloc(nbytes + 4);
    memset(save_p, 0, nbytes + 4);
    dimv = ARR_DIMS(v); lb = ARR_LBOUND(v);
    p += 4;
    for (i = 0; i < ARR_NDIM(v); i++) {
        sprintf(p, "[%d:%d]", lb[i], dimv[i]+lb[i]-1);
        p += strlen(p);
    }
    nbytes = strlen(save_p + 4) + 4;
    memmove(save_p, &nbytes,4); 
    return (save_p);
} 

/*---------------------------------------------------------------------------
 * array_ref :
 *    This routing takes an array pointer and an index array and returns
 *    a pointer to the referred element if element is passed by 
 *    reference otherwise returns the value of the referred element.
 *---------------------------------------------------------------------------
 */
Datum
array_ref(ArrayType *array,
	  int n,
	  int indx[],
	  int reftype,
	  int elmlen,
	  int arraylen,
	  bool *isNull)
{
    int i, ndim, *dim, *lb, offset, nbytes;
    struct varlena *v;
    char *retval;
    
    if (array == (ArrayType *) NULL) RETURN_NULL;
    if (arraylen > 0) {
        /*
         * fixed length arrays -- these are assumed to be 1-d
         */
        if (indx[0]*elmlen > arraylen) 
            elog(WARN, "array_ref: array bound exceeded");
        retval = (char *)array + indx[0]*elmlen;
        return _ArrayCast(retval, reftype, elmlen);
    }
    dim = ARR_DIMS(array);
    lb = ARR_LBOUND(array);
    ndim = ARR_NDIM(array);
    nbytes = (* (int32 *) array) - ARR_OVERHEAD(ndim);
    
    if (!SanityCheckInput(ndim, n,  dim, lb, indx))
        RETURN_NULL;
    
    offset = GetOffset(n, dim, lb, indx);
    
    if (ARR_IS_LO(array)) {
        char * lo_name;
        int fd;
	
        /* We are assuming fixed element lengths here */
        offset *= elmlen;
        lo_name = (char *)ARR_DATA_PTR(array);
#ifdef LOARRAY
        if ((fd = LOopen(lo_name, ARR_IS_INV(array)?INV_READ:O_RDONLY)) < 0)
            RETURN_NULL;
#endif	
        if (ARR_IS_CHUNKED(array))
            v = _ReadChunkArray1El(indx, elmlen, fd, array, isNull);
        else {
            if (lo_lseek(fd, offset, SEEK_SET) < 0)
                RETURN_NULL;
#ifdef LOARRAY
            v = (struct varlena *) LOread(fd, elmlen);
#endif
        }
        if (*isNull) RETURN_NULL;
        if (VARSIZE(v) - 4 < elmlen)
            RETURN_NULL;
        (void) lo_close(fd);
        retval  = (char *)_ArrayCast((char *)VARDATA(v), reftype, elmlen);
	if ( reftype == 0) { /* not by value */
	    char * tempdata = palloc (elmlen);
	    memmove(tempdata, retval, elmlen);
	    retval = tempdata;
	}
	pfree(v);
        return (Datum) retval;
    }
    
    if (elmlen >  0) {
        offset = offset * elmlen;
        /*  off the end of the array */
        if (nbytes - offset < 1) RETURN_NULL;
        retval = ARR_DATA_PTR (array) + offset;
        return _ArrayCast(retval, reftype, elmlen);
    } else {
        bool done = false;
        char *temp;
        int bytes = nbytes;
        temp = ARR_DATA_PTR (array);
        i = 0;
        while (bytes > 0 && !done) {
            if (i == offset) {
                retval = temp;
                done = true;
            }
            bytes -= INTALIGN(* (int32 *) temp);
            temp += INTALIGN(* (int32 *) temp);
            i++;
        }
        if (! done) 
            RETURN_NULL;
        return (Datum) retval;
    }
}

/*-----------------------------------------------------------------------------
 * array_clip :
 *        This routine takes an array and a range of indices (upperIndex and 
 *         lowerIndx), creates a new array structure for the referred elements 
 *         and returns a pointer to it.
 *-----------------------------------------------------------------------------
 */
Datum
array_clip(ArrayType *array,
	   int n,
	   int upperIndx[],
	   int lowerIndx[],
	   int reftype,
	   int len,
	   bool *isNull)
{
    int i, ndim, *dim, *lb, nbytes;
    ArrayType *newArr; 
    int bytes, span[MAXDIM];
    
    /* timer_start(); */
    if (array == (ArrayType *) NULL) 
        RETURN_NULL;
    dim = ARR_DIMS(array);
    lb = ARR_LBOUND(array);
    ndim = ARR_NDIM(array);
    nbytes = (* (int32 *) array) - ARR_OVERHEAD(ndim);
    
    if (!SanityCheckInput(ndim, n,  dim, lb, upperIndx))
        RETURN_NULL;
    
    if (!SanityCheckInput(ndim, n, dim, lb, lowerIndx))
        RETURN_NULL;
    
    for (i = 0; i < n; i++)
        if (lowerIndx[i] > upperIndx[i])
            elog(WARN, "lowerIndex cannot be larger than upperIndx"); 
    mda_get_range(n, span, lowerIndx, upperIndx);
    
    if (ARR_IS_LO(array)) {
        char * lo_name, * newname;
        int fd, newfd, isDestLO = true, rsize;
	
        if (len < 0) 
            elog(WARN, "array_clip: array of variable length objects not supported");  
#ifdef LOARRAY
        lo_name = (char *)ARR_DATA_PTR(array);
        if ((fd = LOopen(lo_name, ARR_IS_INV(array)?INV_READ:O_RDONLY)) < 0)
            RETURN_NULL;
        newname = _array_newLO( &newfd, Unix );
#endif
        bytes = strlen(newname) + 1 + ARR_OVERHEAD(n);
        newArr = (ArrayType *) palloc(bytes);
        memmove(newArr, array, sizeof(ArrayType));
        memmove(newArr, &bytes, sizeof(int));
        memmove(ARR_DIMS(newArr), span, n*sizeof(int));
        memmove(ARR_LBOUND(newArr), lowerIndx, n*sizeof(int));
        strcpy(ARR_DATA_PTR(newArr), newname);
	
        rsize = compute_size (lowerIndx, upperIndx, n, len);
        if (rsize < MAX_BUFF_SIZE) { 
            char *buff;
            rsize += 4;
            buff = palloc(rsize);
            if ( buff ) 
                isDestLO = false;
            if (ARR_IS_CHUNKED(array)) {
		_ReadChunkArray(lowerIndx, upperIndx, len, fd, &(buff[4]), 
				array,0,isNull);
            } else { 
		_ReadArray(lowerIndx, upperIndx, len, fd, (int)&(buff[4]),
			   array, 
			   0,isNull);
            }
            memmove(buff, &rsize, 4);
#ifdef LOARRAY
            if (! *isNull)
                bytes = LOwrite(newfd, (struct varlena *)buff);
#endif
            pfree (buff);
        }
        if (isDestLO)   
            if (ARR_IS_CHUNKED(array)) {
		_ReadChunkArray(lowerIndx, upperIndx, len, fd, (char*)newfd, array,
				1,isNull);
            } else {
		_ReadArray(lowerIndx, upperIndx, len, fd, newfd, array, 1,isNull);
            }
#ifdef LOARRAY
        (void) LOclose(fd);
        (void) LOclose(newfd);
#endif
        if (*isNull) { 
            pfree(newArr); 
            newArr = NULL;
        }
        /* timer_end(); */
        return ((Datum) newArr);
    }
    
    if (len >  0) {
        bytes = getNitems(n, span);
        bytes = bytes*len + ARR_OVERHEAD(n);
    } else {
        bytes = _ArrayClipCount(lowerIndx, upperIndx, array);
        bytes += ARR_OVERHEAD(n);
    }
    newArr = (ArrayType *) palloc(bytes);
    memmove(newArr, array, sizeof(ArrayType));
    memmove(newArr, &bytes, sizeof(int));
    memmove(ARR_DIMS(newArr), span, n*sizeof(int));
    memmove(ARR_LBOUND(newArr), lowerIndx, n*sizeof(int));
    _ArrayRange(lowerIndx, upperIndx, len, ARR_DATA_PTR(newArr), array, 1);
    return (Datum) newArr;
}

/*-----------------------------------------------------------------------------
 * array_set :
 *        This routine sets the value of an array location (specified by an index array)
 *        to a new value specified by "dataPtr".
 * result :
 *        returns a pointer to the modified array.
 *-----------------------------------------------------------------------------
 */
char *
array_set(ArrayType *array,
	  int n,
	  int indx[],
	  char *dataPtr,
	  int reftype,
	  int elmlen,
	  int arraylen,
	  bool *isNull)
{
    int ndim, *dim, *lb, offset, nbytes;
    char *pos;
    
    if (array == (ArrayType *) NULL) 
        RETURN_NULL;
    if (arraylen > 0) {
        /*
         * fixed length arrays -- these are assumed to be 1-d
         */
        if (indx[0]*elmlen > arraylen) 
#ifdef ARRAY_PATCH
            elog(WARN, "array_ref: array bound exceeded");
#else
            elog(WARN, "array_set: array bound exceeded");
#endif
        pos = (char *)array + indx[0]*elmlen;
        ArrayCastAndSet(dataPtr, (bool) reftype, elmlen, pos);
        return((char *)array);
    }
    dim = ARR_DIMS(array);
    lb = ARR_LBOUND(array);
    ndim = ARR_NDIM(array);
    nbytes = (* (int32 *) array) - ARR_OVERHEAD(ndim);
    
    if (!SanityCheckInput(ndim, n,  dim, lb, indx)) 
#ifdef ARRAY_PATCH
    {
	elog(WARN, "array_set: array bound exceeded");
        return((char *)array);
    }
#else
        return((char *)array);
#endif
    offset = GetOffset( n, dim, lb, indx);
    
    if (ARR_IS_LO(array)) {
        int fd;
        char * lo_name;
        struct varlena *v;
	
        /* We are assuming fixed element lengths here */
        offset *= elmlen;
#ifdef LOARRAY
        lo_name = ARR_DATA_PTR(array);
        if ((fd = LOopen(lo_name, ARR_IS_INV(array)?INV_WRITE:O_WRONLY)) < 0)
            return((char *)array);
#endif	
        if (lo_lseek(fd, offset, SEEK_SET) < 0)
            return((char *)array);
        v = (struct varlena *) palloc(elmlen + 4);
        VARSIZE (v) = elmlen + 4;
        ArrayCastAndSet(dataPtr, (bool) reftype, elmlen, VARDATA(v));
#ifdef LOARRAY
        n =  LOwrite(fd, v);
#endif
        /* if (n < VARSIZE(v) - 4) 
	   RETURN_NULL;
	   */
        pfree(v);
        (void) lo_close(fd);
        return((char *)array);
    }
    if (elmlen >  0) {
        offset = offset * elmlen;
        /*  off the end of the array */
        if (nbytes - offset < 1) return((char *)array);
        pos = ARR_DATA_PTR (array) + offset;
    } else {
#ifdef ARRAY_PATCH
	ArrayType *newarray;
	char *elt_ptr;
	int oldsize, newsize, oldlen, newlen, lth0, lth1, lth2;

	elt_ptr = array_seek(ARR_DATA_PTR(array), -1, offset);
	oldlen = INTALIGN(*(int32 *)elt_ptr);
	newlen = INTALIGN(*(int32 *)dataPtr);

	if (oldlen == newlen) {
	    /* new element with same size, overwrite old data */
	    ArrayCastAndSet(dataPtr, (bool)reftype, elmlen, elt_ptr);
	    return((char *)array);
	}

	/* new element with different size, reallocate the array */
	oldsize = array->size;
	lth0 = ARR_OVERHEAD(n);
	lth1 = (int)(elt_ptr - ARR_DATA_PTR(array));
	lth2 = (int)(oldsize - lth0 - lth1 - oldlen);
	newsize = lth0 + lth1 + newlen + lth2;

	newarray = (ArrayType *)palloc(newsize);
	memmove((char *)newarray, (char *)array, lth0+lth1);
	newarray->size = newsize;
	newlen = ArrayCastAndSet(dataPtr, (bool)reftype, elmlen,
				 (char *)newarray+lth0+lth1);
	memmove((char *)newarray+lth0+lth1+newlen,
		(char *)array+lth0+lth1+oldlen, lth2);

	/* ??? who should free this storage ??? */
	return((char *)newarray);
#else
        elog(WARN, "array_set: update of variable length fields not supported");
#endif
    } 
    ArrayCastAndSet(dataPtr, (bool) reftype, elmlen, pos);
    return((char *)array);
}

/*----------------------------------------------------------------------------
 * array_assgn :
 *        This routine sets the value of a range of array locations (specified
 *        by upper and lower index values ) to new values passed as 
 *        another array
 * result :
 *        returns a pointer to the modified array.
 *----------------------------------------------------------------------------
 */
char *
array_assgn(ArrayType *array,
	    int n,
	    int upperIndx[],
	    int lowerIndx[],
	    ArrayType *newArr,
	    int reftype,
	    int len,
	    bool *isNull)
{
    int i, ndim, *dim, *lb;
    
    if (array == (ArrayType *) NULL) 
        RETURN_NULL;
    if (len < 0) 
        elog(WARN,"array_assgn:updates on arrays of variable length elements not allowed");
    
    dim = ARR_DIMS(array);
    lb = ARR_LBOUND(array);
    ndim = ARR_NDIM(array);
    
    if (!SanityCheckInput(ndim, n,  dim, lb, upperIndx) || 
	!SanityCheckInput(ndim, n, dim, lb, lowerIndx)) {
        return((char *)array);
    }
    
    for (i = 0; i < n; i++)
        if (lowerIndx[i] > upperIndx[i])
            elog(WARN, "lowerIndex larger than upperIndx"); 
    
    if (ARR_IS_LO(array)) {
        char * lo_name;
        int fd, newfd;
	
#ifdef LOARRAY
        lo_name = (char *)ARR_DATA_PTR(array);
        if ((fd = LOopen(lo_name,  ARR_IS_INV(array)?INV_WRITE:O_WRONLY)) < 0)
            return((char *)array);
#endif
        if (ARR_IS_LO(newArr)) {
#ifdef LOARRAY
            lo_name = (char *)ARR_DATA_PTR(newArr);
            if ((newfd = LOopen(lo_name, ARR_IS_INV(newArr)?INV_READ:O_RDONLY)) < 0)
                return((char *)array);
#endif
            _LOArrayRange(lowerIndx, upperIndx, len, fd, newfd, array, 1, isNull);
            (void) lo_close(newfd);
        } else {
            _LOArrayRange(lowerIndx, upperIndx, len, fd, (int)ARR_DATA_PTR(newArr), 
			  array, 0, isNull);
        }
        (void) lo_close(fd);
        return ((char *) array);
    }
    _ArrayRange(lowerIndx, upperIndx, len, ARR_DATA_PTR(newArr), array, 0);
    return (char *) array;
}

/*-----------------------------------------------------------------------------
 * array_eq :
 *        compares two arrays for equality    
 * result :
 *        returns 1 if the arrays are equal, 0 otherwise.
 *-----------------------------------------------------------------------------
 */
int
array_eq (ArrayType *array1, ArrayType *array2)
{
    if ((array1 == NULL) || (array2 == NULL))
	return(0);
    if (*(int *)array1 != *(int *)array2)
	return (0);
    if (memcmp(array1, array2, *(int *)array1))
	return(0);
    return(1);
}

/***************************************************************************/
/******************|          Support  Routines           |*****************/
/***************************************************************************/
static void
system_cache_lookup(Oid element_type,
		    bool input,
		    int *typlen,
		    bool *typbyval,
		    char *typdelim,
		    Oid *typelem,
		    Oid *proc,
		    char *typalign)
{
    HeapTuple typeTuple;
    TypeTupleForm typeStruct;
    
    typeTuple = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(element_type),
				    0,0,0);
    
    if (!HeapTupleIsValid(typeTuple)) {
        elog(WARN, "array_out: Cache lookup failed for type %d\n",
             element_type);
        return;
    }
    typeStruct = (TypeTupleForm) GETSTRUCT(typeTuple);
    *typlen    = typeStruct->typlen;
    *typbyval  = typeStruct->typbyval;
    *typdelim  = typeStruct->typdelim;
    *typelem   = typeStruct->typelem;
    *typalign  = typeStruct->typalign;
    if (input) {
        *proc = typeStruct->typinput;
    } else {
        *proc = typeStruct->typoutput;
    }
}

static Datum
_ArrayCast(char *value, bool byval, int len)
{
    if (byval) {
        switch (len) {
	case 1:
	    return((Datum) * value);
	case 2:
	    return((Datum) * (int16 *) value);
	case 3:
	case 4:
	    return((Datum) * (int32 *) value);
	default:
	    elog(WARN, "array_ref: byval and elt len > 4!");
	    break;
        }
    } else {
        return (Datum) value;
    }
 return 0;
}


static int
ArrayCastAndSet(char *src,
		bool typbyval,
		int typlen,
		char *dest)
{
    int inc;
    
    if (typlen > 0) {
	if (typbyval) {
            switch(typlen) {
	    case 1: 
		*dest = DatumGetChar(src);
		break;
	    case 2: 
		* (int16 *) dest = DatumGetInt16(src);
		break;
	    case 4:
		* (int32 *) dest = (int32)src;
		break;
            }
        } else {
            memmove(dest, src, typlen);
        }
        inc = typlen;
    } else {
        memmove(dest, src, *(int32 *)src);
        inc = (INTALIGN(* (int32 *) src));
    }
    return(inc);
} 

static char *
_AdvanceBy1word(char *str, char **word)
{
    char *retstr, *space;
    
    *word = NULL;
    if (str == NULL) return str;
    while (isspace(*str)) str++;
    *word = str;
    if ((space = (char *)strchr(str, ' ')) != (char *) NULL) {
        retstr = space + 1;
        *space = '\0';
    }
    else 
        retstr = NULL;
    return retstr;
}

int
SanityCheckInput(int ndim, int n, int dim[], int lb[], int indx[])
{
    int i;
    /* Do Sanity check on input */
    if (n != ndim) return 0;
    for (i = 0; i < ndim; i++)
        if ((lb[i] > indx[i]) || (indx[i] >= (dim[i] + lb[i])))
            return 0;
    return 1;
}

static void
_ArrayRange(int st[],
	    int endp[],
	    int bsize,
	    char *destPtr,
	    ArrayType *array,
	    int from)
{
    int n, *dim, *lb, st_pos, prod[MAXDIM];
    int span[MAXDIM], dist[MAXDIM], indx[MAXDIM];
    int i, j, inc;
    char *srcPtr, *array_seek();
    
    n = ARR_NDIM(array); dim = ARR_DIMS(array);
    lb = ARR_LBOUND(array); srcPtr = ARR_DATA_PTR(array);
    for (i = 0; i < n; st[i] -= lb[i], endp[i] -= lb[i], i++); 
    mda_get_prod(n, dim, prod);
    st_pos = tuple2linear(n, st, prod);
    srcPtr = array_seek(srcPtr, bsize, st_pos);
    mda_get_range(n, span, st, endp);
    mda_get_offset_values(n, dist, prod, span);
    for (i=0; i < n; indx[i++]=0);
    i = j = n-1; inc = bsize;
    do {
        srcPtr = array_seek(srcPtr, bsize,  dist[j]); 
        if (from) 
            inc = array_read(destPtr, bsize, 1, srcPtr);
        else 
            inc = array_read(srcPtr, bsize, 1, destPtr);
        destPtr += inc; srcPtr += inc;
    } while ((j = next_tuple(i+1, indx, span)) != -1);
}

static int
_ArrayClipCount(int stI[], int endpI[], ArrayType *array)
{
    int n, *dim, *lb, st_pos, prod[MAXDIM];
    int span[MAXDIM], dist[MAXDIM], indx[MAXDIM];
    int i, j, inc, st[MAXDIM], endp[MAXDIM];
    int count = 0;
    char *ptr, *array_seek();
    
    n = ARR_NDIM(array); dim = ARR_DIMS(array);
    lb = ARR_LBOUND(array); ptr = ARR_DATA_PTR(array);
    for (i = 0; i < n; st[i] = stI[i]-lb[i], endp[i]=endpI[i]-lb[i], i++);
    mda_get_prod(n, dim, prod);
    st_pos = tuple2linear(n, st, prod);
    ptr = array_seek(ptr, -1, st_pos);
    mda_get_range(n, span, st, endp);
    mda_get_offset_values(n, dist, prod, span);
    for (i=0; i < n; indx[i++]=0);
    i = j = n-1;
    do {
        ptr = array_seek(ptr, -1,  dist[j]);
        inc =  INTALIGN(* (int32 *) ptr);
        ptr += inc; count += inc;
    } while ((j = next_tuple(i+1, indx, span)) != -1);
    return count;
}

char *
array_seek(char *ptr, int eltsize, int nitems)
{
    int i;
    
    if (eltsize > 0) 
        return(ptr + eltsize*nitems);
    for (i = 0; i < nitems; i++) 
	ptr += INTALIGN(* (int32 *) ptr);
    return(ptr);
}

int
array_read(char *destptr, int eltsize, int nitems, char *srcptr)
{
    int i, inc, tmp;
    
    if (eltsize > 0)  {
        memmove(destptr, srcptr, eltsize*nitems);
        return(eltsize*nitems);
    }
    for (i = inc = 0; i < nitems; i++) {
	tmp = (INTALIGN(* (int32 *) srcptr));
	memmove(destptr, srcptr, tmp);
	srcptr += tmp;
	destptr += tmp;
	inc += tmp;
    }
    return(inc);
}

static void
_LOArrayRange(int st[],
	      int endp[],
	      int bsize,
	      int srcfd,
	      int destfd,
	      ArrayType *array,
	      int isSrcLO,
	      bool *isNull)
{
    int n, *dim, st_pos, prod[MAXDIM];
    int span[MAXDIM], dist[MAXDIM], indx[MAXDIM];
    int i, j, inc, tmp, *lb, offset;
    
    n = ARR_NDIM(array); dim = ARR_DIMS(array);
    lb = ARR_LBOUND(array);
    for (i = 0; i < n; st[i] -= lb[i], endp[i] -= lb[i], i++);
    
    mda_get_prod(n, dim, prod);
    st_pos = tuple2linear(n, st, prod);
    offset = st_pos*bsize;
    if (lo_lseek(srcfd, offset, SEEK_SET) < 0) 
        return;
    mda_get_range(n, span, st, endp);
    mda_get_offset_values(n, dist, prod, span);
    for (i=0; i < n; indx[i++]=0);
    for (i = n-1, inc = bsize; i >= 0; inc *= span[i--])
        if (dist[i]) 
            break;
    j = n-1;
    do {
        offset += (dist[j]*bsize);
        if (lo_lseek(srcfd,  offset, SEEK_SET) < 0) 
            return;
        tmp = _LOtransfer((char**)&srcfd, inc, 1, (char**)&destfd, isSrcLO, 1);
        if ( tmp < inc )
	    return;
        offset += inc;
    } while ((j = next_tuple(i+1, indx, span)) != -1);
}


static void
_ReadArray (int st[],
	    int endp[],
	    int bsize,
	    int srcfd,
	    int destfd,
	    ArrayType *array,
	    int isDestLO,
	    bool *isNull)
{
    int n, *dim, st_pos, prod[MAXDIM];
    int span[MAXDIM], dist[MAXDIM], indx[MAXDIM];
    int i, j, inc, tmp, *lb, offset;
    
    n = ARR_NDIM(array); dim = ARR_DIMS(array);
    lb = ARR_LBOUND(array);
    for (i = 0; i < n; st[i] -= lb[i], endp[i] -= lb[i], i++);
    
    mda_get_prod(n, dim, prod);
    st_pos = tuple2linear(n, st, prod);
    offset = st_pos*bsize;
    if (lo_lseek(srcfd, offset, SEEK_SET) < 0)
	return;
    mda_get_range(n, span, st, endp);
    mda_get_offset_values(n, dist, prod, span);
    for (i=0; i < n; indx[i++]=0);
    for (i = n-1, inc = bsize; i >= 0; inc *= span[i--])
        if (dist[i]) 
            break;
    j = n-1;
    do {
        offset += (dist[j]*bsize);
        if (lo_lseek(srcfd,  offset, SEEK_SET) < 0) 
            return;
        tmp = _LOtransfer((char**)&destfd, inc, 1, (char**)&srcfd, 1, isDestLO);
        if ( tmp < inc ) 
	    return;
        offset += inc;
    } while ((j = next_tuple(i+1, indx, span)) != -1);
}


int
_LOtransfer(char **destfd,
	    int size,
	    int nitems,
	    char **srcfd,
	    int isSrcLO,
	    int isDestLO)
{
#define MAX_READ (512 * 1024)
#define min(a, b) (a < b ? a : b)
    struct varlena *v;
    int tmp, inc, resid;
    
    inc = nitems*size; 
    if (isSrcLO && isDestLO && inc > 0)
	for (tmp = 0, resid = inc;
	     resid > 0 && (inc = min(resid, MAX_READ)) > 0; resid -= inc) { 
#ifdef LOARRAY
	    v = (struct varlena *) LOread((int) *srcfd, inc);
	    if (VARSIZE(v) - 4 < inc) 
		{pfree(v); return(-1);}
	    tmp += LOwrite((int) *destfd, v);    
#endif
	    pfree(v);
	    
	} 
    else if (!isSrcLO && isDestLO) {
        tmp = lo_write((int) *destfd, *srcfd, inc);
        *srcfd = *srcfd + tmp;
    } 
    else if (isSrcLO && !isDestLO) {
        tmp = lo_read((int) *srcfd, *destfd, inc);
        *destfd = *destfd + tmp;
    } 
    else {
        memmove(*destfd, *srcfd, inc);
        tmp = inc;
        *srcfd += inc;
        *destfd += inc;
    }
    return(tmp);
#undef MAX_READ
}

char *
_array_newLO(int *fd, int flag)
{
    char *p;
    char saveName[NAME_LEN];
    
    p = (char *) palloc(NAME_LEN);
    sprintf(p, "/Arry.%d", newoid());
    strcpy (saveName, p);
#ifdef LOARRAY
    if ( (*fd = LOcreat (saveName, 0600, flag)) < 0)
	elog(WARN, "Large object create failed");
#endif
    return (p);
}

