Thread: array support patch phase 1 patch

array support patch phase 1 patch

From
Joe Conway
Date:
This patch is starting to get large enough that it is a challenge to
keep in sync with cvs, and it is reasonably complete as a package, so I
was hoping it could be reviewed and committed as "array support phase
1". The most notable missing item is documentation, but if possible I'd
like to defer that until a few more items are complete (see
"Yet-to-come" below).

If a more complete package (e.g. want to wait for items 1-5 of
"Yet-to-come") is preferred, please let me know and I'll keep plugging
along.

Note that the gram.y changes are a bit ugly, but I struggled with
getting anything less ugly to work.

As is, it passes all regression tests (make installcheck), and covers
the following:
----------------------------------------------------------------------
1. Support for polymorphic functions, accepting and returning ANYARRAY
and ANYELEMENT datatypes that are "tied" to each other and resolved to
an actual type at runtime. This also includes the ability to define
aggregates using the polymorphic functions.

2. Array handling functions:
    - singleton_array(ANYELEMENT) returns ANYARRAY
    - array_push(ANYARRAY, ANYELEMENT) returns ANYARRAY
    - array_accum(ANYARRAY, ANYELEMENT) returns ANYARRAY
    - array_assign(ANYARRAY, int, ANYELEMENT) returns ANYARRAY
    - array_subscript(ANYARRAY, int) returns ANYELEMENT

3. Grammar and underlying support for the following (examples):

    create table foo(f1 integer ARRAY);
    create table foo(f1 integer ARRAY[]);
    create table foo(f1 integer ARRAY[x]);
    create table foo(f1 integer ARRAY[][]);   (and more [] etc)
    create table foo(f1 integer ARRAY[x][y]); (and more [] etc)

    select ARRAY[1,2,3];
    select ARRAY[[1,2,3],[4,5,6]];
    select ARRAY[ARRAY[1,2,3],ARRAY[4,5,6]];
    etc up to 6 dimensions

    select ARRAY(select oid from pg_class order by relname);


Yet-to-come:
---------------------------------
1. Functions:
    - str_to_array(str TEXT, delim TEXT) returns TEXT[]
    - array_to_str(array ANYARRAY, delim TEXT) returns TEXT

2. Grammar:
    select ARRAY[1,2] || 3;
    select ARRAY[1,2] || ARRAY[3,4];
    select ARRAY[[1,2],[3,4]] || 5;
    select ARRAY[[1,2],[3,4]] || [5,6]

3. Documentation update:
     Update "User's Guide"->"Data Types"->"Arrays" documentation
     create a new section: "User's Guide"->
                           "Functions and Operators"->
                           "Array Functions and Operators"

4. PL/pgSQL support for polymorphic types

5. SQL function support for polymorphic types.

6. Move as much of contrib/array into backend as makes sense (I haven't
    looked too close yet), including migration to use polymorphic
    semantics.

7. Move as much of contrib/intarray into backend as makes sense (I
    haven't looked too close yet), including migration to use
    polymorphic semantics (therefore make work on other than int
    where possible).

8. Additional documentation and regression test updates

Thanks,

Joe


Attachment

Re: array support patch phase 1 patch

From
"Christopher Kings-Lynne"
Date:
> This patch is starting to get large enough that it is a challenge to
> keep in sync with cvs, and it is reasonably complete as a package, so I
> was hoping it could be reviewed and committed as "array support phase
> 1". The most notable missing item is documentation, but if possible I'd
> like to defer that until a few more items are complete (see
> "Yet-to-come" below).
>
> If a more complete package (e.g. want to wait for items 1-5 of
> "Yet-to-come") is preferred, please let me know and I'll keep plugging
> along.
>
> Note that the gram.y changes are a bit ugly, but I struggled with
> getting anything less ugly to work.
>
> As is, it passes all regression tests (make installcheck), and covers
> the following:

I can confirm that this patch applies cleanly, compiles and passes all
regression tests on FreeBSD/alpha.

Chris


Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> This patch is starting to get large enough that it is a challenge to
> keep in sync with cvs, and it is reasonably complete as a package, so I
> was hoping it could be reviewed and committed as "array support phase
> 1".

I looked over this patch a little bit, mostly at the anyarray/anyelement
changes; I didn't study the ArrayExpr stuff (except to notice that yeah,
the grammar change is very ugly; can't it be made independent of the
number of levels?).

I like the anyarray/anyelement stuff with the exception of the
actualRetType field added to FunctionCallInfoData.  In the first place,
that's the wrong place for such data; FunctionCallInfoData should only
contain data that will change for each call, which resolved type won't.
This should go into FmgrInfo, perhaps, or maybe we could treat it as a
call-context item (though I think we might have conflicts with existing
uses of the context link).  A bigger gripe is that passing only
actualRetType isn't future-proof.  It doesn't help functions that take
anyelement but return a fixed type; they won't know what the argument type
is.  And it won't scale if we end up adding anyarray2/anyelement2/etc to
allow multiple generic parameter types; at most one of the actual
parameter types could be made visible to the function.  I see your concern
here, but I think the only adequate solution is to make sure the function
can learn the types of all its arguments, as well as the assigned return
type.  (Of course it could deduce the latter from the former, but we may
as well save it the effort, especially since it would have to repeat
parse-time catalog lookups in many interesting cases.)

A notion that I've toyed with more than once is to allow a function
to get at the parse tree representation for its call (ie, the FuncExpr
or OpExpr node).  If we did that, a function could learn all these
types by examining the parse tree, and it could learn other things
besides.  A disadvantage is that functions would have to be ready to
cope with all the wooliness of parse trees.  It'd probably be bright
to make some convenience functions "get my return type", "get type
of my n'th argument" to localize this logic as much as possible.

In short then, what about passing an Expr* link in FmgrInfo as a
substitute for actualRetType?  (An additional advantage of this way
is that it's obvious whether or not the information has been provided,
which it would not be for, say, functions invoked via DirectFunctionCallN.
The patch as it stands fails silently if a polymorphic function is called
from a call site that doesn't provide the needed info.)

            regards, tom lane


Re: array support patch phase 1 patch

From
Joe Conway
Date:
Tom Lane wrote:
> I looked over this patch a little bit, mostly at the anyarray/anyelement
> changes; I didn't study the ArrayExpr stuff (except to notice that yeah,
> the grammar change is very ugly; can't it be made independent of the
> number of levels?).

I struggled with it for quite a while, but then again, I can't claim
yacc grammar as a particularly strong point. I kept running into
reduce/reduce errors with anything completely recursive, or when it did
work, I found myself restricted to one level of nesting. I'll try again
-- any suggestions for something similar to study?

> parameter types could be made visible to the function.  I see your concern
> here, but I think the only adequate solution is to make sure the function
> can learn the types of all its arguments, as well as the assigned return
> type.  (Of course it could deduce the latter from the former, but we may
> as well save it the effort, especially since it would have to repeat
> parse-time catalog lookups in many interesting cases.)

I came to the same conclusion with ArrayExpr/ArrayExprState -- you can
always deduce the array type from the element type and vice-versa, but
it seemed costly to do in the executor when it can be done just once in
the parser.

> A notion that I've toyed with more than once is to allow a function
> to get at the parse tree representation for its call (ie, the FuncExpr
> or OpExpr node).  If we did that, a function could learn all these
> types by examining the parse tree, and it could learn other things
> besides.  A disadvantage is that functions would have to be ready to
> cope with all the wooliness of parse trees.  It'd probably be bright
> to make some convenience functions "get my return type", "get type
> of my n'th argument" to localize this logic as much as possible.

OK -- I'll take a look at that option.


> In short then, what about passing an Expr* link in FmgrInfo as a
> substitute for actualRetType?  (An additional advantage of this way
> is that it's obvious whether or not the information has been provided,
> which it would not be for, say, functions invoked via DirectFunctionCallN.
> The patch as it stands fails silently if a polymorphic function is called
> from a call site that doesn't provide the needed info.)

Sounds good to me -- thanks for the quick feedback!

A question -- I was thinking that we will need to allow one array type
to be coerced into another in this brave new world (e.g.
ARRAY[1,2,3]::float8[] results in "ERROR:  Cannot cast type integer[] to
double precision[]"). My plan was to check the source and target
datatypes in can_coerce_type() and coerce_type() to see if both are
array types. If so, use the element types to determine whether we can
coerce, and loop over the array to coerce, etc.

There are a few issues that I can think of with this (and undoubtedly
you will have some others ;-)):
  - this would add at least one cache lookup to every call to
    can_coerce_type
  - if we pass by can_coerce_type with an array, we'd need to do the same
    cache lookup in coerce_type unless we modify the api of the related
    functions to pass along the "isarray" status of the datatype
  - currently the only reliable(?) method to determine if a
    datatype is a varlena array is to check for '_' as the first
    character of the type name -- it seems wrong to depend on that
    forever.

Any suggestions? I was toying with the idea that an isarray attribute
should be added to pg_type -- thoughts on that?

Joe


Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> Any suggestions? I was toying with the idea that an isarray attribute
> should be added to pg_type -- thoughts on that?

Don't think you need it to go in that direction: the property of having
nonzero typelem indicates that something is an array.

The real issue is whether we should add a link to let you find the array
type given the base type (without relying on the '_' naming convention).
I'm undecided about that...

            regards, tom lane


Re: array support patch phase 1 patch

From
Joe Conway
Date:
Tom Lane wrote:
> Joe Conway <mail@joeconway.com> writes:
>>Any suggestions? I was toying with the idea that an isarray attribute
>>should be added to pg_type -- thoughts on that?
>
> Don't think you need it to go in that direction: the property of having
> nonzero typelem indicates that something is an array.

But I don't think of the following as arrays, at least not in the same
sense of _text, etc.:

regression=# select typname from pg_type where typelem != 0 and typname
not like '\\_%';
   typname
------------
  name
  int2vector
  oidvector
  point
  lseg
  box
  line
(7 rows)

Joe


Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> Tom Lane wrote:
>> Don't think you need it to go in that direction: the property of having
>> nonzero typelem indicates that something is an array.

> But I don't think of the following as arrays, at least not in the same
> sense of _text, etc.:

If you want to reject fixed-length array types for this purpose, you can
also insist on typlen < 0.

            regards, tom lane


Re: array support patch phase 1 patch

From
Joe Conway
Date:
Tom Lane wrote:
> I looked over this patch a little bit, mostly at the anyarray/anyelement
> changes; I didn't study the ArrayExpr stuff (except to notice that yeah,
> the grammar change is very ugly; can't it be made independent of the
> number of levels?).
[...snip...]
> In short then, what about passing an Expr* link in FmgrInfo as a
> substitute for actualRetType?  (An additional advantage of this way
> is that it's obvious whether or not the information has been provided,
> which it would not be for, say, functions invoked via DirectFunctionCallN.
> The patch as it stands fails silently if a polymorphic function is called
> from a call site that doesn't provide the needed info.)

Here's a new patch that fixes the grammar ugliness (amazing how easily I
got that to work after a week of not looking at it!) and adds an
(Expr *) link in FmgrInfo. It passes all regression tests. If there are
no objections, please apply.

(as phase 1 -- see:
   http://archives.postgresql.org/pgsql-patches/2003-03/msg00191.php
  for what the patch covers and the planned "yet-to-come").

Thanks,

Joe

Attachment

Re: array support patch phase 1 patch

From
Joe Conway
Date:
Joe Conway wrote:
> Here's a new patch that fixes the grammar ugliness (amazing how easily I
> got that to work after a week of not looking at it!) and adds an
> (Expr *) link in FmgrInfo. It passes all regression tests. If there are
> no objections, please apply.
>

I just found a huge bogosity in this patch (ExecEvalArray)-- I must have
been tired, and my test cases were obviously too simple ;(

I'm working on a fix right now, but I guess I ought to add some good
test cases to the regression suite before submitting again. Please
retract this patch for the moment, and I'll send in the fix with
regression test adjustments in the next few days.

Sorry if I wasted anyone's time!

Joe


Re: array support patch phase 1 patch

From
Joe Conway
Date:
Joe Conway wrote:
> I just found a huge bogosity in this patch (ExecEvalArray)-- I must have
> been tired, and my test cases were obviously too simple ;(
>
> I'm working on a fix right now, but I guess I ought to add some good
> test cases to the regression suite before submitting again. Please
> retract this patch for the moment, and I'll send in the fix with
> regression test adjustments in the next few days.

Here's an updated patch that fixes the above mentioned issues. I also
added significantly more functionality.

It needs a good review, but everything seems to work, it passes all
regression tests**, and I think it is ready to apply (I needed to change
some of the regression tests, so someone please take a close look at
those too).

**except horology due to DST, and for some reason that I don't yet
understand, /expected/misc.out loses my changes every time I update from
CVS -- is misc.out autogenerated?

It covers the following:
----------------------------------------------------------------------
1. Support for polymorphic functions, accepting and returning ANYARRAY
    and ANYELEMENT datatypes that are "tied" to each other and resolved
    to an actual type at runtime. This also includes the ability to
    define aggregates using the polymorphic functions.

2. Array handling functions:
    - singleton_array(ANYELEMENT) returns ANYARRAY
    - array_append(ANYARRAY, ANYELEMENT) returns ANYARRAY
    - array_prepend(ANYELEMENT, ANYARRAY) returns ANYARRAY
    - array_accum(ANYARRAY, ANYELEMENT) returns ANYARRAY
    - array_assign(ANYARRAY, int, ANYELEMENT) returns ANYARRAY
    - array_subscript(ANYARRAY, int) returns ANYELEMENT
    - array_cat(ANYARRAY, ANYARRAY) returns ANYARRAY

3. Grammar and underlying support for the following (examples):

    create table foo(f1 integer ARRAY);
    create table foo(f1 integer ARRAY[]);
    create table foo(f1 integer ARRAY[x]);
    create table foo(f1 integer ARRAY[][]);   (and more [] etc)
    create table foo(f1 integer ARRAY[x][y]); (and more [] etc)

    select ARRAY[1,2,3];
    select ARRAY[[1,2,3],[4,5,6]];
    select ARRAY[ARRAY[1,2,3],ARRAY[4,5,6]];
    ...etc up to 6 dimensions

    select ARRAY[1,2] || 3;
    select 0 || ARRAY[1,2];
    select ARRAY[1,2] || ARRAY[3,4];
    select ARRAY[1,2] || ARRAY[[3,4],[5,6]];
    select ARRAY[[1,2],[3,4]] || ARRAY[5,6];
    select ARRAY(select oid from pg_class order by relname);

    select ARRAY[[1,2,3],[4,5,6]]::float8[];
    select ARRAY[[1,2,3],[4,5,6]]::text[];
    select CAST(ARRAY[[1,2,3],[4,5,6]] AS float8[]);

4. Duplicated contrib/array functionality (and then some) in the
    backend using polymorphic functions and operators. Also cleared
    out the datatype specific "=" operators. Now there is just one
    polymorphic one. If the patch is applied, I think contrib/array can
    be safely retired. Examples:

    SELECT ARRAY[1,2,3] *= 2 AS "TRUE";
    SELECT ARRAY[1,2,3] *<> 4 AS "TRUE";
    SELECT ARRAY[1,2,3] *< 2 AS "TRUE";
    SELECT ARRAY[1,2,3] *> 2 AS "TRUE";
    SELECT ARRAY[1,2,3] *<= 1 AS "TRUE";
    SELECT ARRAY[1,2,3] *>= 3 AS "TRUE";
    SELECT ARRAY['abc','def','qwerty'] *~ 'wer' AS "TRUE";
    SELECT ARRAY['abc','def','qwerty'] *!~ 'xyz' AS "TRUE";
    SELECT ARRAY['abc','def','qwerty'] *~~ '_wer%' AS "TRUE";
    SELECT 'hello' *~~ ARRAY['abc','def','_ell%'] AS "TRUE";
    SELECT ARRAY['abc','def','qwerty'] *!~~ '%xyz%' AS "TRUE";

5. Side note: I added ANYARRAY1 and ANYELEMENT1 in this version. I
    needed ANYARRAY1 for polymorphic array coercion, but did not add
    support for it to be used in any other way. I added ANYELEMENT1
    only for symmetry, and did not use it at all.

Still needs to be done (in roughly this order):
----------------------------------------------------------------------
1. Functions:
    - str_to_array(str TEXT, delim TEXT) returns TEXT[]
    - array_to_str(array ANYARRAY, delim TEXT) returns TEXT

2. Documentation update:
     Update "User's Guide"->"Data Types"->"Arrays" documentation
     create a new section: "User's Guide"->
                           "Functions and Operators"->
                           "Array Functions and Operators"


4. PL/pgSQL support for polymorphic types

5. SQL function support for polymorphic types.

6. Move as much of contrib/intarray into backend as makes sense,
    including migration to use polymorphic semantics (therefore make
    work on other than int where possible). Note: this may not happen
    for 7.4 as it looks to be fairly involved, at least at first glance.

7. Additional documentation and regression test updates

----------------------------------------------------------------------

Hopefully I haven't forgotten anything. If so, be gentle ;-)

Regards,

Joe

Attachment

Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> **except horology due to DST, and for some reason that I don't yet
> understand, /expected/misc.out loses my changes every time I update from
> CVS -- is misc.out autogenerated?

Yeah.  You need to back-patch src/test/regress/output/misc.source ...

            regards, tom lane


Re: array support patch phase 1 patch

From
Peter Eisentraut
Date:
Joe Conway writes:

>     select ARRAY[1,2,3];
>     select ARRAY[[1,2,3],[4,5,6]];
>     select ARRAY[ARRAY[1,2,3],ARRAY[4,5,6]];
>     ...etc up to 6 dimensions

Why only 6?

> 4. Duplicated contrib/array functionality (and then some) in the
>     backend using polymorphic functions and operators.

>     SELECT ARRAY[1,2,3] *= 2 AS "TRUE";
>     SELECT ARRAY[1,2,3] *<> 4 AS "TRUE";

Couldn't this kind of operation be handled more cleanly (at least
semantically speaking), if we provide a function that converts an array to
a set and then use standard set searching operations?  For example,

SELECT 2 IN TABLE(ARRAY[1,2,3]);

> 5. Side note: I added ANYARRAY1 and ANYELEMENT1 in this version.

Doing what?

--
Peter Eisentraut   peter_e@gmx.net


Re: array support patch phase 1 patch

From
Hannu Krosing
Date:
Joe Conway kirjutas E, 07.04.2003 kell 05:12:
> Joe Conway wrote:
> It covers the following:
> ----------------------------------------------------------------------
> 1. Support for polymorphic functions, accepting and returning ANYARRAY
>     and ANYELEMENT datatypes that are "tied" to each other and resolved
>     to an actual type at runtime. This also includes the ability to
>     define aggregates using the polymorphic functions.
>
> 2. Array handling functions:
>     - singleton_array(ANYELEMENT) returns ANYARRAY
>     - array_append(ANYARRAY, ANYELEMENT) returns ANYARRAY
>     - array_prepend(ANYELEMENT, ANYARRAY) returns ANYARRAY
>     - array_accum(ANYARRAY, ANYELEMENT) returns ANYARRAY
>     - array_assign(ANYARRAY, int, ANYELEMENT) returns ANYARRAY
>     - array_subscript(ANYARRAY, int) returns ANYELEMENT
>     - array_cat(ANYARRAY, ANYARRAY) returns ANYARRAY
>

How hard would it be to add

array_eq, array_ne, array_gt, array_le and corresponding operators

SELECT ARRAY[1,2,3] = ARRAY[1,2,3];  # --> TRUE
SELECT ARRAY[1,2,3] < ARRAY[1,2,3];  # --> FALSE
SELECT ARRAY[1,2,3] <= ARRAY[1,2,3];  # --> TRUE
SELECT ARRAY[1,2,3] > ARRAY[1,2,3];  # --> FALSE
SELECT ARRAY[1,2,3] >= ARRAY[1,2,3];  # --> TRUE

I'd assume them to behave like string comparisons, i.e shorter subarray
is smaller:

SELECT ARRAY[1,2] < ARRAY[1,2,3];  # --> FALSE

Support for sorting and b-tree indexing could be nice too.

> Still needs to be done (in roughly this order):
> ----------------------------------------------------------------------
> 1. Functions:
>     - str_to_array(str TEXT, delim TEXT) returns TEXT[]
>     - array_to_str(array ANYARRAY, delim TEXT) returns TEXT
>
> 2. Documentation update:
>      Update "User's Guide"->"Data Types"->"Arrays" documentation
>      create a new section: "User's Guide"->
>                            "Functions and Operators"->
>                            "Array Functions and Operators"
>
>
> 4. PL/pgSQL support for polymorphic types

Where should one start to add PL/Python support for polymorphic types ?

> 6. Move as much of contrib/intarray into backend as makes sense,
>     including migration to use polymorphic semantics (therefore make
>     work on other than int where possible). Note: this may not happen
>     for 7.4 as it looks to be fairly involved, at least at first glance.

What about moving contrib/intagg into backend ?

(And converting it into ANYagg on the way ;)

--------------------
Hannu


Re: array support patch phase 1 patch

From
Tom Lane
Date:
Peter Eisentraut <peter_e@gmx.net> writes:
> Joe Conway writes:
>> ...etc up to 6 dimensions

> Why only 6?

See uses of MAXDIM.  If you feel that 6 isn't enough, I wouldn't have
a problem with raising MAXDIM to 10 or so.  I don't think it's worth
trying to eliminate the limit completely; that would add palloc overhead
to every array operation, for a feature people are unlikely to use.

>> 4. Duplicated contrib/array functionality (and then some) in the
>> backend using polymorphic functions and operators.

> Couldn't this kind of operation be handled more cleanly (at least
> semantically speaking), if we provide a function that converts an array to
> a set and then use standard set searching operations?  For example,
> SELECT 2 IN TABLE(ARRAY[1,2,3]);

Not sure about that.  Is there any guidance in the SQL 200x spec about
what they expect people to actually *do* with the ARRAY[] syntax?

I'm currently working over the patch, but am not going to commit this
part (yet), since I have some problems with the implementation anyway.

>> 5. Side note: I added ANYARRAY1 and ANYELEMENT1 in this version.

> Doing what?

I'm hoping to avoid committing those, as they don't seem to be
completely implemented in this patch.  They may be required to
represent the behavior of array_coerce() though.  Not sure yet.

            regards, tom lane


Re: array support patch phase 1 patch

From
Joe Conway
Date:
Peter Eisentraut wrote:
> Joe Conway writes:
>>    ...etc up to 6 dimensions
>
> Why only 6?

As Tom mentioned MAXDIM is currently set to 6. I can't imagine many
people using anything over 3 or maybe 4 dimensions.

>>4. Duplicated contrib/array functionality (and then some) in the
>>    backend using polymorphic functions and operators.
>
>>    SELECT ARRAY[1,2,3] *= 2 AS "TRUE";
>>    SELECT ARRAY[1,2,3] *<> 4 AS "TRUE";
>
> Couldn't this kind of operation be handled more cleanly (at least
> semantically speaking), if we provide a function that converts an array to
> a set and then use standard set searching operations?  For example,
>
> SELECT 2 IN TABLE(ARRAY[1,2,3]);

I thought about that too. It wouldn't be general enough to handle other
operators though, so I decided to stick with the already somewhat
established contrib/array operators. It sounds like Tom has some
concerns with those anyway, so in the meantime I'll take another look at
SQL200x to see if this is covered somewhere.


>>5. Side note: I added ANYARRAY1 and ANYELEMENT1 in this version.
>
> Doing what?

As I said:
  " I needed ANYARRAY1 for polymorphic array coercion, but did not add
    support for it to be used in any other way. I added ANYELEMENT1
    only for symmetry, and did not use it at all."

The problem lies in that fact that more than one reference to ANYARRAY
in the argument list of a function implies that the said arrays are all
the same data type. This doesn't work for a coercion function where you
need two array arguments, of arbitrary types, but that are not the same
as each other. I could not see any other clean way to achieve this.

I specifically did not add any other support for these data types
because there is not yet a strong case that user defined functions need
this capability. Of course if there is a strong feeling that they should
have full fledged support similar to ANYARRAY and ANYELEMENT, I'll
gladly add it.

Joe


Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
>>> 5. Side note: I added ANYARRAY1 and ANYELEMENT1 in this version.
>>
>> Doing what?

> The problem lies in that fact that more than one reference to ANYARRAY
> in the argument list of a function implies that the said arrays are all
> the same data type. This doesn't work for a coercion function where you
> need two array arguments, of arbitrary types, but that are not the same
> as each other. I could not see any other clean way to achieve this.

What I'm currently thinking of is declaring array_coerce to take and
return ANYARRAY (which will make it a no-op if someone tries to invoke
it by hand).  The coercion code will have to force the output type to
the desired thing after building the FuncExpr node.  But the other way
needs special-case code in parse_coerce too, and it doesn't do something
reasonable if array_coerce is invoked by hand.

            regards, tom lane


Re: array support patch phase 1 patch

From
Joe Conway
Date:
Hannu Krosing wrote:
> How hard would it be to add
>
> array_eq, array_ne, array_gt, array_le and corresponding operators
>
> SELECT ARRAY[1,2,3] = ARRAY[1,2,3];  # --> TRUE
> SELECT ARRAY[1,2,3] < ARRAY[1,2,3];  # --> FALSE
> SELECT ARRAY[1,2,3] <= ARRAY[1,2,3];  # --> TRUE
> SELECT ARRAY[1,2,3] > ARRAY[1,2,3];  # --> FALSE
> SELECT ARRAY[1,2,3] >= ARRAY[1,2,3];  # --> TRUE
>
> I'd assume them to behave like string comparisons, i.e shorter subarray
> is smaller:
>
> SELECT ARRAY[1,2] < ARRAY[1,2,3];  # --> FALSE
>
> Support for sorting and b-tree indexing could be nice too.

I thought briefly about this, but it wasn't immediately clear what the
semantics ought to be in all cases. I've also spent literally all my
available "hacking" time for the last several weeks just to get to the
patch submitted. I'd like to see at least some of it committed before I
take on anything new ;-)

If you want to propose in detail how these would behave - including:
   - different length arrays
   - different dimension arrays
   - is comparison by ordinal position, or is each element compared to
     all elements of the other side, e.g. is
     (ARRAY[1,2,3] < ARRAY[2,3,4]) TRUE or FALSE?
     If you compare by ordinal position TRUE, but in the latter case
     FALSE

> Where should one start to add PL/Python support for polymorphic types ?

Not entirely sure. In plperl, pltcl, and plr there is a section of code
in the compile function that looks something like:

/* Disallow pseudotype result, except VOID */
if (typeStruct->typtype == 'p')
{
     if (procStruct->prorettype == VOIDOID)
       /* okay */ ;
     else if (procStruct->prorettype == TRIGGEROID)
     {
       free(prodesc->proname);
       free(prodesc);
       elog(ERROR, "plperl functions cannot return type %s"
         "\n\texcept when used as triggers",
                  format_type_be(procStruct->prorettype));

And something similar for arguments. But I don't at quick glance see
something like this in plpython.

This needs to be changed to allow types ANYARRAYOID and ANYELEMENTOID.
Then you need to do the right thing with the arguments/return type. For
example, from plr:

#define GET_PROARGS(pronargs_, proargtypes_) \
do { \
   int i; \
   pronargs_ = procStruct->pronargs; \
   for (i = 0; i < pronargs_; i++) \
   { \
     if (procStruct->proargtypes[i] == ANYARRAYOID || \
       procStruct->proargtypes[i] == ANYELEMENTOID) \
       { \
         proargtypes_[i] = get_expr_argtype(fcinfo, i); \
         if (proargtypes_[i] == InvalidOid) \
           proargtypes_[i] = procStruct->proargtypes[i]; \
       } \
       else \
         proargtypes_[i] = procStruct->proargtypes[i]; \
       } \
} while (0)

This grabs the parser determined runtime datatype from the expression
node that has been added to FmgrInfo.

Last thing I can think of is an important safety tip. If plpython caches
the compiled function, you need to take care to invalidate it if the
return or argument types change from call to call.

> What about moving contrib/intagg into backend ?
> (And converting it into ANYagg on the way ;)
>

As I said earlier, all in good time ;-) One question about that though
-- how is intagg different from array_accum? (which is already
polymorphic and in the submitted patch)

Joe


Re: array support patch phase 1 patch

From
Hannu Krosing
Date:
Joe Conway kirjutas T, 08.04.2003 kell 19:55:
> Hannu Krosing wrote:
> > SELECT ARRAY[1,2] < ARRAY[1,2,3];  # --> FALSE
> >
> > Support for sorting and b-tree indexing could be nice too.
>
> I thought briefly about this, but it wasn't immediately clear what the
> semantics ought to be in all cases. I've also spent literally all my
> available "hacking" time for the last several weeks just to get to the
> patch submitted. I'd like to see at least some of it committed before I
> take on anything new ;-)
>
> If you want to propose in detail how these would behave - including:

I like the "compare like strings" approach

>    - different length arrays

shorter is smaller if all elements are same

>    - different dimension arrays

not comparable, just as array elemant is not comparable with array

>    - is comparison by ordinal position, or is each element compared to
>      all elements of the other side,

neither, pairs of elements at same positions are compared until first !=
or one array ends, then the shorter array the array with smaller element
value is considered smaller.

>  e.g. is
>      (ARRAY[1,2,3] < ARRAY[2,3,4]) TRUE or FALSE?
>      If you compare by ordinal position TRUE, but in the latter case
>      FALSE

all the following should also be TRUE

(ARRAY[1,2,3] < ARRAY[2,1,1])
(ARRAY[1,2,3] < ARRAY[1,2,3,4])
(ARRAY[] < ARRAY[1,2,3,4])
(ARRAY[1,2,3,4] < ARRAY[1,2,4,3])


> > Where should one start to add PL/Python support for polymorphic types ?
>
> Not entirely sure. In plperl, pltcl, and plr there is a section of code
> in the compile function that looks something like:
>
> ....

Thanks, I'll take a look.

> > What about moving contrib/intagg into backend ?
> > (And converting it into ANYagg on the way ;)
> >
>
> As I said earlier, all in good time ;-) One question about that though
> -- how is intagg different from array_accum? (which is already
> polymorphic and in the submitted patch)

Sorry, didn't notice array_accum.

intagg has functions for doing it both ways (array->table and table ->
array).

-----------------
Hannu


Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> Here's an updated patch that fixes the above mentioned issues. I also
> added significantly more functionality.

I've applied much but not all of this.  Some comments:

I didn't apply any of the aggregate changes, because I'm unsure of how
we actually want to do that.  AFAICT you've set it up so that the
polymorphism of the transition function is hidden within the aggregate,
and one must still create a separate aggregate for each input datatype.
Couldn't we fix things so that a polymorphic transition function leads
to a polymorphic aggregate?  I'm not totally sure that makes sense, but
it seems worth thinking about.

Also I didn't put in the bool_op stuff.  That seemed pretty messy; in
particular I didn't care for looking at the operator names to decide
what to do.  Another problem is that the actual lookup of the scalar
operators would be schema search path dependent.  I'd feel more
comfortable with something that created a tighter binding of the array
operators to the underlying scalar operators.  Not sure how to do it,
though.

There are a number of remaining annoyances in array type assignment and
coercion.  Here's one:

create table t1 (p point[]);
insert into t1 values(array['(3,4)','(4,5)','(6,7)']);
ERROR:  column "p" is of type point[] but expression is of type text[]
        You will need to rewrite or cast the expression
Instead you have to write
insert into t1 values(array['(3,4)'::point,'(4,5)','(6,7)']);

I'm not sure if there's any way around this, but it certainly reduces the
usefulness of untyped literals.  Right offhand it seems like maybe we could
invent an UNKNOWNARRAY type so as to postpone the decision about what the
array's type should be --- but I'm worried about possible side-effects.
I think there are places where the existence of "unknown[]" might allow
the type resolution logic to follow paths we don't want it to follow.

I think there are a lot of places where binary-compatible element types
and domain element types will not be treated reasonably.  We need to think
about where and how to handle that.

Another problem is:

regression=# select distinct array(select * from text_tbl) from foo;
ERROR:  Unable to identify an equality operator for type text[]

This DISTINCT query would fail anyway of course, for lack of a '<'
operator for arrays, but it seems like we ought to be able to find the
polymorphic '=' operator.

We really ought to reimplement array_eq to be less bogus: instead of bit
equality it ought to be applying the element type's equality operator.

I rearranged the way that parse_coerce and function/operator overloading
resolution handle arrays.  It seems cleaner to me, but take a look and
see what you think.

One thing I didn't like about the implementation of many of these functions
is that they were willing to repeat catalog lookups on every call.  I fixed
array_type_coerce to cache lookup results, please see if you can apply the
technique elsewhere.

The selectivity estimation functions you'd assigned to the bool_ops operators
seemed like pretty bogus choices.  I am not sure we can use scalar estimators
for these at all --- perhaps a new set of estimators needs to be written.

>     create table foo(f1 integer ARRAY);
>     create table foo(f1 integer ARRAY[]);
>     create table foo(f1 integer ARRAY[x]);
>     create table foo(f1 integer ARRAY[][]);   (and more [] etc)
>     create table foo(f1 integer ARRAY[x][y]); (and more [] etc)

AFAICT, SQL99 specifies the syntax "typename ARRAY [ intconst ]" and
nothing else.  I do not see a reason to accept ARRAY without [],
ARRAY with empty [], ARRAY with multiple [], etc if the spec doesn't
make us do so.  The existing Postgres syntax without the word ARRAY
gets the job done just fine.

            regards, tom lane


Re: array support patch phase 1 patch

From
Joe Conway
Date:
Tom Lane wrote:
> I've applied much but not all of this.  Some comments:

Thanks for the review and cleanup!

> I didn't apply any of the aggregate changes, because I'm unsure of how
> we actually want to do that.  AFAICT you've set it up so that the
> polymorphism of the transition function is hidden within the aggregate,
> and one must still create a separate aggregate for each input datatype.
> Couldn't we fix things so that a polymorphic transition function leads
> to a polymorphic aggregate?  I'm not totally sure that makes sense, but
> it seems worth thinking about.

I went back and forth on that issue myself more than once while I was
working on this. I'll take another look at making aggregate polymorphic
as well.

> Also I didn't put in the bool_op stuff.  That seemed pretty messy; in
> particular I didn't care for looking at the operator names to decide
> what to do.  Another problem is that the actual lookup of the scalar
> operators would be schema search path dependent.  I'd feel more
> comfortable with something that created a tighter binding of the array
> operators to the underlying scalar operators.  Not sure how to do it,
> though.

But the lookup would be schema search path dependent if we were given
two scalars, so I don't see this as any different. Would it be better to
use the same operators as the scalars ("=", "<>", ...etc)? It makes
sense to me that "array = element" should apply the "=" operator for the
element data type, across all of the array elements. Maybe this takes us
back to Peter's suggestion:
   expression IN (array)
   expression NOT IN (array)
   expression operator ANY (array)
   expression operator SOME (array)
   (expression) operator (array)
   (expression) operator ALL (array)

In this case operator is taken as the appropriate operator for the
expression type as both left and right arguments, and array is treated
the same as a one column subquery.

> There are a number of remaining annoyances in array type assignment and
> coercion.  Here's one:
>
> create table t1 (p point[]);
> insert into t1 values(array['(3,4)','(4,5)','(6,7)']);
> ERROR:  column "p" is of type point[] but expression is of type text[]
>         You will need to rewrite or cast the expression
> Instead you have to write
> insert into t1 values(array['(3,4)'::point,'(4,5)','(6,7)']);
>
> I'm not sure if there's any way around this, but it certainly reduces the
> usefulness of untyped literals.  Right offhand it seems like maybe we could
> invent an UNKNOWNARRAY type so as to postpone the decision about what the
> array's type should be --- but I'm worried about possible side-effects.
> I think there are places where the existence of "unknown[]" might allow
> the type resolution logic to follow paths we don't want it to follow.
>
> I think there are a lot of places where binary-compatible element types
> and domain element types will not be treated reasonably.  We need to think
> about where and how to handle that.

Yeah, I think this is what led me to the hack in parse_coerce that you
didn't like ;-), but I'm not sure how to better handle it. I'll think a
bit more on it.

>
> Another problem is:
>
> regression=# select distinct array(select * from text_tbl) from foo;
> ERROR:  Unable to identify an equality operator for type text[]
>
> This DISTINCT query would fail anyway of course, for lack of a '<'
> operator for arrays, but it seems like we ought to be able to find the
> polymorphic '=' operator.
>
> We really ought to reimplement array_eq to be less bogus: instead of bit
> equality it ought to be applying the element type's equality operator.

OK. I'll look at these issues again. Should I also look to implement:
   array <> array
   array > array
   array < array
   array >= array
   array <= array

as Hannu suggested?


> I rearranged the way that parse_coerce and function/operator overloading
> resolution handle arrays.  It seems cleaner to me, but take a look and
> see what you think.

OK.


> One thing I didn't like about the implementation of many of these functions
> is that they were willing to repeat catalog lookups on every call.  I fixed
> array_type_coerce to cache lookup results, please see if you can apply the
> technique elsewhere.

OK.

> The selectivity estimation functions you'd assigned to the bool_ops operators
> seemed like pretty bogus choices.  I am not sure we can use scalar estimators
> for these at all --- perhaps a new set of estimators needs to be written.

I figured you wouldn't like that, but I was at a loss as to how to
approach something better. I'll take a shot at writing new estimators
based on the scalar ones.


> AFAICT, SQL99 specifies the syntax "typename ARRAY [ intconst ]" and
> nothing else.  I do not see a reason to accept ARRAY without [],
> ARRAY with empty [], ARRAY with multiple [], etc if the spec doesn't
> make us do so.  The existing Postgres syntax without the word ARRAY
> gets the job done just fine.

I had in my notes this (I think from SQL200x):

6.1 <data type>
<collection type> ::= <array type> | <multiset type>
<array type> ::=
   <data type> ARRAY [ <left bracket or trigraph>
                       <unsigned integer>
                       <right bracket or trigraph> ]

So I thought
   typename ARRAY [ intconst ]
   typename ARRAY
were both per spec. As far as "typename ARRAY [ ]", ARRAY with multiple
[], etc goes, I only included them for consistency, but I don't care if
we leave them out either.

As always, a thorough review and a lot to think about! Thanks for the
help. It will be at least a couple days before I can pick this up again
(my development system lost a cpu fan yesterday, so I'm taking this as
an opportunity to upgrade ;-)), but I'll get on it as soon as I can.

Joe


Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> Tom Lane wrote:
>> Also I didn't put in the bool_op stuff.  That seemed pretty messy; in
>> particular I didn't care for looking at the operator names to decide
>> what to do.  Another problem is that the actual lookup of the scalar
>> operators would be schema search path dependent.  I'd feel more
>> comfortable with something that created a tighter binding of the array
>> operators to the underlying scalar operators.  Not sure how to do it,
>> though.

> But the lookup would be schema search path dependent if we were given
> two scalars, so I don't see this as any different.

But it is different, because the lookup happens at run time not at parse
time.  In particular consider rules or views that might be executed with
current search paths completely unrelated to what was used when they
were defined.  You've now got a situation where the rule/view author
does not have control of what code is being executed on his behalf.
Not good.

(Come to think of it, much the same complaint might be laid for PL and
SQL functions; maybe it'd be better to associate a schema search path
with a function when it's created?)

Perhaps we could insist that the relevant scalar operators be found in
the same schema the array operator lives in?  Safe but restrictive...

> Would it be better to
> use the same operators as the scalars ("=", "<>", ...etc)? It makes
> sense to me that "array = element" should apply the "=" operator for the
> element data type, across all of the array elements.

It seems a little surprising to me.  "IN" and "=" mean quite different
things, and I'd not expect them to be represented by the same operator name.

> Maybe this takes us
> back to Peter's suggestion:
>    expression IN (array)
>    expression NOT IN (array)
>    expression operator ANY (array)
>    expression operator SOME (array)
>    (expression) operator (array)
>    (expression) operator ALL (array)

There's a lot to be said for that, if we can think of a way to do it.
I believe this way would force us to integrate the operations into the
parser, though, rather than just throwing a few polymorphic functions
at the problem.  It's probably a lot more work :-(

> OK. I'll look at these issues again. Should I also look to implement:
>    array <> array
>    array > array
>    array < array
>    array >= array
>    array <= array

> as Hannu suggested?

Not sure about that.  A point that should have been made in that
conversation is that SQL already defines "row comparison" operations
that are supposed to act in much the same fashion Hannu suggested.
We don't currently implement those correctly, but we should.

Consider also that the minute we have the above functions, people will
expect to be able to build indexes on array columns and sort by array
columns.  The bool_ops code as it stands can't effectively support that,
because these contexts do not have expression parsetrees hanging around
to pass to fn_expr.

            regards, tom lane


Re: array support patch phase 1 patch

From
Joe Conway
Date:
Tom Lane wrote:
> I've applied much but not all of this.  Some comments:

Here (finally!) is a doc patch to go with the previously applied array
support changes. If there are no objections, please apply.

Thanks,

Joe
Index: doc/src/sgml/array.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
retrieving revision 1.25
diff -c -r1.25 array.sgml
*** doc/src/sgml/array.sgml    13 Mar 2003 01:30:26 -0000    1.25
--- doc/src/sgml/array.sgml    4 May 2003 05:14:04 -0000
***************
*** 9,15 ****

   <para>
    <productname>PostgreSQL</productname> allows columns of a table to be
!   defined as variable-length multidimensional arrays. Arrays of any
    built-in type or user-defined type can be created.
   </para>

--- 9,15 ----

   <para>
    <productname>PostgreSQL</productname> allows columns of a table to be
!   defined as variable-length multi-dimensional arrays. Arrays of any
    built-in type or user-defined type can be created.
   </para>

***************
*** 60,73 ****
  </programlisting>
   </para>

   <note>
    <para>
!    A limitation of the present array implementation is that individual
!    elements of an array cannot be SQL null values.  The entire array can be set
!    to null, but you can't have an array with some elements null and some
!    not.  Fixing this is on the to-do list.
    </para>
   </note>
   </sect2>

   <sect2>
--- 60,133 ----
  </programlisting>
   </para>

+  <para>
+   A limitation of the present array implementation is that individual
+   elements of an array cannot be SQL null values.  The entire array can be set
+   to null, but you can't have an array with some elements null and some
+   not.
+  </para>
+  <para>
+   This can lead to surprising results. For example, the result of the
+   previous two inserts looks like this:
+ <programlisting>
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |      schedule
+ -------+---------------------------+--------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting},{""}}
+  Carol | {20000,25000,25000,25000} | {{talk},{meeting}}
+ (2 rows)
+ </programlisting>
+   Because the <literal>[2][2]</literal> element of
+   <structfield>schedule</structfield> is missing in each of the
+   <command>INSERT</command> statements, the <literal>[1][2]</literal>
+   element is discarded.
+  </para>
+
+  <note>
+   <para>
+    Fixing this is on the to-do list.
+   </para>
+  </note>
+
+  <para>
+   The <command>ARRAY</command> expression syntax may also be used:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Bill',
+     ARRAY[10000, 10000, 10000, 10000],
+     ARRAY[['meeting', 'lunch'], ['','']]);
+
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting', '']]);
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |           schedule
+ -------+---------------------------+-------------------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}}
+  Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}}
+ (2 rows)
+ </programlisting>
+   Note that with this syntax, multi-dimesion arrays must have matching
+   extents for each dimension. This eliminates the missing-array-elements
+   problem above. For example:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting']]);
+ ERROR:  Multiple dimension arrays must have array expressions with matching dimensions
+ </programlisting>
+   Also notice that string literals are single quoted instead of double quoted.
+  </para>
+
   <note>
    <para>
!    The examples in the rest of this section are based on the
!    <command>ARRAY</command> expression syntax <command>INSERT</command>s.
    </para>
   </note>
+
   </sect2>

   <sect2>
***************
*** 132,142 ****
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the
!   form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified.
   </para>

   <para>
--- 192,221 ----
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified; another example follows:
! <programlisting>
! SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
!          schedule
! ---------------------------
!  {{meeting,lunch},{"",""}}
! (1 row)
! </programlisting>
!  </para>
!
!  <para>
!   Additionally, we can also access a single arbitrary array element of
!   a one-dimensional array with the <function>array_subscript</function>
!   function:
! <programlisting>
! SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
!  array_subscript
! -----------------
!            10000
! (1 row)
! </programlisting>
   </para>

   <para>
***************
*** 147,153 ****
      WHERE name = 'Carol';
  </programlisting>

!   or updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
--- 226,248 ----
      WHERE name = 'Carol';
  </programlisting>

!   or using the <command>ARRAY</command> expression syntax:
!
! <programlisting>
! UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
!     WHERE name = 'Carol';
! </programlisting>
!
!   <note>
!    <para>
!     Anywhere you can use the <quote>curly braces</quote> array syntax,
!     you can also use the <command>ARRAY</command> expression syntax. The
!     remainder of this section will illustrate only one or the other, but
!     not both.
!    </para>
!   </note>
!
!   An array may also be updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
***************
*** 160,165 ****
--- 255,268 ----
  UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
      WHERE name = 'Carol';
  </programlisting>
+
+   A one-dimensional array may also be updated with the
+   <function>array_assign</function> function:
+
+ <programlisting>
+ UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000)
+     WHERE name = 'Bill';
+ </programListing>
   </para>

   <para>
***************
*** 169,175 ****
    value currently has 4 elements, it will have five elements after an
    update that assigns to <literal>array[5]</>.  Currently, enlargement in
    this fashion is only allowed for one-dimensional arrays, not
!   multidimensional arrays.
   </para>

   <para>
--- 272,278 ----
    value currently has 4 elements, it will have five elements after an
    update that assigns to <literal>array[5]</>.  Currently, enlargement in
    this fashion is only allowed for one-dimensional arrays, not
!   multi-dimensional arrays.
   </para>

   <para>
***************
*** 179,184 ****
--- 282,367 ----
   </para>

   <para>
+   An array can also be enlarged by using the functions
+   <function>array_prepend</function>, <function>array_append</function>,
+   or <function>array_cat</function>. The first two only support one-dimensional
+   arrays, but <function>array_cat</function> supports multi-dimensional arrays.
+   Some examples:
+
+ <programlisting>
+ SELECT array_prepend(1, ARRAY[2,3]);
+  array_prepend
+ ---------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_append(ARRAY[1,2], 3);
+  array_append
+ --------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
+    array_cat
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
+       array_cat
+ ---------------------
+  {{1,2},{3,4},{5,6}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
+       array_cat
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ </programlisting>
+
+   <function>array_prepend</function> and <function>array_append</function>
+   work with a one-dimensional array and a single element to be pushed on
+   to the beginning or end of the array, respectively. The array is extended
+   in the direction of the push. Hence, by pushing onto the beginning of an
+   array with a one-based subscript, a zero-based subscript array is created:
+
+ <programlisting>
+ SELECT array_dims(t.f) FROM (SELECT array_prepend(1, ARRAY[2,3]) AS f) AS t;
+  array_dims
+ ------------
+  [0:2]
+ (1 row)
+ </programlisting>
+
+   <function>array_cat</function> works with either two
+   <replaceable>n</>-dimension arrays, or an <replaceable>n</>-dimension
+   and an <replaceable>n+1</> dimension array. In the former case, the two
+   <replaceable>n</>-dimension arrays become outer elements of an
+   <replaceable>n+1</> dimension array. In the latter, the
+   <replaceable>n</>-dimension array is added as either the first or last
+   outer element of the <replaceable>n+1</> dimension array.
+  </para>
+
+  <para>
+   A final method of enlarging arrays is through the concatenation operator,
+   <command>||</command>, which works exactly as <function>array_cat</function>
+   does.
+ <programlisting>
+ SELECT ARRAY[1,2] || ARRAY[3,4];
+    ?column?
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
+       ?column?
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ (1 row)
+ </programlisting>
+  </para>
+
+  <para>
    The syntax for <command>CREATE TABLE</command> allows fixed-length
    arrays to be defined:

***************
*** 194,199 ****
--- 377,392 ----
   </para>

   <para>
+   An alternative syntax for one-dimensional arrays may be used.
+   <structfield>pay_by_quarter</structfield> could have been defined as:
+ <programlisting>
+     pay_by_quarter  integer ARRAY[4],
+ </programlisting>
+   This syntax may <emphasis>only</emphasis> be used with the integer
+   constant to denote the array size.
+  </para>
+
+  <para>
    Actually, the current implementation does not enforce the declared
    number of dimensions either.  Arrays of a particular element type are
    all considered to be of the same type, regardless of size or number
***************
*** 292,298 ****
     for the array's element type.  (Among the standard data types provided
     in the <productname>PostgreSQL</productname> distribution, type
     <literal>box</> uses a semicolon (<literal>;</>) but all the others
!    use comma.)  In a multidimensional array, each dimension (row, plane,
     cube, etc.) gets its own level of curly braces, and delimiters
     must be written between adjacent curly-braced entities of the same level.
     You may write whitespace before a left brace, after a right
--- 485,491 ----
     for the array's element type.  (Among the standard data types provided
     in the <productname>PostgreSQL</productname> distribution, type
     <literal>box</> uses a semicolon (<literal>;</>) but all the others
!    use comma.)  In a multi-dimensional array, each dimension (row, plane,
     cube, etc.) gets its own level of curly braces, and delimiters
     must be written between adjacent curly-braced entities of the same level.
     You may write whitespace before a left brace, after a right
***************
*** 300,305 ****
--- 493,564 ----
     is not ignored, however: after skipping leading whitespace, everything
     up to the next right brace or delimiter is taken as the item value.
    </para>
+
+   <para>
+    As illustrated earlier in this chapter, arrays may also be represented
+    using the <command>ARRAY</command> expression syntax. This representation
+    of an array value consists of items that are interpreted according to the
+    I/O conversion rules for the array's element type, plus decoration that
+    indicates the array structure. The decoration consists of the keyword
+    <command>ARRAY</command> and square brackets (<literal>[</> and
+    <literal>]</>) around the array values, plus delimiter characters between
+    adjacent items. The delimiter character is always a comma (<literal>,</>).
+    When representing multi-dimensional arrays, the keyword
+    <command>ARRAY</command> is only necessary for the outer level. For example,
+    <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as:
+ <programlisting>
+ SELECT ARRAY[['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   or it also could be written as:
+ <programlisting>
+ SELECT ARRAY[ARRAY['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   </para>
+
+   <para>
+    A final method to represent an array, is through an
+    <command>ARRAY</command> sub-select expression. For example:
+ <programlisting>
+ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
+                           ?column?
+ -------------------------------------------------------------
+  {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
+ (1 row)
+ </programlisting>
+   The sub-select may <emphasis>only</emphasis> return a single column. The
+   resulting one-dimensional array will have an element for each row in the
+   sub-select result, with an element type matching that of the sub-select's
+   target column.
+   </para>
+
+   <para>
+    Arrays may be cast from one type to another in similar fashion to other
+    data types:
+
+ <programlisting>
+ SELECT ARRAY[1,2,3]::oid[];
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+
+ SELECT CAST(ARRAY[1,2,3] AS float8[]);
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+ </programlisting>
+
+   </para>
+
   </sect2>

   <sect2>
***************
*** 316,321 ****
--- 575,588 ----
     Alternatively, you can use backslash-escaping to protect all data characters
     that would otherwise be taken as array syntax or ignorable white space.
    </para>
+
+  <note>
+   <para>
+    The discussion in the preceding paragraph with respect to double quoting does
+    not pertain to the <command>ARRAY</command> expression syntax. In that case,
+    each element is quoted exactly as any other literal value of the element type.
+   </para>
+  </note>

    <para>
     The array output routine will put double quotes around element values
Index: doc/src/sgml/func.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v
retrieving revision 1.153
diff -c -r1.153 func.sgml
*** doc/src/sgml/func.sgml    1 May 2003 00:57:05 -0000    1.153
--- doc/src/sgml/func.sgml    5 May 2003 00:42:26 -0000
***************
*** 6962,6967 ****
--- 6962,7194 ----

    </sect1>

+  <sect1 id="functions-array">
+   <title>Array Functions</title>
+
+   <para>
+    <xref linkend="array-operators-table"> shows the operators
+    available for the <type>array</type> types.
+   </para>
+
+     <table id="array-operators-table">
+      <title><type>array</type> Operators</title>
+      <tgroup cols="4">
+       <thead>
+        <row>
+     <entry>Operator</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry> <literal>=</literal> </entry>
+     <entry>equals</entry>
+     <entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry>
+     <entry><literal>t</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>element-to-array concatenation</entry>
+     <entry><literal>3 || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{3,4,5,6}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-element concatenation</entry>
+     <entry><literal>ARRAY[4,5,6] || 7</literal></entry>
+     <entry><literal>{4,5,6,7}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+
+   <para>
+    <xref linkend="array-functions-table"> shows the functions
+    available for use with array types. See <xref linkend="arrays">
+    for more discussion and examples for the use of these functions.
+   </para>
+
+     <table id="array-functions-table">
+      <title><type>array</type> Functions</title>
+      <tgroup cols="5">
+       <thead>
+        <row>
+     <entry>Function</entry>
+     <entry>Return Type</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry>
+      <literal>
+       <function>array_accum</function>
+       (<type>anyarray</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the end of an array, ignoring
+      <literal>NULL</literal> elements, and creating an array if needed
+     </entry>
+     <entry><literal>array_accum(null, 1)</literal></entry>
+     <entry><literal>{1}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_append</function>
+       (<type>anyarray</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the end of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_append(ARRAY[1,2], 3)</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_assign</function>
+       (<type>anyarray</type>, <type>integer</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      assign a value to a specific array element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_assign(ARRAY[1,2,3], 2, 99)</literal></entry>
+     <entry><literal>{1,99,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_cat</function>
+       (<type>anyarray</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      concatenate two arrays, returning <literal>NULL</literal>
+      for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_dims</function>
+       (<type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      returns a text representation of array dimension lower and upper bounds,
+      generating an ERROR for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry>
+     <entry><literal>[1:2][1:3]</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_lower</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns lower bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry>
+     <entry><literal>0</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_prepend</function>
+       (<type>anyelement</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the beginning of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_subscript</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>anyelement</type></entry>
+     <entry>
+      returns requested array element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_subscript(ARRAY[1,2,3], 3)</literal></entry>
+     <entry><literal>3</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_upper</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns upper bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry>
+     <entry><literal>4</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>singleton_array</function>
+       (<type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      create an array from the provided element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>singleton_array(1)</literal></entry>
+     <entry><literal>{1}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+   </sect1>

   <sect1 id="functions-aggregate">
    <title>Aggregate Functions</title>

Re: array support patch phase 1 patch

From
Joe Conway
Date:
Joe Conway wrote:
> Tom Lane wrote:
>> I've applied much but not all of this.  Some comments:
>
> Here (finally!) is a doc patch to go with the previously applied array
> support changes. If there are no objections, please apply.

Attached is an updated version of the doc patch along with the addition
of two new functions, as previously discussed:

    - str_to_array(str TEXT, delim TEXT) returns TEXT[]
    - array_to_str(array ANYARRAY, delim TEXT) returns TEXT

The patch is fairly large because:

    1. the documentation part of it is large
    2. I moved some static functions around and made them accessible so
       that I could use them in implementing the two functions.

It passes all regression tests, and as mentioned above includes
documentation.

If there are no objections, please apply.

Thanks,

Joe
Index: doc/src/sgml/array.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
retrieving revision 1.25
diff -c -r1.25 array.sgml
*** doc/src/sgml/array.sgml    13 Mar 2003 01:30:26 -0000    1.25
--- doc/src/sgml/array.sgml    21 May 2003 19:39:51 -0000
***************
*** 9,15 ****

   <para>
    <productname>PostgreSQL</productname> allows columns of a table to be
!   defined as variable-length multidimensional arrays. Arrays of any
    built-in type or user-defined type can be created.
   </para>

--- 9,15 ----

   <para>
    <productname>PostgreSQL</productname> allows columns of a table to be
!   defined as variable-length multi-dimensional arrays. Arrays of any
    built-in type or user-defined type can be created.
   </para>

***************
*** 60,73 ****
  </programlisting>
   </para>

   <note>
    <para>
!    A limitation of the present array implementation is that individual
!    elements of an array cannot be SQL null values.  The entire array can be set
!    to null, but you can't have an array with some elements null and some
!    not.  Fixing this is on the to-do list.
    </para>
   </note>
   </sect2>

   <sect2>
--- 60,133 ----
  </programlisting>
   </para>

+  <para>
+   A limitation of the present array implementation is that individual
+   elements of an array cannot be SQL null values.  The entire array can be set
+   to null, but you can't have an array with some elements null and some
+   not.
+  </para>
+  <para>
+   This can lead to surprising results. For example, the result of the
+   previous two inserts looks like this:
+ <programlisting>
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |      schedule
+ -------+---------------------------+--------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting},{""}}
+  Carol | {20000,25000,25000,25000} | {{talk},{meeting}}
+ (2 rows)
+ </programlisting>
+   Because the <literal>[2][2]</literal> element of
+   <structfield>schedule</structfield> is missing in each of the
+   <command>INSERT</command> statements, the <literal>[1][2]</literal>
+   element is discarded.
+  </para>
+
+  <note>
+   <para>
+    Fixing this is on the to-do list.
+   </para>
+  </note>
+
+  <para>
+   The <command>ARRAY</command> expression syntax may also be used:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Bill',
+     ARRAY[10000, 10000, 10000, 10000],
+     ARRAY[['meeting', 'lunch'], ['','']]);
+
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting', '']]);
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |           schedule
+ -------+---------------------------+-------------------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}}
+  Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}}
+ (2 rows)
+ </programlisting>
+   Note that with this syntax, multi-dimesion arrays must have matching
+   extents for each dimension. This eliminates the missing-array-elements
+   problem above. For example:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting']]);
+ ERROR:  Multiple dimension arrays must have array expressions with matching dimensions
+ </programlisting>
+   Also notice that string literals are single quoted instead of double quoted.
+  </para>
+
   <note>
    <para>
!    The examples in the rest of this section are based on the
!    <command>ARRAY</command> expression syntax <command>INSERT</command>s.
    </para>
   </note>
+
   </sect2>

   <sect2>
***************
*** 132,142 ****
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the
!   form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified.
   </para>

   <para>
--- 192,221 ----
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified; another example follows:
! <programlisting>
! SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
!          schedule
! ---------------------------
!  {{meeting,lunch},{"",""}}
! (1 row)
! </programlisting>
!  </para>
!
!  <para>
!   Additionally, we can also access a single arbitrary array element of
!   a one-dimensional array with the <function>array_subscript</function>
!   function:
! <programlisting>
! SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
!  array_subscript
! -----------------
!            10000
! (1 row)
! </programlisting>
   </para>

   <para>
***************
*** 147,153 ****
      WHERE name = 'Carol';
  </programlisting>

!   or updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
--- 226,248 ----
      WHERE name = 'Carol';
  </programlisting>

!   or using the <command>ARRAY</command> expression syntax:
!
! <programlisting>
! UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
!     WHERE name = 'Carol';
! </programlisting>
!
!   <note>
!    <para>
!     Anywhere you can use the <quote>curly braces</quote> array syntax,
!     you can also use the <command>ARRAY</command> expression syntax. The
!     remainder of this section will illustrate only one or the other, but
!     not both.
!    </para>
!   </note>
!
!   An array may also be updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
***************
*** 160,165 ****
--- 255,268 ----
  UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
      WHERE name = 'Carol';
  </programlisting>
+
+   A one-dimensional array may also be updated with the
+   <function>array_assign</function> function:
+
+ <programlisting>
+ UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000)
+     WHERE name = 'Bill';
+ </programListing>
   </para>

   <para>
***************
*** 169,175 ****
    value currently has 4 elements, it will have five elements after an
    update that assigns to <literal>array[5]</>.  Currently, enlargement in
    this fashion is only allowed for one-dimensional arrays, not
!   multidimensional arrays.
   </para>

   <para>
--- 272,278 ----
    value currently has 4 elements, it will have five elements after an
    update that assigns to <literal>array[5]</>.  Currently, enlargement in
    this fashion is only allowed for one-dimensional arrays, not
!   multi-dimensional arrays.
   </para>

   <para>
***************
*** 179,184 ****
--- 282,367 ----
   </para>

   <para>
+   An array can also be enlarged by using the functions
+   <function>array_prepend</function>, <function>array_append</function>,
+   or <function>array_cat</function>. The first two only support one-dimensional
+   arrays, but <function>array_cat</function> supports multi-dimensional arrays.
+   Some examples:
+
+ <programlisting>
+ SELECT array_prepend(1, ARRAY[2,3]);
+  array_prepend
+ ---------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_append(ARRAY[1,2], 3);
+  array_append
+ --------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
+    array_cat
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
+       array_cat
+ ---------------------
+  {{1,2},{3,4},{5,6}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
+       array_cat
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ </programlisting>
+
+   <function>array_prepend</function> and <function>array_append</function>
+   work with a one-dimensional array and a single element to be pushed on
+   to the beginning or end of the array, respectively. The array is extended
+   in the direction of the push. Hence, by pushing onto the beginning of an
+   array with a one-based subscript, a zero-based subscript array is created:
+
+ <programlisting>
+ SELECT array_dims(t.f) FROM (SELECT array_prepend(1, ARRAY[2,3]) AS f) AS t;
+  array_dims
+ ------------
+  [0:2]
+ (1 row)
+ </programlisting>
+
+   <function>array_cat</function> works with either two
+   <replaceable>n</>-dimension arrays, or an <replaceable>n</>-dimension
+   and an <replaceable>n+1</> dimension array. In the former case, the two
+   <replaceable>n</>-dimension arrays become outer elements of an
+   <replaceable>n+1</> dimension array. In the latter, the
+   <replaceable>n</>-dimension array is added as either the first or last
+   outer element of the <replaceable>n+1</> dimension array.
+  </para>
+
+  <para>
+   A final method of enlarging arrays is through the concatenation operator,
+   <command>||</command>, which works exactly as <function>array_cat</function>
+   does.
+ <programlisting>
+ SELECT ARRAY[1,2] || ARRAY[3,4];
+    ?column?
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
+       ?column?
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ (1 row)
+ </programlisting>
+  </para>
+
+  <para>
    The syntax for <command>CREATE TABLE</command> allows fixed-length
    arrays to be defined:

***************
*** 194,199 ****
--- 377,392 ----
   </para>

   <para>
+   An alternative syntax for one-dimensional arrays may be used.
+   <structfield>pay_by_quarter</structfield> could have been defined as:
+ <programlisting>
+     pay_by_quarter  integer ARRAY[4],
+ </programlisting>
+   This syntax may <emphasis>only</emphasis> be used with the integer
+   constant to denote the array size.
+  </para>
+
+  <para>
    Actually, the current implementation does not enforce the declared
    number of dimensions either.  Arrays of a particular element type are
    all considered to be of the same type, regardless of size or number
***************
*** 292,298 ****
     for the array's element type.  (Among the standard data types provided
     in the <productname>PostgreSQL</productname> distribution, type
     <literal>box</> uses a semicolon (<literal>;</>) but all the others
!    use comma.)  In a multidimensional array, each dimension (row, plane,
     cube, etc.) gets its own level of curly braces, and delimiters
     must be written between adjacent curly-braced entities of the same level.
     You may write whitespace before a left brace, after a right
--- 485,491 ----
     for the array's element type.  (Among the standard data types provided
     in the <productname>PostgreSQL</productname> distribution, type
     <literal>box</> uses a semicolon (<literal>;</>) but all the others
!    use comma.)  In a multi-dimensional array, each dimension (row, plane,
     cube, etc.) gets its own level of curly braces, and delimiters
     must be written between adjacent curly-braced entities of the same level.
     You may write whitespace before a left brace, after a right
***************
*** 300,305 ****
--- 493,564 ----
     is not ignored, however: after skipping leading whitespace, everything
     up to the next right brace or delimiter is taken as the item value.
    </para>
+
+   <para>
+    As illustrated earlier in this chapter, arrays may also be represented
+    using the <command>ARRAY</command> expression syntax. This representation
+    of an array value consists of items that are interpreted according to the
+    I/O conversion rules for the array's element type, plus decoration that
+    indicates the array structure. The decoration consists of the keyword
+    <command>ARRAY</command> and square brackets (<literal>[</> and
+    <literal>]</>) around the array values, plus delimiter characters between
+    adjacent items. The delimiter character is always a comma (<literal>,</>).
+    When representing multi-dimensional arrays, the keyword
+    <command>ARRAY</command> is only necessary for the outer level. For example,
+    <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as:
+ <programlisting>
+ SELECT ARRAY[['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   or it also could be written as:
+ <programlisting>
+ SELECT ARRAY[ARRAY['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   </para>
+
+   <para>
+    A final method to represent an array, is through an
+    <command>ARRAY</command> sub-select expression. For example:
+ <programlisting>
+ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
+                           ?column?
+ -------------------------------------------------------------
+  {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
+ (1 row)
+ </programlisting>
+   The sub-select may <emphasis>only</emphasis> return a single column. The
+   resulting one-dimensional array will have an element for each row in the
+   sub-select result, with an element type matching that of the sub-select's
+   target column.
+   </para>
+
+   <para>
+    Arrays may be cast from one type to another in similar fashion to other
+    data types:
+
+ <programlisting>
+ SELECT ARRAY[1,2,3]::oid[];
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+
+ SELECT CAST(ARRAY[1,2,3] AS float8[]);
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+ </programlisting>
+
+   </para>
+
   </sect2>

   <sect2>
***************
*** 316,321 ****
--- 575,588 ----
     Alternatively, you can use backslash-escaping to protect all data characters
     that would otherwise be taken as array syntax or ignorable white space.
    </para>
+
+  <note>
+   <para>
+    The discussion in the preceding paragraph with respect to double quoting does
+    not pertain to the <command>ARRAY</command> expression syntax. In that case,
+    each element is quoted exactly as any other literal value of the element type.
+   </para>
+  </note>

    <para>
     The array output routine will put double quotes around element values
Index: doc/src/sgml/func.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v
retrieving revision 1.154
diff -c -r1.154 func.sgml
*** doc/src/sgml/func.sgml    5 May 2003 15:08:49 -0000    1.154
--- doc/src/sgml/func.sgml    21 May 2003 19:50:08 -0000
***************
*** 6962,6967 ****
--- 6962,7224 ----

    </sect1>

+  <sect1 id="functions-array">
+   <title>Array Functions</title>
+
+   <para>
+    <xref linkend="array-operators-table"> shows the operators
+    available for the <type>array</type> types.
+   </para>
+
+     <table id="array-operators-table">
+      <title><type>array</type> Operators</title>
+      <tgroup cols="4">
+       <thead>
+        <row>
+     <entry>Operator</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry> <literal>=</literal> </entry>
+     <entry>equals</entry>
+     <entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry>
+     <entry><literal>t</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>element-to-array concatenation</entry>
+     <entry><literal>3 || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{3,4,5,6}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-element concatenation</entry>
+     <entry><literal>ARRAY[4,5,6] || 7</literal></entry>
+     <entry><literal>{4,5,6,7}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+
+   <para>
+    <xref linkend="array-functions-table"> shows the functions
+    available for use with array types. See <xref linkend="arrays">
+    for more discussion and examples for the use of these functions.
+   </para>
+
+     <table id="array-functions-table">
+      <title><type>array</type> Functions</title>
+      <tgroup cols="5">
+       <thead>
+        <row>
+     <entry>Function</entry>
+     <entry>Return Type</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry>
+      <literal>
+       <function>array_accum</function>
+       (<type>anyarray</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the end of an array, ignoring
+      <literal>NULL</literal> elements, and creating an array if needed
+     </entry>
+     <entry><literal>array_accum(null, 1)</literal></entry>
+     <entry><literal>{1}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_append</function>
+       (<type>anyarray</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the end of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_append(ARRAY[1,2], 3)</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_assign</function>
+       (<type>anyarray</type>, <type>integer</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      assign a value to a specific array element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_assign(ARRAY[1,2,3], 2, 99)</literal></entry>
+     <entry><literal>{1,99,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_cat</function>
+       (<type>anyarray</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      concatenate two arrays, returning <literal>NULL</literal>
+      for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_dims</function>
+       (<type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      returns a text representation of array dimension lower and upper bounds,
+      generating an ERROR for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry>
+     <entry><literal>[1:2][1:3]</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_lower</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns lower bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry>
+     <entry><literal>0</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_prepend</function>
+       (<type>anyelement</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the beginning of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_subscript</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>anyelement</type></entry>
+     <entry>
+      returns requested array element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_subscript(ARRAY[1,2,3], 3)</literal></entry>
+     <entry><literal>3</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_to_str</function>
+       (<type>anyarray</type>, <type>text</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      concatenates array elements using provided delimiter, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_to_str(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry>
+     <entry><literal>1.10~^~2.20~^~3.30</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_upper</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns upper bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry>
+     <entry><literal>4</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>singleton_array</function>
+       (<type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      create an array from the provided element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>singleton_array(1)</literal></entry>
+     <entry><literal>{1}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>str_to_array</function>
+       (<type>text</type>, <type>text</type>)
+      </literal>
+     </entry>
+     <entry><type>text[]</type></entry>
+     <entry>
+      splits string into array elements using provided delimiter, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>str_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry>
+     <entry><literal>{1.1,2.2,3.3}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+   </sect1>

   <sect1 id="functions-aggregate">
    <title>Aggregate Functions</title>
Index: src/backend/executor/nodeSubplan.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v
retrieving revision 1.45
diff -c -r1.45 nodeSubplan.c
*** src/backend/executor/nodeSubplan.c    8 Apr 2003 23:20:01 -0000    1.45
--- src/backend/executor/nodeSubplan.c    21 May 2003 14:31:16 -0000
***************
*** 28,50 ****
  #include "utils/datum.h"
  #include "utils/lsyscache.h"

-
- typedef struct ArrayBuildState
- {
-     MemoryContext mcontext;        /* where all the temp stuff is kept */
-     Datum       *dvalues;        /* array of accumulated Datums */
-     /*
-      * The allocated size of dvalues[] is always a multiple of
-      * ARRAY_ELEMS_CHUNKSIZE
-      */
- #define ARRAY_ELEMS_CHUNKSIZE    64
-     int            nelems;            /* number of valid Datums in dvalues[] */
-     Oid            element_type;    /* data type of the Datums */
-     int16        typlen;            /* needed info about datatype */
-     bool        typbyval;
-     char        typalign;
- } ArrayBuildState;
-
  static Datum ExecHashSubPlan(SubPlanState *node,
                               ExprContext *econtext,
                               bool *isNull);
--- 28,33 ----
***************
*** 54,66 ****
  static void buildSubPlanHash(SubPlanState *node);
  static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
  static bool tupleAllNulls(HeapTuple tuple);
- static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
-                                          Datum dvalue, bool disnull,
-                                          Oid element_type,
-                                          MemoryContext rcontext);
- static Datum makeArrayResult(ArrayBuildState *astate,
-                              MemoryContext rcontext);
-

  /* ----------------------------------------------------------------
   *        ExecSubPlan
--- 37,42 ----
***************
*** 1084,1185 ****
          prm->execPlan = node;
          parent->chgParam = bms_add_member(parent->chgParam, paramid);
      }
- }
-
- /*
-  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
-  *
-  *    astate is working state (NULL on first call)
-  *    rcontext is where to keep working state
-  */
- static ArrayBuildState *
- accumArrayResult(ArrayBuildState *astate,
-                  Datum dvalue, bool disnull,
-                  Oid element_type,
-                  MemoryContext rcontext)
- {
-     MemoryContext arr_context,
-                   oldcontext;
-
-     if (astate == NULL)
-     {
-         /* First time through --- initialize */
-
-         /* Make a temporary context to hold all the junk */
-         arr_context = AllocSetContextCreate(rcontext,
-                                             "ARRAY_SUBLINK Result",
-                                             ALLOCSET_DEFAULT_MINSIZE,
-                                             ALLOCSET_DEFAULT_INITSIZE,
-                                             ALLOCSET_DEFAULT_MAXSIZE);
-         oldcontext = MemoryContextSwitchTo(arr_context);
-         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
-         astate->mcontext = arr_context;
-         astate->dvalues = (Datum *)
-             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
-         astate->nelems = 0;
-         astate->element_type = element_type;
-         get_typlenbyvalalign(element_type,
-                              &astate->typlen,
-                              &astate->typbyval,
-                              &astate->typalign);
-     }
-     else
-     {
-         oldcontext = MemoryContextSwitchTo(astate->mcontext);
-         Assert(astate->element_type == element_type);
-         /* enlarge dvalues[] if needed */
-         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
-             astate->dvalues = (Datum *)
-                 repalloc(astate->dvalues,
-                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
-     }
-
-     if (disnull)
-         elog(ERROR, "NULL elements not allowed in Arrays");
-
-     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
-     astate->dvalues[astate->nelems++] =
-         datumCopy(dvalue, astate->typbyval, astate->typlen);
-
-     MemoryContextSwitchTo(oldcontext);
-
-     return astate;
- }
-
- /*
-  * makeArrayResult - produce final result of ARRAY_SUBLINK
-  *
-  *    astate is working state (not NULL)
-  *    rcontext is where to construct result
-  */
- static Datum
- makeArrayResult(ArrayBuildState *astate,
-                 MemoryContext rcontext)
- {
-     ArrayType  *result;
-     int            dims[1];
-     int            lbs[1];
-     MemoryContext oldcontext;
-
-     /* Build the final array result in rcontext */
-     oldcontext = MemoryContextSwitchTo(rcontext);
-
-     dims[0] = astate->nelems;
-     lbs[0] = 1;
-
-     result = construct_md_array(astate->dvalues,
-                                 1,
-                                 dims,
-                                 lbs,
-                                 astate->element_type,
-                                 astate->typlen,
-                                 astate->typbyval,
-                                 astate->typalign);
-
-     MemoryContextSwitchTo(oldcontext);
-
-     /* Clean up all the junk */
-     MemoryContextDelete(astate->mcontext);
-
-     return PointerGetDatum(result);
  }
--- 1060,1063 ----
Index: src/backend/utils/adt/arrayfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v
retrieving revision 1.89
diff -c -r1.89 arrayfuncs.c
*** src/backend/utils/adt/arrayfuncs.c    9 May 2003 23:01:45 -0000    1.89
--- src/backend/utils/adt/arrayfuncs.c    21 May 2003 17:59:01 -0000
***************
*** 23,28 ****
--- 23,29 ----
  #include "parser/parse_coerce.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
+ #include "utils/datum.h"
  #include "utils/memutils.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
***************
*** 70,85 ****

  #define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)

- /* I/O function selector for system_cache_lookup */
- typedef enum IOFuncSelector
- {
-     IOFunc_input,
-     IOFunc_output,
-     IOFunc_receive,
-     IOFunc_send
- } IOFuncSelector;
-
-
  static int    ArrayCount(char *str, int *dim, char typdelim);
  static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
               FmgrInfo *inputproc, Oid typelem, int32 typmod,
--- 71,76 ----
***************
*** 93,102 ****
  static void CopyArrayEls(char *p, Datum *values, int nitems,
               int typlen, bool typbyval, char typalign,
               bool freedata);
- static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
-                                 int *typlen, bool *typbyval,
-                                 char *typdelim, Oid *typelem,
-                                 Oid *proc, char *typalign);
  static Datum ArrayCast(char *value, bool byval, int len);
  static int ArrayCastAndSet(Datum src,
                  int typlen, bool typbyval, char typalign,
--- 84,89 ----
***************
*** 156,162 ****
      char        typalign;

      /* Get info about element type, including its input conversion proc */
!     system_cache_lookup(element_type, IOFunc_input,
                          &typlen, &typbyval, &typdelim,
                          &typelem, &typinput, &typalign);
      fmgr_info(typinput, &inputproc);
--- 143,149 ----
      char        typalign;

      /* Get info about element type, including its input conversion proc */
!     get_type_metadata(element_type, IOFunc_input,
                          &typlen, &typbyval, &typdelim,
                          &typelem, &typinput, &typalign);
      fmgr_info(typinput, &inputproc);
***************
*** 638,644 ****
                 *dim;

      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_output,
                          &typlen, &typbyval, &typdelim,
                          &typelem, &typoutput, &typalign);
      fmgr_info(typoutput, &outputproc);
--- 625,631 ----
                 *dim;

      element_type = ARR_ELEMTYPE(v);
!     get_type_metadata(element_type, IOFunc_output,
                          &typlen, &typbyval, &typdelim,
                          &typelem, &typoutput, &typalign);
      fmgr_info(typoutput, &outputproc);
***************
*** 832,838 ****
      }

      /* Get info about element type, including its receive conversion proc */
!     system_cache_lookup(element_type, IOFunc_receive,
                          &typlen, &typbyval, &typdelim,
                          &typelem, &typreceive, &typalign);
      if (!OidIsValid(typreceive))
--- 819,825 ----
      }

      /* Get info about element type, including its receive conversion proc */
!     get_type_metadata(element_type, IOFunc_receive,
                          &typlen, &typbyval, &typdelim,
                          &typelem, &typreceive, &typalign);
      if (!OidIsValid(typreceive))
***************
*** 979,985 ****

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
                          &typdelim, &typelem, &typsend, &typalign);
      if (!OidIsValid(typsend))
          elog(ERROR, "No binary output function available for type %s",
--- 966,972 ----

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!     get_type_metadata(element_type, IOFunc_send, &typlen, &typbyval,
                          &typdelim, &typelem, &typsend, &typalign);
      if (!OidIsValid(typsend))
          elog(ERROR, "No binary output function available for type %s",
***************
*** 1830,1838 ****
          PG_RETURN_ARRAYTYPE_P(v);

      /* Lookup source and result types. Unneeded variables are reused. */
!     system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
                          &typdelim, &typelem, &proc, &inp_typalign);
!     system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
                          &typdelim, &typelem, &proc, &typalign);

      /* Allocate temporary array for new values */
--- 1817,1825 ----
          PG_RETURN_ARRAYTYPE_P(v);

      /* Lookup source and result types. Unneeded variables are reused. */
!     get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
                          &typdelim, &typelem, &proc, &inp_typalign);
!     get_type_metadata(retType, IOFunc_input, &typlen, &typbyval,
                          &typdelim, &typelem, &proc, &typalign);

      /* Allocate temporary array for new values */
***************
*** 2077,2125 ****
  /******************|          Support  Routines              |*****************/
  /***************************************************************************/

- static void
- system_cache_lookup(Oid element_type,
-                     IOFuncSelector which_func,
-                     int *typlen,
-                     bool *typbyval,
-                     char *typdelim,
-                     Oid *typelem,
-                     Oid *proc,
-                     char *typalign)
- {
-     HeapTuple    typeTuple;
-     Form_pg_type typeStruct;
-
-     typeTuple = SearchSysCache(TYPEOID,
-                                ObjectIdGetDatum(element_type),
-                                0, 0, 0);
-     if (!HeapTupleIsValid(typeTuple))
-         elog(ERROR, "cache lookup failed for type %u", element_type);
-     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
-
-     *typlen = typeStruct->typlen;
-     *typbyval = typeStruct->typbyval;
-     *typdelim = typeStruct->typdelim;
-     *typelem = typeStruct->typelem;
-     *typalign = typeStruct->typalign;
-     switch (which_func)
-     {
-         case IOFunc_input:
-             *proc = typeStruct->typinput;
-             break;
-         case IOFunc_output:
-             *proc = typeStruct->typoutput;
-             break;
-         case IOFunc_receive:
-             *proc = typeStruct->typreceive;
-             break;
-         case IOFunc_send:
-             *proc = typeStruct->typsend;
-             break;
-     }
-     ReleaseSysCache(typeTuple);
- }
-
  /*
   * Fetch array element at pointer, converted correctly to a Datum
   */
--- 2064,2069 ----
***************
*** 2453,2456 ****
--- 2397,2515 ----
      locfcinfo.arg[0] = PointerGetDatum(src);

      return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
+ }
+
+ /*
+  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
+  *
+  *    astate is working state (NULL on first call)
+  *    rcontext is where to keep working state
+  */
+ ArrayBuildState *
+ accumArrayResult(ArrayBuildState *astate,
+                  Datum dvalue, bool disnull,
+                  Oid element_type,
+                  MemoryContext rcontext)
+ {
+     MemoryContext arr_context,
+                   oldcontext;
+
+     if (astate == NULL)
+     {
+         /* First time through --- initialize */
+
+         /* Make a temporary context to hold all the junk */
+         arr_context = AllocSetContextCreate(rcontext,
+                                             "accumArrayResult",
+                                             ALLOCSET_DEFAULT_MINSIZE,
+                                             ALLOCSET_DEFAULT_INITSIZE,
+                                             ALLOCSET_DEFAULT_MAXSIZE);
+         oldcontext = MemoryContextSwitchTo(arr_context);
+         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
+         astate->mcontext = arr_context;
+         astate->dvalues = (Datum *)
+             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
+         astate->nelems = 0;
+         astate->element_type = element_type;
+         get_typlenbyvalalign(element_type,
+                              &astate->typlen,
+                              &astate->typbyval,
+                              &astate->typalign);
+     }
+     else
+     {
+         oldcontext = MemoryContextSwitchTo(astate->mcontext);
+         Assert(astate->element_type == element_type);
+         /* enlarge dvalues[] if needed */
+         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
+             astate->dvalues = (Datum *)
+                 repalloc(astate->dvalues,
+                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
+     }
+
+     if (disnull)
+         elog(ERROR, "NULL elements not allowed in Arrays");
+
+     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
+     astate->dvalues[astate->nelems++] =
+         datumCopy(dvalue, astate->typbyval, astate->typlen);
+
+     MemoryContextSwitchTo(oldcontext);
+
+     return astate;
+ }
+
+ /*
+  * makeArrayResult - produce final result of accumArrayResult
+  *
+  *    astate is working state (not NULL)
+  *    rcontext is where to construct result
+  */
+ Datum
+ makeArrayResult(ArrayBuildState *astate,
+                 MemoryContext rcontext)
+ {
+     int            dims[1];
+     int            lbs[1];
+
+     dims[0] = astate->nelems;
+     lbs[0] = 1;
+
+     return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
+ }
+
+ /*
+  * makeMdArrayResult - produce md final result of accumArrayResult
+  *
+  *    astate is working state (not NULL)
+  *    rcontext is where to construct result
+  */
+ Datum
+ makeMdArrayResult(ArrayBuildState *astate,
+                 int ndims,
+                 int *dims,
+                 int *lbs,
+                 MemoryContext rcontext)
+ {
+     ArrayType  *result;
+     MemoryContext oldcontext;
+
+     /* Build the final array result in rcontext */
+     oldcontext = MemoryContextSwitchTo(rcontext);
+
+     result = construct_md_array(astate->dvalues,
+                                 ndims,
+                                 dims,
+                                 lbs,
+                                 astate->element_type,
+                                 astate->typlen,
+                                 astate->typbyval,
+                                 astate->typalign);
+
+     MemoryContextSwitchTo(oldcontext);
+
+     /* Clean up all the junk */
+     MemoryContextDelete(astate->mcontext);
+
+     return PointerGetDatum(result);
  }
Index: src/backend/utils/adt/varlena.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/varlena.c,v
retrieving revision 1.98
diff -c -r1.98 varlena.c
*** src/backend/utils/adt/varlena.c    15 May 2003 15:50:19 -0000    1.98
--- src/backend/utils/adt/varlena.c    21 May 2003 18:18:33 -0000
***************
*** 19,29 ****
--- 19,32 ----
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
  #include "access/tuptoaster.h"
+ #include "catalog/pg_type.h"
  #include "lib/stringinfo.h"
  #include "libpq/crypt.h"
  #include "libpq/pqformat.h"
+ #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/pg_locale.h"
+ #include "utils/lsyscache.h"


  typedef struct varlena unknown;
***************
*** 1983,1990 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

--- 1986,1992 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

***************
*** 2004,2011 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
--- 2006,2012 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
***************
*** 2026,2031 ****
--- 2027,2181 ----
          result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn -
fldsep_len,false); 
          PG_RETURN_TEXT_P(result_text);
      }
+ }
+
+ /*
+  * text_to_array
+  * parse input string
+  * return text array of elements
+  * based on provided field separator
+  */
+ Datum
+ text_to_array(PG_FUNCTION_ARGS)
+ {
+     text       *inputstring = PG_GETARG_TEXT_P(0);
+     int            inputstring_len = TEXTLEN(inputstring);
+     text       *fldsep = PG_GETARG_TEXT_P(1);
+     int            fldsep_len = TEXTLEN(fldsep);
+     int            fldnum;
+     int            start_posn = 0;
+     int            end_posn = 0;
+     text       *result_text = NULL;
+     ArrayBuildState *astate = NULL;
+     MemoryContext oldcontext = CurrentMemoryContext;
+
+
+     /* return NULL for empty input string */
+     if (inputstring_len < 1)
+         PG_RETURN_NULL();
+
+     /* empty field separator
+      * return one element, 1D, array using the input string */
+     if (fldsep_len < 1)
+         PG_RETURN_ARRAYTYPE_P(create_singleton_array(TEXTOID,
+                               CStringGetDatum(inputstring),
+                               1));
+
+     /* start with end position holding the initial start position */
+     end_posn = 0;
+     for (fldnum=1;;fldnum++)    /* field number is 1 based */
+     {
+         Datum    dvalue;
+         bool    disnull = false;
+
+         start_posn = end_posn;
+         end_posn = text_position(PointerGetDatum(inputstring),
+                                  PointerGetDatum(fldsep),
+                                  fldnum);
+
+         if ((start_posn == 0) && (end_posn == 0))    /* fldsep not found */
+         {
+             if (fldnum == 1)
+             {
+                 /* first element
+                  * return one element, 1D, array using the input string */
+                 PG_RETURN_ARRAYTYPE_P(create_singleton_array(TEXTOID,
+                                       CStringGetDatum(inputstring),
+                                       1));
+             }
+             else
+             {
+                 /* otherwise create array and exit */
+                 PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
+             }
+         }
+         else if ((start_posn != 0) && (end_posn == 0))
+         {
+             /* last field requested */
+             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
+         }
+         else if ((start_posn == 0) && (end_posn != 0))
+         {
+             /* first field requested */
+             result_text = LEFT(inputstring, fldsep);
+         }
+         else
+         {
+             /* prior to last field requested */
+             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn
-fldsep_len, false); 
+         }
+
+         /* stash away current value */
+         dvalue = PointerGetDatum(result_text);
+         astate = accumArrayResult(astate, dvalue,
+                                   disnull, TEXTOID, oldcontext);
+
+     }
+
+     /* never reached -- keep compiler quiet */
+     PG_RETURN_NULL();
+ }
+
+ /*
+  * array_to_text
+  * concatenate Cstring representation of input array elements
+  * using provided field separator
+  */
+ Datum
+ array_to_text(PG_FUNCTION_ARGS)
+ {
+     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
+     char       *fldsep = PG_TEXTARG_GET_STR(1);
+     int            nitems, *dims, ndims;
+     char       *p;
+     Oid            element_type;
+     int            typlen;
+     bool        typbyval;
+     char        typdelim;
+     Oid            typoutput,
+                 typelem;
+     FmgrInfo    outputproc;
+     char        typalign;
+     StringInfo    result_str = makeStringInfo();
+     int            i;
+
+     p = ARR_DATA_PTR(v);
+     ndims = ARR_NDIM(v);
+     dims = ARR_DIMS(v);
+     nitems = ArrayGetNItems(ndims, dims);
+
+     /* if there are no elements, return an empty string */
+     if (nitems == 0)
+         PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
+
+     element_type = ARR_ELEMTYPE(v);
+     get_type_metadata(element_type, IOFunc_output,
+                         &typlen, &typbyval, &typdelim,
+                         &typelem, &typoutput, &typalign);
+     fmgr_info(typoutput, &outputproc);
+
+     for (i = 0; i < nitems; i++)
+     {
+         Datum        itemvalue;
+         char       *value;
+
+         itemvalue = fetch_att(p, typbyval, typlen);
+
+         value = DatumGetCString(FunctionCall3(&outputproc,
+                                               itemvalue,
+                                               ObjectIdGetDatum(typelem),
+                                               Int32GetDatum(-1)));
+
+         if (i > 0)
+             appendStringInfo(result_str, "%s%s", fldsep, value);
+         else
+             appendStringInfo(result_str, "%s", value);
+
+         p = att_addlength(p, typlen, PointerGetDatum(p));
+         p = (char *) att_align(p, typalign);
+     }
+
+     PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
  }

  #define HEXBASE 16
Index: src/backend/utils/cache/lsyscache.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v
retrieving revision 1.94
diff -c -r1.94 lsyscache.c
*** src/backend/utils/cache/lsyscache.c    13 May 2003 04:38:58 -0000    1.94
--- src/backend/utils/cache/lsyscache.c    21 May 2003 17:57:40 -0000
***************
*** 969,974 ****
--- 969,1024 ----
      ReleaseSysCache(tp);
  }

+ /*
+  * get_type_metadata
+  *
+  *        A six-fer:    given the type OID, return typlen, typbyval, typalign,
+  *                    typdelim, typelem, IO function Oid. The IO function
+  *                    returned is controlled by IOFuncSelector
+  */
+ void
+ get_type_metadata(Oid element_type,
+                     IOFuncSelector which_func,
+                     int *typlen,
+                     bool *typbyval,
+                     char *typdelim,
+                     Oid *typelem,
+                     Oid *proc,
+                     char *typalign)
+ {
+     HeapTuple    typeTuple;
+     Form_pg_type typeStruct;
+
+     typeTuple = SearchSysCache(TYPEOID,
+                                ObjectIdGetDatum(element_type),
+                                0, 0, 0);
+     if (!HeapTupleIsValid(typeTuple))
+         elog(ERROR, "cache lookup failed for type %u", element_type);
+     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+
+     *typlen = typeStruct->typlen;
+     *typbyval = typeStruct->typbyval;
+     *typdelim = typeStruct->typdelim;
+     *typelem = typeStruct->typelem;
+     *typalign = typeStruct->typalign;
+     switch (which_func)
+     {
+         case IOFunc_input:
+             *proc = typeStruct->typinput;
+             break;
+         case IOFunc_output:
+             *proc = typeStruct->typoutput;
+             break;
+         case IOFunc_receive:
+             *proc = typeStruct->typreceive;
+             break;
+         case IOFunc_send:
+             *proc = typeStruct->typsend;
+             break;
+     }
+     ReleaseSysCache(typeTuple);
+ }
+
  #ifdef NOT_USED
  char
  get_typalign(Oid typid)
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v
retrieving revision 1.300
diff -c -r1.300 pg_proc.h
*** src/include/catalog/pg_proc.h    15 May 2003 15:50:19 -0000    1.300
--- src/include/catalog/pg_proc.h    21 May 2003 15:18:27 -0000
***************
*** 1016,1021 ****
--- 1016,1025 ----
  DESCR("concatenate two arrays");
  DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
  DESCR("coerce array type to another array type");
+ DATA(insert OID = 385 (  str_to_array       PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ ));
+ DESCR("split delimited text into text[]");
+ DATA(insert OID = 386 (  array_to_str       PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ ));
+ DESCR("concatenate array elements, using delimiter, into text");

  DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
  DESCR("I/O");
Index: src/include/utils/array.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v
retrieving revision 1.38
diff -c -r1.38 array.h
*** src/include/utils/array.h    8 May 2003 22:19:57 -0000    1.38
--- src/include/utils/array.h    21 May 2003 14:31:17 -0000
***************
*** 32,37 ****
--- 32,53 ----
      Oid            elemtype;        /* element type OID */
  } ArrayType;

+ typedef struct ArrayBuildState
+ {
+     MemoryContext mcontext;        /* where all the temp stuff is kept */
+     Datum       *dvalues;        /* array of accumulated Datums */
+     /*
+      * The allocated size of dvalues[] is always a multiple of
+      * ARRAY_ELEMS_CHUNKSIZE
+      */
+ #define ARRAY_ELEMS_CHUNKSIZE    64
+     int            nelems;            /* number of valid Datums in dvalues[] */
+     Oid            element_type;    /* data type of the Datums */
+     int16        typlen;            /* needed info about datatype */
+     bool        typbyval;
+     char        typalign;
+ } ArrayBuildState;
+
  /*
   * fmgr macros for array objects
   */
***************
*** 124,130 ****
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
!

  /*
   * prototypes for functions defined in arrayutils.c
--- 140,153 ----
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
! extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
!                                          Datum dvalue, bool disnull,
!                                          Oid element_type,
!                                          MemoryContext rcontext);
! extern Datum makeArrayResult(ArrayBuildState *astate,
!                              MemoryContext rcontext);
! extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
!                                int *dims, int *lbs, MemoryContext rcontext);

  /*
   * prototypes for functions defined in arrayutils.c
Index: src/include/utils/builtins.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/builtins.h,v
retrieving revision 1.217
diff -c -r1.217 builtins.h
*** src/include/utils/builtins.h    15 May 2003 15:50:20 -0000    1.217
--- src/include/utils/builtins.h    21 May 2003 15:15:58 -0000
***************
*** 539,544 ****
--- 539,546 ----
                        List **namelist);
  extern Datum replace_text(PG_FUNCTION_ARGS);
  extern Datum split_text(PG_FUNCTION_ARGS);
+ extern Datum text_to_array(PG_FUNCTION_ARGS);
+ extern Datum array_to_text(PG_FUNCTION_ARGS);
  extern Datum to_hex32(PG_FUNCTION_ARGS);
  extern Datum to_hex64(PG_FUNCTION_ARGS);
  extern Datum md5_text(PG_FUNCTION_ARGS);
Index: src/include/utils/lsyscache.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v
retrieving revision 1.69
diff -c -r1.69 lsyscache.h
*** src/include/utils/lsyscache.h    9 May 2003 18:08:48 -0000    1.69
--- src/include/utils/lsyscache.h    21 May 2003 17:57:44 -0000
***************
*** 15,20 ****
--- 15,29 ----

  #include "access/htup.h"

+ /* I/O function selector for system_cache_lookup */
+ typedef enum IOFuncSelector
+ {
+     IOFunc_input,
+     IOFunc_output,
+     IOFunc_receive,
+     IOFunc_send
+ } IOFuncSelector;
+
  extern bool op_in_opclass(Oid opno, Oid opclass);
  extern bool op_requires_recheck(Oid opno, Oid opclass);
  extern char *get_attname(Oid relid, AttrNumber attnum);
***************
*** 53,58 ****
--- 62,75 ----
  extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
  extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
                       char *typalign);
+ extern void get_type_metadata(Oid element_type,
+                                 IOFuncSelector which_func,
+                                 int *typlen,
+                                 bool *typbyval,
+                                 char *typdelim,
+                                 Oid *typelem,
+                                 Oid *proc,
+                                 char *typalign);
  extern char get_typstorage(Oid typid);
  extern int32 get_typtypmod(Oid typid);
  extern Node *get_typdefault(Oid typid);

Re: array support patch phase 1 patch

From
Joe Conway
Date:
Joe Conway wrote:
> Attached is an updated version of the doc patch along with the addition
> of two new functions, as previously discussed:
>
>    - str_to_array(str TEXT, delim TEXT) returns TEXT[]
>    - array_to_str(array ANYARRAY, delim TEXT) returns TEXT
>
> The patch is fairly large because:
>
>    1. the documentation part of it is large
>    2. I moved some static functions around and made them accessible so
>       that I could use them in implementing the two functions.
>
> It passes all regression tests, and as mentioned above includes
> documentation.
>
> If there are no objections, please apply.

The attached is an update to the one above, with the following additions
     (recommended by Tom here -
     http://archives.postgresql.org/pgsql-patches/2003-04/msg00030.php):

1. array_eq() now applies the element type's equality operator on an
    element-by-element basis.
2. applied Tom's cache lookup results caching technique in arrayfuncs.c,
    array_userfuncs.c, and in the new functions in varlena.c.

Still passes all regression tests. No additional doc updates needed for
this one. If there are no objections, please apply.

Thanks,

Joe
Index: doc/src/sgml/array.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
retrieving revision 1.25
diff -c -r1.25 array.sgml
*** doc/src/sgml/array.sgml    13 Mar 2003 01:30:26 -0000    1.25
--- doc/src/sgml/array.sgml    22 May 2003 23:42:01 -0000
***************
*** 9,15 ****

   <para>
    <productname>PostgreSQL</productname> allows columns of a table to be
!   defined as variable-length multidimensional arrays. Arrays of any
    built-in type or user-defined type can be created.
   </para>

--- 9,15 ----

   <para>
    <productname>PostgreSQL</productname> allows columns of a table to be
!   defined as variable-length multi-dimensional arrays. Arrays of any
    built-in type or user-defined type can be created.
   </para>

***************
*** 60,73 ****
  </programlisting>
   </para>

   <note>
    <para>
!    A limitation of the present array implementation is that individual
!    elements of an array cannot be SQL null values.  The entire array can be set
!    to null, but you can't have an array with some elements null and some
!    not.  Fixing this is on the to-do list.
    </para>
   </note>
   </sect2>

   <sect2>
--- 60,133 ----
  </programlisting>
   </para>

+  <para>
+   A limitation of the present array implementation is that individual
+   elements of an array cannot be SQL null values.  The entire array can be set
+   to null, but you can't have an array with some elements null and some
+   not.
+  </para>
+  <para>
+   This can lead to surprising results. For example, the result of the
+   previous two inserts looks like this:
+ <programlisting>
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |      schedule
+ -------+---------------------------+--------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting},{""}}
+  Carol | {20000,25000,25000,25000} | {{talk},{meeting}}
+ (2 rows)
+ </programlisting>
+   Because the <literal>[2][2]</literal> element of
+   <structfield>schedule</structfield> is missing in each of the
+   <command>INSERT</command> statements, the <literal>[1][2]</literal>
+   element is discarded.
+  </para>
+
+  <note>
+   <para>
+    Fixing this is on the to-do list.
+   </para>
+  </note>
+
+  <para>
+   The <command>ARRAY</command> expression syntax may also be used:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Bill',
+     ARRAY[10000, 10000, 10000, 10000],
+     ARRAY[['meeting', 'lunch'], ['','']]);
+
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting', '']]);
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |           schedule
+ -------+---------------------------+-------------------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}}
+  Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}}
+ (2 rows)
+ </programlisting>
+   Note that with this syntax, multi-dimesion arrays must have matching
+   extents for each dimension. This eliminates the missing-array-elements
+   problem above. For example:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting']]);
+ ERROR:  Multiple dimension arrays must have array expressions with matching dimensions
+ </programlisting>
+   Also notice that string literals are single quoted instead of double quoted.
+  </para>
+
   <note>
    <para>
!    The examples in the rest of this section are based on the
!    <command>ARRAY</command> expression syntax <command>INSERT</command>s.
    </para>
   </note>
+
   </sect2>

   <sect2>
***************
*** 132,142 ****
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the
!   form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified.
   </para>

   <para>
--- 192,221 ----
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified; another example follows:
! <programlisting>
! SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
!          schedule
! ---------------------------
!  {{meeting,lunch},{"",""}}
! (1 row)
! </programlisting>
!  </para>
!
!  <para>
!   Additionally, we can also access a single arbitrary array element of
!   a one-dimensional array with the <function>array_subscript</function>
!   function:
! <programlisting>
! SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
!  array_subscript
! -----------------
!            10000
! (1 row)
! </programlisting>
   </para>

   <para>
***************
*** 147,153 ****
      WHERE name = 'Carol';
  </programlisting>

!   or updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
--- 226,248 ----
      WHERE name = 'Carol';
  </programlisting>

!   or using the <command>ARRAY</command> expression syntax:
!
! <programlisting>
! UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
!     WHERE name = 'Carol';
! </programlisting>
!
!   <note>
!    <para>
!     Anywhere you can use the <quote>curly braces</quote> array syntax,
!     you can also use the <command>ARRAY</command> expression syntax. The
!     remainder of this section will illustrate only one or the other, but
!     not both.
!    </para>
!   </note>
!
!   An array may also be updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
***************
*** 160,165 ****
--- 255,268 ----
  UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
      WHERE name = 'Carol';
  </programlisting>
+
+   A one-dimensional array may also be updated with the
+   <function>array_assign</function> function:
+
+ <programlisting>
+ UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000)
+     WHERE name = 'Bill';
+ </programListing>
   </para>

   <para>
***************
*** 169,175 ****
    value currently has 4 elements, it will have five elements after an
    update that assigns to <literal>array[5]</>.  Currently, enlargement in
    this fashion is only allowed for one-dimensional arrays, not
!   multidimensional arrays.
   </para>

   <para>
--- 272,278 ----
    value currently has 4 elements, it will have five elements after an
    update that assigns to <literal>array[5]</>.  Currently, enlargement in
    this fashion is only allowed for one-dimensional arrays, not
!   multi-dimensional arrays.
   </para>

   <para>
***************
*** 179,184 ****
--- 282,367 ----
   </para>

   <para>
+   An array can also be enlarged by using the functions
+   <function>array_prepend</function>, <function>array_append</function>,
+   or <function>array_cat</function>. The first two only support one-dimensional
+   arrays, but <function>array_cat</function> supports multi-dimensional arrays.
+   Some examples:
+
+ <programlisting>
+ SELECT array_prepend(1, ARRAY[2,3]);
+  array_prepend
+ ---------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_append(ARRAY[1,2], 3);
+  array_append
+ --------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
+    array_cat
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
+       array_cat
+ ---------------------
+  {{1,2},{3,4},{5,6}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
+       array_cat
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ </programlisting>
+
+   <function>array_prepend</function> and <function>array_append</function>
+   work with a one-dimensional array and a single element to be pushed on
+   to the beginning or end of the array, respectively. The array is extended
+   in the direction of the push. Hence, by pushing onto the beginning of an
+   array with a one-based subscript, a zero-based subscript array is created:
+
+ <programlisting>
+ SELECT array_dims(t.f) FROM (SELECT array_prepend(1, ARRAY[2,3]) AS f) AS t;
+  array_dims
+ ------------
+  [0:2]
+ (1 row)
+ </programlisting>
+
+   <function>array_cat</function> works with either two
+   <replaceable>n</>-dimension arrays, or an <replaceable>n</>-dimension
+   and an <replaceable>n+1</> dimension array. In the former case, the two
+   <replaceable>n</>-dimension arrays become outer elements of an
+   <replaceable>n+1</> dimension array. In the latter, the
+   <replaceable>n</>-dimension array is added as either the first or last
+   outer element of the <replaceable>n+1</> dimension array.
+  </para>
+
+  <para>
+   A final method of enlarging arrays is through the concatenation operator,
+   <command>||</command>, which works exactly as <function>array_cat</function>
+   does.
+ <programlisting>
+ SELECT ARRAY[1,2] || ARRAY[3,4];
+    ?column?
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
+       ?column?
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ (1 row)
+ </programlisting>
+  </para>
+
+  <para>
    The syntax for <command>CREATE TABLE</command> allows fixed-length
    arrays to be defined:

***************
*** 194,199 ****
--- 377,392 ----
   </para>

   <para>
+   An alternative syntax for one-dimensional arrays may be used.
+   <structfield>pay_by_quarter</structfield> could have been defined as:
+ <programlisting>
+     pay_by_quarter  integer ARRAY[4],
+ </programlisting>
+   This syntax may <emphasis>only</emphasis> be used with the integer
+   constant to denote the array size.
+  </para>
+
+  <para>
    Actually, the current implementation does not enforce the declared
    number of dimensions either.  Arrays of a particular element type are
    all considered to be of the same type, regardless of size or number
***************
*** 292,298 ****
     for the array's element type.  (Among the standard data types provided
     in the <productname>PostgreSQL</productname> distribution, type
     <literal>box</> uses a semicolon (<literal>;</>) but all the others
!    use comma.)  In a multidimensional array, each dimension (row, plane,
     cube, etc.) gets its own level of curly braces, and delimiters
     must be written between adjacent curly-braced entities of the same level.
     You may write whitespace before a left brace, after a right
--- 485,491 ----
     for the array's element type.  (Among the standard data types provided
     in the <productname>PostgreSQL</productname> distribution, type
     <literal>box</> uses a semicolon (<literal>;</>) but all the others
!    use comma.)  In a multi-dimensional array, each dimension (row, plane,
     cube, etc.) gets its own level of curly braces, and delimiters
     must be written between adjacent curly-braced entities of the same level.
     You may write whitespace before a left brace, after a right
***************
*** 300,305 ****
--- 493,564 ----
     is not ignored, however: after skipping leading whitespace, everything
     up to the next right brace or delimiter is taken as the item value.
    </para>
+
+   <para>
+    As illustrated earlier in this chapter, arrays may also be represented
+    using the <command>ARRAY</command> expression syntax. This representation
+    of an array value consists of items that are interpreted according to the
+    I/O conversion rules for the array's element type, plus decoration that
+    indicates the array structure. The decoration consists of the keyword
+    <command>ARRAY</command> and square brackets (<literal>[</> and
+    <literal>]</>) around the array values, plus delimiter characters between
+    adjacent items. The delimiter character is always a comma (<literal>,</>).
+    When representing multi-dimensional arrays, the keyword
+    <command>ARRAY</command> is only necessary for the outer level. For example,
+    <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as:
+ <programlisting>
+ SELECT ARRAY[['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   or it also could be written as:
+ <programlisting>
+ SELECT ARRAY[ARRAY['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   </para>
+
+   <para>
+    A final method to represent an array, is through an
+    <command>ARRAY</command> sub-select expression. For example:
+ <programlisting>
+ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
+                           ?column?
+ -------------------------------------------------------------
+  {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
+ (1 row)
+ </programlisting>
+   The sub-select may <emphasis>only</emphasis> return a single column. The
+   resulting one-dimensional array will have an element for each row in the
+   sub-select result, with an element type matching that of the sub-select's
+   target column.
+   </para>
+
+   <para>
+    Arrays may be cast from one type to another in similar fashion to other
+    data types:
+
+ <programlisting>
+ SELECT ARRAY[1,2,3]::oid[];
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+
+ SELECT CAST(ARRAY[1,2,3] AS float8[]);
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+ </programlisting>
+
+   </para>
+
   </sect2>

   <sect2>
***************
*** 316,321 ****
--- 575,588 ----
     Alternatively, you can use backslash-escaping to protect all data characters
     that would otherwise be taken as array syntax or ignorable white space.
    </para>
+
+  <note>
+   <para>
+    The discussion in the preceding paragraph with respect to double quoting does
+    not pertain to the <command>ARRAY</command> expression syntax. In that case,
+    each element is quoted exactly as any other literal value of the element type.
+   </para>
+  </note>

    <para>
     The array output routine will put double quotes around element values
Index: doc/src/sgml/func.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v
retrieving revision 1.154
diff -c -r1.154 func.sgml
*** doc/src/sgml/func.sgml    5 May 2003 15:08:49 -0000    1.154
--- doc/src/sgml/func.sgml    22 May 2003 23:42:01 -0000
***************
*** 6962,6967 ****
--- 6962,7224 ----

    </sect1>

+  <sect1 id="functions-array">
+   <title>Array Functions</title>
+
+   <para>
+    <xref linkend="array-operators-table"> shows the operators
+    available for the <type>array</type> types.
+   </para>
+
+     <table id="array-operators-table">
+      <title><type>array</type> Operators</title>
+      <tgroup cols="4">
+       <thead>
+        <row>
+     <entry>Operator</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry> <literal>=</literal> </entry>
+     <entry>equals</entry>
+     <entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry>
+     <entry><literal>t</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>element-to-array concatenation</entry>
+     <entry><literal>3 || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{3,4,5,6}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-element concatenation</entry>
+     <entry><literal>ARRAY[4,5,6] || 7</literal></entry>
+     <entry><literal>{4,5,6,7}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+
+   <para>
+    <xref linkend="array-functions-table"> shows the functions
+    available for use with array types. See <xref linkend="arrays">
+    for more discussion and examples for the use of these functions.
+   </para>
+
+     <table id="array-functions-table">
+      <title><type>array</type> Functions</title>
+      <tgroup cols="5">
+       <thead>
+        <row>
+     <entry>Function</entry>
+     <entry>Return Type</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry>
+      <literal>
+       <function>array_accum</function>
+       (<type>anyarray</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the end of an array, ignoring
+      <literal>NULL</literal> elements, and creating an array if needed
+     </entry>
+     <entry><literal>array_accum(null, 1)</literal></entry>
+     <entry><literal>{1}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_append</function>
+       (<type>anyarray</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the end of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_append(ARRAY[1,2], 3)</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_assign</function>
+       (<type>anyarray</type>, <type>integer</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      assign a value to a specific array element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_assign(ARRAY[1,2,3], 2, 99)</literal></entry>
+     <entry><literal>{1,99,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_cat</function>
+       (<type>anyarray</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      concatenate two arrays, returning <literal>NULL</literal>
+      for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_dims</function>
+       (<type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      returns a text representation of array dimension lower and upper bounds,
+      generating an ERROR for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry>
+     <entry><literal>[1:2][1:3]</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_lower</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns lower bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry>
+     <entry><literal>0</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_prepend</function>
+       (<type>anyelement</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the beginning of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_subscript</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>anyelement</type></entry>
+     <entry>
+      returns requested array element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_subscript(ARRAY[1,2,3], 3)</literal></entry>
+     <entry><literal>3</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_to_str</function>
+       (<type>anyarray</type>, <type>text</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      concatenates array elements using provided delimiter, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_to_str(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry>
+     <entry><literal>1.10~^~2.20~^~3.30</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_upper</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns upper bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry>
+     <entry><literal>4</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>singleton_array</function>
+       (<type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      create an array from the provided element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>singleton_array(1)</literal></entry>
+     <entry><literal>{1}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>str_to_array</function>
+       (<type>text</type>, <type>text</type>)
+      </literal>
+     </entry>
+     <entry><type>text[]</type></entry>
+     <entry>
+      splits string into array elements using provided delimiter, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>str_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry>
+     <entry><literal>{1.1,2.2,3.3}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+   </sect1>

   <sect1 id="functions-aggregate">
    <title>Aggregate Functions</title>
Index: src/backend/executor/nodeSubplan.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v
retrieving revision 1.45
diff -c -r1.45 nodeSubplan.c
*** src/backend/executor/nodeSubplan.c    8 Apr 2003 23:20:01 -0000    1.45
--- src/backend/executor/nodeSubplan.c    22 May 2003 23:42:01 -0000
***************
*** 28,50 ****
  #include "utils/datum.h"
  #include "utils/lsyscache.h"

-
- typedef struct ArrayBuildState
- {
-     MemoryContext mcontext;        /* where all the temp stuff is kept */
-     Datum       *dvalues;        /* array of accumulated Datums */
-     /*
-      * The allocated size of dvalues[] is always a multiple of
-      * ARRAY_ELEMS_CHUNKSIZE
-      */
- #define ARRAY_ELEMS_CHUNKSIZE    64
-     int            nelems;            /* number of valid Datums in dvalues[] */
-     Oid            element_type;    /* data type of the Datums */
-     int16        typlen;            /* needed info about datatype */
-     bool        typbyval;
-     char        typalign;
- } ArrayBuildState;
-
  static Datum ExecHashSubPlan(SubPlanState *node,
                               ExprContext *econtext,
                               bool *isNull);
--- 28,33 ----
***************
*** 54,66 ****
  static void buildSubPlanHash(SubPlanState *node);
  static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
  static bool tupleAllNulls(HeapTuple tuple);
- static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
-                                          Datum dvalue, bool disnull,
-                                          Oid element_type,
-                                          MemoryContext rcontext);
- static Datum makeArrayResult(ArrayBuildState *astate,
-                              MemoryContext rcontext);
-

  /* ----------------------------------------------------------------
   *        ExecSubPlan
--- 37,42 ----
***************
*** 1084,1185 ****
          prm->execPlan = node;
          parent->chgParam = bms_add_member(parent->chgParam, paramid);
      }
- }
-
- /*
-  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
-  *
-  *    astate is working state (NULL on first call)
-  *    rcontext is where to keep working state
-  */
- static ArrayBuildState *
- accumArrayResult(ArrayBuildState *astate,
-                  Datum dvalue, bool disnull,
-                  Oid element_type,
-                  MemoryContext rcontext)
- {
-     MemoryContext arr_context,
-                   oldcontext;
-
-     if (astate == NULL)
-     {
-         /* First time through --- initialize */
-
-         /* Make a temporary context to hold all the junk */
-         arr_context = AllocSetContextCreate(rcontext,
-                                             "ARRAY_SUBLINK Result",
-                                             ALLOCSET_DEFAULT_MINSIZE,
-                                             ALLOCSET_DEFAULT_INITSIZE,
-                                             ALLOCSET_DEFAULT_MAXSIZE);
-         oldcontext = MemoryContextSwitchTo(arr_context);
-         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
-         astate->mcontext = arr_context;
-         astate->dvalues = (Datum *)
-             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
-         astate->nelems = 0;
-         astate->element_type = element_type;
-         get_typlenbyvalalign(element_type,
-                              &astate->typlen,
-                              &astate->typbyval,
-                              &astate->typalign);
-     }
-     else
-     {
-         oldcontext = MemoryContextSwitchTo(astate->mcontext);
-         Assert(astate->element_type == element_type);
-         /* enlarge dvalues[] if needed */
-         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
-             astate->dvalues = (Datum *)
-                 repalloc(astate->dvalues,
-                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
-     }
-
-     if (disnull)
-         elog(ERROR, "NULL elements not allowed in Arrays");
-
-     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
-     astate->dvalues[astate->nelems++] =
-         datumCopy(dvalue, astate->typbyval, astate->typlen);
-
-     MemoryContextSwitchTo(oldcontext);
-
-     return astate;
- }
-
- /*
-  * makeArrayResult - produce final result of ARRAY_SUBLINK
-  *
-  *    astate is working state (not NULL)
-  *    rcontext is where to construct result
-  */
- static Datum
- makeArrayResult(ArrayBuildState *astate,
-                 MemoryContext rcontext)
- {
-     ArrayType  *result;
-     int            dims[1];
-     int            lbs[1];
-     MemoryContext oldcontext;
-
-     /* Build the final array result in rcontext */
-     oldcontext = MemoryContextSwitchTo(rcontext);
-
-     dims[0] = astate->nelems;
-     lbs[0] = 1;
-
-     result = construct_md_array(astate->dvalues,
-                                 1,
-                                 dims,
-                                 lbs,
-                                 astate->element_type,
-                                 astate->typlen,
-                                 astate->typbyval,
-                                 astate->typalign);
-
-     MemoryContextSwitchTo(oldcontext);
-
-     /* Clean up all the junk */
-     MemoryContextDelete(astate->mcontext);
-
-     return PointerGetDatum(result);
  }
--- 1060,1063 ----
Index: src/backend/utils/adt/array_userfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/array_userfuncs.c,v
retrieving revision 1.1
diff -c -r1.1 array_userfuncs.c
*** src/backend/utils/adt/array_userfuncs.c    8 Apr 2003 23:20:02 -0000    1.1
--- src/backend/utils/adt/array_userfuncs.c    23 May 2003 00:05:38 -0000
***************
*** 42,48 ****
      else
          ndims = 1;

!     PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
                                                   PG_GETARG_DATUM(0),
                                                   ndims));
  }
--- 42,48 ----
      else
          ndims = 1;

!     PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, elem_type,
                                                   PG_GETARG_DATUM(0),
                                                   ndims));
  }
***************
*** 70,75 ****
--- 70,76 ----
      Oid            arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
      Oid            arg0_elemid;
      Oid            arg1_elemid;
+     ArrayMetaState *my_extra;

      if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
          elog(ERROR, "array_push: cannot determine input data types");
***************
*** 113,119 ****
          indx = lb[0] - 1;
      }

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      result = array_set(v, 1, &indx, newelem, -1,
                         typlen, typbyval, typalign, &isNull);
--- 114,148 ----
          indx = lb[0] - 1;
      }

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      result = array_set(v, 1, &indx, newelem, -1,
                         typlen, typbyval, typalign, &isNull);
***************
*** 293,299 ****
          if (tgt_elem_type == InvalidOid)
              elog(ERROR, "Target type is not an array");

!         PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
                                                       PG_GETARG_DATUM(1),
                                                       1));
      }
--- 322,328 ----
          if (tgt_elem_type == InvalidOid)
              elog(ERROR, "Target type is not an array");

!         PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, tgt_elem_type,
                                                       PG_GETARG_DATUM(1),
                                                       1));
      }
***************
*** 329,334 ****
--- 358,364 ----
      int16        typlen;
      bool        typbyval;
      char        typalign;
+     ArrayMetaState *my_extra;

      v = PG_GETARG_ARRAYTYPE_P(0);
      idx_to_chg = PG_GETARG_INT32(1);
***************
*** 349,355 ****
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      result = array_set(v, 1, &idx_to_chg, newelem, -1,
                         typlen, typbyval, typalign, &isNull);
--- 379,413 ----
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      result = array_set(v, 1, &idx_to_chg, newelem, -1,
                         typlen, typbyval, typalign, &isNull);
***************
*** 375,380 ****
--- 433,439 ----
      int16        typlen;
      bool        typbyval;
      char        typalign;
+     ArrayMetaState *my_extra;

      v = PG_GETARG_ARRAYTYPE_P(0);
      idx = PG_GETARG_INT32(1);
***************
*** 394,400 ****
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);

--- 453,487 ----
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);

***************
*** 406,412 ****
   * given a null input array.
   */
  ArrayType *
! create_singleton_array(Oid element_type, Datum element, int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
--- 493,502 ----
   * given a null input array.
   */
  ArrayType *
! create_singleton_array(FunctionCallInfo fcinfo,
!                        Oid element_type,
!                        Datum element,
!                        int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
***************
*** 415,420 ****
--- 505,511 ----
      int        dims[MAXDIM];
      int        lbs[MAXDIM];
      int        i;
+     ArrayMetaState *my_extra;

      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);
***************
*** 429,435 ****
          lbs[i] = 1;
      }

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      return construct_md_array(dvalues, ndims, dims, lbs, element_type,
                                typlen, typbyval, typalign);
--- 520,554 ----
          lbs[i] = 1;
      }

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      return construct_md_array(dvalues, ndims, dims, lbs, element_type,
                                typlen, typbyval, typalign);
Index: src/backend/utils/adt/arrayfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v
retrieving revision 1.89
diff -c -r1.89 arrayfuncs.c
*** src/backend/utils/adt/arrayfuncs.c    9 May 2003 23:01:45 -0000    1.89
--- src/backend/utils/adt/arrayfuncs.c    22 May 2003 23:42:01 -0000
***************
*** 21,28 ****
--- 21,30 ----
  #include "catalog/pg_type.h"
  #include "libpq/pqformat.h"
  #include "parser/parse_coerce.h"
+ #include "parser/parse_oper.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
+ #include "utils/datum.h"
  #include "utils/memutils.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
***************
*** 70,85 ****

  #define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)

- /* I/O function selector for system_cache_lookup */
- typedef enum IOFuncSelector
- {
-     IOFunc_input,
-     IOFunc_output,
-     IOFunc_receive,
-     IOFunc_send
- } IOFuncSelector;
-
-
  static int    ArrayCount(char *str, int *dim, char typdelim);
  static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
               FmgrInfo *inputproc, Oid typelem, int32 typmod,
--- 72,77 ----
***************
*** 93,102 ****
  static void CopyArrayEls(char *p, Datum *values, int nitems,
               int typlen, bool typbyval, char typalign,
               bool freedata);
- static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
-                                 int *typlen, bool *typbyval,
-                                 char *typdelim, Oid *typelem,
-                                 Oid *proc, char *typalign);
  static Datum ArrayCast(char *value, bool byval, int len);
  static int ArrayCastAndSet(Datum src,
                  int typlen, bool typbyval, char typalign,
--- 85,90 ----
***************
*** 154,165 ****
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;

!     /* Get info about element type, including its input conversion proc */
!     system_cache_lookup(element_type, IOFunc_input,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typinput, &typalign);
!     fmgr_info(typinput, &inputproc);

      /* Make a modifiable copy of the input */
      /* XXX why are we allocating an extra 2 bytes here? */
--- 142,190 ----
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;
+     ArrayMetaState *my_extra;

!     /*
!      * We arrange to look up info about element type, including its input
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its input conversion proc */
!         get_type_metadata(element_type, IOFunc_input,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typinput, &typalign);
!         fmgr_info(typinput, &inputproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typinput;
!         my_extra->typalign = typalign;
!         my_extra->proc = inputproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typinput = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         inputproc = my_extra->proc;
!     }

      /* Make a modifiable copy of the input */
      /* XXX why are we allocating an extra 2 bytes here? */
***************
*** 636,647 ****
                  indx[MAXDIM];
      int            ndim,
                 *dim;

      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_output,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typoutput, &typalign);
!     fmgr_info(typoutput, &outputproc);

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
--- 661,711 ----
                  indx[MAXDIM];
      int            ndim,
                 *dim;
+     ArrayMetaState *my_extra;

      element_type = ARR_ELEMTYPE(v);
!
!     /*
!      * We arrange to look up info about element type, including its input
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its output conversion proc */
!         get_type_metadata(element_type, IOFunc_output,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typoutput, &typalign);
!         fmgr_info(typoutput, &outputproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typoutput;
!         my_extra->typalign = typalign;
!         my_extra->proc = outputproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typoutput = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         outputproc = my_extra->proc;
!     }

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
***************
*** 800,805 ****
--- 864,870 ----
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;
+     ArrayMetaState *my_extra;

      /* Get the array header information */
      ndim = pq_getmsgint(buf, 4);
***************
*** 831,844 ****
          PG_RETURN_ARRAYTYPE_P(retval);
      }

!     /* Get info about element type, including its receive conversion proc */
!     system_cache_lookup(element_type, IOFunc_receive,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typreceive, &typalign);
!     if (!OidIsValid(typreceive))
!         elog(ERROR, "No binary input function available for type %s",
!              format_type_be(element_type));
!     fmgr_info(typreceive, &receiveproc);

      dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
                                typlen, typbyval, typalign,
--- 896,945 ----
          PG_RETURN_ARRAYTYPE_P(retval);
      }

!     /*
!      * We arrange to look up info about element type, including its receive
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its receive conversion proc */
!         get_type_metadata(element_type, IOFunc_receive,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typreceive, &typalign);
!         if (!OidIsValid(typreceive))
!             elog(ERROR, "No binary input function available for type %s",
!                  format_type_be(element_type));
!         fmgr_info(typreceive, &receiveproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typreceive;
!         my_extra->typalign = typalign;
!         my_extra->proc = receiveproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typreceive = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         receiveproc = my_extra->proc;
!     }

      dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
                                typlen, typbyval, typalign,
***************
*** 976,990 ****
      int            ndim,
                 *dim;
      StringInfoData buf;

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
!                         &typdelim, &typelem, &typsend, &typalign);
!     if (!OidIsValid(typsend))
!         elog(ERROR, "No binary output function available for type %s",
!              format_type_be(element_type));
!     fmgr_info(typsend, &sendproc);

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
--- 1077,1130 ----
      int            ndim,
                 *dim;
      StringInfoData buf;
+     ArrayMetaState *my_extra;

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!
!     /*
!      * We arrange to look up info about element type, including its send
!      * proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its send proc */
!         get_type_metadata(element_type, IOFunc_send, &typlen, &typbyval,
!                             &typdelim, &typelem, &typsend, &typalign);
!         if (!OidIsValid(typsend))
!             elog(ERROR, "No binary output function available for type %s",
!                  format_type_be(element_type));
!         fmgr_info(typsend, &sendproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typsend;
!         my_extra->typalign = typalign;
!         my_extra->proc = sendproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typsend = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         sendproc = my_extra->proc;
!     }

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
***************
*** 1811,1816 ****
--- 1951,1963 ----
      Oid            typelem;
      Oid            proc;
      char       *s;
+     typedef struct {
+         ArrayMetaState *inp_extra;
+         ArrayMetaState *ret_extra;
+     } am_extra;
+     am_extra  *my_extra;
+     ArrayMetaState *inp_extra;
+     ArrayMetaState *ret_extra;

      /* Get input array */
      if (fcinfo->nargs < 1)
***************
*** 1829,1839 ****
      if (nitems <= 0)
          PG_RETURN_ARRAYTYPE_P(v);

!     /* Lookup source and result types. Unneeded variables are reused. */
!     system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
!                         &typdelim, &typelem, &proc, &inp_typalign);
!     system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
!                         &typdelim, &typelem, &proc, &typalign);

      /* Allocate temporary array for new values */
      values = (Datum *) palloc(nitems * sizeof(Datum));
--- 1976,2056 ----
      if (nitems <= 0)
          PG_RETURN_ARRAYTYPE_P(v);

!     /*
!      * We arrange to look up info about input and return element types only
!      * once per series of calls, assuming the element type doesn't change
!      * underneath us.
!      */
!     my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(am_extra));
!         my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
!
!         my_extra->inp_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         inp_extra = my_extra->inp_extra;
!         inp_extra->element_type = InvalidOid;
!
!         my_extra->ret_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         ret_extra = my_extra->ret_extra;
!         ret_extra->element_type = InvalidOid;
!     }
!     else
!     {
!         inp_extra = my_extra->inp_extra;
!         ret_extra = my_extra->ret_extra;
!     }
!
!     if (inp_extra->element_type != inpType)
!     {
!         /* Lookup source and result types. Unneeded variables are reused. */
!         get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
!                             &typdelim, &typelem, &proc, &inp_typalign);
!
!         inp_extra->element_type = inpType;
!         inp_extra->typlen = inp_typlen;
!         inp_extra->typbyval = inp_typbyval;
!         inp_extra->typdelim = typdelim;
!         inp_extra->typelem = typelem;
!         inp_extra->typiofunc = proc;
!         inp_extra->typalign = inp_typalign;
!     }
!     else
!     {
!         inp_typlen = inp_extra->typlen;
!         inp_typbyval = inp_extra->typbyval;
!         typdelim = inp_extra->typdelim;
!         typelem = inp_extra->typelem;
!         proc = inp_extra->typiofunc;
!         inp_typalign = inp_extra->typalign;
!     }
!
!     if (ret_extra->element_type != retType)
!     {
!         /* Lookup source and result types. Unneeded variables are reused. */
!         get_type_metadata(retType, IOFunc_input, &typlen, &typbyval,
!                             &typdelim, &typelem, &proc, &typalign);
!
!         ret_extra->element_type = retType;
!         ret_extra->typlen = typlen;
!         ret_extra->typbyval = typbyval;
!         ret_extra->typdelim = typdelim;
!         ret_extra->typelem = typelem;
!         ret_extra->typiofunc = proc;
!         ret_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = ret_extra->typlen;
!         typbyval = ret_extra->typbyval;
!         typdelim = ret_extra->typdelim;
!         typelem = ret_extra->typelem;
!         proc = ret_extra->typiofunc;
!         typalign = ret_extra->typalign;
!     }

      /* Allocate temporary array for new values */
      values = (Datum *) palloc(nitems * sizeof(Datum));
***************
*** 2049,2056 ****
   *          compares two arrays for equality
   * result :
   *          returns true if the arrays are equal, false otherwise.
-  *
-  * XXX bitwise equality is pretty bogus ...
   *-----------------------------------------------------------------------------
   */
  Datum
--- 2266,2271 ----
***************
*** 2058,2069 ****
  {
      ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
      ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
      bool        result = true;

!     if (ARR_SIZE(array1) != ARR_SIZE(array2))
!         result = false;
!     else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
          result = false;

      /* Avoid leaking memory when handed toasted input. */
      PG_FREE_IF_COPY(array1, 0);
--- 2273,2390 ----
  {
      ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
      ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
+     char       *p1 = (char *) ARR_DATA_PTR(array1);
+     char       *p2 = (char *) ARR_DATA_PTR(array2);
+     int            ndims1 = ARR_NDIM(array1);
+     int            ndims2 = ARR_NDIM(array2);
+     int           *dims1 = ARR_DIMS(array1);
+     int           *dims2 = ARR_DIMS(array2);
+     int            nitems1 = ArrayGetNItems(ndims1, dims1);
+     int            nitems2 = ArrayGetNItems(ndims2, dims2);
+     Oid            element_type = ARR_ELEMTYPE(array1);
+     FmgrInfo   *fmgr_info = fcinfo->flinfo;
      bool        result = true;
+     int            typlen;
+     bool        typbyval;
+     char        typdelim;
+     Oid            typelem;
+     char        typalign;
+     Oid            typiofunc;
+     int            i;
+     ArrayMetaState *my_extra;
+     FunctionCallInfoData locfcinfo;

!     /* fast path if the arrays do not have the same number of elements */
!     if (nitems1 != nitems2)
          result = false;
+     else
+     {
+         /*
+          * We arrange to look up the equality function only once per series of
+          * calls, assuming the element type doesn't change underneath us.
+          */
+         my_extra = (ArrayMetaState *) fmgr_info->fn_extra;
+         if (my_extra == NULL)
+         {
+             fmgr_info->fn_extra = MemoryContextAlloc(fmgr_info->fn_mcxt,
+                                                      sizeof(ArrayMetaState));
+             my_extra = (ArrayMetaState *) fmgr_info->fn_extra;
+             my_extra->element_type = InvalidOid;
+         }
+
+         if (my_extra->element_type != element_type)
+         {
+             Oid        opfuncid = equality_oper_funcid(element_type);
+
+             if (OidIsValid(opfuncid))
+                 fmgr_info_cxt(opfuncid, &my_extra->proc, fmgr_info->fn_mcxt);
+             else
+                 elog(ERROR,
+                      "array_eq: cannot find equality operator for type: %u",
+                      element_type);
+
+             get_type_metadata(element_type, IOFunc_output,
+                               &typlen, &typbyval, &typdelim,
+                               &typelem, &typiofunc, &typalign);
+
+             my_extra->element_type = element_type;
+             my_extra->typlen = typlen;
+             my_extra->typbyval = typbyval;
+             my_extra->typdelim = typdelim;
+             my_extra->typelem = typelem;
+             my_extra->typiofunc = typiofunc;
+             my_extra->typalign = typalign;
+         }
+         else
+         {
+             typlen = my_extra->typlen;
+             typbyval = my_extra->typbyval;
+             typdelim = my_extra->typdelim;
+             typelem = my_extra->typelem;
+             typiofunc = my_extra->typiofunc;
+             typalign = my_extra->typalign;
+         }
+
+         /*
+          * apply the operator to each pair of array elements.
+          */
+         MemSet(&locfcinfo, 0, sizeof(locfcinfo));
+         locfcinfo.flinfo = &my_extra->proc;
+         locfcinfo.nargs = 2;
+
+         /* Loop over source data */
+         for (i = 0; i < nitems1; i++)
+         {
+             Datum    elt1;
+             Datum    elt2;
+             bool    oprresult;
+
+             /* Get element pair */
+             elt1 = fetch_att(p1, typbyval, typlen);
+             elt2 = fetch_att(p2, typbyval, typlen);
+
+             p1 = att_addlength(p1, typlen, PointerGetDatum(p1));
+             p1 = (char *) att_align(p1, typalign);
+
+             p2 = att_addlength(p2, typlen, PointerGetDatum(p2));
+             p2 = (char *) att_align(p2, typalign);
+
+             /*
+              * Apply the operator to the element pair
+              */
+             locfcinfo.arg[0] = elt1;
+             locfcinfo.arg[1] = elt2;
+             locfcinfo.argnull[0] = false;
+             locfcinfo.argnull[1] = false;
+             locfcinfo.isnull = false;
+             oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+             if (!oprresult)
+             {
+                 result = false;
+                 break;
+             }
+         }
+     }

      /* Avoid leaking memory when handed toasted input. */
      PG_FREE_IF_COPY(array1, 0);
***************
*** 2077,2125 ****
  /******************|          Support  Routines              |*****************/
  /***************************************************************************/

- static void
- system_cache_lookup(Oid element_type,
-                     IOFuncSelector which_func,
-                     int *typlen,
-                     bool *typbyval,
-                     char *typdelim,
-                     Oid *typelem,
-                     Oid *proc,
-                     char *typalign)
- {
-     HeapTuple    typeTuple;
-     Form_pg_type typeStruct;
-
-     typeTuple = SearchSysCache(TYPEOID,
-                                ObjectIdGetDatum(element_type),
-                                0, 0, 0);
-     if (!HeapTupleIsValid(typeTuple))
-         elog(ERROR, "cache lookup failed for type %u", element_type);
-     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
-
-     *typlen = typeStruct->typlen;
-     *typbyval = typeStruct->typbyval;
-     *typdelim = typeStruct->typdelim;
-     *typelem = typeStruct->typelem;
-     *typalign = typeStruct->typalign;
-     switch (which_func)
-     {
-         case IOFunc_input:
-             *proc = typeStruct->typinput;
-             break;
-         case IOFunc_output:
-             *proc = typeStruct->typoutput;
-             break;
-         case IOFunc_receive:
-             *proc = typeStruct->typreceive;
-             break;
-         case IOFunc_send:
-             *proc = typeStruct->typsend;
-             break;
-     }
-     ReleaseSysCache(typeTuple);
- }
-
  /*
   * Fetch array element at pointer, converted correctly to a Datum
   */
--- 2398,2403 ----
***************
*** 2453,2456 ****
--- 2731,2849 ----
      locfcinfo.arg[0] = PointerGetDatum(src);

      return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
+ }
+
+ /*
+  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
+  *
+  *    astate is working state (NULL on first call)
+  *    rcontext is where to keep working state
+  */
+ ArrayBuildState *
+ accumArrayResult(ArrayBuildState *astate,
+                  Datum dvalue, bool disnull,
+                  Oid element_type,
+                  MemoryContext rcontext)
+ {
+     MemoryContext arr_context,
+                   oldcontext;
+
+     if (astate == NULL)
+     {
+         /* First time through --- initialize */
+
+         /* Make a temporary context to hold all the junk */
+         arr_context = AllocSetContextCreate(rcontext,
+                                             "accumArrayResult",
+                                             ALLOCSET_DEFAULT_MINSIZE,
+                                             ALLOCSET_DEFAULT_INITSIZE,
+                                             ALLOCSET_DEFAULT_MAXSIZE);
+         oldcontext = MemoryContextSwitchTo(arr_context);
+         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
+         astate->mcontext = arr_context;
+         astate->dvalues = (Datum *)
+             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
+         astate->nelems = 0;
+         astate->element_type = element_type;
+         get_typlenbyvalalign(element_type,
+                              &astate->typlen,
+                              &astate->typbyval,
+                              &astate->typalign);
+     }
+     else
+     {
+         oldcontext = MemoryContextSwitchTo(astate->mcontext);
+         Assert(astate->element_type == element_type);
+         /* enlarge dvalues[] if needed */
+         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
+             astate->dvalues = (Datum *)
+                 repalloc(astate->dvalues,
+                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
+     }
+
+     if (disnull)
+         elog(ERROR, "NULL elements not allowed in Arrays");
+
+     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
+     astate->dvalues[astate->nelems++] =
+         datumCopy(dvalue, astate->typbyval, astate->typlen);
+
+     MemoryContextSwitchTo(oldcontext);
+
+     return astate;
+ }
+
+ /*
+  * makeArrayResult - produce final result of accumArrayResult
+  *
+  *    astate is working state (not NULL)
+  *    rcontext is where to construct result
+  */
+ Datum
+ makeArrayResult(ArrayBuildState *astate,
+                 MemoryContext rcontext)
+ {
+     int            dims[1];
+     int            lbs[1];
+
+     dims[0] = astate->nelems;
+     lbs[0] = 1;
+
+     return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
+ }
+
+ /*
+  * makeMdArrayResult - produce md final result of accumArrayResult
+  *
+  *    astate is working state (not NULL)
+  *    rcontext is where to construct result
+  */
+ Datum
+ makeMdArrayResult(ArrayBuildState *astate,
+                 int ndims,
+                 int *dims,
+                 int *lbs,
+                 MemoryContext rcontext)
+ {
+     ArrayType  *result;
+     MemoryContext oldcontext;
+
+     /* Build the final array result in rcontext */
+     oldcontext = MemoryContextSwitchTo(rcontext);
+
+     result = construct_md_array(astate->dvalues,
+                                 ndims,
+                                 dims,
+                                 lbs,
+                                 astate->element_type,
+                                 astate->typlen,
+                                 astate->typbyval,
+                                 astate->typalign);
+
+     MemoryContextSwitchTo(oldcontext);
+
+     /* Clean up all the junk */
+     MemoryContextDelete(astate->mcontext);
+
+     return PointerGetDatum(result);
  }
Index: src/backend/utils/adt/varlena.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/varlena.c,v
retrieving revision 1.98
diff -c -r1.98 varlena.c
*** src/backend/utils/adt/varlena.c    15 May 2003 15:50:19 -0000    1.98
--- src/backend/utils/adt/varlena.c    23 May 2003 00:11:27 -0000
***************
*** 19,29 ****
--- 19,32 ----
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
  #include "access/tuptoaster.h"
+ #include "catalog/pg_type.h"
  #include "lib/stringinfo.h"
  #include "libpq/crypt.h"
  #include "libpq/pqformat.h"
+ #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/pg_locale.h"
+ #include "utils/lsyscache.h"


  typedef struct varlena unknown;
***************
*** 1983,1990 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

--- 1986,1992 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

***************
*** 2004,2011 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
--- 2006,2012 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
***************
*** 2026,2031 ****
--- 2027,2217 ----
          result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn -
fldsep_len,false); 
          PG_RETURN_TEXT_P(result_text);
      }
+ }
+
+ /*
+  * text_to_array
+  * parse input string
+  * return text array of elements
+  * based on provided field separator
+  */
+ Datum
+ text_to_array(PG_FUNCTION_ARGS)
+ {
+     text       *inputstring = PG_GETARG_TEXT_P(0);
+     int            inputstring_len = TEXTLEN(inputstring);
+     text       *fldsep = PG_GETARG_TEXT_P(1);
+     int            fldsep_len = TEXTLEN(fldsep);
+     int            fldnum;
+     int            start_posn = 0;
+     int            end_posn = 0;
+     text       *result_text = NULL;
+     ArrayBuildState *astate = NULL;
+     MemoryContext oldcontext = CurrentMemoryContext;
+
+     /* return NULL for empty input string */
+     if (inputstring_len < 1)
+         PG_RETURN_NULL();
+
+     /* empty field separator
+      * return one element, 1D, array using the input string */
+     if (fldsep_len < 1)
+         PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+                               CStringGetDatum(inputstring), 1));
+
+     /* start with end position holding the initial start position */
+     end_posn = 0;
+     for (fldnum=1;;fldnum++)    /* field number is 1 based */
+     {
+         Datum    dvalue;
+         bool    disnull = false;
+
+         start_posn = end_posn;
+         end_posn = text_position(PointerGetDatum(inputstring),
+                                  PointerGetDatum(fldsep),
+                                  fldnum);
+
+         if ((start_posn == 0) && (end_posn == 0))    /* fldsep not found */
+         {
+             if (fldnum == 1)
+             {
+                 /* first element
+                  * return one element, 1D, array using the input string */
+                 PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+                                       CStringGetDatum(inputstring), 1));
+             }
+             else
+             {
+                 /* otherwise create array and exit */
+                 PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
+             }
+         }
+         else if ((start_posn != 0) && (end_posn == 0))
+         {
+             /* last field requested */
+             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
+         }
+         else if ((start_posn == 0) && (end_posn != 0))
+         {
+             /* first field requested */
+             result_text = LEFT(inputstring, fldsep);
+         }
+         else
+         {
+             /* prior to last field requested */
+             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn
-fldsep_len, false); 
+         }
+
+         /* stash away current value */
+         dvalue = PointerGetDatum(result_text);
+         astate = accumArrayResult(astate, dvalue,
+                                   disnull, TEXTOID, oldcontext);
+
+     }
+
+     /* never reached -- keep compiler quiet */
+     PG_RETURN_NULL();
+ }
+
+ /*
+  * array_to_text
+  * concatenate Cstring representation of input array elements
+  * using provided field separator
+  */
+ Datum
+ array_to_text(PG_FUNCTION_ARGS)
+ {
+     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
+     char       *fldsep = PG_TEXTARG_GET_STR(1);
+     int            nitems, *dims, ndims;
+     char       *p;
+     Oid            element_type;
+     int            typlen;
+     bool        typbyval;
+     char        typdelim;
+     Oid            typoutput,
+                 typelem;
+     FmgrInfo    outputproc;
+     char        typalign;
+     StringInfo    result_str = makeStringInfo();
+     int            i;
+     ArrayMetaState *my_extra;
+
+     p = ARR_DATA_PTR(v);
+     ndims = ARR_NDIM(v);
+     dims = ARR_DIMS(v);
+     nitems = ArrayGetNItems(ndims, dims);
+
+     /* if there are no elements, return an empty string */
+     if (nitems == 0)
+         PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
+
+     element_type = ARR_ELEMTYPE(v);
+
+     /*
+      * We arrange to look up info about element type, including its output
+      * conversion proc only once per series of calls, assuming the element
+      * type doesn't change underneath us.
+      */
+     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+     if (my_extra == NULL)
+     {
+         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+                                                      sizeof(ArrayMetaState));
+         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+         my_extra->element_type = InvalidOid;
+     }
+
+     if (my_extra->element_type != element_type)
+     {
+         /* Get info about element type, including its output conversion proc */
+         get_type_metadata(element_type, IOFunc_output,
+                             &typlen, &typbyval, &typdelim,
+                             &typelem, &typoutput, &typalign);
+         fmgr_info(typoutput, &outputproc);
+
+         my_extra->element_type = element_type;
+         my_extra->typlen = typlen;
+         my_extra->typbyval = typbyval;
+         my_extra->typdelim = typdelim;
+         my_extra->typelem = typelem;
+         my_extra->typiofunc = typoutput;
+         my_extra->typalign = typalign;
+         my_extra->proc = outputproc;
+     }
+     else
+     {
+         typlen = my_extra->typlen;
+         typbyval = my_extra->typbyval;
+         typdelim = my_extra->typdelim;
+         typelem = my_extra->typelem;
+         typoutput = my_extra->typiofunc;
+         typalign = my_extra->typalign;
+         outputproc = my_extra->proc;
+     }
+
+     for (i = 0; i < nitems; i++)
+     {
+         Datum        itemvalue;
+         char       *value;
+
+         itemvalue = fetch_att(p, typbyval, typlen);
+
+         value = DatumGetCString(FunctionCall3(&outputproc,
+                                               itemvalue,
+                                               ObjectIdGetDatum(typelem),
+                                               Int32GetDatum(-1)));
+
+         if (i > 0)
+             appendStringInfo(result_str, "%s%s", fldsep, value);
+         else
+             appendStringInfo(result_str, "%s", value);
+
+         p = att_addlength(p, typlen, PointerGetDatum(p));
+         p = (char *) att_align(p, typalign);
+     }
+
+     PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
  }

  #define HEXBASE 16
Index: src/backend/utils/cache/lsyscache.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v
retrieving revision 1.94
diff -c -r1.94 lsyscache.c
*** src/backend/utils/cache/lsyscache.c    13 May 2003 04:38:58 -0000    1.94
--- src/backend/utils/cache/lsyscache.c    22 May 2003 23:42:01 -0000
***************
*** 969,974 ****
--- 969,1024 ----
      ReleaseSysCache(tp);
  }

+ /*
+  * get_type_metadata
+  *
+  *        A six-fer:    given the type OID, return typlen, typbyval, typalign,
+  *                    typdelim, typelem, IO function Oid. The IO function
+  *                    returned is controlled by IOFuncSelector
+  */
+ void
+ get_type_metadata(Oid element_type,
+                     IOFuncSelector which_func,
+                     int *typlen,
+                     bool *typbyval,
+                     char *typdelim,
+                     Oid *typelem,
+                     Oid *proc,
+                     char *typalign)
+ {
+     HeapTuple    typeTuple;
+     Form_pg_type typeStruct;
+
+     typeTuple = SearchSysCache(TYPEOID,
+                                ObjectIdGetDatum(element_type),
+                                0, 0, 0);
+     if (!HeapTupleIsValid(typeTuple))
+         elog(ERROR, "cache lookup failed for type %u", element_type);
+     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+
+     *typlen = typeStruct->typlen;
+     *typbyval = typeStruct->typbyval;
+     *typdelim = typeStruct->typdelim;
+     *typelem = typeStruct->typelem;
+     *typalign = typeStruct->typalign;
+     switch (which_func)
+     {
+         case IOFunc_input:
+             *proc = typeStruct->typinput;
+             break;
+         case IOFunc_output:
+             *proc = typeStruct->typoutput;
+             break;
+         case IOFunc_receive:
+             *proc = typeStruct->typreceive;
+             break;
+         case IOFunc_send:
+             *proc = typeStruct->typsend;
+             break;
+     }
+     ReleaseSysCache(typeTuple);
+ }
+
  #ifdef NOT_USED
  char
  get_typalign(Oid typid)
Index: src/backend/utils/fmgr/fmgr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/fmgr/fmgr.c,v
retrieving revision 1.68
diff -c -r1.68 fmgr.c
*** src/backend/utils/fmgr/fmgr.c    8 Apr 2003 23:20:02 -0000    1.68
--- src/backend/utils/fmgr/fmgr.c    22 May 2003 23:42:01 -0000
***************
*** 1673,1675 ****
--- 1673,1701 ----

      return exprType((Node *) nth(argnum, args));
  }
+
+ /*
+  * Get the OID of the function or operator
+  *
+  * Returns InvalidOid if information is not available
+  */
+ Oid
+ get_fn_expr_functype(FunctionCallInfo fcinfo)
+ {
+     Node   *expr;
+
+     /*
+      * can't return anything useful if we have no FmgrInfo or if
+      * its fn_expr node has not been initialized
+      */
+     if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
+         return InvalidOid;
+
+     expr = fcinfo->flinfo->fn_expr;
+     if (IsA(expr, FuncExpr))
+         return ((FuncExpr *) expr)->funcid;
+     else if (IsA(expr, OpExpr))
+         return ((OpExpr *) expr)->opno;
+     else
+         return InvalidOid;
+ }
Index: src/include/fmgr.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/fmgr.h,v
retrieving revision 1.27
diff -c -r1.27 fmgr.h
*** src/include/fmgr.h    8 Apr 2003 23:20:04 -0000    1.27
--- src/include/fmgr.h    22 May 2003 23:42:01 -0000
***************
*** 377,385 ****
   * Routines in fmgr.c
   */
  extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
! extern Oid    fmgr_internal_function(const char *proname);
! extern Oid    get_fn_expr_rettype(FunctionCallInfo fcinfo);
! extern Oid    get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);

  /*
   * Routines in dfmgr.c
--- 377,386 ----
   * Routines in fmgr.c
   */
  extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
! extern Oid fmgr_internal_function(const char *proname);
! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo);
! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
! extern Oid get_fn_expr_functype(FunctionCallInfo fcinfo);

  /*
   * Routines in dfmgr.c
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v
retrieving revision 1.300
diff -c -r1.300 pg_proc.h
*** src/include/catalog/pg_proc.h    15 May 2003 15:50:19 -0000    1.300
--- src/include/catalog/pg_proc.h    22 May 2003 23:42:01 -0000
***************
*** 1016,1021 ****
--- 1016,1025 ----
  DESCR("concatenate two arrays");
  DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
  DESCR("coerce array type to another array type");
+ DATA(insert OID = 385 (  str_to_array       PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ ));
+ DESCR("split delimited text into text[]");
+ DATA(insert OID = 386 (  array_to_str       PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ ));
+ DESCR("concatenate array elements, using delimiter, into text");

  DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
  DESCR("I/O");
Index: src/include/utils/array.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v
retrieving revision 1.38
diff -c -r1.38 array.h
*** src/include/utils/array.h    8 May 2003 22:19:57 -0000    1.38
--- src/include/utils/array.h    23 May 2003 00:06:49 -0000
***************
*** 32,37 ****
--- 32,68 ----
      Oid            elemtype;        /* element type OID */
  } ArrayType;

+ typedef struct ArrayBuildState
+ {
+     MemoryContext mcontext;        /* where all the temp stuff is kept */
+     Datum       *dvalues;        /* array of accumulated Datums */
+     /*
+      * The allocated size of dvalues[] is always a multiple of
+      * ARRAY_ELEMS_CHUNKSIZE
+      */
+ #define ARRAY_ELEMS_CHUNKSIZE    64
+     int            nelems;            /* number of valid Datums in dvalues[] */
+     Oid            element_type;    /* data type of the Datums */
+     int16        typlen;            /* needed info about datatype */
+     bool        typbyval;
+     char        typalign;
+ } ArrayBuildState;
+
+ /*
+  * structure to cache type metadata needed for array manipulation
+  */
+ typedef struct ArrayMetaState
+ {
+     Oid                element_type;
+     int                typlen;
+     bool            typbyval;
+     char            typdelim;
+     Oid                typelem;
+     Oid                typiofunc;
+     char            typalign;
+     FmgrInfo        proc;
+ } ArrayMetaState;
+
  /*
   * fmgr macros for array objects
   */
***************
*** 124,130 ****
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
!

  /*
   * prototypes for functions defined in arrayutils.c
--- 155,168 ----
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
! extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
!                                          Datum dvalue, bool disnull,
!                                          Oid element_type,
!                                          MemoryContext rcontext);
! extern Datum makeArrayResult(ArrayBuildState *astate,
!                              MemoryContext rcontext);
! extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
!                                int *dims, int *lbs, MemoryContext rcontext);

  /*
   * prototypes for functions defined in arrayutils.c
***************
*** 146,152 ****
  extern Datum array_accum(PG_FUNCTION_ARGS);
  extern Datum array_cat(PG_FUNCTION_ARGS);

! extern ArrayType *create_singleton_array(Oid element_type,
                                           Datum element,
                                           int ndims);

--- 184,191 ----
  extern Datum array_accum(PG_FUNCTION_ARGS);
  extern Datum array_cat(PG_FUNCTION_ARGS);

! extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
!                                          Oid element_type,
                                           Datum element,
                                           int ndims);

Index: src/include/utils/builtins.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/builtins.h,v
retrieving revision 1.217
diff -c -r1.217 builtins.h
*** src/include/utils/builtins.h    15 May 2003 15:50:20 -0000    1.217
--- src/include/utils/builtins.h    22 May 2003 23:42:01 -0000
***************
*** 539,544 ****
--- 539,546 ----
                        List **namelist);
  extern Datum replace_text(PG_FUNCTION_ARGS);
  extern Datum split_text(PG_FUNCTION_ARGS);
+ extern Datum text_to_array(PG_FUNCTION_ARGS);
+ extern Datum array_to_text(PG_FUNCTION_ARGS);
  extern Datum to_hex32(PG_FUNCTION_ARGS);
  extern Datum to_hex64(PG_FUNCTION_ARGS);
  extern Datum md5_text(PG_FUNCTION_ARGS);
Index: src/include/utils/lsyscache.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v
retrieving revision 1.69
diff -c -r1.69 lsyscache.h
*** src/include/utils/lsyscache.h    9 May 2003 18:08:48 -0000    1.69
--- src/include/utils/lsyscache.h    22 May 2003 23:42:01 -0000
***************
*** 15,20 ****
--- 15,29 ----

  #include "access/htup.h"

+ /* I/O function selector for system_cache_lookup */
+ typedef enum IOFuncSelector
+ {
+     IOFunc_input,
+     IOFunc_output,
+     IOFunc_receive,
+     IOFunc_send
+ } IOFuncSelector;
+
  extern bool op_in_opclass(Oid opno, Oid opclass);
  extern bool op_requires_recheck(Oid opno, Oid opclass);
  extern char *get_attname(Oid relid, AttrNumber attnum);
***************
*** 53,58 ****
--- 62,75 ----
  extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
  extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
                       char *typalign);
+ extern void get_type_metadata(Oid element_type,
+                                 IOFuncSelector which_func,
+                                 int *typlen,
+                                 bool *typbyval,
+                                 char *typdelim,
+                                 Oid *typelem,
+                                 Oid *proc,
+                                 char *typalign);
  extern char get_typstorage(Oid typid);
  extern int32 get_typtypmod(Oid typid);
  extern Node *get_typdefault(Oid typid);

Re: array support patch phase 1 patch

From
Peter Eisentraut
Date:
Joe Conway writes:

> Attached is an updated version of the doc patch along with the addition
> of two new functions, as previously discussed:
>
>     - str_to_array(str TEXT, delim TEXT) returns TEXT[]
>     - array_to_str(array ANYARRAY, delim TEXT) returns TEXT

Please use "string".  No need to introduce a new abbreviation into the
world of function names.

I'm not happy with all these nonstandard new function names being
advertised.  They can serve as internal implementations of standard
operators and other constructs, but it seems pointless to publicly add
redundant and nonstandard interfaces where the standard ones work
perfectly well.

In particular, array_subscript is not needed, users should simply use the
standard subscripting mechanism.  array_prepend, array_cat, array_append
should not be used; instead the standard operator || should be used.
array_accum is not needed because || plus coalesce do the same thing
standardly.  singleton_array strikes me as unnecessary as well.

Also note that "multidimensional array" is the correct spelling and
preferred wording (not "multi-dimensional array" or "multi-dimesion array"
[sic] or "multiple dimension array").

--
Peter Eisentraut   peter_e@gmx.net


Re: array support patch phase 1 patch

From
Joe Conway
Date:
Peter Eisentraut wrote:
> Joe Conway writes:
>>Attached is an updated version of the doc patch along with the addition
>>of two new functions, as previously discussed:
>>
>>    - str_to_array(str TEXT, delim TEXT) returns TEXT[]
>>    - array_to_str(array ANYARRAY, delim TEXT) returns TEXT
>
> Please use "string".  No need to introduce a new abbreviation into the
> world of function names.

It's not new. We already have strpos and substr. But I'm not opposed to
using string_to_array and array_to_string if there's a consensus WRT
those names.

> Also note that "multidimensional array" is the correct spelling and
> preferred wording (not "multi-dimensional array" or "multi-dimesion array"
> [sic] or "multiple dimension array").

"multi-dimensional" is not incorrect by my dictionary, and most existing
examples of the word sprinkled throughout the source tree use this
spelling. Are you proposing that I change all of those too? Again, I'm
not opposed if there is general agreement, but we should at least be
consistent throughout.

In any case I'll correct "multi-dimesion" -- thanks for catching that
(new version of patch attached with spelling correction).  I don't grep
"multiple dimension array" anywhere, so I'm not sure where you got that one.

Joe

# grep -r "multidimensional" *
contrib/cube/README.cube:CUBE, representing multidimensional cubes.
src/backend/utils/adt/arrayfuncs.c:
                  * successively in a multidimensional array.


# grep -r "multi-dimensional" *
contrib/cube/cube.sql.in:COMMENT ON TYPE cube IS 'multi-dimensional cube
''(FLOAT-1, FLOAT-2, ..., FLOAT-N), (FLOAT-1, FLOAT-2, ..., FLOAT-N)''';
doc/FAQ:   single dimension. R-trees can handle multi-dimensional data. For
doc/FAQ_german:   werden. R-Trees können multi-dimensionale Daten
abhandeln. Ein
doc/src/FAQ/FAQ.html:    multi-dimensional data. For example, if an
R-tree index can be
doc/src/FAQ/FAQ_german.html:    werden. R-Trees können
multi-dimensionale Daten abhandeln. Ein
doc/src/sgml/array.sgml:  defined as variable-length multi-dimensional
arrays. Arrays of any
doc/src/sgml/array.sgml:  Note that with this syntax, multi-dimensional
arrays must have matching
doc/src/sgml/array.sgml:  multi-dimensional arrays.
doc/src/sgml/array.sgml:  arrays, but <function>array_cat</function>
supports multi-dimensional arrays.
doc/src/sgml/array.sgml:   use comma.)  In a multi-dimensional array,
each dimension (row, plane,
doc/src/sgml/array.sgml:   When representing multi-dimensional arrays,
the keyword
src/backend/utils/adt/array_userfuncs.c: *              Form a
multi-dimensional array given one starting element.
src/interfaces/ecpg/preproc/preproc.y:
mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for
simple data types");
src/interfaces/ecpg/preproc/preproc.y:
mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for
simple data types");
src/interfaces/ecpg/preproc/preproc.y:
                 mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional
array support for simple data types");
src/interfaces/ecpg/preproc/type.c:
         yyerror("internal error, found multi-dimensional array\n");
src/interfaces/ecpg/preproc/variable.c:
mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
src/interfaces/ecpg/preproc/variable.c:
mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
src/interfaces/ecpg/preproc/variable.c:         mmerror(PARSE_ERROR,
ET_FATAL, "No multi-dimensional array support");
src/interfaces/ecpg/preproc/variable.c:         mmerror(PARSE_ERROR,
ET_FATAL, "No multi-dimensional array support");
src/interfaces/ecpg/preproc/variable.c:
mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for
structures");
src/interfaces/ecpg/preproc/variable.c:
mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for
simple data types");
src/interfaces/ecpg/preproc/preproc.c:
mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for
simple data types");
src/interfaces/ecpg/preproc/preproc.c:
mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for
simple data types");
src/interfaces/ecpg/preproc/preproc.c:
                 mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional
array support for simple data types");
Index: doc/src/sgml/array.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
retrieving revision 1.25
diff -c -r1.25 array.sgml
*** doc/src/sgml/array.sgml    13 Mar 2003 01:30:26 -0000    1.25
--- doc/src/sgml/array.sgml    24 May 2003 13:39:07 -0000
***************
*** 9,15 ****

   <para>
    <productname>PostgreSQL</productname> allows columns of a table to be
!   defined as variable-length multidimensional arrays. Arrays of any
    built-in type or user-defined type can be created.
   </para>

--- 9,15 ----

   <para>
    <productname>PostgreSQL</productname> allows columns of a table to be
!   defined as variable-length multi-dimensional arrays. Arrays of any
    built-in type or user-defined type can be created.
   </para>

***************
*** 60,73 ****
  </programlisting>
   </para>

   <note>
    <para>
!    A limitation of the present array implementation is that individual
!    elements of an array cannot be SQL null values.  The entire array can be set
!    to null, but you can't have an array with some elements null and some
!    not.  Fixing this is on the to-do list.
    </para>
   </note>
   </sect2>

   <sect2>
--- 60,133 ----
  </programlisting>
   </para>

+  <para>
+   A limitation of the present array implementation is that individual
+   elements of an array cannot be SQL null values.  The entire array can be set
+   to null, but you can't have an array with some elements null and some
+   not.
+  </para>
+  <para>
+   This can lead to surprising results. For example, the result of the
+   previous two inserts looks like this:
+ <programlisting>
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |      schedule
+ -------+---------------------------+--------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting},{""}}
+  Carol | {20000,25000,25000,25000} | {{talk},{meeting}}
+ (2 rows)
+ </programlisting>
+   Because the <literal>[2][2]</literal> element of
+   <structfield>schedule</structfield> is missing in each of the
+   <command>INSERT</command> statements, the <literal>[1][2]</literal>
+   element is discarded.
+  </para>
+
+  <note>
+   <para>
+    Fixing this is on the to-do list.
+   </para>
+  </note>
+
+  <para>
+   The <command>ARRAY</command> expression syntax may also be used:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Bill',
+     ARRAY[10000, 10000, 10000, 10000],
+     ARRAY[['meeting', 'lunch'], ['','']]);
+
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting', '']]);
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |           schedule
+ -------+---------------------------+-------------------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}}
+  Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}}
+ (2 rows)
+ </programlisting>
+   Note that with this syntax, multi-dimensional arrays must have matching
+   extents for each dimension. This eliminates the missing-array-elements
+   problem above. For example:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting']]);
+ ERROR:  Multiple dimension arrays must have array expressions with matching dimensions
+ </programlisting>
+   Also notice that string literals are single quoted instead of double quoted.
+  </para>
+
   <note>
    <para>
!    The examples in the rest of this section are based on the
!    <command>ARRAY</command> expression syntax <command>INSERT</command>s.
    </para>
   </note>
+
   </sect2>

   <sect2>
***************
*** 132,142 ****
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the
!   form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified.
   </para>

   <para>
--- 192,221 ----
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified; another example follows:
! <programlisting>
! SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
!          schedule
! ---------------------------
!  {{meeting,lunch},{"",""}}
! (1 row)
! </programlisting>
!  </para>
!
!  <para>
!   Additionally, we can also access a single arbitrary array element of
!   a one-dimensional array with the <function>array_subscript</function>
!   function:
! <programlisting>
! SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
!  array_subscript
! -----------------
!            10000
! (1 row)
! </programlisting>
   </para>

   <para>
***************
*** 147,153 ****
      WHERE name = 'Carol';
  </programlisting>

!   or updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
--- 226,248 ----
      WHERE name = 'Carol';
  </programlisting>

!   or using the <command>ARRAY</command> expression syntax:
!
! <programlisting>
! UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
!     WHERE name = 'Carol';
! </programlisting>
!
!   <note>
!    <para>
!     Anywhere you can use the <quote>curly braces</quote> array syntax,
!     you can also use the <command>ARRAY</command> expression syntax. The
!     remainder of this section will illustrate only one or the other, but
!     not both.
!    </para>
!   </note>
!
!   An array may also be updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
***************
*** 160,165 ****
--- 255,268 ----
  UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
      WHERE name = 'Carol';
  </programlisting>
+
+   A one-dimensional array may also be updated with the
+   <function>array_assign</function> function:
+
+ <programlisting>
+ UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000)
+     WHERE name = 'Bill';
+ </programListing>
   </para>

   <para>
***************
*** 169,175 ****
    value currently has 4 elements, it will have five elements after an
    update that assigns to <literal>array[5]</>.  Currently, enlargement in
    this fashion is only allowed for one-dimensional arrays, not
!   multidimensional arrays.
   </para>

   <para>
--- 272,278 ----
    value currently has 4 elements, it will have five elements after an
    update that assigns to <literal>array[5]</>.  Currently, enlargement in
    this fashion is only allowed for one-dimensional arrays, not
!   multi-dimensional arrays.
   </para>

   <para>
***************
*** 179,184 ****
--- 282,367 ----
   </para>

   <para>
+   An array can also be enlarged by using the functions
+   <function>array_prepend</function>, <function>array_append</function>,
+   or <function>array_cat</function>. The first two only support one-dimensional
+   arrays, but <function>array_cat</function> supports multi-dimensional arrays.
+   Some examples:
+
+ <programlisting>
+ SELECT array_prepend(1, ARRAY[2,3]);
+  array_prepend
+ ---------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_append(ARRAY[1,2], 3);
+  array_append
+ --------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
+    array_cat
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
+       array_cat
+ ---------------------
+  {{1,2},{3,4},{5,6}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
+       array_cat
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ </programlisting>
+
+   <function>array_prepend</function> and <function>array_append</function>
+   work with a one-dimensional array and a single element to be pushed on
+   to the beginning or end of the array, respectively. The array is extended
+   in the direction of the push. Hence, by pushing onto the beginning of an
+   array with a one-based subscript, a zero-based subscript array is created:
+
+ <programlisting>
+ SELECT array_dims(t.f) FROM (SELECT array_prepend(1, ARRAY[2,3]) AS f) AS t;
+  array_dims
+ ------------
+  [0:2]
+ (1 row)
+ </programlisting>
+
+   <function>array_cat</function> works with either two
+   <replaceable>n</>-dimension arrays, or an <replaceable>n</>-dimension
+   and an <replaceable>n+1</> dimension array. In the former case, the two
+   <replaceable>n</>-dimension arrays become outer elements of an
+   <replaceable>n+1</> dimension array. In the latter, the
+   <replaceable>n</>-dimension array is added as either the first or last
+   outer element of the <replaceable>n+1</> dimension array.
+  </para>
+
+  <para>
+   A final method of enlarging arrays is through the concatenation operator,
+   <command>||</command>, which works exactly as <function>array_cat</function>
+   does.
+ <programlisting>
+ SELECT ARRAY[1,2] || ARRAY[3,4];
+    ?column?
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
+       ?column?
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ (1 row)
+ </programlisting>
+  </para>
+
+  <para>
    The syntax for <command>CREATE TABLE</command> allows fixed-length
    arrays to be defined:

***************
*** 194,199 ****
--- 377,392 ----
   </para>

   <para>
+   An alternative syntax for one-dimensional arrays may be used.
+   <structfield>pay_by_quarter</structfield> could have been defined as:
+ <programlisting>
+     pay_by_quarter  integer ARRAY[4],
+ </programlisting>
+   This syntax may <emphasis>only</emphasis> be used with the integer
+   constant to denote the array size.
+  </para>
+
+  <para>
    Actually, the current implementation does not enforce the declared
    number of dimensions either.  Arrays of a particular element type are
    all considered to be of the same type, regardless of size or number
***************
*** 292,298 ****
     for the array's element type.  (Among the standard data types provided
     in the <productname>PostgreSQL</productname> distribution, type
     <literal>box</> uses a semicolon (<literal>;</>) but all the others
!    use comma.)  In a multidimensional array, each dimension (row, plane,
     cube, etc.) gets its own level of curly braces, and delimiters
     must be written between adjacent curly-braced entities of the same level.
     You may write whitespace before a left brace, after a right
--- 485,491 ----
     for the array's element type.  (Among the standard data types provided
     in the <productname>PostgreSQL</productname> distribution, type
     <literal>box</> uses a semicolon (<literal>;</>) but all the others
!    use comma.)  In a multi-dimensional array, each dimension (row, plane,
     cube, etc.) gets its own level of curly braces, and delimiters
     must be written between adjacent curly-braced entities of the same level.
     You may write whitespace before a left brace, after a right
***************
*** 300,305 ****
--- 493,564 ----
     is not ignored, however: after skipping leading whitespace, everything
     up to the next right brace or delimiter is taken as the item value.
    </para>
+
+   <para>
+    As illustrated earlier in this chapter, arrays may also be represented
+    using the <command>ARRAY</command> expression syntax. This representation
+    of an array value consists of items that are interpreted according to the
+    I/O conversion rules for the array's element type, plus decoration that
+    indicates the array structure. The decoration consists of the keyword
+    <command>ARRAY</command> and square brackets (<literal>[</> and
+    <literal>]</>) around the array values, plus delimiter characters between
+    adjacent items. The delimiter character is always a comma (<literal>,</>).
+    When representing multi-dimensional arrays, the keyword
+    <command>ARRAY</command> is only necessary for the outer level. For example,
+    <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as:
+ <programlisting>
+ SELECT ARRAY[['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   or it also could be written as:
+ <programlisting>
+ SELECT ARRAY[ARRAY['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   </para>
+
+   <para>
+    A final method to represent an array, is through an
+    <command>ARRAY</command> sub-select expression. For example:
+ <programlisting>
+ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
+                           ?column?
+ -------------------------------------------------------------
+  {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
+ (1 row)
+ </programlisting>
+   The sub-select may <emphasis>only</emphasis> return a single column. The
+   resulting one-dimensional array will have an element for each row in the
+   sub-select result, with an element type matching that of the sub-select's
+   target column.
+   </para>
+
+   <para>
+    Arrays may be cast from one type to another in similar fashion to other
+    data types:
+
+ <programlisting>
+ SELECT ARRAY[1,2,3]::oid[];
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+
+ SELECT CAST(ARRAY[1,2,3] AS float8[]);
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+ </programlisting>
+
+   </para>
+
   </sect2>

   <sect2>
***************
*** 316,321 ****
--- 575,588 ----
     Alternatively, you can use backslash-escaping to protect all data characters
     that would otherwise be taken as array syntax or ignorable white space.
    </para>
+
+  <note>
+   <para>
+    The discussion in the preceding paragraph with respect to double quoting does
+    not pertain to the <command>ARRAY</command> expression syntax. In that case,
+    each element is quoted exactly as any other literal value of the element type.
+   </para>
+  </note>

    <para>
     The array output routine will put double quotes around element values
Index: doc/src/sgml/func.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v
retrieving revision 1.154
diff -c -r1.154 func.sgml
*** doc/src/sgml/func.sgml    5 May 2003 15:08:49 -0000    1.154
--- doc/src/sgml/func.sgml    23 May 2003 02:05:28 -0000
***************
*** 6962,6967 ****
--- 6962,7224 ----

    </sect1>

+  <sect1 id="functions-array">
+   <title>Array Functions</title>
+
+   <para>
+    <xref linkend="array-operators-table"> shows the operators
+    available for the <type>array</type> types.
+   </para>
+
+     <table id="array-operators-table">
+      <title><type>array</type> Operators</title>
+      <tgroup cols="4">
+       <thead>
+        <row>
+     <entry>Operator</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry> <literal>=</literal> </entry>
+     <entry>equals</entry>
+     <entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry>
+     <entry><literal>t</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>element-to-array concatenation</entry>
+     <entry><literal>3 || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{3,4,5,6}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-element concatenation</entry>
+     <entry><literal>ARRAY[4,5,6] || 7</literal></entry>
+     <entry><literal>{4,5,6,7}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+
+   <para>
+    <xref linkend="array-functions-table"> shows the functions
+    available for use with array types. See <xref linkend="arrays">
+    for more discussion and examples for the use of these functions.
+   </para>
+
+     <table id="array-functions-table">
+      <title><type>array</type> Functions</title>
+      <tgroup cols="5">
+       <thead>
+        <row>
+     <entry>Function</entry>
+     <entry>Return Type</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry>
+      <literal>
+       <function>array_accum</function>
+       (<type>anyarray</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the end of an array, ignoring
+      <literal>NULL</literal> elements, and creating an array if needed
+     </entry>
+     <entry><literal>array_accum(null, 1)</literal></entry>
+     <entry><literal>{1}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_append</function>
+       (<type>anyarray</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the end of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_append(ARRAY[1,2], 3)</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_assign</function>
+       (<type>anyarray</type>, <type>integer</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      assign a value to a specific array element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_assign(ARRAY[1,2,3], 2, 99)</literal></entry>
+     <entry><literal>{1,99,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_cat</function>
+       (<type>anyarray</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      concatenate two arrays, returning <literal>NULL</literal>
+      for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_dims</function>
+       (<type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      returns a text representation of array dimension lower and upper bounds,
+      generating an ERROR for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry>
+     <entry><literal>[1:2][1:3]</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_lower</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns lower bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry>
+     <entry><literal>0</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_prepend</function>
+       (<type>anyelement</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the beginning of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_subscript</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>anyelement</type></entry>
+     <entry>
+      returns requested array element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_subscript(ARRAY[1,2,3], 3)</literal></entry>
+     <entry><literal>3</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_to_str</function>
+       (<type>anyarray</type>, <type>text</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      concatenates array elements using provided delimiter, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_to_str(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry>
+     <entry><literal>1.10~^~2.20~^~3.30</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_upper</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns upper bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry>
+     <entry><literal>4</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>singleton_array</function>
+       (<type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      create an array from the provided element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>singleton_array(1)</literal></entry>
+     <entry><literal>{1}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>str_to_array</function>
+       (<type>text</type>, <type>text</type>)
+      </literal>
+     </entry>
+     <entry><type>text[]</type></entry>
+     <entry>
+      splits string into array elements using provided delimiter, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>str_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry>
+     <entry><literal>{1.1,2.2,3.3}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+   </sect1>

   <sect1 id="functions-aggregate">
    <title>Aggregate Functions</title>
Index: src/backend/executor/nodeSubplan.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v
retrieving revision 1.45
diff -c -r1.45 nodeSubplan.c
*** src/backend/executor/nodeSubplan.c    8 Apr 2003 23:20:01 -0000    1.45
--- src/backend/executor/nodeSubplan.c    23 May 2003 02:05:28 -0000
***************
*** 28,50 ****
  #include "utils/datum.h"
  #include "utils/lsyscache.h"

-
- typedef struct ArrayBuildState
- {
-     MemoryContext mcontext;        /* where all the temp stuff is kept */
-     Datum       *dvalues;        /* array of accumulated Datums */
-     /*
-      * The allocated size of dvalues[] is always a multiple of
-      * ARRAY_ELEMS_CHUNKSIZE
-      */
- #define ARRAY_ELEMS_CHUNKSIZE    64
-     int            nelems;            /* number of valid Datums in dvalues[] */
-     Oid            element_type;    /* data type of the Datums */
-     int16        typlen;            /* needed info about datatype */
-     bool        typbyval;
-     char        typalign;
- } ArrayBuildState;
-
  static Datum ExecHashSubPlan(SubPlanState *node,
                               ExprContext *econtext,
                               bool *isNull);
--- 28,33 ----
***************
*** 54,66 ****
  static void buildSubPlanHash(SubPlanState *node);
  static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
  static bool tupleAllNulls(HeapTuple tuple);
- static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
-                                          Datum dvalue, bool disnull,
-                                          Oid element_type,
-                                          MemoryContext rcontext);
- static Datum makeArrayResult(ArrayBuildState *astate,
-                              MemoryContext rcontext);
-

  /* ----------------------------------------------------------------
   *        ExecSubPlan
--- 37,42 ----
***************
*** 1084,1185 ****
          prm->execPlan = node;
          parent->chgParam = bms_add_member(parent->chgParam, paramid);
      }
- }
-
- /*
-  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
-  *
-  *    astate is working state (NULL on first call)
-  *    rcontext is where to keep working state
-  */
- static ArrayBuildState *
- accumArrayResult(ArrayBuildState *astate,
-                  Datum dvalue, bool disnull,
-                  Oid element_type,
-                  MemoryContext rcontext)
- {
-     MemoryContext arr_context,
-                   oldcontext;
-
-     if (astate == NULL)
-     {
-         /* First time through --- initialize */
-
-         /* Make a temporary context to hold all the junk */
-         arr_context = AllocSetContextCreate(rcontext,
-                                             "ARRAY_SUBLINK Result",
-                                             ALLOCSET_DEFAULT_MINSIZE,
-                                             ALLOCSET_DEFAULT_INITSIZE,
-                                             ALLOCSET_DEFAULT_MAXSIZE);
-         oldcontext = MemoryContextSwitchTo(arr_context);
-         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
-         astate->mcontext = arr_context;
-         astate->dvalues = (Datum *)
-             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
-         astate->nelems = 0;
-         astate->element_type = element_type;
-         get_typlenbyvalalign(element_type,
-                              &astate->typlen,
-                              &astate->typbyval,
-                              &astate->typalign);
-     }
-     else
-     {
-         oldcontext = MemoryContextSwitchTo(astate->mcontext);
-         Assert(astate->element_type == element_type);
-         /* enlarge dvalues[] if needed */
-         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
-             astate->dvalues = (Datum *)
-                 repalloc(astate->dvalues,
-                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
-     }
-
-     if (disnull)
-         elog(ERROR, "NULL elements not allowed in Arrays");
-
-     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
-     astate->dvalues[astate->nelems++] =
-         datumCopy(dvalue, astate->typbyval, astate->typlen);
-
-     MemoryContextSwitchTo(oldcontext);
-
-     return astate;
- }
-
- /*
-  * makeArrayResult - produce final result of ARRAY_SUBLINK
-  *
-  *    astate is working state (not NULL)
-  *    rcontext is where to construct result
-  */
- static Datum
- makeArrayResult(ArrayBuildState *astate,
-                 MemoryContext rcontext)
- {
-     ArrayType  *result;
-     int            dims[1];
-     int            lbs[1];
-     MemoryContext oldcontext;
-
-     /* Build the final array result in rcontext */
-     oldcontext = MemoryContextSwitchTo(rcontext);
-
-     dims[0] = astate->nelems;
-     lbs[0] = 1;
-
-     result = construct_md_array(astate->dvalues,
-                                 1,
-                                 dims,
-                                 lbs,
-                                 astate->element_type,
-                                 astate->typlen,
-                                 astate->typbyval,
-                                 astate->typalign);
-
-     MemoryContextSwitchTo(oldcontext);
-
-     /* Clean up all the junk */
-     MemoryContextDelete(astate->mcontext);
-
-     return PointerGetDatum(result);
  }
--- 1060,1063 ----
Index: src/backend/utils/adt/array_userfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/array_userfuncs.c,v
retrieving revision 1.1
diff -c -r1.1 array_userfuncs.c
*** src/backend/utils/adt/array_userfuncs.c    8 Apr 2003 23:20:02 -0000    1.1
--- src/backend/utils/adt/array_userfuncs.c    23 May 2003 02:05:28 -0000
***************
*** 42,48 ****
      else
          ndims = 1;

!     PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
                                                   PG_GETARG_DATUM(0),
                                                   ndims));
  }
--- 42,48 ----
      else
          ndims = 1;

!     PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, elem_type,
                                                   PG_GETARG_DATUM(0),
                                                   ndims));
  }
***************
*** 70,75 ****
--- 70,76 ----
      Oid            arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
      Oid            arg0_elemid;
      Oid            arg1_elemid;
+     ArrayMetaState *my_extra;

      if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
          elog(ERROR, "array_push: cannot determine input data types");
***************
*** 113,119 ****
          indx = lb[0] - 1;
      }

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      result = array_set(v, 1, &indx, newelem, -1,
                         typlen, typbyval, typalign, &isNull);
--- 114,148 ----
          indx = lb[0] - 1;
      }

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      result = array_set(v, 1, &indx, newelem, -1,
                         typlen, typbyval, typalign, &isNull);
***************
*** 293,299 ****
          if (tgt_elem_type == InvalidOid)
              elog(ERROR, "Target type is not an array");

!         PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
                                                       PG_GETARG_DATUM(1),
                                                       1));
      }
--- 322,328 ----
          if (tgt_elem_type == InvalidOid)
              elog(ERROR, "Target type is not an array");

!         PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, tgt_elem_type,
                                                       PG_GETARG_DATUM(1),
                                                       1));
      }
***************
*** 329,334 ****
--- 358,364 ----
      int16        typlen;
      bool        typbyval;
      char        typalign;
+     ArrayMetaState *my_extra;

      v = PG_GETARG_ARRAYTYPE_P(0);
      idx_to_chg = PG_GETARG_INT32(1);
***************
*** 349,355 ****
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      result = array_set(v, 1, &idx_to_chg, newelem, -1,
                         typlen, typbyval, typalign, &isNull);
--- 379,413 ----
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      result = array_set(v, 1, &idx_to_chg, newelem, -1,
                         typlen, typbyval, typalign, &isNull);
***************
*** 375,380 ****
--- 433,439 ----
      int16        typlen;
      bool        typbyval;
      char        typalign;
+     ArrayMetaState *my_extra;

      v = PG_GETARG_ARRAYTYPE_P(0);
      idx = PG_GETARG_INT32(1);
***************
*** 394,400 ****
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);

--- 453,487 ----
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);

***************
*** 406,412 ****
   * given a null input array.
   */
  ArrayType *
! create_singleton_array(Oid element_type, Datum element, int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
--- 493,502 ----
   * given a null input array.
   */
  ArrayType *
! create_singleton_array(FunctionCallInfo fcinfo,
!                        Oid element_type,
!                        Datum element,
!                        int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
***************
*** 415,420 ****
--- 505,511 ----
      int        dims[MAXDIM];
      int        lbs[MAXDIM];
      int        i;
+     ArrayMetaState *my_extra;

      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);
***************
*** 429,435 ****
          lbs[i] = 1;
      }

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      return construct_md_array(dvalues, ndims, dims, lbs, element_type,
                                typlen, typbyval, typalign);
--- 520,554 ----
          lbs[i] = 1;
      }

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      return construct_md_array(dvalues, ndims, dims, lbs, element_type,
                                typlen, typbyval, typalign);
Index: src/backend/utils/adt/arrayfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v
retrieving revision 1.89
diff -c -r1.89 arrayfuncs.c
*** src/backend/utils/adt/arrayfuncs.c    9 May 2003 23:01:45 -0000    1.89
--- src/backend/utils/adt/arrayfuncs.c    23 May 2003 02:05:28 -0000
***************
*** 21,28 ****
--- 21,30 ----
  #include "catalog/pg_type.h"
  #include "libpq/pqformat.h"
  #include "parser/parse_coerce.h"
+ #include "parser/parse_oper.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
+ #include "utils/datum.h"
  #include "utils/memutils.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
***************
*** 70,85 ****

  #define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)

- /* I/O function selector for system_cache_lookup */
- typedef enum IOFuncSelector
- {
-     IOFunc_input,
-     IOFunc_output,
-     IOFunc_receive,
-     IOFunc_send
- } IOFuncSelector;
-
-
  static int    ArrayCount(char *str, int *dim, char typdelim);
  static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
               FmgrInfo *inputproc, Oid typelem, int32 typmod,
--- 72,77 ----
***************
*** 93,102 ****
  static void CopyArrayEls(char *p, Datum *values, int nitems,
               int typlen, bool typbyval, char typalign,
               bool freedata);
- static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
-                                 int *typlen, bool *typbyval,
-                                 char *typdelim, Oid *typelem,
-                                 Oid *proc, char *typalign);
  static Datum ArrayCast(char *value, bool byval, int len);
  static int ArrayCastAndSet(Datum src,
                  int typlen, bool typbyval, char typalign,
--- 85,90 ----
***************
*** 154,165 ****
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;

!     /* Get info about element type, including its input conversion proc */
!     system_cache_lookup(element_type, IOFunc_input,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typinput, &typalign);
!     fmgr_info(typinput, &inputproc);

      /* Make a modifiable copy of the input */
      /* XXX why are we allocating an extra 2 bytes here? */
--- 142,190 ----
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;
+     ArrayMetaState *my_extra;

!     /*
!      * We arrange to look up info about element type, including its input
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its input conversion proc */
!         get_type_metadata(element_type, IOFunc_input,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typinput, &typalign);
!         fmgr_info(typinput, &inputproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typinput;
!         my_extra->typalign = typalign;
!         my_extra->proc = inputproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typinput = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         inputproc = my_extra->proc;
!     }

      /* Make a modifiable copy of the input */
      /* XXX why are we allocating an extra 2 bytes here? */
***************
*** 636,647 ****
                  indx[MAXDIM];
      int            ndim,
                 *dim;

      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_output,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typoutput, &typalign);
!     fmgr_info(typoutput, &outputproc);

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
--- 661,711 ----
                  indx[MAXDIM];
      int            ndim,
                 *dim;
+     ArrayMetaState *my_extra;

      element_type = ARR_ELEMTYPE(v);
!
!     /*
!      * We arrange to look up info about element type, including its input
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its output conversion proc */
!         get_type_metadata(element_type, IOFunc_output,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typoutput, &typalign);
!         fmgr_info(typoutput, &outputproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typoutput;
!         my_extra->typalign = typalign;
!         my_extra->proc = outputproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typoutput = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         outputproc = my_extra->proc;
!     }

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
***************
*** 800,805 ****
--- 864,870 ----
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;
+     ArrayMetaState *my_extra;

      /* Get the array header information */
      ndim = pq_getmsgint(buf, 4);
***************
*** 831,844 ****
          PG_RETURN_ARRAYTYPE_P(retval);
      }

!     /* Get info about element type, including its receive conversion proc */
!     system_cache_lookup(element_type, IOFunc_receive,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typreceive, &typalign);
!     if (!OidIsValid(typreceive))
!         elog(ERROR, "No binary input function available for type %s",
!              format_type_be(element_type));
!     fmgr_info(typreceive, &receiveproc);

      dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
                                typlen, typbyval, typalign,
--- 896,945 ----
          PG_RETURN_ARRAYTYPE_P(retval);
      }

!     /*
!      * We arrange to look up info about element type, including its receive
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its receive conversion proc */
!         get_type_metadata(element_type, IOFunc_receive,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typreceive, &typalign);
!         if (!OidIsValid(typreceive))
!             elog(ERROR, "No binary input function available for type %s",
!                  format_type_be(element_type));
!         fmgr_info(typreceive, &receiveproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typreceive;
!         my_extra->typalign = typalign;
!         my_extra->proc = receiveproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typreceive = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         receiveproc = my_extra->proc;
!     }

      dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
                                typlen, typbyval, typalign,
***************
*** 976,990 ****
      int            ndim,
                 *dim;
      StringInfoData buf;

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
!                         &typdelim, &typelem, &typsend, &typalign);
!     if (!OidIsValid(typsend))
!         elog(ERROR, "No binary output function available for type %s",
!              format_type_be(element_type));
!     fmgr_info(typsend, &sendproc);

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
--- 1077,1130 ----
      int            ndim,
                 *dim;
      StringInfoData buf;
+     ArrayMetaState *my_extra;

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!
!     /*
!      * We arrange to look up info about element type, including its send
!      * proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its send proc */
!         get_type_metadata(element_type, IOFunc_send, &typlen, &typbyval,
!                             &typdelim, &typelem, &typsend, &typalign);
!         if (!OidIsValid(typsend))
!             elog(ERROR, "No binary output function available for type %s",
!                  format_type_be(element_type));
!         fmgr_info(typsend, &sendproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typsend;
!         my_extra->typalign = typalign;
!         my_extra->proc = sendproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typsend = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         sendproc = my_extra->proc;
!     }

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
***************
*** 1811,1816 ****
--- 1951,1963 ----
      Oid            typelem;
      Oid            proc;
      char       *s;
+     typedef struct {
+         ArrayMetaState *inp_extra;
+         ArrayMetaState *ret_extra;
+     } am_extra;
+     am_extra  *my_extra;
+     ArrayMetaState *inp_extra;
+     ArrayMetaState *ret_extra;

      /* Get input array */
      if (fcinfo->nargs < 1)
***************
*** 1829,1839 ****
      if (nitems <= 0)
          PG_RETURN_ARRAYTYPE_P(v);

!     /* Lookup source and result types. Unneeded variables are reused. */
!     system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
!                         &typdelim, &typelem, &proc, &inp_typalign);
!     system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
!                         &typdelim, &typelem, &proc, &typalign);

      /* Allocate temporary array for new values */
      values = (Datum *) palloc(nitems * sizeof(Datum));
--- 1976,2056 ----
      if (nitems <= 0)
          PG_RETURN_ARRAYTYPE_P(v);

!     /*
!      * We arrange to look up info about input and return element types only
!      * once per series of calls, assuming the element type doesn't change
!      * underneath us.
!      */
!     my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(am_extra));
!         my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
!
!         my_extra->inp_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         inp_extra = my_extra->inp_extra;
!         inp_extra->element_type = InvalidOid;
!
!         my_extra->ret_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         ret_extra = my_extra->ret_extra;
!         ret_extra->element_type = InvalidOid;
!     }
!     else
!     {
!         inp_extra = my_extra->inp_extra;
!         ret_extra = my_extra->ret_extra;
!     }
!
!     if (inp_extra->element_type != inpType)
!     {
!         /* Lookup source and result types. Unneeded variables are reused. */
!         get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
!                             &typdelim, &typelem, &proc, &inp_typalign);
!
!         inp_extra->element_type = inpType;
!         inp_extra->typlen = inp_typlen;
!         inp_extra->typbyval = inp_typbyval;
!         inp_extra->typdelim = typdelim;
!         inp_extra->typelem = typelem;
!         inp_extra->typiofunc = proc;
!         inp_extra->typalign = inp_typalign;
!     }
!     else
!     {
!         inp_typlen = inp_extra->typlen;
!         inp_typbyval = inp_extra->typbyval;
!         typdelim = inp_extra->typdelim;
!         typelem = inp_extra->typelem;
!         proc = inp_extra->typiofunc;
!         inp_typalign = inp_extra->typalign;
!     }
!
!     if (ret_extra->element_type != retType)
!     {
!         /* Lookup source and result types. Unneeded variables are reused. */
!         get_type_metadata(retType, IOFunc_input, &typlen, &typbyval,
!                             &typdelim, &typelem, &proc, &typalign);
!
!         ret_extra->element_type = retType;
!         ret_extra->typlen = typlen;
!         ret_extra->typbyval = typbyval;
!         ret_extra->typdelim = typdelim;
!         ret_extra->typelem = typelem;
!         ret_extra->typiofunc = proc;
!         ret_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = ret_extra->typlen;
!         typbyval = ret_extra->typbyval;
!         typdelim = ret_extra->typdelim;
!         typelem = ret_extra->typelem;
!         proc = ret_extra->typiofunc;
!         typalign = ret_extra->typalign;
!     }

      /* Allocate temporary array for new values */
      values = (Datum *) palloc(nitems * sizeof(Datum));
***************
*** 2049,2056 ****
   *          compares two arrays for equality
   * result :
   *          returns true if the arrays are equal, false otherwise.
-  *
-  * XXX bitwise equality is pretty bogus ...
   *-----------------------------------------------------------------------------
   */
  Datum
--- 2266,2271 ----
***************
*** 2058,2069 ****
  {
      ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
      ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
      bool        result = true;

!     if (ARR_SIZE(array1) != ARR_SIZE(array2))
!         result = false;
!     else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
          result = false;

      /* Avoid leaking memory when handed toasted input. */
      PG_FREE_IF_COPY(array1, 0);
--- 2273,2390 ----
  {
      ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
      ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
+     char       *p1 = (char *) ARR_DATA_PTR(array1);
+     char       *p2 = (char *) ARR_DATA_PTR(array2);
+     int            ndims1 = ARR_NDIM(array1);
+     int            ndims2 = ARR_NDIM(array2);
+     int           *dims1 = ARR_DIMS(array1);
+     int           *dims2 = ARR_DIMS(array2);
+     int            nitems1 = ArrayGetNItems(ndims1, dims1);
+     int            nitems2 = ArrayGetNItems(ndims2, dims2);
+     Oid            element_type = ARR_ELEMTYPE(array1);
+     FmgrInfo   *fmgr_info = fcinfo->flinfo;
      bool        result = true;
+     int            typlen;
+     bool        typbyval;
+     char        typdelim;
+     Oid            typelem;
+     char        typalign;
+     Oid            typiofunc;
+     int            i;
+     ArrayMetaState *my_extra;
+     FunctionCallInfoData locfcinfo;

!     /* fast path if the arrays do not have the same number of elements */
!     if (nitems1 != nitems2)
          result = false;
+     else
+     {
+         /*
+          * We arrange to look up the equality function only once per series of
+          * calls, assuming the element type doesn't change underneath us.
+          */
+         my_extra = (ArrayMetaState *) fmgr_info->fn_extra;
+         if (my_extra == NULL)
+         {
+             fmgr_info->fn_extra = MemoryContextAlloc(fmgr_info->fn_mcxt,
+                                                      sizeof(ArrayMetaState));
+             my_extra = (ArrayMetaState *) fmgr_info->fn_extra;
+             my_extra->element_type = InvalidOid;
+         }
+
+         if (my_extra->element_type != element_type)
+         {
+             Oid        opfuncid = equality_oper_funcid(element_type);
+
+             if (OidIsValid(opfuncid))
+                 fmgr_info_cxt(opfuncid, &my_extra->proc, fmgr_info->fn_mcxt);
+             else
+                 elog(ERROR,
+                      "array_eq: cannot find equality operator for type: %u",
+                      element_type);
+
+             get_type_metadata(element_type, IOFunc_output,
+                               &typlen, &typbyval, &typdelim,
+                               &typelem, &typiofunc, &typalign);
+
+             my_extra->element_type = element_type;
+             my_extra->typlen = typlen;
+             my_extra->typbyval = typbyval;
+             my_extra->typdelim = typdelim;
+             my_extra->typelem = typelem;
+             my_extra->typiofunc = typiofunc;
+             my_extra->typalign = typalign;
+         }
+         else
+         {
+             typlen = my_extra->typlen;
+             typbyval = my_extra->typbyval;
+             typdelim = my_extra->typdelim;
+             typelem = my_extra->typelem;
+             typiofunc = my_extra->typiofunc;
+             typalign = my_extra->typalign;
+         }
+
+         /*
+          * apply the operator to each pair of array elements.
+          */
+         MemSet(&locfcinfo, 0, sizeof(locfcinfo));
+         locfcinfo.flinfo = &my_extra->proc;
+         locfcinfo.nargs = 2;
+
+         /* Loop over source data */
+         for (i = 0; i < nitems1; i++)
+         {
+             Datum    elt1;
+             Datum    elt2;
+             bool    oprresult;
+
+             /* Get element pair */
+             elt1 = fetch_att(p1, typbyval, typlen);
+             elt2 = fetch_att(p2, typbyval, typlen);
+
+             p1 = att_addlength(p1, typlen, PointerGetDatum(p1));
+             p1 = (char *) att_align(p1, typalign);
+
+             p2 = att_addlength(p2, typlen, PointerGetDatum(p2));
+             p2 = (char *) att_align(p2, typalign);
+
+             /*
+              * Apply the operator to the element pair
+              */
+             locfcinfo.arg[0] = elt1;
+             locfcinfo.arg[1] = elt2;
+             locfcinfo.argnull[0] = false;
+             locfcinfo.argnull[1] = false;
+             locfcinfo.isnull = false;
+             oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+             if (!oprresult)
+             {
+                 result = false;
+                 break;
+             }
+         }
+     }

      /* Avoid leaking memory when handed toasted input. */
      PG_FREE_IF_COPY(array1, 0);
***************
*** 2077,2125 ****
  /******************|          Support  Routines              |*****************/
  /***************************************************************************/

- static void
- system_cache_lookup(Oid element_type,
-                     IOFuncSelector which_func,
-                     int *typlen,
-                     bool *typbyval,
-                     char *typdelim,
-                     Oid *typelem,
-                     Oid *proc,
-                     char *typalign)
- {
-     HeapTuple    typeTuple;
-     Form_pg_type typeStruct;
-
-     typeTuple = SearchSysCache(TYPEOID,
-                                ObjectIdGetDatum(element_type),
-                                0, 0, 0);
-     if (!HeapTupleIsValid(typeTuple))
-         elog(ERROR, "cache lookup failed for type %u", element_type);
-     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
-
-     *typlen = typeStruct->typlen;
-     *typbyval = typeStruct->typbyval;
-     *typdelim = typeStruct->typdelim;
-     *typelem = typeStruct->typelem;
-     *typalign = typeStruct->typalign;
-     switch (which_func)
-     {
-         case IOFunc_input:
-             *proc = typeStruct->typinput;
-             break;
-         case IOFunc_output:
-             *proc = typeStruct->typoutput;
-             break;
-         case IOFunc_receive:
-             *proc = typeStruct->typreceive;
-             break;
-         case IOFunc_send:
-             *proc = typeStruct->typsend;
-             break;
-     }
-     ReleaseSysCache(typeTuple);
- }
-
  /*
   * Fetch array element at pointer, converted correctly to a Datum
   */
--- 2398,2403 ----
***************
*** 2453,2456 ****
--- 2731,2849 ----
      locfcinfo.arg[0] = PointerGetDatum(src);

      return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
+ }
+
+ /*
+  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
+  *
+  *    astate is working state (NULL on first call)
+  *    rcontext is where to keep working state
+  */
+ ArrayBuildState *
+ accumArrayResult(ArrayBuildState *astate,
+                  Datum dvalue, bool disnull,
+                  Oid element_type,
+                  MemoryContext rcontext)
+ {
+     MemoryContext arr_context,
+                   oldcontext;
+
+     if (astate == NULL)
+     {
+         /* First time through --- initialize */
+
+         /* Make a temporary context to hold all the junk */
+         arr_context = AllocSetContextCreate(rcontext,
+                                             "accumArrayResult",
+                                             ALLOCSET_DEFAULT_MINSIZE,
+                                             ALLOCSET_DEFAULT_INITSIZE,
+                                             ALLOCSET_DEFAULT_MAXSIZE);
+         oldcontext = MemoryContextSwitchTo(arr_context);
+         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
+         astate->mcontext = arr_context;
+         astate->dvalues = (Datum *)
+             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
+         astate->nelems = 0;
+         astate->element_type = element_type;
+         get_typlenbyvalalign(element_type,
+                              &astate->typlen,
+                              &astate->typbyval,
+                              &astate->typalign);
+     }
+     else
+     {
+         oldcontext = MemoryContextSwitchTo(astate->mcontext);
+         Assert(astate->element_type == element_type);
+         /* enlarge dvalues[] if needed */
+         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
+             astate->dvalues = (Datum *)
+                 repalloc(astate->dvalues,
+                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
+     }
+
+     if (disnull)
+         elog(ERROR, "NULL elements not allowed in Arrays");
+
+     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
+     astate->dvalues[astate->nelems++] =
+         datumCopy(dvalue, astate->typbyval, astate->typlen);
+
+     MemoryContextSwitchTo(oldcontext);
+
+     return astate;
+ }
+
+ /*
+  * makeArrayResult - produce final result of accumArrayResult
+  *
+  *    astate is working state (not NULL)
+  *    rcontext is where to construct result
+  */
+ Datum
+ makeArrayResult(ArrayBuildState *astate,
+                 MemoryContext rcontext)
+ {
+     int            dims[1];
+     int            lbs[1];
+
+     dims[0] = astate->nelems;
+     lbs[0] = 1;
+
+     return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
+ }
+
+ /*
+  * makeMdArrayResult - produce md final result of accumArrayResult
+  *
+  *    astate is working state (not NULL)
+  *    rcontext is where to construct result
+  */
+ Datum
+ makeMdArrayResult(ArrayBuildState *astate,
+                 int ndims,
+                 int *dims,
+                 int *lbs,
+                 MemoryContext rcontext)
+ {
+     ArrayType  *result;
+     MemoryContext oldcontext;
+
+     /* Build the final array result in rcontext */
+     oldcontext = MemoryContextSwitchTo(rcontext);
+
+     result = construct_md_array(astate->dvalues,
+                                 ndims,
+                                 dims,
+                                 lbs,
+                                 astate->element_type,
+                                 astate->typlen,
+                                 astate->typbyval,
+                                 astate->typalign);
+
+     MemoryContextSwitchTo(oldcontext);
+
+     /* Clean up all the junk */
+     MemoryContextDelete(astate->mcontext);
+
+     return PointerGetDatum(result);
  }
Index: src/backend/utils/adt/varlena.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/varlena.c,v
retrieving revision 1.98
diff -c -r1.98 varlena.c
*** src/backend/utils/adt/varlena.c    15 May 2003 15:50:19 -0000    1.98
--- src/backend/utils/adt/varlena.c    23 May 2003 02:05:28 -0000
***************
*** 19,29 ****
--- 19,32 ----
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
  #include "access/tuptoaster.h"
+ #include "catalog/pg_type.h"
  #include "lib/stringinfo.h"
  #include "libpq/crypt.h"
  #include "libpq/pqformat.h"
+ #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/pg_locale.h"
+ #include "utils/lsyscache.h"


  typedef struct varlena unknown;
***************
*** 1983,1990 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

--- 1986,1992 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

***************
*** 2004,2011 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
--- 2006,2012 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
***************
*** 2026,2031 ****
--- 2027,2217 ----
          result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn -
fldsep_len,false); 
          PG_RETURN_TEXT_P(result_text);
      }
+ }
+
+ /*
+  * text_to_array
+  * parse input string
+  * return text array of elements
+  * based on provided field separator
+  */
+ Datum
+ text_to_array(PG_FUNCTION_ARGS)
+ {
+     text       *inputstring = PG_GETARG_TEXT_P(0);
+     int            inputstring_len = TEXTLEN(inputstring);
+     text       *fldsep = PG_GETARG_TEXT_P(1);
+     int            fldsep_len = TEXTLEN(fldsep);
+     int            fldnum;
+     int            start_posn = 0;
+     int            end_posn = 0;
+     text       *result_text = NULL;
+     ArrayBuildState *astate = NULL;
+     MemoryContext oldcontext = CurrentMemoryContext;
+
+     /* return NULL for empty input string */
+     if (inputstring_len < 1)
+         PG_RETURN_NULL();
+
+     /* empty field separator
+      * return one element, 1D, array using the input string */
+     if (fldsep_len < 1)
+         PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+                               CStringGetDatum(inputstring), 1));
+
+     /* start with end position holding the initial start position */
+     end_posn = 0;
+     for (fldnum=1;;fldnum++)    /* field number is 1 based */
+     {
+         Datum    dvalue;
+         bool    disnull = false;
+
+         start_posn = end_posn;
+         end_posn = text_position(PointerGetDatum(inputstring),
+                                  PointerGetDatum(fldsep),
+                                  fldnum);
+
+         if ((start_posn == 0) && (end_posn == 0))    /* fldsep not found */
+         {
+             if (fldnum == 1)
+             {
+                 /* first element
+                  * return one element, 1D, array using the input string */
+                 PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+                                       CStringGetDatum(inputstring), 1));
+             }
+             else
+             {
+                 /* otherwise create array and exit */
+                 PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
+             }
+         }
+         else if ((start_posn != 0) && (end_posn == 0))
+         {
+             /* last field requested */
+             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
+         }
+         else if ((start_posn == 0) && (end_posn != 0))
+         {
+             /* first field requested */
+             result_text = LEFT(inputstring, fldsep);
+         }
+         else
+         {
+             /* prior to last field requested */
+             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn
-fldsep_len, false); 
+         }
+
+         /* stash away current value */
+         dvalue = PointerGetDatum(result_text);
+         astate = accumArrayResult(astate, dvalue,
+                                   disnull, TEXTOID, oldcontext);
+
+     }
+
+     /* never reached -- keep compiler quiet */
+     PG_RETURN_NULL();
+ }
+
+ /*
+  * array_to_text
+  * concatenate Cstring representation of input array elements
+  * using provided field separator
+  */
+ Datum
+ array_to_text(PG_FUNCTION_ARGS)
+ {
+     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
+     char       *fldsep = PG_TEXTARG_GET_STR(1);
+     int            nitems, *dims, ndims;
+     char       *p;
+     Oid            element_type;
+     int            typlen;
+     bool        typbyval;
+     char        typdelim;
+     Oid            typoutput,
+                 typelem;
+     FmgrInfo    outputproc;
+     char        typalign;
+     StringInfo    result_str = makeStringInfo();
+     int            i;
+     ArrayMetaState *my_extra;
+
+     p = ARR_DATA_PTR(v);
+     ndims = ARR_NDIM(v);
+     dims = ARR_DIMS(v);
+     nitems = ArrayGetNItems(ndims, dims);
+
+     /* if there are no elements, return an empty string */
+     if (nitems == 0)
+         PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
+
+     element_type = ARR_ELEMTYPE(v);
+
+     /*
+      * We arrange to look up info about element type, including its output
+      * conversion proc only once per series of calls, assuming the element
+      * type doesn't change underneath us.
+      */
+     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+     if (my_extra == NULL)
+     {
+         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+                                                      sizeof(ArrayMetaState));
+         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+         my_extra->element_type = InvalidOid;
+     }
+
+     if (my_extra->element_type != element_type)
+     {
+         /* Get info about element type, including its output conversion proc */
+         get_type_metadata(element_type, IOFunc_output,
+                             &typlen, &typbyval, &typdelim,
+                             &typelem, &typoutput, &typalign);
+         fmgr_info(typoutput, &outputproc);
+
+         my_extra->element_type = element_type;
+         my_extra->typlen = typlen;
+         my_extra->typbyval = typbyval;
+         my_extra->typdelim = typdelim;
+         my_extra->typelem = typelem;
+         my_extra->typiofunc = typoutput;
+         my_extra->typalign = typalign;
+         my_extra->proc = outputproc;
+     }
+     else
+     {
+         typlen = my_extra->typlen;
+         typbyval = my_extra->typbyval;
+         typdelim = my_extra->typdelim;
+         typelem = my_extra->typelem;
+         typoutput = my_extra->typiofunc;
+         typalign = my_extra->typalign;
+         outputproc = my_extra->proc;
+     }
+
+     for (i = 0; i < nitems; i++)
+     {
+         Datum        itemvalue;
+         char       *value;
+
+         itemvalue = fetch_att(p, typbyval, typlen);
+
+         value = DatumGetCString(FunctionCall3(&outputproc,
+                                               itemvalue,
+                                               ObjectIdGetDatum(typelem),
+                                               Int32GetDatum(-1)));
+
+         if (i > 0)
+             appendStringInfo(result_str, "%s%s", fldsep, value);
+         else
+             appendStringInfo(result_str, "%s", value);
+
+         p = att_addlength(p, typlen, PointerGetDatum(p));
+         p = (char *) att_align(p, typalign);
+     }
+
+     PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
  }

  #define HEXBASE 16
Index: src/backend/utils/cache/lsyscache.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v
retrieving revision 1.94
diff -c -r1.94 lsyscache.c
*** src/backend/utils/cache/lsyscache.c    13 May 2003 04:38:58 -0000    1.94
--- src/backend/utils/cache/lsyscache.c    23 May 2003 02:05:28 -0000
***************
*** 969,974 ****
--- 969,1024 ----
      ReleaseSysCache(tp);
  }

+ /*
+  * get_type_metadata
+  *
+  *        A six-fer:    given the type OID, return typlen, typbyval, typalign,
+  *                    typdelim, typelem, IO function Oid. The IO function
+  *                    returned is controlled by IOFuncSelector
+  */
+ void
+ get_type_metadata(Oid element_type,
+                     IOFuncSelector which_func,
+                     int *typlen,
+                     bool *typbyval,
+                     char *typdelim,
+                     Oid *typelem,
+                     Oid *proc,
+                     char *typalign)
+ {
+     HeapTuple    typeTuple;
+     Form_pg_type typeStruct;
+
+     typeTuple = SearchSysCache(TYPEOID,
+                                ObjectIdGetDatum(element_type),
+                                0, 0, 0);
+     if (!HeapTupleIsValid(typeTuple))
+         elog(ERROR, "cache lookup failed for type %u", element_type);
+     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+
+     *typlen = typeStruct->typlen;
+     *typbyval = typeStruct->typbyval;
+     *typdelim = typeStruct->typdelim;
+     *typelem = typeStruct->typelem;
+     *typalign = typeStruct->typalign;
+     switch (which_func)
+     {
+         case IOFunc_input:
+             *proc = typeStruct->typinput;
+             break;
+         case IOFunc_output:
+             *proc = typeStruct->typoutput;
+             break;
+         case IOFunc_receive:
+             *proc = typeStruct->typreceive;
+             break;
+         case IOFunc_send:
+             *proc = typeStruct->typsend;
+             break;
+     }
+     ReleaseSysCache(typeTuple);
+ }
+
  #ifdef NOT_USED
  char
  get_typalign(Oid typid)
Index: src/backend/utils/fmgr/fmgr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/fmgr/fmgr.c,v
retrieving revision 1.68
diff -c -r1.68 fmgr.c
*** src/backend/utils/fmgr/fmgr.c    8 Apr 2003 23:20:02 -0000    1.68
--- src/backend/utils/fmgr/fmgr.c    23 May 2003 02:05:28 -0000
***************
*** 1673,1675 ****
--- 1673,1701 ----

      return exprType((Node *) nth(argnum, args));
  }
+
+ /*
+  * Get the OID of the function or operator
+  *
+  * Returns InvalidOid if information is not available
+  */
+ Oid
+ get_fn_expr_functype(FunctionCallInfo fcinfo)
+ {
+     Node   *expr;
+
+     /*
+      * can't return anything useful if we have no FmgrInfo or if
+      * its fn_expr node has not been initialized
+      */
+     if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
+         return InvalidOid;
+
+     expr = fcinfo->flinfo->fn_expr;
+     if (IsA(expr, FuncExpr))
+         return ((FuncExpr *) expr)->funcid;
+     else if (IsA(expr, OpExpr))
+         return ((OpExpr *) expr)->opno;
+     else
+         return InvalidOid;
+ }
Index: src/include/fmgr.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/fmgr.h,v
retrieving revision 1.27
diff -c -r1.27 fmgr.h
*** src/include/fmgr.h    8 Apr 2003 23:20:04 -0000    1.27
--- src/include/fmgr.h    23 May 2003 02:05:28 -0000
***************
*** 377,385 ****
   * Routines in fmgr.c
   */
  extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
! extern Oid    fmgr_internal_function(const char *proname);
! extern Oid    get_fn_expr_rettype(FunctionCallInfo fcinfo);
! extern Oid    get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);

  /*
   * Routines in dfmgr.c
--- 377,386 ----
   * Routines in fmgr.c
   */
  extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
! extern Oid fmgr_internal_function(const char *proname);
! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo);
! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
! extern Oid get_fn_expr_functype(FunctionCallInfo fcinfo);

  /*
   * Routines in dfmgr.c
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v
retrieving revision 1.300
diff -c -r1.300 pg_proc.h
*** src/include/catalog/pg_proc.h    15 May 2003 15:50:19 -0000    1.300
--- src/include/catalog/pg_proc.h    23 May 2003 02:05:28 -0000
***************
*** 1016,1021 ****
--- 1016,1025 ----
  DESCR("concatenate two arrays");
  DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
  DESCR("coerce array type to another array type");
+ DATA(insert OID = 385 (  str_to_array       PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ ));
+ DESCR("split delimited text into text[]");
+ DATA(insert OID = 386 (  array_to_str       PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ ));
+ DESCR("concatenate array elements, using delimiter, into text");

  DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
  DESCR("I/O");
Index: src/include/utils/array.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v
retrieving revision 1.38
diff -c -r1.38 array.h
*** src/include/utils/array.h    8 May 2003 22:19:57 -0000    1.38
--- src/include/utils/array.h    23 May 2003 02:05:28 -0000
***************
*** 32,37 ****
--- 32,68 ----
      Oid            elemtype;        /* element type OID */
  } ArrayType;

+ typedef struct ArrayBuildState
+ {
+     MemoryContext mcontext;        /* where all the temp stuff is kept */
+     Datum       *dvalues;        /* array of accumulated Datums */
+     /*
+      * The allocated size of dvalues[] is always a multiple of
+      * ARRAY_ELEMS_CHUNKSIZE
+      */
+ #define ARRAY_ELEMS_CHUNKSIZE    64
+     int            nelems;            /* number of valid Datums in dvalues[] */
+     Oid            element_type;    /* data type of the Datums */
+     int16        typlen;            /* needed info about datatype */
+     bool        typbyval;
+     char        typalign;
+ } ArrayBuildState;
+
+ /*
+  * structure to cache type metadata needed for array manipulation
+  */
+ typedef struct ArrayMetaState
+ {
+     Oid                element_type;
+     int                typlen;
+     bool            typbyval;
+     char            typdelim;
+     Oid                typelem;
+     Oid                typiofunc;
+     char            typalign;
+     FmgrInfo        proc;
+ } ArrayMetaState;
+
  /*
   * fmgr macros for array objects
   */
***************
*** 124,130 ****
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
!

  /*
   * prototypes for functions defined in arrayutils.c
--- 155,168 ----
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
! extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
!                                          Datum dvalue, bool disnull,
!                                          Oid element_type,
!                                          MemoryContext rcontext);
! extern Datum makeArrayResult(ArrayBuildState *astate,
!                              MemoryContext rcontext);
! extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
!                                int *dims, int *lbs, MemoryContext rcontext);

  /*
   * prototypes for functions defined in arrayutils.c
***************
*** 146,152 ****
  extern Datum array_accum(PG_FUNCTION_ARGS);
  extern Datum array_cat(PG_FUNCTION_ARGS);

! extern ArrayType *create_singleton_array(Oid element_type,
                                           Datum element,
                                           int ndims);

--- 184,191 ----
  extern Datum array_accum(PG_FUNCTION_ARGS);
  extern Datum array_cat(PG_FUNCTION_ARGS);

! extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
!                                          Oid element_type,
                                           Datum element,
                                           int ndims);

Index: src/include/utils/builtins.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/builtins.h,v
retrieving revision 1.217
diff -c -r1.217 builtins.h
*** src/include/utils/builtins.h    15 May 2003 15:50:20 -0000    1.217
--- src/include/utils/builtins.h    23 May 2003 02:05:28 -0000
***************
*** 539,544 ****
--- 539,546 ----
                        List **namelist);
  extern Datum replace_text(PG_FUNCTION_ARGS);
  extern Datum split_text(PG_FUNCTION_ARGS);
+ extern Datum text_to_array(PG_FUNCTION_ARGS);
+ extern Datum array_to_text(PG_FUNCTION_ARGS);
  extern Datum to_hex32(PG_FUNCTION_ARGS);
  extern Datum to_hex64(PG_FUNCTION_ARGS);
  extern Datum md5_text(PG_FUNCTION_ARGS);
Index: src/include/utils/lsyscache.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v
retrieving revision 1.69
diff -c -r1.69 lsyscache.h
*** src/include/utils/lsyscache.h    9 May 2003 18:08:48 -0000    1.69
--- src/include/utils/lsyscache.h    23 May 2003 02:05:28 -0000
***************
*** 15,20 ****
--- 15,29 ----

  #include "access/htup.h"

+ /* I/O function selector for system_cache_lookup */
+ typedef enum IOFuncSelector
+ {
+     IOFunc_input,
+     IOFunc_output,
+     IOFunc_receive,
+     IOFunc_send
+ } IOFuncSelector;
+
  extern bool op_in_opclass(Oid opno, Oid opclass);
  extern bool op_requires_recheck(Oid opno, Oid opclass);
  extern char *get_attname(Oid relid, AttrNumber attnum);
***************
*** 53,58 ****
--- 62,75 ----
  extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
  extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
                       char *typalign);
+ extern void get_type_metadata(Oid element_type,
+                                 IOFuncSelector which_func,
+                                 int *typlen,
+                                 bool *typbyval,
+                                 char *typdelim,
+                                 Oid *typelem,
+                                 Oid *proc,
+                                 char *typalign);
  extern char get_typstorage(Oid typid);
  extern int32 get_typtypmod(Oid typid);
  extern Node *get_typdefault(Oid typid);

Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> Peter Eisentraut wrote:
>> Also note that "multidimensional array" is the correct spelling

> "multi-dimensional" is not incorrect by my dictionary,

The first two online dictionaries I checked (Merriam-Webster and
Cambridge) list *only* "multi-dimensional".  IMHO the word is not
common enough in normal use to have lost its hyphen.  You can get
away with not hyphenating in technical contexts where it's more
common, but that does not make the hyphenated form less correct.

            regards, tom lane

Re: array support patch phase 1 patch

From
Peter Eisentraut
Date:
Tom Lane writes:

> The first two online dictionaries I checked (Merriam-Webster and
> Cambridge) list *only* "multi-dimensional".  IMHO the word is not
> common enough in normal use to have lost its hyphen.  You can get
> away with not hyphenating in technical contexts where it's more
> common, but that does not make the hyphenated form less correct.

In my educated opinion, prefixes that are not words by themselves are
never hyphenated (except possibly to avoid ambiguity).  This applies to
pre-, post-, non-, un-, in-, uni-, bi-, re- and so on, and there is no
grammatical reason why it shouldn't apply to multi- as well.  The rule
that once a word becomes common enough it can be closed up only applies to
compounds where each part is an independent word.  webster.com agrees with
this and also the dead tree dictionary I have here.  There's also a more
detailed explanation of this here: <http://www.bartleby.com/64/84.html>.

--
Peter Eisentraut   peter_e@gmx.net


Re: array support patch phase 1 patch

From
Peter Eisentraut
Date:
Joe Conway writes:

> I personally don't understand why we should hide them from users. If I
> prefer to use array_append(var1, var2) rather than (var1 || var2),
> what's the problem? Its a matter of taste as to which is better.

The problem is that this approach leads to bloat without bound.  Maybe
tomorrow someone prefers append_array(var1, var2) or var1 + var2.  The
standard says it's var1 || var2, there is no technical or substantial
aesthetical argument against it, so that's what we should use.

> And in any case, array_accum() is intended to be used for building
> custom aggregates, so that needs to be documented.

Can you give an example of an aggregate or a class of aggregates where
this would be useful?

--
Peter Eisentraut   peter_e@gmx.net


Re: array support patch phase 1 patch

From
Joe Conway
Date:
Peter Eisentraut wrote:
> Joe Conway writes:
>>I personally don't understand why we should hide them from users. If I
>>prefer to use array_append(var1, var2) rather than (var1 || var2),
>>what's the problem? Its a matter of taste as to which is better.
>
> The problem is that this approach leads to bloat without bound.  Maybe
> tomorrow someone prefers append_array(var1, var2) or var1 + var2.  The
> standard says it's var1 || var2, there is no technical or substantial
> aesthetical argument against it, so that's what we should use.

I can see your point. But is there any circumstance where the function
will work and the standard syntax won't? Until recently, it was not
possible to assign individual array elements in PL/pgSQL (I think Tom
fixed this). Are there any more examples?

Let's go down the list:

array_accum(anyarray, anyelement)
   -- no standard syntax; more on this later
array_append(anyarray, anyelement)
   -- can be easily done with "||" operator; is there any case where "||"
      won't work that array_append() will? If not, don't document as a
      user function.
array_assign(anyarray, integer, anyelement)
   -- can be easily done with "var[n] = X" syntax; is there any case
      where "var[n] = X" won't work that array_assign() will? If not,
      don't document as a user function.
array_cat(anyarray, anyarray)
   -- see array_append
array_dims(anyarray)
   -- no standard syntax; should be documented.
array_lower(anyarray, integer)
   -- no standard syntax; should be documented.
array_prepend(anyelement, anyarray)
   -- see array_append
array_subscript(anyarray, integer)
   -- can be easily done with "var[n]" syntax; is there any case
      where "var[n]" won't work that array_subscript() will? If not,
      don't document as a user function.
array_to_str(anyarray, text)
   -- no standard syntax; should be documented.
array_upper(anyarray, integer)
   -- no standard syntax; should be documented.
singleton_array(anyelement)
   -- can be easily done with "array[x]" or "'{x}'" syntax; is there any
      case where one of these won't work that singleton_array() will? If
      not, don't document as a user function.
str_to_array(text, text)
   -- no standard syntax; should be documented.

BTW, should this discussion be on HACKERS or even GENERAL in order to
get a wider audience of opinion?

>>And in any case, array_accum() is intended to be used for building
>>custom aggregates, so that needs to be documented.
> Can you give an example of an aggregate or a class of aggregates where
> this would be useful?
>

There are many discussions on the lists over the past few years in which
people have requested this kind of functionality. There is even a
contrib/intagg that does this for integers only. I don't think there is
any question that there is a demand for the feature. Some examples:

http://fts.postgresql.org/db/msg.html?mid=1021530
http://fts.postgresql.org/db/msg.html?mid=1096592
http://fts.postgresql.org/db/msg.html?mid=1031700
http://fts.postgresql.org/db/msg.html?mid=1353047
http://fts.postgresql.org/db/msg.html?mid=1063738
http://fts.postgresql.org/db/msg.html?mid=1050837
http://fts.postgresql.org/db/msg.html?mid=1066349

Some people would like to simply aggregate rows of data into arrays for
analysis, some want to write custom final functions to post-process the
resulting array (e.g. median). With array_accum() and some of the other
new functions (array_lower & array_upper specifically come to mind),
people have a relatively easy way to create there own custom aggregates
using PL/pgSQL.

With PL/R, I can use array_accum to create an aggregate for just about
any summary statistic that I'm interested in. And while, yes, there is
already an implementation of array_accum in PL/R, it isn't reasonable to
require anyone who wants to use it to install libR also. And, yes, there
are undoubtedly other ways to create those aggregates, but none as
simple to use.

Again, I'd suggest that if we want to debate this much further, we
should move the discussion to the GENERAL and SQL lists so that users
can have a chance to chime in.

Joe




Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> BTW, should this discussion be on HACKERS or even GENERAL in order to
> get a wider audience of opinion?

Definitely.  Not many people read -patches, AFAIK.  If there's any
serious discussion needed, it should be taken elsewhere.  I'd suggest
-hackers for this one.

            regards, tom lane

Re: array support patch phase 1 patch

From
Joe Conway
Date:
Peter Eisentraut wrote:
> Tom Lane writes:
>
>>The first two online dictionaries I checked (Merriam-Webster and
>>Cambridge) list *only* "multi-dimensional".  IMHO the word is not
>>common enough in normal use to have lost its hyphen.  You can get
>>away with not hyphenating in technical contexts where it's more
>>common, but that does not make the hyphenated form less correct.
>
> In my educated opinion, prefixes that are not words by themselves are
> never hyphenated (except possibly to avoid ambiguity).  This applies to
> pre-, post-, non-, un-, in-, uni-, bi-, re- and so on, and there is no
> grammatical reason why it shouldn't apply to multi- as well.  The rule
> that once a word becomes common enough it can be closed up only applies to
> compounds where each part is an independent word.  webster.com agrees with
> this and also the dead tree dictionary I have here.  There's also a more
> detailed explanation of this here: <http://www.bartleby.com/64/84.html>.
>

I see both forms in various references as being correct, but I think we
should consistently use one or the other. I'll make the change, but I'd
need a definitive answer on which way to go. Any other opinions out there?

Joe


Re: array support patch phase 1 patch

From
Joe Conway
Date:
Joe Conway wrote:
> Joe Conway wrote:
>
>> Attached is an updated version of the doc patch along with the
>> addition of two new functions, as previously discussed:
>>
>>    - str_to_array(str TEXT, delim TEXT) returns TEXT[]
>>    - array_to_str(array ANYARRAY, delim TEXT) returns TEXT
>>
>> The patch is fairly large because:
>>
>>    1. the documentation part of it is large
>>    2. I moved some static functions around and made them accessible so
>>       that I could use them in implementing the two functions.
>>
>> It passes all regression tests, and as mentioned above includes
>> documentation.
>>
>> If there are no objections, please apply.
>
>
> The attached is an update to the one above, with the following additions
>     (recommended by Tom here -
>     http://archives.postgresql.org/pgsql-patches/2003-04/msg00030.php):
>
> 1. array_eq() now applies the element type's equality operator on an
>    element-by-element basis.
> 2. applied Tom's cache lookup results caching technique in arrayfuncs.c,
>    array_userfuncs.c, and in the new functions in varlena.c.
>
> Still passes all regression tests. No additional doc updates needed for
> this one. If there are no objections, please apply.
>

This attached patch includes all of the above, with the following changes:

1. array_type_coerce() fixes discussed off list last weekend while the
    list was mostly down.
2. empty array element and slice assignment, and empty array
    concatenation as recently discussed on HACKERS.
3. Aggregates can now use polymorphic functions and be polymorphic
    themselves (examples below)
4. removes array_accum function and documentation per recent discussions

I specifically did not change the spelling of the term
multi-dimensional/multidimensional -- I'd suggest we agree on one way
and be consistent, but I'd really rather not hold up this patch for that
as it is getting tough to stay in sync with cvs (had a backend function
disappear from under me just today).

I also left singleton_array, array_assign, and array_subscript in place
and documented. I'd still like to see them stay (there was a request on
GENERAL this afternoon that could have used singleton_array as probably
the most simple solution, although undoubtedly they could have kludged
it by concatenating to an empty array). But, again, I'd rather see this
patch committed, so if need be, please rip them out.

Here are some examples of the polymorphic aggregates:

create table t(f1 int, f2 int[], f3 text);
insert into t values(1,array[1],'a');
insert into t values(1,array[11],'b');
insert into t values(1,array[111],'c');
insert into t values(2,array[2],'a');
insert into t values(2,array[22],'b');
insert into t values(2,array[222],'c');
insert into t values(3,array[3],'a');
insert into t values(3,array[3],'b');

CREATE AGGREGATE myagg2
(
   BASETYPE = int4,
   SFUNC = array_append,
   STYPE = int4[],
   INITCOND = '{}'
);

CREATE AGGREGATE myagg3
(
   BASETYPE = anyelement,
   SFUNC = array_append,
   STYPE = anyarray,
   INITCOND = '{}'
);

CREATE AGGREGATE myagg4
(
   BASETYPE = anyarray,
   SFUNC = array_cat,
   STYPE = anyarray,
   INITCOND = '{}'
);

regression=# select f3, myagg2(f1) from t group by f3;
  f3 | myagg2
----+---------
  b  | {1,2,3}
  a  | {1,2,3}
  c  | {1,2}
(3 rows)

regression=# select f3, myagg3(f1) from t group by f3;
  f3 | myagg3
----+---------
  b  | {1,2,3}
  a  | {1,2,3}
  c  | {1,2}
(3 rows)

regression=# select f1, myagg3(f3) from t group by f1;
  f1 | myagg3
----+---------
   1 | {a,b,c}
   3 | {a,b}
   2 | {a,b,c}
(3 rows)

regression=# select f3, myagg4(f2) from t group by f3;
  f3 |     myagg4
----+-----------------
  b  | {{11},{22},{3}}
  a  | {{1},{2},{3}}
  c  | {{111},{222}}
(3 rows)


And assignment to an empty array:


regression=# insert into t values(4,'{}','d');
INSERT 2000865 1
regression=# update t set f2[2][4][6] = 42 where f1 = 4;
UPDATE 1
regression=# select * from t where f1 = 4;
  f1 |    f2    | f3
----+----------+----
   4 | {{{42}}} | d
(1 row)

regression=# select array_dims(f2) from t where f1 = 4;
    array_dims
-----------------
  [2:2][4:4][6:6]
(1 row)



All 90 regression tests passed. More doc updates are needed based on
these changes, but I'd like to do those after the feature freeze if I
may. Please apply.

thanks,

Joe
Index: doc/src/sgml/array.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
retrieving revision 1.25
diff -c -r1.25 array.sgml
*** doc/src/sgml/array.sgml    13 Mar 2003 01:30:26 -0000    1.25
--- doc/src/sgml/array.sgml    29 May 2003 04:30:53 -0000
***************
*** 9,15 ****

   <para>
    <productname>PostgreSQL</productname> allows columns of a table to be
!   defined as variable-length multidimensional arrays. Arrays of any
    built-in type or user-defined type can be created.
   </para>

--- 9,15 ----

   <para>
    <productname>PostgreSQL</productname> allows columns of a table to be
!   defined as variable-length multi-dimensional arrays. Arrays of any
    built-in type or user-defined type can be created.
   </para>

***************
*** 60,73 ****
  </programlisting>
   </para>

   <note>
    <para>
!    A limitation of the present array implementation is that individual
!    elements of an array cannot be SQL null values.  The entire array can be set
!    to null, but you can't have an array with some elements null and some
!    not.  Fixing this is on the to-do list.
    </para>
   </note>
   </sect2>

   <sect2>
--- 60,133 ----
  </programlisting>
   </para>

+  <para>
+   A limitation of the present array implementation is that individual
+   elements of an array cannot be SQL null values.  The entire array can be set
+   to null, but you can't have an array with some elements null and some
+   not.
+  </para>
+  <para>
+   This can lead to surprising results. For example, the result of the
+   previous two inserts looks like this:
+ <programlisting>
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |      schedule
+ -------+---------------------------+--------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting},{""}}
+  Carol | {20000,25000,25000,25000} | {{talk},{meeting}}
+ (2 rows)
+ </programlisting>
+   Because the <literal>[2][2]</literal> element of
+   <structfield>schedule</structfield> is missing in each of the
+   <command>INSERT</command> statements, the <literal>[1][2]</literal>
+   element is discarded.
+  </para>
+
+  <note>
+   <para>
+    Fixing this is on the to-do list.
+   </para>
+  </note>
+
+  <para>
+   The <command>ARRAY</command> expression syntax may also be used:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Bill',
+     ARRAY[10000, 10000, 10000, 10000],
+     ARRAY[['meeting', 'lunch'], ['','']]);
+
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting', '']]);
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |           schedule
+ -------+---------------------------+-------------------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}}
+  Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}}
+ (2 rows)
+ </programlisting>
+   Note that with this syntax, multi-dimensional arrays must have matching
+   extents for each dimension. This eliminates the missing-array-elements
+   problem above. For example:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting']]);
+ ERROR:  Multiple dimension arrays must have array expressions with matching dimensions
+ </programlisting>
+   Also notice that string literals are single quoted instead of double quoted.
+  </para>
+
   <note>
    <para>
!    The examples in the rest of this section are based on the
!    <command>ARRAY</command> expression syntax <command>INSERT</command>s.
    </para>
   </note>
+
   </sect2>

   <sect2>
***************
*** 132,142 ****
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the
!   form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified.
   </para>

   <para>
--- 192,221 ----
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified; another example follows:
! <programlisting>
! SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
!          schedule
! ---------------------------
!  {{meeting,lunch},{"",""}}
! (1 row)
! </programlisting>
!  </para>
!
!  <para>
!   Additionally, we can also access a single arbitrary array element of
!   a one-dimensional array with the <function>array_subscript</function>
!   function:
! <programlisting>
! SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
!  array_subscript
! -----------------
!            10000
! (1 row)
! </programlisting>
   </para>

   <para>
***************
*** 147,153 ****
      WHERE name = 'Carol';
  </programlisting>

!   or updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
--- 226,248 ----
      WHERE name = 'Carol';
  </programlisting>

!   or using the <command>ARRAY</command> expression syntax:
!
! <programlisting>
! UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
!     WHERE name = 'Carol';
! </programlisting>
!
!   <note>
!    <para>
!     Anywhere you can use the <quote>curly braces</quote> array syntax,
!     you can also use the <command>ARRAY</command> expression syntax. The
!     remainder of this section will illustrate only one or the other, but
!     not both.
!    </para>
!   </note>
!
!   An array may also be updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
***************
*** 160,165 ****
--- 255,268 ----
  UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
      WHERE name = 'Carol';
  </programlisting>
+
+   A one-dimensional array may also be updated with the
+   <function>array_assign</function> function:
+
+ <programlisting>
+ UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000)
+     WHERE name = 'Bill';
+ </programListing>
   </para>

   <para>
***************
*** 169,175 ****
    value currently has 4 elements, it will have five elements after an
    update that assigns to <literal>array[5]</>.  Currently, enlargement in
    this fashion is only allowed for one-dimensional arrays, not
!   multidimensional arrays.
   </para>

   <para>
--- 272,278 ----
    value currently has 4 elements, it will have five elements after an
    update that assigns to <literal>array[5]</>.  Currently, enlargement in
    this fashion is only allowed for one-dimensional arrays, not
!   multi-dimensional arrays.
   </para>

   <para>
***************
*** 179,184 ****
--- 282,367 ----
   </para>

   <para>
+   An array can also be enlarged by using the functions
+   <function>array_prepend</function>, <function>array_append</function>,
+   or <function>array_cat</function>. The first two only support one-dimensional
+   arrays, but <function>array_cat</function> supports multi-dimensional arrays.
+   Some examples:
+
+ <programlisting>
+ SELECT array_prepend(1, ARRAY[2,3]);
+  array_prepend
+ ---------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_append(ARRAY[1,2], 3);
+  array_append
+ --------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
+    array_cat
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
+       array_cat
+ ---------------------
+  {{1,2},{3,4},{5,6}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
+       array_cat
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ </programlisting>
+
+   <function>array_prepend</function> and <function>array_append</function>
+   work with a one-dimensional array and a single element to be pushed on
+   to the beginning or end of the array, respectively. The array is extended
+   in the direction of the push. Hence, by pushing onto the beginning of an
+   array with a one-based subscript, a zero-based subscript array is created:
+
+ <programlisting>
+ SELECT array_dims(t.f) FROM (SELECT array_prepend(1, ARRAY[2,3]) AS f) AS t;
+  array_dims
+ ------------
+  [0:2]
+ (1 row)
+ </programlisting>
+
+   <function>array_cat</function> works with either two
+   <replaceable>n</>-dimension arrays, or an <replaceable>n</>-dimension
+   and an <replaceable>n+1</> dimension array. In the former case, the two
+   <replaceable>n</>-dimension arrays become outer elements of an
+   <replaceable>n+1</> dimension array. In the latter, the
+   <replaceable>n</>-dimension array is added as either the first or last
+   outer element of the <replaceable>n+1</> dimension array.
+  </para>
+
+  <para>
+   A final method of enlarging arrays is through the concatenation operator,
+   <command>||</command>, which works exactly as <function>array_cat</function>
+   does.
+ <programlisting>
+ SELECT ARRAY[1,2] || ARRAY[3,4];
+    ?column?
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
+       ?column?
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ (1 row)
+ </programlisting>
+  </para>
+
+  <para>
    The syntax for <command>CREATE TABLE</command> allows fixed-length
    arrays to be defined:

***************
*** 194,199 ****
--- 377,392 ----
   </para>

   <para>
+   An alternative syntax for one-dimensional arrays may be used.
+   <structfield>pay_by_quarter</structfield> could have been defined as:
+ <programlisting>
+     pay_by_quarter  integer ARRAY[4],
+ </programlisting>
+   This syntax may <emphasis>only</emphasis> be used with the integer
+   constant to denote the array size.
+  </para>
+
+  <para>
    Actually, the current implementation does not enforce the declared
    number of dimensions either.  Arrays of a particular element type are
    all considered to be of the same type, regardless of size or number
***************
*** 292,298 ****
     for the array's element type.  (Among the standard data types provided
     in the <productname>PostgreSQL</productname> distribution, type
     <literal>box</> uses a semicolon (<literal>;</>) but all the others
!    use comma.)  In a multidimensional array, each dimension (row, plane,
     cube, etc.) gets its own level of curly braces, and delimiters
     must be written between adjacent curly-braced entities of the same level.
     You may write whitespace before a left brace, after a right
--- 485,491 ----
     for the array's element type.  (Among the standard data types provided
     in the <productname>PostgreSQL</productname> distribution, type
     <literal>box</> uses a semicolon (<literal>;</>) but all the others
!    use comma.)  In a multi-dimensional array, each dimension (row, plane,
     cube, etc.) gets its own level of curly braces, and delimiters
     must be written between adjacent curly-braced entities of the same level.
     You may write whitespace before a left brace, after a right
***************
*** 300,305 ****
--- 493,564 ----
     is not ignored, however: after skipping leading whitespace, everything
     up to the next right brace or delimiter is taken as the item value.
    </para>
+
+   <para>
+    As illustrated earlier in this chapter, arrays may also be represented
+    using the <command>ARRAY</command> expression syntax. This representation
+    of an array value consists of items that are interpreted according to the
+    I/O conversion rules for the array's element type, plus decoration that
+    indicates the array structure. The decoration consists of the keyword
+    <command>ARRAY</command> and square brackets (<literal>[</> and
+    <literal>]</>) around the array values, plus delimiter characters between
+    adjacent items. The delimiter character is always a comma (<literal>,</>).
+    When representing multi-dimensional arrays, the keyword
+    <command>ARRAY</command> is only necessary for the outer level. For example,
+    <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as:
+ <programlisting>
+ SELECT ARRAY[['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   or it also could be written as:
+ <programlisting>
+ SELECT ARRAY[ARRAY['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   </para>
+
+   <para>
+    A final method to represent an array, is through an
+    <command>ARRAY</command> sub-select expression. For example:
+ <programlisting>
+ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
+                           ?column?
+ -------------------------------------------------------------
+  {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
+ (1 row)
+ </programlisting>
+   The sub-select may <emphasis>only</emphasis> return a single column. The
+   resulting one-dimensional array will have an element for each row in the
+   sub-select result, with an element type matching that of the sub-select's
+   target column.
+   </para>
+
+   <para>
+    Arrays may be cast from one type to another in similar fashion to other
+    data types:
+
+ <programlisting>
+ SELECT ARRAY[1,2,3]::oid[];
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+
+ SELECT CAST(ARRAY[1,2,3] AS float8[]);
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+ </programlisting>
+
+   </para>
+
   </sect2>

   <sect2>
***************
*** 316,321 ****
--- 575,588 ----
     Alternatively, you can use backslash-escaping to protect all data characters
     that would otherwise be taken as array syntax or ignorable white space.
    </para>
+
+  <note>
+   <para>
+    The discussion in the preceding paragraph with respect to double quoting does
+    not pertain to the <command>ARRAY</command> expression syntax. In that case,
+    each element is quoted exactly as any other literal value of the element type.
+   </para>
+  </note>

    <para>
     The array output routine will put double quotes around element values
Index: doc/src/sgml/func.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v
retrieving revision 1.154
diff -c -r1.154 func.sgml
*** doc/src/sgml/func.sgml    5 May 2003 15:08:49 -0000    1.154
--- doc/src/sgml/func.sgml    29 May 2003 04:53:00 -0000
***************
*** 6962,6967 ****
--- 6962,7209 ----

    </sect1>

+  <sect1 id="functions-array">
+   <title>Array Functions</title>
+
+   <para>
+    <xref linkend="array-operators-table"> shows the operators
+    available for the <type>array</type> types.
+   </para>
+
+     <table id="array-operators-table">
+      <title><type>array</type> Operators</title>
+      <tgroup cols="4">
+       <thead>
+        <row>
+     <entry>Operator</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry> <literal>=</literal> </entry>
+     <entry>equals</entry>
+     <entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry>
+     <entry><literal>t</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>element-to-array concatenation</entry>
+     <entry><literal>3 || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{3,4,5,6}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-element concatenation</entry>
+     <entry><literal>ARRAY[4,5,6] || 7</literal></entry>
+     <entry><literal>{4,5,6,7}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+
+   <para>
+    <xref linkend="array-functions-table"> shows the functions
+    available for use with array types. See <xref linkend="arrays">
+    for more discussion and examples for the use of these functions.
+   </para>
+
+     <table id="array-functions-table">
+      <title><type>array</type> Functions</title>
+      <tgroup cols="5">
+       <thead>
+        <row>
+     <entry>Function</entry>
+     <entry>Return Type</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry>
+      <literal>
+       <function>array_append</function>
+       (<type>anyarray</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the end of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_append(ARRAY[1,2], 3)</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_assign</function>
+       (<type>anyarray</type>, <type>integer</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      assign a value to a specific array element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_assign(ARRAY[1,2,3], 2, 99)</literal></entry>
+     <entry><literal>{1,99,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_cat</function>
+       (<type>anyarray</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      concatenate two arrays, returning <literal>NULL</literal>
+      for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_dims</function>
+       (<type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      returns a text representation of array dimension lower and upper bounds,
+      generating an ERROR for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry>
+     <entry><literal>[1:2][1:3]</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_lower</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns lower bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry>
+     <entry><literal>0</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_prepend</function>
+       (<type>anyelement</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the beginning of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_subscript</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>anyelement</type></entry>
+     <entry>
+      returns requested array element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_subscript(ARRAY[1,2,3], 3)</literal></entry>
+     <entry><literal>3</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_to_str</function>
+       (<type>anyarray</type>, <type>text</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      concatenates array elements using provided delimiter, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_to_str(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry>
+     <entry><literal>1.10~^~2.20~^~3.30</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_upper</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns upper bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry>
+     <entry><literal>4</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>singleton_array</function>
+       (<type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      create an array from the provided element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>singleton_array(1)</literal></entry>
+     <entry><literal>{1}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>str_to_array</function>
+       (<type>text</type>, <type>text</type>)
+      </literal>
+     </entry>
+     <entry><type>text[]</type></entry>
+     <entry>
+      splits string into array elements using provided delimiter, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>str_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry>
+     <entry><literal>{1.1,2.2,3.3}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+   </sect1>

   <sect1 id="functions-aggregate">
    <title>Aggregate Functions</title>
Index: src/backend/catalog/pg_aggregate.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_aggregate.c,v
retrieving revision 1.56
diff -c -r1.56 pg_aggregate.c
*** src/backend/catalog/pg_aggregate.c    18 Sep 2002 21:35:20 -0000    1.56
--- src/backend/catalog/pg_aggregate.c    29 May 2003 04:30:53 -0000
***************
*** 50,59 ****
      Oid            finalfn = InvalidOid;    /* can be omitted */
      Oid            finaltype;
      Oid            fnArgs[FUNC_MAX_ARGS];
!     int            nargs;
      Oid            procOid;
      TupleDesc    tupDesc;
      int            i;
      ObjectAddress myself,
                  referenced;

--- 50,65 ----
      Oid            finalfn = InvalidOid;    /* can be omitted */
      Oid            finaltype;
      Oid            fnArgs[FUNC_MAX_ARGS];
!     int            nargs_transfn;
!     int            nargs_finalfn;
      Oid            procOid;
      TupleDesc    tupDesc;
      int            i;
+     Oid            rettype;
+     Oid           *true_oid_array_transfn;
+     Oid           *true_oid_array_finalfn;
+     bool        retset;
+     FuncDetailCode fdresult;
      ObjectAddress myself,
                  referenced;

***************
*** 68,91 ****
      MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
      fnArgs[0] = aggTransType;
      if (aggBaseType == ANYOID)
!         nargs = 1;
      else
      {
          fnArgs[1] = aggBaseType;
!         nargs = 2;
      }
!     transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
      if (!OidIsValid(transfn))
!         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
      tup = SearchSysCache(PROCOID,
                           ObjectIdGetDatum(transfn),
                           0, 0, 0);
      if (!HeapTupleIsValid(tup))
!         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
      proc = (Form_pg_proc) GETSTRUCT(tup);
-     if (proc->prorettype != aggTransType)
-         elog(ERROR, "return type of transition function %s is not %s",
-          NameListToString(aggtransfnName), format_type_be(aggTransType));

      /*
       * If the transfn is strict and the initval is NULL, make sure input
--- 74,122 ----
      MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
      fnArgs[0] = aggTransType;
      if (aggBaseType == ANYOID)
!         nargs_transfn = 1;
      else
      {
          fnArgs[1] = aggBaseType;
!         nargs_transfn = 2;
      }
!
!     /*
!      * func_get_detail looks up the function in the catalogs, does
!      * disambiguation for polymorphic functions, handles inheritance, and
!      * returns the funcid and type and set or singleton status of the
!      * function's return value.  it also returns the true argument types
!      * to the function.
!      */
!     fdresult = func_get_detail(aggtransfnName, NIL, nargs_transfn, fnArgs,
!                                &transfn, &rettype, &retset,
!                                &true_oid_array_transfn);
!
!     /* only valid case is a normal function */
!     if (fdresult != FUNCDETAIL_NORMAL)
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
      if (!OidIsValid(transfn))
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
!     /*
!      * enforce consistency with ANYARRAY and ANYELEMENT argument
!      * and return types, possibly modifying return type along the way
!      */
!     rettype = enforce_generic_type_consistency(fnArgs, true_oid_array_transfn,
!                                                        nargs_transfn, rettype);
!
!     if (rettype != aggTransType)
!         elog(ERROR, "return type of transition function %s is not %s",
!          NameListToString(aggtransfnName), format_type_be(aggTransType));
!
      tup = SearchSysCache(PROCOID,
                           ObjectIdGetDatum(transfn),
                           0, 0, 0);
      if (!HeapTupleIsValid(tup))
!         func_error("AggregateCreate", aggtransfnName,
!                         nargs_transfn, fnArgs, NULL);
      proc = (Form_pg_proc) GETSTRUCT(tup);

      /*
       * If the transfn is strict and the initval is NULL, make sure input
***************
*** 105,121 ****
      {
          MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
          fnArgs[0] = aggTransType;
!         finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
!         if (!OidIsValid(finalfn))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!         tup = SearchSysCache(PROCOID,
!                              ObjectIdGetDatum(finalfn),
!                              0, 0, 0);
!         if (!HeapTupleIsValid(tup))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!         proc = (Form_pg_proc) GETSTRUCT(tup);
!         finaltype = proc->prorettype;
!         ReleaseSysCache(tup);
      }
      else
      {
--- 136,161 ----
      {
          MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
          fnArgs[0] = aggTransType;
!         nargs_finalfn = 1;
!
!         fdresult = func_get_detail(aggfinalfnName, NIL, 1, fnArgs,
!                                    &finalfn, &rettype, &retset,
!                                    &true_oid_array_finalfn);
!
!         /* only valid case is a normal function */
!         if (fdresult != FUNCDETAIL_NORMAL)
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!
!         if (!OidIsValid(finalfn))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!
!         /*
!          * enforce consistency with ANYARRAY and ANYELEMENT argument
!          * and return types, possibly modifying return type along the way
!          */
!         finaltype = enforce_generic_type_consistency(fnArgs,
!                                                      true_oid_array_finalfn,
!                                                      nargs_finalfn, rettype);
      }
      else
      {
***************
*** 125,130 ****
--- 165,191 ----
          finaltype = aggTransType;
      }
      Assert(OidIsValid(finaltype));
+
+     /*
+      * special disallowed cases:
+      * 1)    if finaltype is polymorphic, basetype cannot be ANY
+      * 2)    if finaltype is polymorphic, both args to transfn must be
+      *        polymorphic
+      */
+     if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
+     {
+         if (aggBaseType == ANYOID)
+             elog(ERROR, "aggregate with base type ANY must have a " \
+                         "non-polymorphic return type");
+
+         if (nargs_transfn > 1 && (
+             (true_oid_array_transfn[0] != ANYARRAYOID &&
+              true_oid_array_transfn[0] != ANYELEMENTOID) ||
+             (true_oid_array_transfn[1] != ANYARRAYOID &&
+              true_oid_array_transfn[1] != ANYELEMENTOID)))
+             elog(ERROR, "aggregate with polymorphic return type requires " \
+                         "state function with both arguments polymorphic");
+     }

      /*
       * Everything looks okay.  Try to create the pg_proc entry for the
Index: src/backend/commands/aggregatecmds.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/aggregatecmds.c,v
retrieving revision 1.5
diff -c -r1.5 aggregatecmds.c
*** src/backend/commands/aggregatecmds.c    4 Sep 2002 20:31:14 -0000    1.5
--- src/backend/commands/aggregatecmds.c    29 May 2003 04:30:53 -0000
***************
*** 119,125 ****
          baseTypeId = typenameTypeId(baseType);

      transTypeId = typenameTypeId(transType);
!     if (get_typtype(transTypeId) == 'p')
          elog(ERROR, "Aggregate transition datatype cannot be %s",
               format_type_be(transTypeId));

--- 119,127 ----
          baseTypeId = typenameTypeId(baseType);

      transTypeId = typenameTypeId(transType);
!     if (get_typtype(transTypeId) == 'p' &&
!         transTypeId != ANYARRAYOID &&
!         transTypeId != ANYELEMENTOID)
          elog(ERROR, "Aggregate transition datatype cannot be %s",
               format_type_be(transTypeId));

Index: src/backend/executor/nodeAgg.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeAgg.c,v
retrieving revision 1.104
diff -c -r1.104 nodeAgg.c
*** src/backend/executor/nodeAgg.c    9 Feb 2003 00:30:39 -0000    1.104
--- src/backend/executor/nodeAgg.c    29 May 2003 04:30:53 -0000
***************
*** 58,63 ****
--- 58,64 ----
  #include "executor/executor.h"
  #include "executor/nodeAgg.h"
  #include "miscadmin.h"
+ #include "nodes/makefuncs.h"
  #include "optimizer/clauses.h"
  #include "parser/parse_coerce.h"
  #include "parser/parse_expr.h"
***************
*** 212,218 ****
  static void agg_fill_hash_table(AggState *aggstate);
  static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
  static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
!

  /*
   * Initialize all aggregates for a new group of input values.
--- 213,219 ----
  static void agg_fill_hash_table(AggState *aggstate);
  static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
  static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
! static Oid resolve_type(Oid type_to_resolve, Oid context_type);

  /*
   * Initialize all aggregates for a new group of input values.
***************
*** 351,364 ****
      fcinfo.context = NULL;
      fcinfo.resultinfo = NULL;
      fcinfo.isnull = false;
-
      fcinfo.flinfo = &peraggstate->transfn;
      fcinfo.nargs = 2;
      fcinfo.arg[0] = pergroupstate->transValue;
      fcinfo.argnull[0] = pergroupstate->transValueIsNull;
      fcinfo.arg[1] = newVal;
      fcinfo.argnull[1] = isNull;
-
      newVal = FunctionCallInvoke(&fcinfo);

      /*
--- 352,363 ----
***************
*** 1176,1182 ****
--- 1175,1195 ----
          AclResult    aclresult;
          Oid            transfn_oid,
                      finalfn_oid;
+         FuncExpr   *transfnexpr,
+                    *finalfnexpr;
          Datum        textInitVal;
+         List       *fargs;
+         Oid            agg_rt_type;
+         Oid           *transfn_arg_types;
+         List       *transfn_args = NIL;
+         int            transfn_nargs;
+         Oid            transfn_ret_type;
+         Oid           *finalfn_arg_types = NULL;
+         List       *finalfn_args = NIL;
+         Oid            finalfn_ret_type = InvalidOid;
+         int            finalfn_nargs = 0;
+         Node       *arg0;
+         Node       *arg1;
          int            i;

          /* Look for a previous duplicate aggregate */
***************
*** 1224,1229 ****
--- 1237,1402 ----
                          &peraggstate->transtypeLen,
                          &peraggstate->transtypeByVal);

+         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
+         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
+
+         /* get the runtime aggregate argument type */
+         fargs = aggref->args;
+         agg_rt_type = exprType((Node *) nth(0, fargs));
+
+         /* get the transition function argument and return types */
+         transfn_ret_type = get_func_rettype(transfn_oid);
+         transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs);
+
+         /* resolve any polymorphic types */
+         if (transfn_nargs == 2)
+         /* base type was not ANY */
+         {
+             if (transfn_arg_types[1] == ANYARRAYOID ||
+                 transfn_arg_types[1] == ANYELEMENTOID)
+                 transfn_arg_types[1] = agg_rt_type;
+
+             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+                                                         agg_rt_type);
+
+             /*
+              * Build arg list to use on the transfn FuncExpr node. We really
+              * only care that the node type is correct so that the transfn
+              * can discover the actual argument types at runtime using
+              * get_fn_expr_argtype()
+              */
+             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
+                                                             -1, COERCE_DONTCARE);
+             arg1 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[1],
+                                                             -1, COERCE_DONTCARE);
+             transfn_args = makeList2(arg0, arg1);
+
+             /*
+              * the state transition function always returns the same type
+              * as its first argument
+              */
+             if (transfn_ret_type == ANYARRAYOID ||
+                 transfn_ret_type == ANYELEMENTOID)
+                 transfn_ret_type = transfn_arg_types[0];
+         }
+         else if (transfn_nargs == 1)
+         /*
+          * base type was ANY, therefore the aggregate return type should
+          * be non-polymorphic
+          */
+         {
+             Oid    finaltype = get_func_rettype(aggref->aggfnoid);
+
+             /*
+              * this should have been prevented in AggregateCreate,
+              * but check anyway
+              */
+             if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
+                 elog(ERROR, "aggregate with base type ANY must have a " \
+                             "non-polymorphic return type");
+
+             /* see if we have a final function */
+             if (OidIsValid(finalfn_oid))
+             {
+                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
+                 if (finalfn_nargs != 1)
+                     elog(ERROR, "final function takes unexpected number " \
+                                 "of arguments: %d", finalfn_nargs);
+
+                 /*
+                  * final function argument is always the same as the state
+                  * function return type
+                  */
+                 if (finalfn_arg_types[0] != ANYARRAYOID &&
+                     finalfn_arg_types[0] != ANYELEMENTOID)
+                 {
+                     /* if it is not ambiguous, use it */
+                     transfn_ret_type = finalfn_arg_types[0];
+                 }
+                 else
+                 {
+                     /* if it is ambiguous, try to derive it */
+                     finalfn_ret_type = finaltype;
+                     finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0],
+                                                             finalfn_ret_type);
+                     transfn_ret_type = finalfn_arg_types[0];
+                 }
+             }
+             else
+                 transfn_ret_type = finaltype;
+
+             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+                                                         transfn_ret_type);
+
+             /*
+              * Build arg list to use on the transfn FuncExpr node. We really
+              * only care that the node type is correct so that the transfn
+              * can discover the actual argument types at runtime using
+              * get_fn_expr_argtype()
+              */
+             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
+                                                             -1, COERCE_DONTCARE);
+             transfn_args = makeList1(arg0);
+         }
+         else
+             elog(ERROR, "state transition function takes unexpected number " \
+                         "of arguments: %d", transfn_nargs);
+
+         if (OidIsValid(finalfn_oid))
+         {
+             /* get the final function argument and return types */
+             if (finalfn_ret_type == InvalidOid)
+                 finalfn_ret_type = get_func_rettype(finalfn_oid);
+
+             if (!finalfn_arg_types)
+             {
+                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
+                 if (finalfn_nargs != 1)
+                     elog(ERROR, "final function takes unexpected number " \
+                                 "of arguments: %d", finalfn_nargs);
+             }
+
+             /*
+              * final function argument is always the same as the state
+              * function return type, which by now should have been resolved
+              */
+             if (finalfn_arg_types[0] == ANYARRAYOID ||
+                 finalfn_arg_types[0] == ANYELEMENTOID)
+                 finalfn_arg_types[0] = transfn_ret_type;
+
+             /*
+              * Build arg list to use on the finalfn FuncExpr node. We really
+              * only care that the node type is correct so that the finalfn
+              * can discover the actual argument type at runtime using
+              * get_fn_expr_argtype()
+              */
+             arg0 = (Node *) makeRelabelType((Expr *) NULL, finalfn_arg_types[0],
+                                                             -1, COERCE_DONTCARE);
+             finalfn_args = makeList1(arg0);
+
+             finalfn_ret_type = resolve_type(finalfn_ret_type,
+                                                 finalfn_arg_types[0]);
+         }
+
+         fmgr_info(transfn_oid, &peraggstate->transfn);
+         transfnexpr = (FuncExpr *) make_funcclause(transfn_oid,
+                           transfn_ret_type,
+                           false,            /* cannot be a set */
+                           COERCE_DONTCARE,    /* to match any user expr */
+                           transfn_args);
+         peraggstate->transfn.fn_expr = (Node *) transfnexpr;
+
+         if (OidIsValid(finalfn_oid))
+         {
+             fmgr_info(finalfn_oid, &peraggstate->finalfn);
+             finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid,
+                           finalfn_ret_type,
+                           false,            /* cannot be a set */
+                           COERCE_DONTCARE,    /* to match any user expr */
+                           finalfn_args);
+             peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
+         }
+
          /*
           * initval is potentially null, so don't try to access it as a
           * struct field. Must do it the hard way with SysCacheGetAttr.
***************
*** 1236,1249 ****
              peraggstate->initValue = (Datum) 0;
          else
              peraggstate->initValue = GetAggInitVal(textInitVal,
!                                                    aggform->aggtranstype);
!
!         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
!         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
!
!         fmgr_info(transfn_oid, &peraggstate->transfn);
!         if (OidIsValid(finalfn_oid))
!             fmgr_info(finalfn_oid, &peraggstate->finalfn);

          /*
           * If the transfn is strict and the initval is NULL, make sure
--- 1409,1415 ----
              peraggstate->initValue = (Datum) 0;
          else
              peraggstate->initValue = GetAggInitVal(textInitVal,
!                                                    transfn_arg_types[0]);

          /*
           * If the transfn is strict and the initval is NULL, make sure
***************
*** 1425,1428 ****
--- 1591,1627 ----
      elog(ERROR, "Aggregate function %u called as normal function",
           fcinfo->flinfo->fn_oid);
      return (Datum) 0;            /* keep compiler quiet */
+ }
+
+ static Oid
+ resolve_type(Oid type_to_resolve, Oid context_type)
+ {
+     Oid        resolved_type;
+
+     if (context_type == ANYARRAYOID || context_type == ANYELEMENTOID)
+         resolved_type = type_to_resolve;
+     else if (type_to_resolve == ANYARRAYOID)
+     /* any array */
+     {
+         Oid        context_type_arraytype = get_array_type(context_type);
+
+         if (context_type_arraytype != InvalidOid)
+             resolved_type = context_type_arraytype;
+         else
+             resolved_type = context_type;
+     }
+     else if (type_to_resolve == ANYELEMENTOID)
+     /* any element */
+     {
+         Oid        context_type_elemtype = get_element_type(context_type);
+
+         if (context_type_elemtype != InvalidOid)
+             resolved_type = context_type_elemtype;
+         else
+             resolved_type = context_type;
+     }
+     else
+         resolved_type = type_to_resolve;
+
+     return resolved_type;
  }
Index: src/backend/executor/nodeSubplan.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v
retrieving revision 1.45
diff -c -r1.45 nodeSubplan.c
*** src/backend/executor/nodeSubplan.c    8 Apr 2003 23:20:01 -0000    1.45
--- src/backend/executor/nodeSubplan.c    29 May 2003 04:30:53 -0000
***************
*** 28,50 ****
  #include "utils/datum.h"
  #include "utils/lsyscache.h"

-
- typedef struct ArrayBuildState
- {
-     MemoryContext mcontext;        /* where all the temp stuff is kept */
-     Datum       *dvalues;        /* array of accumulated Datums */
-     /*
-      * The allocated size of dvalues[] is always a multiple of
-      * ARRAY_ELEMS_CHUNKSIZE
-      */
- #define ARRAY_ELEMS_CHUNKSIZE    64
-     int            nelems;            /* number of valid Datums in dvalues[] */
-     Oid            element_type;    /* data type of the Datums */
-     int16        typlen;            /* needed info about datatype */
-     bool        typbyval;
-     char        typalign;
- } ArrayBuildState;
-
  static Datum ExecHashSubPlan(SubPlanState *node,
                               ExprContext *econtext,
                               bool *isNull);
--- 28,33 ----
***************
*** 54,66 ****
  static void buildSubPlanHash(SubPlanState *node);
  static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
  static bool tupleAllNulls(HeapTuple tuple);
- static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
-                                          Datum dvalue, bool disnull,
-                                          Oid element_type,
-                                          MemoryContext rcontext);
- static Datum makeArrayResult(ArrayBuildState *astate,
-                              MemoryContext rcontext);
-

  /* ----------------------------------------------------------------
   *        ExecSubPlan
--- 37,42 ----
***************
*** 1084,1185 ****
          prm->execPlan = node;
          parent->chgParam = bms_add_member(parent->chgParam, paramid);
      }
- }
-
- /*
-  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
-  *
-  *    astate is working state (NULL on first call)
-  *    rcontext is where to keep working state
-  */
- static ArrayBuildState *
- accumArrayResult(ArrayBuildState *astate,
-                  Datum dvalue, bool disnull,
-                  Oid element_type,
-                  MemoryContext rcontext)
- {
-     MemoryContext arr_context,
-                   oldcontext;
-
-     if (astate == NULL)
-     {
-         /* First time through --- initialize */
-
-         /* Make a temporary context to hold all the junk */
-         arr_context = AllocSetContextCreate(rcontext,
-                                             "ARRAY_SUBLINK Result",
-                                             ALLOCSET_DEFAULT_MINSIZE,
-                                             ALLOCSET_DEFAULT_INITSIZE,
-                                             ALLOCSET_DEFAULT_MAXSIZE);
-         oldcontext = MemoryContextSwitchTo(arr_context);
-         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
-         astate->mcontext = arr_context;
-         astate->dvalues = (Datum *)
-             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
-         astate->nelems = 0;
-         astate->element_type = element_type;
-         get_typlenbyvalalign(element_type,
-                              &astate->typlen,
-                              &astate->typbyval,
-                              &astate->typalign);
-     }
-     else
-     {
-         oldcontext = MemoryContextSwitchTo(astate->mcontext);
-         Assert(astate->element_type == element_type);
-         /* enlarge dvalues[] if needed */
-         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
-             astate->dvalues = (Datum *)
-                 repalloc(astate->dvalues,
-                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
-     }
-
-     if (disnull)
-         elog(ERROR, "NULL elements not allowed in Arrays");
-
-     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
-     astate->dvalues[astate->nelems++] =
-         datumCopy(dvalue, astate->typbyval, astate->typlen);
-
-     MemoryContextSwitchTo(oldcontext);
-
-     return astate;
- }
-
- /*
-  * makeArrayResult - produce final result of ARRAY_SUBLINK
-  *
-  *    astate is working state (not NULL)
-  *    rcontext is where to construct result
-  */
- static Datum
- makeArrayResult(ArrayBuildState *astate,
-                 MemoryContext rcontext)
- {
-     ArrayType  *result;
-     int            dims[1];
-     int            lbs[1];
-     MemoryContext oldcontext;
-
-     /* Build the final array result in rcontext */
-     oldcontext = MemoryContextSwitchTo(rcontext);
-
-     dims[0] = astate->nelems;
-     lbs[0] = 1;
-
-     result = construct_md_array(astate->dvalues,
-                                 1,
-                                 dims,
-                                 lbs,
-                                 astate->element_type,
-                                 astate->typlen,
-                                 astate->typbyval,
-                                 astate->typalign);
-
-     MemoryContextSwitchTo(oldcontext);
-
-     /* Clean up all the junk */
-     MemoryContextDelete(astate->mcontext);
-
-     return PointerGetDatum(result);
  }
--- 1060,1063 ----
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.251
diff -c -r1.251 copyfuncs.c
*** src/backend/nodes/copyfuncs.c    28 May 2003 16:03:56 -0000    1.251
--- src/backend/nodes/copyfuncs.c    29 May 2003 04:30:53 -0000
***************
*** 727,732 ****
--- 727,733 ----
      COPY_NODE_FIELD(target);
      COPY_SCALAR_FIELD(aggstar);
      COPY_SCALAR_FIELD(aggdistinct);
+     COPY_NODE_FIELD(args);

      return newnode;
  }
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.194
diff -c -r1.194 equalfuncs.c
*** src/backend/nodes/equalfuncs.c    28 May 2003 16:03:56 -0000    1.194
--- src/backend/nodes/equalfuncs.c    29 May 2003 04:30:53 -0000
***************
*** 204,209 ****
--- 204,210 ----
      COMPARE_NODE_FIELD(target);
      COMPARE_SCALAR_FIELD(aggstar);
      COMPARE_SCALAR_FIELD(aggdistinct);
+     COMPARE_NODE_FIELD(args);

      return true;
  }
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v
retrieving revision 1.206
diff -c -r1.206 outfuncs.c
*** src/backend/nodes/outfuncs.c    28 May 2003 16:03:56 -0000    1.206
--- src/backend/nodes/outfuncs.c    29 May 2003 04:30:53 -0000
***************
*** 615,620 ****
--- 615,621 ----
      WRITE_NODE_FIELD(target);
      WRITE_BOOL_FIELD(aggstar);
      WRITE_BOOL_FIELD(aggdistinct);
+     WRITE_NODE_FIELD(args);
  }

  static void
Index: src/backend/nodes/readfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v
retrieving revision 1.153
diff -c -r1.153 readfuncs.c
*** src/backend/nodes/readfuncs.c    6 May 2003 00:20:32 -0000    1.153
--- src/backend/nodes/readfuncs.c    29 May 2003 04:30:53 -0000
***************
*** 415,420 ****
--- 415,421 ----
      READ_NODE_FIELD(target);
      READ_BOOL_FIELD(aggstar);
      READ_BOOL_FIELD(aggdistinct);
+     READ_NODE_FIELD(args);

      READ_DONE();
  }
Index: src/backend/optimizer/util/clauses.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/util/clauses.c,v
retrieving revision 1.138
diff -c -r1.138 clauses.c
*** src/backend/optimizer/util/clauses.c    28 May 2003 22:32:49 -0000    1.138
--- src/backend/optimizer/util/clauses.c    29 May 2003 04:30:53 -0000
***************
*** 133,138 ****
--- 133,160 ----
  }

  /*****************************************************************************
+  *              FUNCTION clause functions
+  *****************************************************************************/
+
+ /*
+  * make_funcclause
+  *        Creates a function clause given its function info and argument list.
+  */
+ Expr *
+ make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
+                             CoercionForm funcformat, List *funcargs)
+ {
+     FuncExpr   *expr = makeNode(FuncExpr);
+
+     expr->funcid = funcid;
+     expr->funcresulttype = funcresulttype;
+     expr->funcretset = funcretset;
+     expr->funcformat = funcformat;
+     expr->args = funcargs;
+     return (Expr *) expr;
+ }
+
+ /*****************************************************************************
   *        NOT clause functions
   *****************************************************************************/

Index: src/backend/parser/parse_coerce.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v
retrieving revision 2.97
diff -c -r2.97 parse_coerce.c
*** src/backend/parser/parse_coerce.c    26 May 2003 00:11:27 -0000    2.97
--- src/backend/parser/parse_coerce.c    29 May 2003 04:30:53 -0000
***************
*** 859,865 ****
      /* Get the element type based on the array type, if we have one */
      if (OidIsValid(array_typeid))
      {
!         array_typelem = get_element_type(array_typeid);
          if (!OidIsValid(array_typelem))
              elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
                   format_type_be(array_typeid));
--- 859,869 ----
      /* Get the element type based on the array type, if we have one */
      if (OidIsValid(array_typeid))
      {
!         if (array_typeid != ANYARRAYOID)
!             array_typelem = get_element_type(array_typeid);
!         else
!             array_typelem = ANYELEMENTOID;
!
          if (!OidIsValid(array_typelem))
              elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
                   format_type_be(array_typeid));
***************
*** 919,925 ****
      {
          if (!OidIsValid(array_typeid))
          {
!             array_typeid = get_array_type(elem_typeid);
              if (!OidIsValid(array_typeid))
                  elog(ERROR, "Cannot find array type for datatype %s",
                       format_type_be(elem_typeid));
--- 923,933 ----
      {
          if (!OidIsValid(array_typeid))
          {
!             if (elem_typeid != ANYELEMENTOID)
!                 array_typeid = get_array_type(elem_typeid);
!             else
!                 array_typeid = ANYARRAYOID;
!
              if (!OidIsValid(array_typeid))
                  elog(ERROR, "Cannot find array type for datatype %s",
                       format_type_be(elem_typeid));
Index: src/backend/parser/parse_func.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_func.c,v
retrieving revision 1.148
diff -c -r1.148 parse_func.c
*** src/backend/parser/parse_func.c    26 May 2003 00:11:27 -0000    1.148
--- src/backend/parser/parse_func.c    29 May 2003 04:30:53 -0000
***************
*** 335,340 ****
--- 335,341 ----
          aggref->target = lfirst(fargs);
          aggref->aggstar = agg_star;
          aggref->aggdistinct = agg_distinct;
+         aggref->args = fargs;

          retval = (Node *) aggref;

Index: src/backend/utils/adt/array_userfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/array_userfuncs.c,v
retrieving revision 1.1
diff -c -r1.1 array_userfuncs.c
*** src/backend/utils/adt/array_userfuncs.c    8 Apr 2003 23:20:02 -0000    1.1
--- src/backend/utils/adt/array_userfuncs.c    29 May 2003 04:51:01 -0000
***************
*** 42,48 ****
      else
          ndims = 1;

!     PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
                                                   PG_GETARG_DATUM(0),
                                                   ndims));
  }
--- 42,48 ----
      else
          ndims = 1;

!     PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, elem_type,
                                                   PG_GETARG_DATUM(0),
                                                   ndims));
  }
***************
*** 70,75 ****
--- 70,76 ----
      Oid            arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
      Oid            arg0_elemid;
      Oid            arg1_elemid;
+     ArrayMetaState *my_extra;

      if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
          elog(ERROR, "array_push: cannot determine input data types");
***************
*** 95,122 ****
          PG_RETURN_NULL();        /* keep compiler quiet */
      }

!     /* Sanity check: do we have a one-dimensional array */
!     if (ARR_NDIM(v) != 1)
!         elog(ERROR, "Arrays greater than one-dimension are not supported");
!
!     lb = ARR_LBOUND(v);
!     dimv = ARR_DIMS(v);
!     if (arg0_elemid != InvalidOid)
      {
!         /* append newelem */
!         int    ub = dimv[0] + lb[0] - 1;
!         indx = ub + 1;
      }
      else
      {
!         /* prepend newelem */
!         indx = lb[0] - 1;
      }

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

!     result = array_set(v, 1, &indx, newelem, -1,
!                        typlen, typbyval, typalign, &isNull);

      PG_RETURN_ARRAYTYPE_P(result);
  }
--- 96,156 ----
          PG_RETURN_NULL();        /* keep compiler quiet */
      }

!     if (ARR_NDIM(v) == 1)
      {
!         lb = ARR_LBOUND(v);
!         dimv = ARR_DIMS(v);
!
!         if (arg0_elemid != InvalidOid)
!         {
!             /* append newelem */
!             int    ub = dimv[0] + lb[0] - 1;
!             indx = ub + 1;
!         }
!         else
!         {
!             /* prepend newelem */
!             indx = lb[0] - 1;
!         }
      }
+     else if (ARR_NDIM(v) == 0)
+         indx = 1;
      else
+         elog(ERROR, "only empty and one-dimensional arrays are supported");
+
+
+     /*
+      * We arrange to look up info about element type only once per series
+      * of calls, assuming the element type doesn't change underneath us.
+      */
+     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+     if (my_extra == NULL)
      {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
      }

!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }
!
!     result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval,
!                                                  typalign, &isNull);

      PG_RETURN_ARRAYTYPE_P(result);
  }
***************
*** 145,157 ****

      /*
       * We must have one of the following combinations of inputs:
!      * 1) two arrays with ndims1 == ndims2
!      * 2) ndims1 == ndims2 - 1
!      * 3) ndims1 == ndims2 + 1
       */
      ndims1 = ARR_NDIM(v1);
      ndims2 = ARR_NDIM(v2);

      if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
          elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
                      "%d dimensions", ndims1, ndims2);
--- 179,206 ----

      /*
       * We must have one of the following combinations of inputs:
!      * 1) one empty array, and one non-empty array
!      * 2) both arrays empty
!      * 3) two arrays with ndims1 == ndims2
!      * 4) ndims1 == ndims2 - 1
!      * 5) ndims1 == ndims2 + 1
       */
      ndims1 = ARR_NDIM(v1);
      ndims2 = ARR_NDIM(v2);

+     /*
+      * short circuit - if one input array is empty, and the other is not,
+      * we return the non-empty one as the result
+      *
+      * if both are empty, return the first one
+      */
+     if (ndims1 == 0 && ndims2 > 0)
+         PG_RETURN_ARRAYTYPE_P(v2);
+
+     if (ndims2 == 0)
+         PG_RETURN_ARRAYTYPE_P(v1);
+
+     /* the rest fall into combo 2, 3, or 4 */
      if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
          elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
                      "%d dimensions", ndims1, ndims2);
***************
*** 266,314 ****
      PG_RETURN_ARRAYTYPE_P(result);
  }

- /*----------------------------------------------------------------------------
-  * array_accum :
-  *        accumulator to build a 1-D array from input values -- this can be used
-  *        to create custom aggregates.
-  *
-  * This function is not marked strict, so we have to be careful about nulls.
-  *----------------------------------------------------------------------------
-  */
- Datum
- array_accum(PG_FUNCTION_ARGS)
- {
-     /* return NULL if both arguments are NULL */
-     if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
-         PG_RETURN_NULL();
-
-     /* create a new 1-D array from the new element if the array is NULL */
-     if (PG_ARGISNULL(0))
-     {
-         Oid            tgt_type = get_fn_expr_rettype(fcinfo);
-         Oid            tgt_elem_type;
-
-         if (tgt_type == InvalidOid)
-             elog(ERROR, "Cannot determine target array type");
-         tgt_elem_type = get_element_type(tgt_type);
-         if (tgt_elem_type == InvalidOid)
-             elog(ERROR, "Target type is not an array");
-
-         PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
-                                                      PG_GETARG_DATUM(1),
-                                                      1));
-     }
-
-     /* return the array if the new element is NULL */
-     if (PG_ARGISNULL(1))
-         PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0));
-
-     /*
-      * Otherwise this is equivalent to array_push.  We hack the call a little
-      * so that array_push can see the fn_expr information.
-      */
-     return array_push(fcinfo);
- }
-
  /*-----------------------------------------------------------------------------
   * array_assign :
   *        assign an element of an array to a new value and return the
--- 315,320 ----
***************
*** 329,334 ****
--- 335,341 ----
      int16        typlen;
      bool        typbyval;
      char        typalign;
+     ArrayMetaState *my_extra;

      v = PG_GETARG_ARRAYTYPE_P(0);
      idx_to_chg = PG_GETARG_INT32(1);
***************
*** 349,355 ****
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      result = array_set(v, 1, &idx_to_chg, newelem, -1,
                         typlen, typbyval, typalign, &isNull);
--- 356,390 ----
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      result = array_set(v, 1, &idx_to_chg, newelem, -1,
                         typlen, typbyval, typalign, &isNull);
***************
*** 375,380 ****
--- 410,416 ----
      int16        typlen;
      bool        typbyval;
      char        typalign;
+     ArrayMetaState *my_extra;

      v = PG_GETARG_ARRAYTYPE_P(0);
      idx = PG_GETARG_INT32(1);
***************
*** 394,400 ****
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);

--- 430,464 ----
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);

***************
*** 402,412 ****
  }

  /*
!  * actually does the work for singleton_array(), and array_accum() if it is
!  * given a null input array.
   */
  ArrayType *
! create_singleton_array(Oid element_type, Datum element, int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
--- 466,478 ----
  }

  /*
!  * actually does the work for singleton_array()
   */
  ArrayType *
! create_singleton_array(FunctionCallInfo fcinfo,
!                        Oid element_type,
!                        Datum element,
!                        int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
***************
*** 415,420 ****
--- 481,487 ----
      int        dims[MAXDIM];
      int        lbs[MAXDIM];
      int        i;
+     ArrayMetaState *my_extra;

      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);
***************
*** 429,435 ****
          lbs[i] = 1;
      }

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      return construct_md_array(dvalues, ndims, dims, lbs, element_type,
                                typlen, typbyval, typalign);
--- 496,530 ----
          lbs[i] = 1;
      }

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      return construct_md_array(dvalues, ndims, dims, lbs, element_type,
                                typlen, typbyval, typalign);
Index: src/backend/utils/adt/arrayfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v
retrieving revision 1.89
diff -c -r1.89 arrayfuncs.c
*** src/backend/utils/adt/arrayfuncs.c    9 May 2003 23:01:45 -0000    1.89
--- src/backend/utils/adt/arrayfuncs.c    29 May 2003 04:55:31 -0000
***************
*** 21,28 ****
--- 21,30 ----
  #include "catalog/pg_type.h"
  #include "libpq/pqformat.h"
  #include "parser/parse_coerce.h"
+ #include "parser/parse_oper.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
+ #include "utils/datum.h"
  #include "utils/memutils.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
***************
*** 70,85 ****

  #define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)

- /* I/O function selector for system_cache_lookup */
- typedef enum IOFuncSelector
- {
-     IOFunc_input,
-     IOFunc_output,
-     IOFunc_receive,
-     IOFunc_send
- } IOFuncSelector;
-
-
  static int    ArrayCount(char *str, int *dim, char typdelim);
  static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
               FmgrInfo *inputproc, Oid typelem, int32 typmod,
--- 72,77 ----
***************
*** 93,102 ****
  static void CopyArrayEls(char *p, Datum *values, int nitems,
               int typlen, bool typbyval, char typalign,
               bool freedata);
- static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
-                                 int *typlen, bool *typbyval,
-                                 char *typdelim, Oid *typelem,
-                                 Oid *proc, char *typalign);
  static Datum ArrayCast(char *value, bool byval, int len);
  static int ArrayCastAndSet(Datum src,
                  int typlen, bool typbyval, char typalign,
--- 85,90 ----
***************
*** 120,126 ****
                     int *st, int *endp, char *srcPtr,
                     int typlen, bool typbyval, char typalign);

-
  /*---------------------------------------------------------------------
   * array_in :
   *          converts an array from the external format in "string" to
--- 108,113 ----
***************
*** 154,165 ****
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;

!     /* Get info about element type, including its input conversion proc */
!     system_cache_lookup(element_type, IOFunc_input,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typinput, &typalign);
!     fmgr_info(typinput, &inputproc);

      /* Make a modifiable copy of the input */
      /* XXX why are we allocating an extra 2 bytes here? */
--- 141,189 ----
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;
+     ArrayMetaState *my_extra;

!     /*
!      * We arrange to look up info about element type, including its input
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its input conversion proc */
!         get_type_metadata(element_type, IOFunc_input,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typinput, &typalign);
!         fmgr_info(typinput, &inputproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typinput;
!         my_extra->typalign = typalign;
!         my_extra->proc = inputproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typinput = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         inputproc = my_extra->proc;
!     }

      /* Make a modifiable copy of the input */
      /* XXX why are we allocating an extra 2 bytes here? */
***************
*** 636,647 ****
                  indx[MAXDIM];
      int            ndim,
                 *dim;

      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_output,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typoutput, &typalign);
!     fmgr_info(typoutput, &outputproc);

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
--- 660,710 ----
                  indx[MAXDIM];
      int            ndim,
                 *dim;
+     ArrayMetaState *my_extra;

      element_type = ARR_ELEMTYPE(v);
!
!     /*
!      * We arrange to look up info about element type, including its input
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its output conversion proc */
!         get_type_metadata(element_type, IOFunc_output,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typoutput, &typalign);
!         fmgr_info(typoutput, &outputproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typoutput;
!         my_extra->typalign = typalign;
!         my_extra->proc = outputproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typoutput = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         outputproc = my_extra->proc;
!     }

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
***************
*** 800,805 ****
--- 863,869 ----
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;
+     ArrayMetaState *my_extra;

      /* Get the array header information */
      ndim = pq_getmsgint(buf, 4);
***************
*** 831,844 ****
          PG_RETURN_ARRAYTYPE_P(retval);
      }

!     /* Get info about element type, including its receive conversion proc */
!     system_cache_lookup(element_type, IOFunc_receive,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typreceive, &typalign);
!     if (!OidIsValid(typreceive))
!         elog(ERROR, "No binary input function available for type %s",
!              format_type_be(element_type));
!     fmgr_info(typreceive, &receiveproc);

      dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
                                typlen, typbyval, typalign,
--- 895,944 ----
          PG_RETURN_ARRAYTYPE_P(retval);
      }

!     /*
!      * We arrange to look up info about element type, including its receive
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its receive conversion proc */
!         get_type_metadata(element_type, IOFunc_receive,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typreceive, &typalign);
!         if (!OidIsValid(typreceive))
!             elog(ERROR, "No binary input function available for type %s",
!                  format_type_be(element_type));
!         fmgr_info(typreceive, &receiveproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typreceive;
!         my_extra->typalign = typalign;
!         my_extra->proc = receiveproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typreceive = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         receiveproc = my_extra->proc;
!     }

      dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
                                typlen, typbyval, typalign,
***************
*** 976,990 ****
      int            ndim,
                 *dim;
      StringInfoData buf;

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
!                         &typdelim, &typelem, &typsend, &typalign);
!     if (!OidIsValid(typsend))
!         elog(ERROR, "No binary output function available for type %s",
!              format_type_be(element_type));
!     fmgr_info(typsend, &sendproc);

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
--- 1076,1129 ----
      int            ndim,
                 *dim;
      StringInfoData buf;
+     ArrayMetaState *my_extra;

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!
!     /*
!      * We arrange to look up info about element type, including its send
!      * proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its send proc */
!         get_type_metadata(element_type, IOFunc_send, &typlen, &typbyval,
!                             &typdelim, &typelem, &typsend, &typalign);
!         if (!OidIsValid(typsend))
!             elog(ERROR, "No binary output function available for type %s",
!                  format_type_be(element_type));
!         fmgr_info(typsend, &sendproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typsend;
!         my_extra->typalign = typalign;
!         my_extra->proc = sendproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typsend = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         sendproc = my_extra->proc;
!     }

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
***************
*** 1476,1481 ****
--- 1615,1640 ----
      array = DatumGetArrayTypeP(PointerGetDatum(array));

      ndim = ARR_NDIM(array);
+
+     /*
+      * if number of dims is zero, i.e. an empty array, create an array
+      * with nSubscripts dimensions, and set the lower bounds to the supplied
+      * subscripts
+      */
+     if (ndim == 0)
+     {
+         Oid        elmtype = ARR_ELEMTYPE(array);
+
+         for (i = 0; i < nSubscripts; i++)
+         {
+             dim[i] = 1;
+             lb[i] = indx[i];
+         }
+
+         return construct_md_array(&dataValue, nSubscripts, dim, lb, elmtype,
+                                                 elmlen, elmbyval, elmalign);
+     }
+
      if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
          elog(ERROR, "Invalid array subscripts");

***************
*** 1632,1637 ****
--- 1791,1821 ----
      /* note: we assume srcArray contains no toasted elements */

      ndim = ARR_NDIM(array);
+
+     /*
+      * if number of dims is zero, i.e. an empty array, create an array
+      * with nSubscripts dimensions, and set the upper and lower bounds
+      * to the supplied subscripts
+      */
+     if (ndim == 0)
+     {
+         Datum  *dvalues;
+         int        nelems;
+         Oid        elmtype = ARR_ELEMTYPE(array);
+
+         deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
+                                                         &dvalues, &nelems);
+
+         for (i = 0; i < nSubscripts; i++)
+         {
+             dim[i] = 1 + upperIndx[i] - lowerIndx[i];
+             lb[i] = lowerIndx[i];
+         }
+
+         return construct_md_array(dvalues, nSubscripts, dim, lb, elmtype,
+                                                  elmlen, elmbyval, elmalign);
+     }
+
      if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
          elog(ERROR, "Invalid array subscripts");

***************
*** 1811,1816 ****
--- 1995,2007 ----
      Oid            typelem;
      Oid            proc;
      char       *s;
+     typedef struct {
+         ArrayMetaState *inp_extra;
+         ArrayMetaState *ret_extra;
+     } am_extra;
+     am_extra  *my_extra;
+     ArrayMetaState *inp_extra;
+     ArrayMetaState *ret_extra;

      /* Get input array */
      if (fcinfo->nargs < 1)
***************
*** 1829,1839 ****
      if (nitems <= 0)
          PG_RETURN_ARRAYTYPE_P(v);

!     /* Lookup source and result types. Unneeded variables are reused. */
!     system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
!                         &typdelim, &typelem, &proc, &inp_typalign);
!     system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
!                         &typdelim, &typelem, &proc, &typalign);

      /* Allocate temporary array for new values */
      values = (Datum *) palloc(nitems * sizeof(Datum));
--- 2020,2100 ----
      if (nitems <= 0)
          PG_RETURN_ARRAYTYPE_P(v);

!     /*
!      * We arrange to look up info about input and return element types only
!      * once per series of calls, assuming the element type doesn't change
!      * underneath us.
!      */
!     my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(am_extra));
!         my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
!
!         my_extra->inp_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         inp_extra = my_extra->inp_extra;
!         inp_extra->element_type = InvalidOid;
!
!         my_extra->ret_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         ret_extra = my_extra->ret_extra;
!         ret_extra->element_type = InvalidOid;
!     }
!     else
!     {
!         inp_extra = my_extra->inp_extra;
!         ret_extra = my_extra->ret_extra;
!     }
!
!     if (inp_extra->element_type != inpType)
!     {
!         /* Lookup source and result types. Unneeded variables are reused. */
!         get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
!                             &typdelim, &typelem, &proc, &inp_typalign);
!
!         inp_extra->element_type = inpType;
!         inp_extra->typlen = inp_typlen;
!         inp_extra->typbyval = inp_typbyval;
!         inp_extra->typdelim = typdelim;
!         inp_extra->typelem = typelem;
!         inp_extra->typiofunc = proc;
!         inp_extra->typalign = inp_typalign;
!     }
!     else
!     {
!         inp_typlen = inp_extra->typlen;
!         inp_typbyval = inp_extra->typbyval;
!         typdelim = inp_extra->typdelim;
!         typelem = inp_extra->typelem;
!         proc = inp_extra->typiofunc;
!         inp_typalign = inp_extra->typalign;
!     }
!
!     if (ret_extra->element_type != retType)
!     {
!         /* Lookup source and result types. Unneeded variables are reused. */
!         get_type_metadata(retType, IOFunc_input, &typlen, &typbyval,
!                             &typdelim, &typelem, &proc, &typalign);
!
!         ret_extra->element_type = retType;
!         ret_extra->typlen = typlen;
!         ret_extra->typbyval = typbyval;
!         ret_extra->typdelim = typdelim;
!         ret_extra->typelem = typelem;
!         ret_extra->typiofunc = proc;
!         ret_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = ret_extra->typlen;
!         typbyval = ret_extra->typbyval;
!         typdelim = ret_extra->typdelim;
!         typelem = ret_extra->typelem;
!         proc = ret_extra->typiofunc;
!         typalign = ret_extra->typalign;
!     }

      /* Allocate temporary array for new values */
      values = (Datum *) palloc(nitems * sizeof(Datum));
***************
*** 2049,2056 ****
   *          compares two arrays for equality
   * result :
   *          returns true if the arrays are equal, false otherwise.
-  *
-  * XXX bitwise equality is pretty bogus ...
   *-----------------------------------------------------------------------------
   */
  Datum
--- 2310,2315 ----
***************
*** 2058,2069 ****
  {
      ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
      ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
      bool        result = true;

!     if (ARR_SIZE(array1) != ARR_SIZE(array2))
!         result = false;
!     else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
          result = false;

      /* Avoid leaking memory when handed toasted input. */
      PG_FREE_IF_COPY(array1, 0);
--- 2317,2434 ----
  {
      ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
      ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
+     char       *p1 = (char *) ARR_DATA_PTR(array1);
+     char       *p2 = (char *) ARR_DATA_PTR(array2);
+     int            ndims1 = ARR_NDIM(array1);
+     int            ndims2 = ARR_NDIM(array2);
+     int           *dims1 = ARR_DIMS(array1);
+     int           *dims2 = ARR_DIMS(array2);
+     int            nitems1 = ArrayGetNItems(ndims1, dims1);
+     int            nitems2 = ArrayGetNItems(ndims2, dims2);
+     Oid            element_type = ARR_ELEMTYPE(array1);
+     FmgrInfo   *ae_fmgr_info = fcinfo->flinfo;
      bool        result = true;
+     int            typlen;
+     bool        typbyval;
+     char        typdelim;
+     Oid            typelem;
+     char        typalign;
+     Oid            typiofunc;
+     int            i;
+     ArrayMetaState *my_extra;
+     FunctionCallInfoData locfcinfo;

!     /* fast path if the arrays do not have the same number of elements */
!     if (nitems1 != nitems2)
          result = false;
+     else
+     {
+         /*
+          * We arrange to look up the equality function only once per series of
+          * calls, assuming the element type doesn't change underneath us.
+          */
+         my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
+         if (my_extra == NULL)
+         {
+             ae_fmgr_info->fn_extra = MemoryContextAlloc(ae_fmgr_info->fn_mcxt,
+                                                      sizeof(ArrayMetaState));
+             my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
+             my_extra->element_type = InvalidOid;
+         }
+
+         if (my_extra->element_type != element_type)
+         {
+             Oid        opfuncid = equality_oper_funcid(element_type);
+
+             if (OidIsValid(opfuncid))
+                 fmgr_info_cxt(opfuncid, &my_extra->proc, ae_fmgr_info->fn_mcxt);
+             else
+                 elog(ERROR,
+                      "array_eq: cannot find equality operator for type: %u",
+                      element_type);
+
+             get_type_metadata(element_type, IOFunc_output,
+                               &typlen, &typbyval, &typdelim,
+                               &typelem, &typiofunc, &typalign);
+
+             my_extra->element_type = element_type;
+             my_extra->typlen = typlen;
+             my_extra->typbyval = typbyval;
+             my_extra->typdelim = typdelim;
+             my_extra->typelem = typelem;
+             my_extra->typiofunc = typiofunc;
+             my_extra->typalign = typalign;
+         }
+         else
+         {
+             typlen = my_extra->typlen;
+             typbyval = my_extra->typbyval;
+             typdelim = my_extra->typdelim;
+             typelem = my_extra->typelem;
+             typiofunc = my_extra->typiofunc;
+             typalign = my_extra->typalign;
+         }
+
+         /*
+          * apply the operator to each pair of array elements.
+          */
+         MemSet(&locfcinfo, 0, sizeof(locfcinfo));
+         locfcinfo.flinfo = &my_extra->proc;
+         locfcinfo.nargs = 2;
+
+         /* Loop over source data */
+         for (i = 0; i < nitems1; i++)
+         {
+             Datum    elt1;
+             Datum    elt2;
+             bool    oprresult;
+
+             /* Get element pair */
+             elt1 = fetch_att(p1, typbyval, typlen);
+             elt2 = fetch_att(p2, typbyval, typlen);
+
+             p1 = att_addlength(p1, typlen, PointerGetDatum(p1));
+             p1 = (char *) att_align(p1, typalign);
+
+             p2 = att_addlength(p2, typlen, PointerGetDatum(p2));
+             p2 = (char *) att_align(p2, typalign);
+
+             /*
+              * Apply the operator to the element pair
+              */
+             locfcinfo.arg[0] = elt1;
+             locfcinfo.arg[1] = elt2;
+             locfcinfo.argnull[0] = false;
+             locfcinfo.argnull[1] = false;
+             locfcinfo.isnull = false;
+             oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+             if (!oprresult)
+             {
+                 result = false;
+                 break;
+             }
+         }
+     }

      /* Avoid leaking memory when handed toasted input. */
      PG_FREE_IF_COPY(array1, 0);
***************
*** 2077,2125 ****
  /******************|          Support  Routines              |*****************/
  /***************************************************************************/

- static void
- system_cache_lookup(Oid element_type,
-                     IOFuncSelector which_func,
-                     int *typlen,
-                     bool *typbyval,
-                     char *typdelim,
-                     Oid *typelem,
-                     Oid *proc,
-                     char *typalign)
- {
-     HeapTuple    typeTuple;
-     Form_pg_type typeStruct;
-
-     typeTuple = SearchSysCache(TYPEOID,
-                                ObjectIdGetDatum(element_type),
-                                0, 0, 0);
-     if (!HeapTupleIsValid(typeTuple))
-         elog(ERROR, "cache lookup failed for type %u", element_type);
-     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
-
-     *typlen = typeStruct->typlen;
-     *typbyval = typeStruct->typbyval;
-     *typdelim = typeStruct->typdelim;
-     *typelem = typeStruct->typelem;
-     *typalign = typeStruct->typalign;
-     switch (which_func)
-     {
-         case IOFunc_input:
-             *proc = typeStruct->typinput;
-             break;
-         case IOFunc_output:
-             *proc = typeStruct->typoutput;
-             break;
-         case IOFunc_receive:
-             *proc = typeStruct->typreceive;
-             break;
-         case IOFunc_send:
-             *proc = typeStruct->typsend;
-             break;
-     }
-     ReleaseSysCache(typeTuple);
- }
-
  /*
   * Fetch array element at pointer, converted correctly to a Datum
   */
--- 2442,2447 ----
***************
*** 2423,2428 ****
--- 2745,2762 ----
          if (tgt_elem_type == InvalidOid)
              elog(ERROR, "Target type is not an array");

+         /*
+          * We don't deal with domain constraints yet, so bail out.
+          * This isn't currently a problem, because we also don't
+          * support arrays of domain type elements either. But in the
+          * future we might. At that point consideration should be given
+          * to removing the check below and adding a domain constraints
+          * check to the coercion.
+          */
+         if (getBaseType(tgt_elem_type) != tgt_elem_type)
+             elog(ERROR, "array coercion to domain type elements not " \
+                         "currently supported");
+
          if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
                                     COERCION_EXPLICIT, &funcId))
          {
***************
*** 2439,2448 ****
      }

      /*
!      * If it's binary-compatible, return the array unmodified.
       */
      if (my_extra->coerce_finfo.fn_oid == InvalidOid)
!         PG_RETURN_ARRAYTYPE_P(src);

      /*
       * Use array_map to apply the function to each array element.
--- 2773,2788 ----
      }

      /*
!      * If it's binary-compatible, modify the element type in the array header,
!      * but otherwise leave the array as we received it.
       */
      if (my_extra->coerce_finfo.fn_oid == InvalidOid)
!     {
!         ArrayType  *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
!
!         ARR_ELEMTYPE(result) = my_extra->desttype;
!         PG_RETURN_ARRAYTYPE_P(result);
!     }

      /*
       * Use array_map to apply the function to each array element.
***************
*** 2453,2456 ****
--- 2793,2911 ----
      locfcinfo.arg[0] = PointerGetDatum(src);

      return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
+ }
+
+ /*
+  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
+  *
+  *    astate is working state (NULL on first call)
+  *    rcontext is where to keep working state
+  */
+ ArrayBuildState *
+ accumArrayResult(ArrayBuildState *astate,
+                  Datum dvalue, bool disnull,
+                  Oid element_type,
+                  MemoryContext rcontext)
+ {
+     MemoryContext arr_context,
+                   oldcontext;
+
+     if (astate == NULL)
+     {
+         /* First time through --- initialize */
+
+         /* Make a temporary context to hold all the junk */
+         arr_context = AllocSetContextCreate(rcontext,
+                                             "accumArrayResult",
+                                             ALLOCSET_DEFAULT_MINSIZE,
+                                             ALLOCSET_DEFAULT_INITSIZE,
+                                             ALLOCSET_DEFAULT_MAXSIZE);
+         oldcontext = MemoryContextSwitchTo(arr_context);
+         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
+         astate->mcontext = arr_context;
+         astate->dvalues = (Datum *)
+             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
+         astate->nelems = 0;
+         astate->element_type = element_type;
+         get_typlenbyvalalign(element_type,
+                              &astate->typlen,
+                              &astate->typbyval,
+                              &astate->typalign);
+     }
+     else
+     {
+         oldcontext = MemoryContextSwitchTo(astate->mcontext);
+         Assert(astate->element_type == element_type);
+         /* enlarge dvalues[] if needed */
+         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
+             astate->dvalues = (Datum *)
+                 repalloc(astate->dvalues,
+                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
+     }
+
+     if (disnull)
+         elog(ERROR, "NULL elements not allowed in Arrays");
+
+     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
+     astate->dvalues[astate->nelems++] =
+         datumCopy(dvalue, astate->typbyval, astate->typlen);
+
+     MemoryContextSwitchTo(oldcontext);
+
+     return astate;
+ }
+
+ /*
+  * makeArrayResult - produce final result of accumArrayResult
+  *
+  *    astate is working state (not NULL)
+  *    rcontext is where to construct result
+  */
+ Datum
+ makeArrayResult(ArrayBuildState *astate,
+                 MemoryContext rcontext)
+ {
+     int            dims[1];
+     int            lbs[1];
+
+     dims[0] = astate->nelems;
+     lbs[0] = 1;
+
+     return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
+ }
+
+ /*
+  * makeMdArrayResult - produce md final result of accumArrayResult
+  *
+  *    astate is working state (not NULL)
+  *    rcontext is where to construct result
+  */
+ Datum
+ makeMdArrayResult(ArrayBuildState *astate,
+                 int ndims,
+                 int *dims,
+                 int *lbs,
+                 MemoryContext rcontext)
+ {
+     ArrayType  *result;
+     MemoryContext oldcontext;
+
+     /* Build the final array result in rcontext */
+     oldcontext = MemoryContextSwitchTo(rcontext);
+
+     result = construct_md_array(astate->dvalues,
+                                 ndims,
+                                 dims,
+                                 lbs,
+                                 astate->element_type,
+                                 astate->typlen,
+                                 astate->typbyval,
+                                 astate->typalign);
+
+     MemoryContextSwitchTo(oldcontext);
+
+     /* Clean up all the junk */
+     MemoryContextDelete(astate->mcontext);
+
+     return PointerGetDatum(result);
  }
Index: src/backend/utils/adt/varlena.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/varlena.c,v
retrieving revision 1.98
diff -c -r1.98 varlena.c
*** src/backend/utils/adt/varlena.c    15 May 2003 15:50:19 -0000    1.98
--- src/backend/utils/adt/varlena.c    29 May 2003 04:30:53 -0000
***************
*** 19,29 ****
--- 19,32 ----
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
  #include "access/tuptoaster.h"
+ #include "catalog/pg_type.h"
  #include "lib/stringinfo.h"
  #include "libpq/crypt.h"
  #include "libpq/pqformat.h"
+ #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/pg_locale.h"
+ #include "utils/lsyscache.h"


  typedef struct varlena unknown;
***************
*** 1983,1990 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

--- 1986,1992 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

***************
*** 2004,2011 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
--- 2006,2012 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
***************
*** 2026,2031 ****
--- 2027,2217 ----
          result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn -
fldsep_len,false); 
          PG_RETURN_TEXT_P(result_text);
      }
+ }
+
+ /*
+  * text_to_array
+  * parse input string
+  * return text array of elements
+  * based on provided field separator
+  */
+ Datum
+ text_to_array(PG_FUNCTION_ARGS)
+ {
+     text       *inputstring = PG_GETARG_TEXT_P(0);
+     int            inputstring_len = TEXTLEN(inputstring);
+     text       *fldsep = PG_GETARG_TEXT_P(1);
+     int            fldsep_len = TEXTLEN(fldsep);
+     int            fldnum;
+     int            start_posn = 0;
+     int            end_posn = 0;
+     text       *result_text = NULL;
+     ArrayBuildState *astate = NULL;
+     MemoryContext oldcontext = CurrentMemoryContext;
+
+     /* return NULL for empty input string */
+     if (inputstring_len < 1)
+         PG_RETURN_NULL();
+
+     /* empty field separator
+      * return one element, 1D, array using the input string */
+     if (fldsep_len < 1)
+         PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+                               CStringGetDatum(inputstring), 1));
+
+     /* start with end position holding the initial start position */
+     end_posn = 0;
+     for (fldnum=1;;fldnum++)    /* field number is 1 based */
+     {
+         Datum    dvalue;
+         bool    disnull = false;
+
+         start_posn = end_posn;
+         end_posn = text_position(PointerGetDatum(inputstring),
+                                  PointerGetDatum(fldsep),
+                                  fldnum);
+
+         if ((start_posn == 0) && (end_posn == 0))    /* fldsep not found */
+         {
+             if (fldnum == 1)
+             {
+                 /* first element
+                  * return one element, 1D, array using the input string */
+                 PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+                                       CStringGetDatum(inputstring), 1));
+             }
+             else
+             {
+                 /* otherwise create array and exit */
+                 PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
+             }
+         }
+         else if ((start_posn != 0) && (end_posn == 0))
+         {
+             /* last field requested */
+             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
+         }
+         else if ((start_posn == 0) && (end_posn != 0))
+         {
+             /* first field requested */
+             result_text = LEFT(inputstring, fldsep);
+         }
+         else
+         {
+             /* prior to last field requested */
+             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn
-fldsep_len, false); 
+         }
+
+         /* stash away current value */
+         dvalue = PointerGetDatum(result_text);
+         astate = accumArrayResult(astate, dvalue,
+                                   disnull, TEXTOID, oldcontext);
+
+     }
+
+     /* never reached -- keep compiler quiet */
+     PG_RETURN_NULL();
+ }
+
+ /*
+  * array_to_text
+  * concatenate Cstring representation of input array elements
+  * using provided field separator
+  */
+ Datum
+ array_to_text(PG_FUNCTION_ARGS)
+ {
+     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
+     char       *fldsep = PG_TEXTARG_GET_STR(1);
+     int            nitems, *dims, ndims;
+     char       *p;
+     Oid            element_type;
+     int            typlen;
+     bool        typbyval;
+     char        typdelim;
+     Oid            typoutput,
+                 typelem;
+     FmgrInfo    outputproc;
+     char        typalign;
+     StringInfo    result_str = makeStringInfo();
+     int            i;
+     ArrayMetaState *my_extra;
+
+     p = ARR_DATA_PTR(v);
+     ndims = ARR_NDIM(v);
+     dims = ARR_DIMS(v);
+     nitems = ArrayGetNItems(ndims, dims);
+
+     /* if there are no elements, return an empty string */
+     if (nitems == 0)
+         PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
+
+     element_type = ARR_ELEMTYPE(v);
+
+     /*
+      * We arrange to look up info about element type, including its output
+      * conversion proc only once per series of calls, assuming the element
+      * type doesn't change underneath us.
+      */
+     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+     if (my_extra == NULL)
+     {
+         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+                                                      sizeof(ArrayMetaState));
+         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+         my_extra->element_type = InvalidOid;
+     }
+
+     if (my_extra->element_type != element_type)
+     {
+         /* Get info about element type, including its output conversion proc */
+         get_type_metadata(element_type, IOFunc_output,
+                             &typlen, &typbyval, &typdelim,
+                             &typelem, &typoutput, &typalign);
+         fmgr_info(typoutput, &outputproc);
+
+         my_extra->element_type = element_type;
+         my_extra->typlen = typlen;
+         my_extra->typbyval = typbyval;
+         my_extra->typdelim = typdelim;
+         my_extra->typelem = typelem;
+         my_extra->typiofunc = typoutput;
+         my_extra->typalign = typalign;
+         my_extra->proc = outputproc;
+     }
+     else
+     {
+         typlen = my_extra->typlen;
+         typbyval = my_extra->typbyval;
+         typdelim = my_extra->typdelim;
+         typelem = my_extra->typelem;
+         typoutput = my_extra->typiofunc;
+         typalign = my_extra->typalign;
+         outputproc = my_extra->proc;
+     }
+
+     for (i = 0; i < nitems; i++)
+     {
+         Datum        itemvalue;
+         char       *value;
+
+         itemvalue = fetch_att(p, typbyval, typlen);
+
+         value = DatumGetCString(FunctionCall3(&outputproc,
+                                               itemvalue,
+                                               ObjectIdGetDatum(typelem),
+                                               Int32GetDatum(-1)));
+
+         if (i > 0)
+             appendStringInfo(result_str, "%s%s", fldsep, value);
+         else
+             appendStringInfo(result_str, "%s", value);
+
+         p = att_addlength(p, typlen, PointerGetDatum(p));
+         p = (char *) att_align(p, typalign);
+     }
+
+     PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
  }

  #define HEXBASE 16
Index: src/backend/utils/cache/lsyscache.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v
retrieving revision 1.95
diff -c -r1.95 lsyscache.c
*** src/backend/utils/cache/lsyscache.c    26 May 2003 00:11:27 -0000    1.95
--- src/backend/utils/cache/lsyscache.c    29 May 2003 04:30:53 -0000
***************
*** 625,630 ****
--- 625,664 ----
  }

  /*
+  * get_func_argtypes
+  *        Given procedure id, return the function's argument types.
+  *        Also pass back the number of arguments.
+  */
+ Oid *
+ get_func_argtypes(Oid funcid, int *nargs)
+ {
+     HeapTuple        tp;
+     Form_pg_proc    procstruct;
+     Oid               *result = NULL;
+     int                i;
+
+     tp = SearchSysCache(PROCOID,
+                         ObjectIdGetDatum(funcid),
+                         0, 0, 0);
+     if (!HeapTupleIsValid(tp))
+         elog(ERROR, "Function OID %u does not exist", funcid);
+
+     procstruct = (Form_pg_proc) GETSTRUCT(tp);
+     *nargs = (int) procstruct->pronargs;
+
+     if (*nargs > 0)
+     {
+         result = (Oid *) palloc(*nargs * sizeof(Oid));
+
+         for (i = 0; i < *nargs; i++)
+             result[i] = procstruct->proargtypes[i];
+     }
+
+     ReleaseSysCache(tp);
+     return result;
+ }
+
+ /*
   * get_func_retset
   *        Given procedure id, return the function's proretset flag.
   */
***************
*** 994,999 ****
--- 1028,1083 ----
      *typbyval = typtup->typbyval;
      *typalign = typtup->typalign;
      ReleaseSysCache(tp);
+ }
+
+ /*
+  * get_type_metadata
+  *
+  *        A six-fer:    given the type OID, return typlen, typbyval, typalign,
+  *                    typdelim, typelem, IO function Oid. The IO function
+  *                    returned is controlled by IOFuncSelector
+  */
+ void
+ get_type_metadata(Oid element_type,
+                     IOFuncSelector which_func,
+                     int *typlen,
+                     bool *typbyval,
+                     char *typdelim,
+                     Oid *typelem,
+                     Oid *proc,
+                     char *typalign)
+ {
+     HeapTuple    typeTuple;
+     Form_pg_type typeStruct;
+
+     typeTuple = SearchSysCache(TYPEOID,
+                                ObjectIdGetDatum(element_type),
+                                0, 0, 0);
+     if (!HeapTupleIsValid(typeTuple))
+         elog(ERROR, "cache lookup failed for type %u", element_type);
+     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+
+     *typlen = typeStruct->typlen;
+     *typbyval = typeStruct->typbyval;
+     *typdelim = typeStruct->typdelim;
+     *typelem = typeStruct->typelem;
+     *typalign = typeStruct->typalign;
+     switch (which_func)
+     {
+         case IOFunc_input:
+             *proc = typeStruct->typinput;
+             break;
+         case IOFunc_output:
+             *proc = typeStruct->typoutput;
+             break;
+         case IOFunc_receive:
+             *proc = typeStruct->typreceive;
+             break;
+         case IOFunc_send:
+             *proc = typeStruct->typsend;
+             break;
+     }
+     ReleaseSysCache(typeTuple);
  }

  #ifdef NOT_USED
Index: src/backend/utils/fmgr/fmgr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/fmgr/fmgr.c,v
retrieving revision 1.68
diff -c -r1.68 fmgr.c
*** src/backend/utils/fmgr/fmgr.c    8 Apr 2003 23:20:02 -0000    1.68
--- src/backend/utils/fmgr/fmgr.c    29 May 2003 04:54:06 -0000
***************
*** 1673,1675 ****
--- 1673,1701 ----

      return exprType((Node *) nth(argnum, args));
  }
+
+ /*
+  * Get the OID of the function or operator
+  *
+  * Returns InvalidOid if information is not available
+  */
+ Oid
+ get_fn_expr_functype(FunctionCallInfo fcinfo)
+ {
+     Node   *expr;
+
+     /*
+      * can't return anything useful if we have no FmgrInfo or if
+      * its fn_expr node has not been initialized
+      */
+     if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
+         return InvalidOid;
+
+     expr = fcinfo->flinfo->fn_expr;
+     if (IsA(expr, FuncExpr))
+         return ((FuncExpr *) expr)->funcid;
+     else if (IsA(expr, OpExpr))
+         return ((OpExpr *) expr)->opno;
+     else
+         return InvalidOid;
+ }
Index: src/include/fmgr.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/fmgr.h,v
retrieving revision 1.27
diff -c -r1.27 fmgr.h
*** src/include/fmgr.h    8 Apr 2003 23:20:04 -0000    1.27
--- src/include/fmgr.h    29 May 2003 04:54:04 -0000
***************
*** 18,23 ****
--- 18,24 ----
  #ifndef FMGR_H
  #define FMGR_H

+ #include "nodes/nodes.h"

  /*
   * All functions that can be called directly by fmgr must have this signature.
***************
*** 372,385 ****
                   Datum arg6, Datum arg7, Datum arg8,
                   Datum arg9);

-
  /*
   * Routines in fmgr.c
   */
  extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
! extern Oid    fmgr_internal_function(const char *proname);
! extern Oid    get_fn_expr_rettype(FunctionCallInfo fcinfo);
! extern Oid    get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);

  /*
   * Routines in dfmgr.c
--- 373,386 ----
                   Datum arg6, Datum arg7, Datum arg8,
                   Datum arg9);

  /*
   * Routines in fmgr.c
   */
  extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
! extern Oid fmgr_internal_function(const char *proname);
! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo);
! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
! extern Oid get_fn_expr_functype(FunctionCallInfo fcinfo);

  /*
   * Routines in dfmgr.c
Index: src/include/catalog/pg_operator.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_operator.h,v
retrieving revision 1.114
diff -c -r1.114 pg_operator.h
*** src/include/catalog/pg_operator.h    26 May 2003 00:11:27 -0000    1.114
--- src/include/catalog/pg_operator.h    29 May 2003 04:56:31 -0000
***************
*** 116,125 ****
  DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
  DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 

! DATA(insert OID = 329 (  "="       PGNSP PGUID b f 2277 2277 16   329 0 0 0    0 0 array_eq     eqsel eqjoinsel ));
! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277   0 0 0 0    0 0 array_append   -       -     ));
! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277   0 0 0 0    0 0 array_prepend  -       -     ));
! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277   0 0 0 0    0 0 array_cat      -       -     ));

  DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
  DATA(insert OID = 353 (  "="       PGNSP PGUID b t    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
--- 116,125 ----
  DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
  DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 

! DATA(insert OID = 329 (  "="       PGNSP PGUID b f 2277 2277 16 329 0  0 0 0 0 array_eq     eqsel eqjoinsel ));
! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277 0 0  0 0 0 0 array_append   -       -     ));
! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277 0 0  0 0 0 0 array_prepend  -       -     ));
! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277 0 0  0 0 0 0 array_cat      -       -     ));

  DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
  DATA(insert OID = 353 (  "="       PGNSP PGUID b t    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v
retrieving revision 1.302
diff -c -r1.302 pg_proc.h
*** src/include/catalog/pg_proc.h    26 May 2003 00:11:27 -0000    1.302
--- src/include/catalog/pg_proc.h    29 May 2003 04:56:54 -0000
***************
*** 1008,1015 ****
  DESCR("append element onto end of array");
  DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
  DESCR("prepend element onto front of array");
- DATA(insert OID = 380 (  array_accum       PGNSP PGUID 12 f f f f i 2 2277 "2277 2283" array_accum - _null_ ));
- DESCR("push element onto end of array, creating array if needed");
  DATA(insert OID = 381 (  array_assign       PGNSP PGUID 12 f f t f i 3 2277 "2277 23 2283" array_assign - _null_ ));
  DESCR("assign specific array element");
  DATA(insert OID = 382 (  array_subscript  PGNSP PGUID 12 f f t f i 2 2283 "2277 23" array_subscript - _null_ ));
--- 1008,1013 ----
***************
*** 1018,1023 ****
--- 1016,1025 ----
  DESCR("concatenate two arrays");
  DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
  DESCR("coerce array type to another array type");
+ DATA(insert OID = 394 (  str_to_array       PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ ));
+ DESCR("split delimited text into text[]");
+ DATA(insert OID = 395 (  array_to_str       PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ ));
+ DESCR("concatenate array elements, using delimiter, into text");

  DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
  DESCR("I/O");
Index: src/include/nodes/primnodes.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v
retrieving revision 1.82
diff -c -r1.82 primnodes.h
*** src/include/nodes/primnodes.h    6 May 2003 00:20:33 -0000    1.82
--- src/include/nodes/primnodes.h    29 May 2003 04:30:53 -0000
***************
*** 225,230 ****
--- 225,231 ----
      Expr       *target;            /* expression we are aggregating on */
      bool        aggstar;        /* TRUE if argument was really '*' */
      bool        aggdistinct;    /* TRUE if it's agg(DISTINCT ...) */
+     List       *args;            /* arguments to the aggregate */
  } Aggref;

  /* ----------------
Index: src/include/optimizer/clauses.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/optimizer/clauses.h,v
retrieving revision 1.63
diff -c -r1.63 clauses.h
*** src/include/optimizer/clauses.h    28 May 2003 16:04:02 -0000    1.63
--- src/include/optimizer/clauses.h    29 May 2003 04:30:53 -0000
***************
*** 28,33 ****
--- 28,36 ----
  extern Node *get_leftop(Expr *clause);
  extern Node *get_rightop(Expr *clause);

+ extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
+                                     CoercionForm funcformat, List *funcargs);
+
  extern bool not_clause(Node *clause);
  extern Expr *make_notclause(Expr *notclause);
  extern Expr *get_notclausearg(Expr *notclause);
Index: src/include/utils/array.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v
retrieving revision 1.38
diff -c -r1.38 array.h
*** src/include/utils/array.h    8 May 2003 22:19:57 -0000    1.38
--- src/include/utils/array.h    29 May 2003 04:58:53 -0000
***************
*** 32,37 ****
--- 32,68 ----
      Oid            elemtype;        /* element type OID */
  } ArrayType;

+ typedef struct ArrayBuildState
+ {
+     MemoryContext mcontext;        /* where all the temp stuff is kept */
+     Datum       *dvalues;        /* array of accumulated Datums */
+     /*
+      * The allocated size of dvalues[] is always a multiple of
+      * ARRAY_ELEMS_CHUNKSIZE
+      */
+ #define ARRAY_ELEMS_CHUNKSIZE    64
+     int            nelems;            /* number of valid Datums in dvalues[] */
+     Oid            element_type;    /* data type of the Datums */
+     int16        typlen;            /* needed info about datatype */
+     bool        typbyval;
+     char        typalign;
+ } ArrayBuildState;
+
+ /*
+  * structure to cache type metadata needed for array manipulation
+  */
+ typedef struct ArrayMetaState
+ {
+     Oid                element_type;
+     int                typlen;
+     bool            typbyval;
+     char            typdelim;
+     Oid                typelem;
+     Oid                typiofunc;
+     char            typalign;
+     FmgrInfo        proc;
+ } ArrayMetaState;
+
  /*
   * fmgr macros for array objects
   */
***************
*** 124,130 ****
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
!

  /*
   * prototypes for functions defined in arrayutils.c
--- 155,168 ----
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
! extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
!                                          Datum dvalue, bool disnull,
!                                          Oid element_type,
!                                          MemoryContext rcontext);
! extern Datum makeArrayResult(ArrayBuildState *astate,
!                              MemoryContext rcontext);
! extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
!                                int *dims, int *lbs, MemoryContext rcontext);

  /*
   * prototypes for functions defined in arrayutils.c
***************
*** 143,152 ****
   */
  extern Datum singleton_array(PG_FUNCTION_ARGS);
  extern Datum array_push(PG_FUNCTION_ARGS);
- extern Datum array_accum(PG_FUNCTION_ARGS);
  extern Datum array_cat(PG_FUNCTION_ARGS);

! extern ArrayType *create_singleton_array(Oid element_type,
                                           Datum element,
                                           int ndims);

--- 181,190 ----
   */
  extern Datum singleton_array(PG_FUNCTION_ARGS);
  extern Datum array_push(PG_FUNCTION_ARGS);
  extern Datum array_cat(PG_FUNCTION_ARGS);

! extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
!                                          Oid element_type,
                                           Datum element,
                                           int ndims);

Index: src/include/utils/builtins.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/builtins.h,v
retrieving revision 1.219
diff -c -r1.219 builtins.h
*** src/include/utils/builtins.h    26 May 2003 00:11:28 -0000    1.219
--- src/include/utils/builtins.h    29 May 2003 04:30:53 -0000
***************
*** 530,535 ****
--- 530,537 ----
                        List **namelist);
  extern Datum replace_text(PG_FUNCTION_ARGS);
  extern Datum split_text(PG_FUNCTION_ARGS);
+ extern Datum text_to_array(PG_FUNCTION_ARGS);
+ extern Datum array_to_text(PG_FUNCTION_ARGS);
  extern Datum to_hex32(PG_FUNCTION_ARGS);
  extern Datum to_hex64(PG_FUNCTION_ARGS);
  extern Datum md5_text(PG_FUNCTION_ARGS);
Index: src/include/utils/lsyscache.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v
retrieving revision 1.70
diff -c -r1.70 lsyscache.h
*** src/include/utils/lsyscache.h    26 May 2003 00:11:28 -0000    1.70
--- src/include/utils/lsyscache.h    29 May 2003 04:30:53 -0000
***************
*** 15,20 ****
--- 15,29 ----

  #include "access/htup.h"

+ /* I/O function selector for system_cache_lookup */
+ typedef enum IOFuncSelector
+ {
+     IOFunc_input,
+     IOFunc_output,
+     IOFunc_receive,
+     IOFunc_send
+ } IOFuncSelector;
+
  extern bool op_in_opclass(Oid opno, Oid opclass);
  extern bool op_requires_recheck(Oid opno, Oid opclass);
  extern Oid    get_opclass_member(Oid opclass, int16 strategy);
***************
*** 39,44 ****
--- 48,54 ----
  extern RegProcedure get_oprjoin(Oid opno);
  extern char *get_func_name(Oid funcid);
  extern Oid    get_func_rettype(Oid funcid);
+ extern Oid *get_func_argtypes(Oid funcid, int *nargs);
  extern bool get_func_retset(Oid funcid);
  extern bool func_strict(Oid funcid);
  extern char func_volatile(Oid funcid);
***************
*** 54,59 ****
--- 64,77 ----
  extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
  extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
                       char *typalign);
+ extern void get_type_metadata(Oid element_type,
+                                 IOFuncSelector which_func,
+                                 int *typlen,
+                                 bool *typbyval,
+                                 char *typdelim,
+                                 Oid *typelem,
+                                 Oid *proc,
+                                 char *typalign);
  extern char get_typstorage(Oid typid);
  extern int32 get_typtypmod(Oid typid);
  extern Node *get_typdefault(Oid typid);

Tom Lane wrote:
 > Joe Conway <mail@joeconway.com> writes:
>>Maybe this takes us
>>back to Peter's suggestion:
>>   expression IN (array)
>>   expression NOT IN (array)
>>   expression operator ANY (array)
>>   expression operator SOME (array)
>>   (expression) operator (array)
>>   (expression) operator ALL (array)
>
> There's a lot to be said for that, if we can think of a way to do it.
> I believe this way would force us to integrate the operations into the
> parser, though, rather than just throwing a few polymorphic functions
> at the problem.  It's probably a lot more work :-(

The attached implements the middle 2 examples above. The IN and NOT IN
examples would be ambiguous due to the fact that we already accept
"IN (list_of_expressions)" and "NOT IN (list_of_expressions)".

Specifically implemented:
    expression1 operator ANY (expression2)
    expression1 operator SOME (expression2)
    expression1 operator ALL (expression2)

If expression2 is scalar, it simplifies down to the same as doing:
    expression1 operator ANY | SOME | ALL
     (select expression2)

If expression2 is an array, the array elements are tested as if you did
this:
    expression1 operator ANY | SOME | ALL
     (select element_a
       union all
      select element_b...
       union all
      select element_n)

Here are a few examples:

create table tse(f1 int, f2 int[], f3 text[]);
insert into tse values(1,array[69,42,54], array['g','d','e']);
insert into tse values(2,array[1,2,3], array['x','y','z']);
insert into tse values(3,array[2,99,0], array['Tom','Bruce']);
insert into tse values(4,array[1,1,1], array['a','a','a']);
insert into tse values(5,array[5,6,7], array['a','b','c']);

regression=# select * from tse where 1 = any (f2);
  f1 |   f2    |   f3
----+---------+---------
   2 | {1,2,3} | {x,y,z}
   4 | {1,1,1} | {a,a,a}
(2 rows)

regression=# select * from tse where 1 = all (f2);
  f1 |   f2    |   f3
----+---------+---------
   4 | {1,1,1} | {a,a,a}
(1 row)

regression=# select * from tse where 'a' != any (f3);
  f1 |     f2     |     f3
----+------------+-------------
   1 | {69,42,54} | {g,d,e}
   2 | {1,2,3}    | {x,y,z}
   3 | {2,99,0}   | {Tom,Bruce}
   5 | {5,6,7}    | {a,b,c}
(4 rows)

regression=# select * from tse where 'a' != all (f3);
  f1 |     f2     |     f3
----+------------+-------------
   1 | {69,42,54} | {g,d,e}
   2 | {1,2,3}    | {x,y,z}
   3 | {2,99,0}   | {Tom,Bruce}
(3 rows)


regression=# select * from tse where 'y' < some (f3);
  f1 |   f2    |   f3
----+---------+---------
   2 | {1,2,3} | {x,y,z}
(1 row)

The patch passes all regression tests. It is independent of the phase2
patch submitted earlier this week.

I am already planning to update the array related docs one more time
after the feature freeze, so I'd like to wait until then to document
this. If there are no objections, please apply.

Thanks,

Joe
Index: src/backend/executor/nodeSubplan.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v
retrieving revision 1.45
diff -c -r1.45 nodeSubplan.c
*** src/backend/executor/nodeSubplan.c    8 Apr 2003 23:20:01 -0000    1.45
--- src/backend/executor/nodeSubplan.c    31 May 2003 20:38:27 -0000
***************
*** 224,229 ****
--- 224,230 ----
      PlanState  *planstate = node->planstate;
      SubLinkType subLinkType = subplan->subLinkType;
      bool        useOr = subplan->useOr;
+     bool        isExpr = subplan->isExpr;
      MemoryContext oldcontext;
      TupleTableSlot *slot;
      Datum        result;
***************
*** 292,297 ****
--- 293,303 ----
          bool        rownull = false;
          int            col = 1;
          List       *plst;
+         int            numelems;
+         int            elemnum;
+         Datum        dvalue;
+         Datum       *dvalues = NULL;
+         bool        disnull;

          if (subLinkType == EXISTS_SUBLINK)
          {
***************
*** 329,337 ****

          if (subLinkType == ARRAY_SUBLINK)
          {
-             Datum    dvalue;
-             bool    disnull;
-
              found = true;
              /* stash away current value */
              dvalue = heap_getattr(tup, 1, tdesc, &disnull);
--- 335,340 ----
***************
*** 349,446 ****
          found = true;

          /*
!          * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
!          * operators for columns of tuple.
           */
!         plst = subplan->paramIds;
!         foreach(lst, node->exprs)
          {
!             ExprState  *exprstate = (ExprState *) lfirst(lst);
!             int            paramid = lfirsti(plst);
!             ParamExecData *prmdata;
!             Datum        expresult;
!             bool        expnull;
!
!             /*
!              * Load up the Param representing this column of the sub-select.
!              */
!             prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
!             Assert(prmdata->execPlan == NULL);
!             prmdata->value = heap_getattr(tup, col, tdesc,
!                                           &(prmdata->isnull));
!
!             /*
!              * Now we can eval the combining operator for this column.
!              */
!             expresult = ExecEvalExprSwitchContext(exprstate, econtext,
!                                                   &expnull, NULL);

!             /*
!              * Combine the result into the row result as appropriate.
!              */
!             if (col == 1)
              {
!                 rowresult = expresult;
!                 rownull = expnull;
              }
!             else if (useOr)
              {
!                 /* combine within row per OR semantics */
!                 if (expnull)
!                     rownull = true;
!                 else if (DatumGetBool(expresult))
                  {
!                     rowresult = BoolGetDatum(true);
!                     rownull = false;
!                     break;        /* needn't look at any more columns */
                  }
              }
              else
              {
!                 /* combine within row per AND semantics */
!                 if (expnull)
!                     rownull = true;
!                 else if (!DatumGetBool(expresult))
!                 {
!                     rowresult = BoolGetDatum(false);
!                     rownull = false;
!                     break;        /* needn't look at any more columns */
!                 }
              }

-             plst = lnext(plst);
-             col++;
          }

!         if (subLinkType == ANY_SUBLINK)
          {
!             /* combine across rows per OR semantics */
!             if (rownull)
!                 *isNull = true;
!             else if (DatumGetBool(rowresult))
!             {
!                 result = BoolGetDatum(true);
!                 *isNull = false;
!                 break;            /* needn't look at any more rows */
              }
!         }
!         else if (subLinkType == ALL_SUBLINK)
!         {
!             /* combine across rows per AND semantics */
!             if (rownull)
!                 *isNull = true;
!             else if (!DatumGetBool(rowresult))
!             {
!                 result = BoolGetDatum(false);
!                 *isNull = false;
!                 break;            /* needn't look at any more rows */
              }
-         }
-         else
-         {
-             /* must be MULTIEXPR_SUBLINK */
-             result = rowresult;
-             *isNull = rownull;
          }
      }

--- 352,514 ----
          found = true;

          /*
!          * When isExpr is true, we have either a scalar expression or an
!          * array. In the former case, this is no different than the !isExpr
!          * case. In the latter case, iterate over the elements as if they
!          * were from multiple input tuples.
           */
!         if (!isExpr)
!             numelems = 1;
!         else
          {
!             Oid        expr_typeid = tdesc->attrs[0]->atttypid;

!             if (expr_typeid != subplan->exprtype)
              {
!                 subplan->exprtype = expr_typeid;
!                 subplan->elemtype = get_element_type(expr_typeid);
!
!                 if (subplan->elemtype != InvalidOid)
!                     get_typlenbyvalalign(subplan->elemtype,
!                                          &subplan->elmlen,
!                                          &subplan->elmbyval,
!                                          &subplan->elmalign);
              }
!
!             /* get current value */
!             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
!
!             /* XXX this will need work if/when arrays support NULL elements */
!             if (!disnull)
              {
!                 if (subplan->elemtype != InvalidOid)
!                 {
!                     ArrayType   *v = DatumGetArrayTypeP(dvalue);
!
!                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
!                                       subplan->elmbyval, subplan->elmalign,
!                                         &dvalues, &numelems);
!                 }
!                 else
                  {
!                     numelems = 1;
!                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                     dvalues[0] = dvalue;
                  }
              }
              else
              {
!                 numelems = 1;
!                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                 dvalues[0] = (Datum) 0;
              }

          }

!         for (elemnum = 0; elemnum < numelems; elemnum++)
          {
!             /*
!              * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
!              * operators for columns of tuple.
!              */
!             col = 1;
!             plst = subplan->paramIds;
!             foreach(lst, node->exprs)
!             {
!                 ExprState  *exprstate = (ExprState *) lfirst(lst);
!                 int            paramid = lfirsti(plst);
!                 ParamExecData *prmdata;
!                 Datum        expresult;
!                 bool        expnull;
!
!                 /*
!                  * Load up the Param representing this column of the sub-select.
!                  */
!                 prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
!                 Assert(prmdata->execPlan == NULL);
!
!                 if (!isExpr)
!                     prmdata->value = heap_getattr(tup, col, tdesc,
!                                                   &(prmdata->isnull));
!                 else
!                 {
!                     prmdata->value = dvalues[elemnum];
!                     prmdata->isnull = disnull;
!                 }
!
!                 /*
!                  * Now we can eval the combining operator for this column.
!                  */
!                 expresult = ExecEvalExprSwitchContext(exprstate, econtext,
!                                                       &expnull, NULL);
!
!                 /*
!                  * Combine the result into the row result as appropriate.
!                  */
!                 if (col == 1)
!                 {
!                     rowresult = expresult;
!                     rownull = expnull;
!                 }
!                 else if (useOr)
!                 {
!                     /* combine within row per OR semantics */
!                     if (expnull)
!                         rownull = true;
!                     else if (DatumGetBool(expresult))
!                     {
!                         rowresult = BoolGetDatum(true);
!                         rownull = false;
!                         break;        /* needn't look at any more columns */
!                     }
!                 }
!                 else
!                 {
!                     /* combine within row per AND semantics */
!                     if (expnull)
!                         rownull = true;
!                     else if (!DatumGetBool(expresult))
!                     {
!                         rowresult = BoolGetDatum(false);
!                         rownull = false;
!                         break;        /* needn't look at any more columns */
!                     }
!                 }
!
!                 plst = lnext(plst);
!                 col++;
              }
!
!             if (subLinkType == ANY_SUBLINK)
!             {
!                 /* combine across rows per OR semantics */
!                 if (rownull)
!                     *isNull = true;
!                 else if (DatumGetBool(rowresult))
!                 {
!                     result = BoolGetDatum(true);
!                     *isNull = false;
!                     break;            /* needn't look at any more rows */
!                 }
!             }
!             else if (subLinkType == ALL_SUBLINK)
!             {
!                 /* combine across rows per AND semantics */
!                 if (rownull)
!                     *isNull = true;
!                 else if (!DatumGetBool(rowresult))
!                 {
!                     result = BoolGetDatum(false);
!                     *isNull = false;
!                     break;            /* needn't look at any more rows */
!                 }
!             }
!             else
!             {
!                 /* must be MULTIEXPR_SUBLINK */
!                 result = rowresult;
!                 *isNull = rownull;
              }
          }
      }

***************
*** 478,483 ****
--- 546,552 ----
  buildSubPlanHash(SubPlanState *node)
  {
      SubPlan       *subplan = (SubPlan *) node->xprstate.expr;
+     bool        isExpr = subplan->isExpr;
      PlanState  *planstate = node->planstate;
      int            ncols = length(node->exprs);
      ExprContext *innerecontext = node->innerecontext;
***************
*** 485,490 ****
--- 554,560 ----
      MemoryContext oldcontext;
      int            nbuckets;
      TupleTableSlot *slot;
+     TupleTableSlot *arrslot = NULL;

      Assert(subplan->subLinkType == ANY_SUBLINK);
      Assert(!subplan->useOr);
***************
*** 562,604 ****
      {
          HeapTuple    tup = slot->val;
          TupleDesc    tdesc = slot->ttc_tupleDescriptor;
!         int            col = 1;
          List       *plst;
          bool        isnew;

          /*
!          * Load up the Params representing the raw sub-select outputs,
!          * then form the projection tuple to store in the hashtable.
           */
!         foreach(plst, subplan->paramIds)
          {
!             int            paramid = lfirsti(plst);
!             ParamExecData *prmdata;

!             prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
!             Assert(prmdata->execPlan == NULL);
!             prmdata->value = heap_getattr(tup, col, tdesc,
!                                           &(prmdata->isnull));
!             col++;
!         }
!         slot = ExecProject(node->projRight, NULL);
!         tup = slot->val;

-         /*
-          * If result contains any nulls, store separately or not at all.
-          * (Since we know the projection tuple has no junk columns, we
-          * can just look at the overall hasnull info bit, instead of
-          * groveling through the columns.)
-          */
-         if (HeapTupleNoNulls(tup))
-         {
-             (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
-             node->havehashrows = true;
          }
!         else if (node->hashnulls)
          {
!             (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
!             node->havenullrows = true;
          }

          /*
--- 632,770 ----
      {
          HeapTuple    tup = slot->val;
          TupleDesc    tdesc = slot->ttc_tupleDescriptor;
!         TupleDesc    arrtdesc = NULL;
          List       *plst;
          bool        isnew;
+         int            numelems;
+         int            elemnum;
+         Datum        dvalue;
+         Datum       *dvalues = NULL;
+         bool        disnull;

          /*
!          * When isExpr is true, we have either a scalar expression or an
!          * array. In the former case, this is no different than the !isExpr
!          * case. In the latter case, iterate over the elements as if they
!          * were from multiple input tuples.
           */
!         if (!isExpr)
!             numelems = 1;
!         else
          {
!             Oid        expr_typeid = tdesc->attrs[0]->atttypid;

!             if (expr_typeid != subplan->exprtype)
!             {
!                 subplan->exprtype = expr_typeid;
!                 subplan->elemtype = get_element_type(expr_typeid);
!
!                 if (subplan->elemtype != InvalidOid)
!                     get_typlenbyvalalign(subplan->elemtype,
!                                          &subplan->elmlen,
!                                          &subplan->elmbyval,
!                                          &subplan->elmalign);
!             }
!
!             /* get current value */
!             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
!
!             if (subplan->elemtype != InvalidOid)
!             {
!                 TupleTable    tupleTable;
!                 ArrayType   *v = DatumGetArrayTypeP(dvalue);
!
!                 arrtdesc = CreateTemplateTupleDesc(1, false);
!                 TupleDescInitEntry(arrtdesc, 1, "elem", subplan->elemtype,
!                                                             -1, 0, false);
!
!                 tupleTable = ExecCreateTupleTable(1);
!                 arrslot = ExecAllocTableSlot(tupleTable);
!                 ExecSetSlotDescriptor(arrslot, arrtdesc, true);
!
!                 /* XXX this will need work if/when arrays support NULL elements */
!                 if (!disnull)
!                 {
!                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
!                                       subplan->elmbyval, subplan->elmalign,
!                                         &dvalues, &numelems);
!                 }
!                 else
!                 {
!                     numelems = 1;
!                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                     dvalues[0] = (Datum) 0;
!                 }
!             }
!             else
!             {
!                 numelems = 1;
!                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                 dvalues[0] = dvalue;
!             }

          }
!
!         for (elemnum = 0; elemnum < numelems; elemnum++)
          {
!             int    col = 1;
!
!             if (!isExpr || subplan->elemtype == InvalidOid)
!             {
!                 /*
!                  * Load up the Params representing the raw sub-select outputs,
!                  * then form the projection tuple to store in the hashtable.
!                  */
!                 foreach(plst, subplan->paramIds)
!                 {
!                     int            paramid = lfirsti(plst);
!                     ParamExecData *prmdata;
!
!                     prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
!                     Assert(prmdata->execPlan == NULL);
!
!                     prmdata->value = heap_getattr(tup, col, tdesc,
!                                                   &(prmdata->isnull));
!
!                     col++;
!                 }
!                 slot = ExecProject(node->projRight, NULL);
!                 tup = slot->val;
!             }
!             else
!             {
!                 /*
!                  * For array type expressions, we need to build up our own
!                  * tuple and slot
!                  */
!                 char        nullflag;
!
!                 nullflag = disnull ? 'n' : ' ';
!                 tup = heap_formtuple(arrtdesc, &dvalues[elemnum], &nullflag);
!                 arrslot = ExecStoreTuple(tup, arrslot, InvalidBuffer, true);
!             }
!
!             /*
!              * If result contains any nulls, store separately or not at all.
!              * (Since we know the projection tuple has no junk columns, we
!              * can just look at the overall hasnull info bit, instead of
!              * groveling through the columns.)
!              */
!             if (HeapTupleNoNulls(tup))
!             {
!                 if (!isExpr)
!                     (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
!                 else
!                     (void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew);
!                 node->havehashrows = true;
!             }
!             else if (node->hashnulls)
!             {
!                 if (!isExpr)
!                     (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
!                 else
!                     (void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew);
!                 node->havenullrows = true;
!             }
          }

          /*
***************
*** 615,620 ****
--- 781,788 ----
       * have the potential for a double free attempt.
       */
      ExecClearTuple(node->projRight->pi_slot);
+     if (arrslot)
+         ExecClearTuple(arrslot);

      MemoryContextSwitchTo(oldcontext);
  }
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.251
diff -c -r1.251 copyfuncs.c
*** src/backend/nodes/copyfuncs.c    28 May 2003 16:03:56 -0000    1.251
--- src/backend/nodes/copyfuncs.c    31 May 2003 20:38:27 -0000
***************
*** 825,830 ****
--- 825,831 ----

      COPY_SCALAR_FIELD(subLinkType);
      COPY_SCALAR_FIELD(useOr);
+     COPY_SCALAR_FIELD(isExpr);
      COPY_NODE_FIELD(lefthand);
      COPY_NODE_FIELD(operName);
      COPY_OIDLIST_FIELD(operOids);
***************
*** 843,848 ****
--- 844,855 ----

      COPY_SCALAR_FIELD(subLinkType);
      COPY_SCALAR_FIELD(useOr);
+     COPY_SCALAR_FIELD(isExpr);
+     COPY_SCALAR_FIELD(exprtype);
+     COPY_SCALAR_FIELD(elemtype);
+     COPY_SCALAR_FIELD(elmlen);
+     COPY_SCALAR_FIELD(elmbyval);
+     COPY_SCALAR_FIELD(elmalign);
      COPY_NODE_FIELD(exprs);
      COPY_INTLIST_FIELD(paramIds);
      COPY_NODE_FIELD(plan);
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.194
diff -c -r1.194 equalfuncs.c
*** src/backend/nodes/equalfuncs.c    28 May 2003 16:03:56 -0000    1.194
--- src/backend/nodes/equalfuncs.c    31 May 2003 20:38:27 -0000
***************
*** 300,305 ****
--- 300,306 ----
  {
      COMPARE_SCALAR_FIELD(subLinkType);
      COMPARE_SCALAR_FIELD(useOr);
+     COMPARE_SCALAR_FIELD(isExpr);
      COMPARE_NODE_FIELD(lefthand);
      COMPARE_NODE_FIELD(operName);
      COMPARE_OIDLIST_FIELD(operOids);
***************
*** 313,318 ****
--- 314,325 ----
  {
      COMPARE_SCALAR_FIELD(subLinkType);
      COMPARE_SCALAR_FIELD(useOr);
+     COMPARE_SCALAR_FIELD(isExpr);
+     COMPARE_SCALAR_FIELD(exprtype);
+     COMPARE_SCALAR_FIELD(elemtype);
+     COMPARE_SCALAR_FIELD(elmlen);
+     COMPARE_SCALAR_FIELD(elmbyval);
+     COMPARE_SCALAR_FIELD(elmalign);
      COMPARE_NODE_FIELD(exprs);
      COMPARE_INTLIST_FIELD(paramIds);
      /* should compare plans, but have to settle for comparing plan IDs */
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v
retrieving revision 1.206
diff -c -r1.206 outfuncs.c
*** src/backend/nodes/outfuncs.c    28 May 2003 16:03:56 -0000    1.206
--- src/backend/nodes/outfuncs.c    31 May 2003 20:38:27 -0000
***************
*** 700,705 ****
--- 700,706 ----

      WRITE_ENUM_FIELD(subLinkType, SubLinkType);
      WRITE_BOOL_FIELD(useOr);
+     WRITE_BOOL_FIELD(isExpr);
      WRITE_NODE_FIELD(lefthand);
      WRITE_NODE_FIELD(operName);
      WRITE_OIDLIST_FIELD(operOids);
***************
*** 713,718 ****
--- 714,725 ----

      WRITE_ENUM_FIELD(subLinkType, SubLinkType);
      WRITE_BOOL_FIELD(useOr);
+     WRITE_BOOL_FIELD(isExpr);
+     WRITE_OID_FIELD(exprtype);
+     WRITE_OID_FIELD(elemtype);
+     WRITE_INT_FIELD(elmlen);
+     WRITE_BOOL_FIELD(elmbyval);
+     WRITE_CHAR_FIELD(elmalign);
      WRITE_NODE_FIELD(exprs);
      WRITE_INTLIST_FIELD(paramIds);
      WRITE_NODE_FIELD(plan);
Index: src/backend/nodes/readfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v
retrieving revision 1.153
diff -c -r1.153 readfuncs.c
*** src/backend/nodes/readfuncs.c    6 May 2003 00:20:32 -0000    1.153
--- src/backend/nodes/readfuncs.c    31 May 2003 20:38:27 -0000
***************
*** 544,549 ****
--- 544,550 ----

      READ_ENUM_FIELD(subLinkType, SubLinkType);
      READ_BOOL_FIELD(useOr);
+     READ_BOOL_FIELD(isExpr);
      READ_NODE_FIELD(lefthand);
      READ_NODE_FIELD(operName);
      READ_OIDLIST_FIELD(operOids);
Index: src/backend/optimizer/plan/subselect.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/plan/subselect.c,v
retrieving revision 1.75
diff -c -r1.75 subselect.c
*** src/backend/optimizer/plan/subselect.c    29 Apr 2003 22:13:09 -0000    1.75
--- src/backend/optimizer/plan/subselect.c    31 May 2003 20:38:27 -0000
***************
*** 67,73 ****

  static List *convert_sublink_opers(List *lefthand, List *operOids,
                                     List *targetlist, int rtindex,
!                                    List **righthandIds);
  static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
  static Node *replace_correlation_vars_mutator(Node *node, void *context);
  static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
--- 67,73 ----

  static List *convert_sublink_opers(List *lefthand, List *operOids,
                                     List *targetlist, int rtindex,
!                                    bool isExpr, List **righthandIds);
  static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
  static Node *replace_correlation_vars_mutator(Node *node, void *context);
  static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
***************
*** 240,245 ****
--- 240,251 ----
       */
      node->subLinkType = slink->subLinkType;
      node->useOr = slink->useOr;
+     node->isExpr = slink->isExpr;
+     node->exprtype = InvalidOid;
+     node->elemtype = InvalidOid;
+     node->elmlen = 0;
+     node->elmbyval = false;
+     node->elmalign = '\0';
      node->exprs = NIL;
      node->paramIds = NIL;
      node->useHashTable = false;
***************
*** 316,322 ****
          exprs = convert_sublink_opers(lefthand,
                                        slink->operOids,
                                        plan->targetlist,
!                                       0,
                                        &node->paramIds);
          node->setParam = listCopy(node->paramIds);
          PlannerInitPlan = lappend(PlannerInitPlan, node);
--- 322,328 ----
          exprs = convert_sublink_opers(lefthand,
                                        slink->operOids,
                                        plan->targetlist,
!                                       0, node->isExpr,
                                        &node->paramIds);
          node->setParam = listCopy(node->paramIds);
          PlannerInitPlan = lappend(PlannerInitPlan, node);
***************
*** 399,405 ****
          node->exprs = convert_sublink_opers(lefthand,
                                              slink->operOids,
                                              plan->targetlist,
!                                             0,
                                              &node->paramIds);

          /*
--- 405,411 ----
          node->exprs = convert_sublink_opers(lefthand,
                                              slink->operOids,
                                              plan->targetlist,
!                                             0, node->isExpr,
                                              &node->paramIds);

          /*
***************
*** 444,450 ****
  static List *
  convert_sublink_opers(List *lefthand, List *operOids,
                        List *targetlist, int rtindex,
!                       List **righthandIds)
  {
      List       *result = NIL;
      List       *lst;
--- 450,456 ----
  static List *
  convert_sublink_opers(List *lefthand, List *operOids,
                        List *targetlist, int rtindex,
!                       bool isExpr, List **righthandIds)
  {
      List       *result = NIL;
      List       *lst;
***************
*** 499,511 ****
           * are not expecting to have to resolve unknown Params, so
           * it's okay to pass a null pstate.)
           */
!         result = lappend(result,
!                          make_op_expr(NULL,
!                                       tup,
!                                       leftop,
!                                       rightop,
!                                       exprType(leftop),
!                                       te->resdom->restype));

          ReleaseSysCache(tup);

--- 505,542 ----
           * are not expecting to have to resolve unknown Params, so
           * it's okay to pass a null pstate.)
           */
!         if (!isExpr)
!         {
!             result = lappend(result,
!                              make_op_expr(NULL,
!                                           tup,
!                                           leftop,
!                                           rightop,
!                                           exprType(leftop),
!                                           te->resdom->restype));
!         }
!         else
!         {
!             Oid        exprtype = te->resdom->restype;
!             Oid        elemtype = get_element_type(exprtype);
!
!             if (elemtype != InvalidOid)
!                 result = lappend(result,
!                                  make_op_expr(NULL,
!                                               tup,
!                                               leftop,
!                                               rightop,
!                                               exprType(leftop),
!                                               elemtype));
!             else
!                 result = lappend(result,
!                                  make_op_expr(NULL,
!                                               tup,
!                                               leftop,
!                                               rightop,
!                                               exprType(leftop),
!                                               exprtype));
!         }

          ReleaseSysCache(tup);

***************
*** 616,628 ****
      /*
       * The sublink type must be "= ANY" --- that is, an IN operator.
       * (We require the operator name to be unqualified, which may be
!      * overly paranoid, or may not be.)
       */
      if (sublink->subLinkType != ANY_SUBLINK)
          return NULL;
      if (length(sublink->operName) != 1 ||
          strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
          return NULL;
      /*
       * The sub-select must not refer to any Vars of the parent query.
       * (Vars of higher levels should be okay, though.)
--- 647,663 ----
      /*
       * The sublink type must be "= ANY" --- that is, an IN operator.
       * (We require the operator name to be unqualified, which may be
!      * overly paranoid, or may not be.) It must not be an Expression
!      * sublink.
       */
      if (sublink->subLinkType != ANY_SUBLINK)
          return NULL;
      if (length(sublink->operName) != 1 ||
          strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
          return NULL;
+     if (sublink->isExpr)
+         return NULL;
+
      /*
       * The sub-select must not refer to any Vars of the parent query.
       * (Vars of higher levels should be okay, though.)
***************
*** 675,681 ****
      exprs = convert_sublink_opers(sublink->lefthand,
                                    sublink->operOids,
                                    subselect->targetList,
!                                   rtindex,
                                    &ininfo->sub_targetlist);
      return (Node *) make_ands_explicit(exprs);
  }
--- 710,716 ----
      exprs = convert_sublink_opers(sublink->lefthand,
                                    sublink->operOids,
                                    subselect->targetList,
!                                   rtindex, sublink->isExpr,
                                    &ininfo->sub_targetlist);
      return (Node *) make_ands_explicit(exprs);
  }
Index: src/backend/parser/gram.y
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/gram.y,v
retrieving revision 2.416
diff -c -r2.416 gram.y
*** src/backend/parser/gram.y    29 May 2003 20:40:36 -0000    2.416
--- src/backend/parser/gram.y    31 May 2003 20:38:27 -0000
***************
*** 5490,5495 ****
--- 5490,5496 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ANY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = makeList1(makeString("="));
                      n->subselect = $3;
***************
*** 5500,5505 ****
--- 5501,5507 ----
                      /* Make an IN node */
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ANY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = makeList1(makeString("="));
                      n->subselect = $4;
***************
*** 5511,5516 ****
--- 5513,5519 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = $3;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = $2;
                      n->subselect = $4;
***************
*** 5521,5526 ****
--- 5524,5530 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = MULTIEXPR_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = $2;
                      n->subselect = $3;
***************
*** 5904,5909 ****
--- 5908,5914 ----
                      {
                              SubLink *n = (SubLink *)$3;
                              n->subLinkType = ANY_SUBLINK;
+                             n->isExpr = false;
                              n->lefthand = makeList1($1);
                              n->operName = makeList1(makeString("="));
                              $$ = (Node *)n;
***************
*** 5931,5936 ****
--- 5936,5942 ----
                      {
                          /* Make an IN node */
                          SubLink *n = (SubLink *)$4;
+                         n->isExpr = false;
                          n->subLinkType = ANY_SUBLINK;
                          n->lefthand = makeList1($1);
                          n->operName = makeList1(makeString("="));
***************
*** 5957,5967 ****
--- 5963,6000 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = $3;
+                     n->isExpr = false;
                      n->lefthand = makeList1($1);
                      n->operName = $2;
                      n->subselect = $4;
                      $$ = (Node *)n;
                  }
+             | a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op
+                 {
+                     SubLink *n = makeNode(SubLink);
+                     SelectStmt *s = makeNode(SelectStmt);
+                     ResTarget *r = makeNode(ResTarget);
+
+                     r->name = NULL;
+                     r->indirection = NIL;
+                     r->val = (Node *)$5;
+
+                     s->distinctClause = NIL;
+                     s->targetList = makeList1(r);
+                     s->into = NULL;
+                     s->intoColNames = NIL;
+                     s->fromClause = NIL;
+                     s->whereClause = NULL;
+                     s->groupClause = NIL;
+                     s->havingClause = NULL;
+
+                     n->subLinkType = $3;
+                     n->isExpr = true;
+                     n->lefthand = makeList1($1);
+                     n->operName = $2;
+                     n->subselect = (Node *) s;
+                     $$ = (Node *)n;
+                 }
              | UNIQUE select_with_parens %prec Op
                  {
                      /* Not sure how to get rid of the parentheses
***************
*** 6538,6543 ****
--- 6571,6577 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = EXPR_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $1;
***************
*** 6547,6552 ****
--- 6581,6587 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = EXISTS_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $2;
***************
*** 6556,6561 ****
--- 6591,6597 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ARRAY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $2;
***************
*** 6730,6735 ****
--- 6766,6772 ----
  in_expr:    select_with_parens
                  {
                      SubLink *n = makeNode(SubLink);
+                     n->isExpr = false;
                      n->subselect = $1;
                      /* other fields will be filled later */
                      $$ = (Node *)n;
Index: src/backend/parser/parse_expr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_expr.c,v
retrieving revision 1.148
diff -c -r1.148 parse_expr.c
*** src/backend/parser/parse_expr.c    29 Apr 2003 22:13:10 -0000    1.148
--- src/backend/parser/parse_expr.c    31 May 2003 20:38:27 -0000
***************
*** 436,441 ****
--- 436,442 ----
                      sublink->operName = NIL;
                      sublink->operOids = NIL;
                      sublink->useOr = FALSE;
+                     sublink->isExpr = FALSE;
                  }
                  else if (sublink->subLinkType == EXPR_SUBLINK ||
                           sublink->subLinkType == ARRAY_SUBLINK)
***************
*** 463,468 ****
--- 464,470 ----
                      sublink->operName = NIL;
                      sublink->operOids = NIL;
                      sublink->useOr = FALSE;
+                     sublink->isExpr = FALSE;
                  }
                  else
                  {
***************
*** 538,547 ****
                           * here, because make_subplan() will insert type
                           * coercion calls if needed.
                           */
!                         optup = oper(op,
!                                      exprType(lexpr),
!                                      exprType((Node *) tent->expr),
!                                      false);
                          opform = (Form_pg_operator) GETSTRUCT(optup);

                          if (opform->oprresult != BOOLOID)
--- 540,569 ----
                           * here, because make_subplan() will insert type
                           * coercion calls if needed.
                           */
!                         if (!sublink->isExpr)
!                         {
!                             optup = oper(op,
!                                          exprType(lexpr),
!                                          exprType((Node *) tent->expr),
!                                          false);
!                         }
!                         else
!                         {
!                             Oid        exprtype = exprType((Node *) tent->expr);
!                             Oid        elemtype = get_element_type(exprtype);
!
!                             if (elemtype != InvalidOid)
!                                 optup = oper(op,
!                                              exprType(lexpr),
!                                              elemtype,
!                                              false);
!                             else
!                                 optup = oper(op,
!                                              exprType(lexpr),
!                                              exprtype,
!                                              false);
!                         }
!
                          opform = (Form_pg_operator) GETSTRUCT(optup);

                          if (opform->oprresult != BOOLOID)
Index: src/include/nodes/primnodes.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v
retrieving revision 1.82
diff -c -r1.82 primnodes.h
*** src/include/nodes/primnodes.h    6 May 2003 00:20:33 -0000    1.82
--- src/include/nodes/primnodes.h    31 May 2003 20:38:27 -0000
***************
*** 357,371 ****
  /* ----------------
   * SubLink
   *
!  * A SubLink represents a subselect appearing in an expression, and in some
!  * cases also the combining operator(s) just above it.    The subLinkType
!  * indicates the form of the expression represented:
   *    EXISTS_SUBLINK        EXISTS(SELECT ...)
   *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
   *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
   *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
   *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
   *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
   * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
   * same length as the subselect's targetlist.  MULTIEXPR will *always* have
   * a list with more than one entry; if the subselect has just one target
--- 357,375 ----
  /* ----------------
   * SubLink
   *
!  * A SubLink represents a subselect, or an expression, appearing in an
!  * expression, and in some cases also the combining operator(s) just above
!  * it.    The subLinkType indicates the form of the expression represented:
   *    EXISTS_SUBLINK        EXISTS(SELECT ...)
   *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
   *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
   *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
   *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
   *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
+  * If an expression is used in place of the subselect, it is transformed
+  * into a simple "(SELECT expr)" in gram.y. This is to allow arrays to be
+  * used as if they were the result of a single column subselect. If the
+  * expression is scalar, it is treated as a one element array.
   * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
   * same length as the subselect's targetlist.  MULTIEXPR will *always* have
   * a list with more than one entry; if the subselect has just one target
***************
*** 414,419 ****
--- 418,425 ----
      SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
      bool        useOr;            /* TRUE to combine column results with
                                   * "OR" not "AND" */
+     bool        isExpr;            /* TRUE if the subselect is really derived
+                                  * from a single expression */
      List       *lefthand;        /* list of outer-query expressions on the
                                   * left */
      List       *operName;        /* originally specified operator name */
***************
*** 455,460 ****
--- 461,475 ----
      SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
      bool        useOr;            /* TRUE to combine column results with
                                   * "OR" not "AND" */
+     bool        isExpr;            /* TRUE if the subselect is really derived
+                                  * from a single expression */
+     /* runtime cache for single array expressions */
+     Oid            exprtype;        /* array and element type, and other info
+                                  * needed deconstruct the array */
+     Oid            elemtype;
+     int16        elmlen;
+     bool        elmbyval;
+     char        elmalign;
      /* The combining operators, transformed to executable expressions: */
      List       *exprs;            /* list of OpExpr expression trees */
      List       *paramIds;        /* IDs of Params embedded in the above */

Re: array support patch phase 1 patch

From
Joe Conway
Date:
Tom Lane wrote:
> Another problem is:
>
> regression=# select distinct array(select * from text_tbl) from foo;
> ERROR:  Unable to identify an equality operator for type text[]
>
> This DISTINCT query would fail anyway of course, for lack of a '<'
> operator for arrays, but it seems like we ought to be able to find the
> polymorphic '=' operator.

The attached small patch addresses the above concern (finding
polymorphic "=" operator) by introducing a new function,
IsPolymorphicCoercible().

Now, as predicted, I get:

regression=# select distinct array(select f1 from tse);
ERROR:  Unable to identify an ordering operator for type integer[]
         Use an explicit ordering operator or modify the query

If there are no objections, please apply.

Next question this begs is, should I work on a '<' operator for arrays?
And if so, how is the behavior defined? Hannu suggested
element-by-element, analogous to character-by-character text string
comparison. Comments?

Thanks,

Joe
Index: src/backend/parser/parse_oper.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_oper.c,v
retrieving revision 1.64
diff -c -r1.64 parse_oper.c
*** src/backend/parser/parse_oper.c    26 May 2003 00:11:27 -0000    1.64
--- src/backend/parser/parse_oper.c    1 Jun 2003 00:01:30 -0000
***************
*** 412,417 ****
--- 412,421 ----
          IsBinaryCoercible(arg2, opform->oprright))
          return optup;

+     /* last check -- polymorphic types? */
+     if (IsPolymorphicCoercible(arg1 , arg2))
+         return optup;
+
      /* nope... */
      ReleaseSysCache(optup);

Index: src/backend/parser/parse_coerce.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v
retrieving revision 2.97
diff -c -r2.97 parse_coerce.c
*** src/backend/parser/parse_coerce.c    26 May 2003 00:11:27 -0000    2.97
--- src/backend/parser/parse_coerce.c    31 May 2003 23:59:07 -0000
***************
*** 1187,1192 ****
--- 1187,1230 ----
      return result;
  }

+ /* IsPolymorphicCoercible()
+  *        Check if srctype and targettype are a polymorphic compatible pair.
+  *
+  * We consider two types polymorphic-coercible if:
+  * 1) both are the same (we should never have been called this way, but what
+  *    the heck)
+  * 2) one is ANYARRAY and the other is an array type
+  * 3) one is ANYELEMENT and the other is not an array type
+
+  */
+ bool
+ IsPolymorphicCoercible(Oid srctype, Oid targettype)
+ {
+     /* Case 1: fast path if same type */
+     if (srctype == targettype)
+         return true;
+
+     /* Case 2: check for matching arrays */
+     if (srctype == ANYARRAYOID)
+         if (get_element_type(targettype) != InvalidOid)
+             return true;
+
+     if (targettype == ANYARRAYOID)
+         if (get_element_type(srctype) != InvalidOid)
+             return true;
+
+     /* Case 3: check for matching elements */
+     if (srctype == ANYELEMENTOID)
+         if (get_element_type(targettype) == InvalidOid)
+             return true;
+
+     if (targettype == ANYELEMENTOID)
+         if (get_element_type(srctype) == InvalidOid)
+             return true;
+
+     /* if all else fails... */
+     return false;
+ }

  /*
   * find_coercion_pathway
Index: src/include/parser/parse_coerce.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_coerce.h,v
retrieving revision 1.51
diff -c -r1.51 parse_coerce.h
*** src/include/parser/parse_coerce.h    29 Apr 2003 22:13:11 -0000    1.51
--- src/include/parser/parse_coerce.h    31 May 2003 23:51:19 -0000
***************
*** 36,41 ****
--- 36,42 ----


  extern bool IsBinaryCoercible(Oid srctype, Oid targettype);
+ extern bool IsPolymorphicCoercible(Oid srctype, Oid targettype);
  extern bool IsPreferredType(CATEGORY category, Oid type);
  extern CATEGORY TypeCategory(Oid type);


Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> +     /* last check -- polymorphic types? */
> +     if (IsPolymorphicCoercible(arg1 , arg2))
> +         return optup;

That is surely not what you intended.  The test must be whether arg1 and
arg2 are (separately) coercible to the operator's two input types.
Moreover, the test must not be symmetric, any more than
IsBinaryCoercible is.  You can coerce int[] to ANYARRAY but not vice
versa.

A bigger problem is that I doubt this will actually work.  Most of the
places that call compatible_oper will then proceed to call the function
from specialized code that does not bother with consing up an expression
tree --- so a polymorphic function is going to fail anyway...

            regards, tom lane

Re: array support patch phase 1 patch

From
Joe Conway
Date:
Tom Lane wrote:
> That is surely not what you intended.  The test must be whether arg1 and
> arg2 are (separately) coercible to the operator's two input types.
> Moreover, the test must not be symmetric, any more than
> IsBinaryCoercible is.  You can coerce int[] to ANYARRAY but not vice
> versa.

Dooh! Yeah, I can see that now.

>
> A bigger problem is that I doubt this will actually work.  Most of the
> places that call compatible_oper will then proceed to call the function
> from specialized code that does not bother with consing up an expression
> tree --- so a polymorphic function is going to fail anyway...

Well, not necessarily in the case of array_type-to-ANYARRAY. In that
case the element type information in the array itself gives the function
all the context it needs (if it looks there, which in the case of
array_eq at least it does). Maybe it makes sense to only allow the
array_type-to-ANYARRAY case?

Joe


Re: array support patch phase 1 patch

From
Joe Conway
Date:
Tom Lane wrote:
> That is surely not what you intended.  The test must be whether arg1 and
> arg2 are (separately) coercible to the operator's two input types.
> Moreover, the test must not be symmetric, any more than
> IsBinaryCoercible is.  You can coerce int[] to ANYARRAY but not vice
> versa.
>
> A bigger problem is that I doubt this will actually work.  Most of the
> places that call compatible_oper will then proceed to call the function
> from specialized code that does not bother with consing up an expression
> tree --- so a polymorphic function is going to fail anyway...
>

How's this attempt?

Couple of issues it raised:

1.) In conjunction with the phase2 patch, which changes array_eq to do
an element-by-element equality comparison, I discovered that type
"aclitem" has no equality operator, and apparently nothing
binary-coercible either. Should I invent "=" for aclitem, or have
array_eq fall back to its original byte-by-byte comparison if it doesn't
find an equality operator?

2.) I noticed that if I start the postmaster without redirecting to a
logfile, I see double error messages in psql. Is this intended? Maybe
due to some conf setting?

[postgres@dev postgres]$ pg_ctl start
LOG:  database system was shut down at 2003-06-01 08:58:18 PDT
IN:  StartupXLOG (xlog.c:2510)
LOG:  checkpoint record is at 0/5BCDA4
IN:  StartupXLOG (xlog.c:2538)
LOG:  redo record is at 0/5BCDA4; undo record is at 0/0; shutdown TRUE
IN:  StartupXLOG (xlog.c:2558)
LOG:  next transaction id: 475; next oid: 17079
IN:  StartupXLOG (xlog.c:2562)
postmaster successfully started
[postgres@dev postgres]$ LOG:  database system is ready
IN:  StartupXLOG (xlog.c:2819)

[postgres@dev postgres]$ psql template1
Welcome to psql 7.4devel, the PostgreSQL interactive terminal.

Type:  \copyright for distribution terms
        \h for help with SQL commands
        \? for help on internal slash commands
        \g or terminate with semicolon to execute query
        \q to quit

template1=# select distinct array(select f1 from tse);
ERROR:  Unable to identify an ordering operator for type integer[]
         Use an explicit ordering operator or modify the query
IN:  ordering_oper (parse_oper.c:202)
ERROR:  Unable to identify an ordering operator for type integer[]
         Use an explicit ordering operator or modify the query


Joe
Index: src/backend/parser/parse_coerce.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v
retrieving revision 2.97
diff -c -r2.97 parse_coerce.c
*** src/backend/parser/parse_coerce.c    26 May 2003 00:11:27 -0000    2.97
--- src/backend/parser/parse_coerce.c    1 Jun 2003 15:50:25 -0000
***************
*** 1187,1192 ****
--- 1195,1222 ----
      return result;
  }

+ /* IsPolymorphicCoercible()
+  *        Check if srctype and targettype are a polymorphic compatible pair.
+  *
+  * We consider two types polymorphic-coercible if:
+  * 1) both are the same
+  * 2) target is ANYARRAY and source is an array type
+  */
+ bool
+ IsPolymorphicCoercible(Oid srctype, Oid targettype)
+ {
+     /* Case 1: fast path if same type */
+     if (srctype == targettype)
+         return true;
+
+     /* Case 2: check for matching arrays */
+     if (targettype == ANYARRAYOID)
+         if (get_element_type(srctype) != InvalidOid)
+             return true;
+
+     /* if all else fails... */
+     return false;
+ }

  /*
   * find_coercion_pathway
Index: src/backend/parser/parse_oper.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_oper.c,v
retrieving revision 1.64
diff -c -r1.64 parse_oper.c
*** src/backend/parser/parse_oper.c    26 May 2003 00:11:27 -0000    1.64
--- src/backend/parser/parse_oper.c    1 Jun 2003 15:54:41 -0000
***************
*** 412,417 ****
--- 412,422 ----
          IsBinaryCoercible(arg2, opform->oprright))
          return optup;

+     /* last check -- polymorphic types? */
+     if (IsPolymorphicCoercible(arg1 , opform->oprleft) &&
+         IsPolymorphicCoercible(arg2, opform->oprright))
+         return optup;
+
      /* nope... */
      ReleaseSysCache(optup);

Index: src/include/parser/parse_coerce.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_coerce.h,v
retrieving revision 1.51
diff -c -r1.51 parse_coerce.h
*** src/include/parser/parse_coerce.h    29 Apr 2003 22:13:11 -0000    1.51
--- src/include/parser/parse_coerce.h    1 Jun 2003 15:41:23 -0000
***************
*** 36,41 ****
--- 36,42 ----


  extern bool IsBinaryCoercible(Oid srctype, Oid targettype);
+ extern bool IsPolymorphicCoercible(Oid srctype, Oid targettype);
  extern bool IsPreferredType(CATEGORY category, Oid type);
  extern CATEGORY TypeCategory(Oid type);


Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
>> A bigger problem is that I doubt this will actually work.  Most of the
>> places that call compatible_oper will then proceed to call the function
>> from specialized code that does not bother with consing up an expression
>> tree --- so a polymorphic function is going to fail anyway...

> Well, not necessarily in the case of array_type-to-ANYARRAY. In that
> case the element type information in the array itself gives the function
> all the context it needs (if it looks there, which in the case of
> array_eq at least it does). Maybe it makes sense to only allow the
> array_type-to-ANYARRAY case?

Yeah, I think we could get away with that.  It might be appropriate to
put that single special case into IsBinaryCoercible, instead of allowing
it only for the compatible_oper case.  I can't recall offhand what else
uses IsBinaryCoercible ...

            regards, tom lane

Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> 1.) In conjunction with the phase2 patch, which changes array_eq to do
> an element-by-element equality comparison, I discovered that type
> "aclitem" has no equality operator, and apparently nothing
> binary-coercible either. Should I invent "=" for aclitem, or have
> array_eq fall back to its original byte-by-byte comparison if it doesn't
> find an equality operator?

The former I would think.

> 2.) I noticed that if I start the postmaster without redirecting to a
> logfile, I see double error messages in psql. Is this intended?

Yeah, if postmaster stderr is coming to your console ...

            regards, tom lane

Re: array support patch phase 1 patch

From
Joe Conway
Date:
Tom Lane wrote:
> Yeah, I think we could get away with that.  It might be appropriate to
> put that single special case into IsBinaryCoercible, instead of allowing
> it only for the compatible_oper case.  I can't recall offhand what else
> uses IsBinaryCoercible ...
>

OK -- here is that version. Certainly simplifies it, and seems
appropriate given the comments in IsBinaryCoercible.

Next question: should I roll the three array related patches floating
around (phase2, phase3, polycoerce) into one big patch again? It's
difficult for me to continue to make progress without doing that.

Joe
Index: src/backend/parser/parse_coerce.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v
retrieving revision 2.97
diff -c -r2.97 parse_coerce.c
*** src/backend/parser/parse_coerce.c    26 May 2003 00:11:27 -0000    2.97
--- src/backend/parser/parse_coerce.c    1 Jun 2003 16:15:40 -0000
***************
*** 1169,1174 ****
--- 1177,1187 ----
      /* Somewhat-fast path for domain -> base type case */
      if (srctype == targettype)
          return true;
+
+     /* Last of the fast-paths: check for matching polymorphic arrays */
+     if (targettype == ANYARRAYOID)
+         if (get_element_type(srctype) != InvalidOid)
+             return true;

      /* Else look in pg_cast */
      tuple = SearchSysCache(CASTSOURCETARGET,

Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> Next question: should I roll the three array related patches floating
> around (phase2, phase3, polycoerce) into one big patch again? It's
> difficult for me to continue to make progress without doing that.

If it's easier at your end.

> +     /* Last of the fast-paths: check for matching polymorphic arrays */
> +     if (targettype == ANYARRAYOID)
> +         if (get_element_type(srctype) != InvalidOid)
> +             return true;

This doesn't seem quite right; won't the second test succeed for NAME
and other fixed-length-array types?  You need to restrict it to varlena
arrays, I'd think.

            regards, tom lane

Re: array support patch phase 1 patch

From
Joe Conway
Date:
Tom Lane wrote:
>>+     /* Last of the fast-paths: check for matching polymorphic arrays */
>>+     if (targettype == ANYARRAYOID)
>>+         if (get_element_type(srctype) != InvalidOid)
>>+             return true;
>
>
> This doesn't seem quite right; won't the second test succeed for NAME
> and other fixed-length-array types?  You need to restrict it to varlena
> arrays, I'd think.
>

get_element_type already does -- in fact, I think that part was you're
innovation because originally I had get_typelem() which didn't make that
distinction ;-)

/*
  * get_element_type
  *
  * Given the type OID, get the typelem (InvalidOid if not an array
  * type).
  *
  * NB: this only considers varlena arrays to be true arrays;
  * InvalidOid is returned if the input is a fixed-length array type.
  */

Joe


Re: array support patch phase 1 patch

From
Joe Conway
Date:
Joe Conway wrote:
> Next question this begs is, should I work on a '<' operator for arrays?
> And if so, how is the behavior defined? Hannu suggested
> element-by-element, analogous to character-by-character text string
> comparison. Comments?

I created an equality operator for aclitem, and then all was well until
I finished the above (i.e. I created array-array operators for "<>",
"<", ">", "<=", and ">=" based on the text comparison analogy). Now on
ANALYZE (and therefore on initdb) I get this:

template1=# analyze;
ERROR:  Unable to identify an ordering operator for type aclitem
         Use an explicit ordering operator or modify the query

This is where I ran into the problem with an equality operator earlier.
Problem this time is that an ordering operator makes no sense for
aclitems -- does it? Any thoughts on how to work around this?

Joe


Re: array support patch phase 1 patch

From
Joe Conway
Date:
Tom Lane wrote:
> Joe Conway <mail@joeconway.com> writes:
>>Next question: should I roll the three array related patches floating
>>around (phase2, phase3, polycoerce) into one big patch again? It's
>>difficult for me to continue to make progress without doing that.
>
> If it's easier at your end.

Attached is the "super combo" array patch ;-)

It includes the following previously sent patches:
array-phase2.19.patch
array-phase3.05.patch
array-polycoerce.3.patch

It also includes the following additional changes:
1) aclitem type now has an equality operator
2) examine_attribute() now checks for the element type's equality and
    ordering operators when dealing with a varlena array type
3) There are now "<>, ">", "<", "<=", and ">=" operators for
    array-to-array comparison, using semantics similar to text's
    character-to-character comparison.
4) There is also now a btarraycmp() function and other required
    btree support. BTree indexes can now be created on varlena arrays.

Below are some examples of the new (since earlier patches)
functionality. CVS tip from this morning plus this patch compiles
cleanly and passes all 90 regression tests on my dev box. If there are
no objections, please apply.

Thanks,

Joe


Examples:
------------------------------------------
create table tse(f1 int, f2 int[], f3 text[]);
insert into tse values(1,array[69,42,54], array['g','d','e']);
insert into tse values(2,array[1,2,3], array['x','y','z']);
insert into tse values(3,array[2,99,0], array['Tom','Bruce']);
insert into tse values(4,array[1,1,1], array['a','a','a']);
insert into tse values(5,array[5,6,7], array['a','b','c']);

regression=# select distinct array(select f1 from tse);
   ?column?
-------------
  {1,2,3,4,5}
(1 row)

regression=# select * from tse where f2 < array[1,2,3,4];
  f1 |   f2    |   f3
----+---------+---------
   2 | {1,2,3} | {x,y,z}
   4 | {1,1,1} | {a,a,a}
(2 rows)

regression=# select * from tse where f2 < array[1,2,3];
  f1 |   f2    |   f3
----+---------+---------
   4 | {1,1,1} | {a,a,a}
(1 row)

regression=# select * from tse where f2 <= array[1,2,3];
  f1 |   f2    |   f3
----+---------+---------
   2 | {1,2,3} | {x,y,z}
   4 | {1,1,1} | {a,a,a}
(2 rows)

regression=# select * from tse where f2 > array[1,2,3];
  f1 |     f2     |     f3
----+------------+-------------
   1 | {69,42,54} | {g,d,e}
   3 | {2,99,0}   | {Tom,Bruce}
   5 | {5,6,7}    | {a,b,c}
(3 rows)

regression=# select * from tse where f2 >= array[1,2,3];
  f1 |     f2     |     f3
----+------------+-------------
   1 | {69,42,54} | {g,d,e}
   2 | {1,2,3}    | {x,y,z}
   3 | {2,99,0}   | {Tom,Bruce}
   5 | {5,6,7}    | {a,b,c}
(4 rows)

regression=# select * from tse where f2 != array[1,2,3];
  f1 |     f2     |     f3
----+------------+-------------
   1 | {69,42,54} | {g,d,e}
   3 | {2,99,0}   | {Tom,Bruce}
   4 | {1,1,1}    | {a,a,a}
   5 | {5,6,7}    | {a,b,c}
(4 rows)

regression=# select * from tse where f2 = array[1,2,3];
  f1 |   f2    |   f3
----+---------+---------
   2 | {1,2,3} | {x,y,z}
(1 row)

regression=# select * from tse where f3 >= array['Tom'] and f3 < array['a'];
  f1 |    f2    |     f3
----+----------+-------------
   3 | {2,99,0} | {Tom,Bruce}
(1 row)

regression=# create index arridx1 on tse(f2);
CREATE INDEX
regression=# set enable_seqscan to off;
SET
regression=# explain analyze select * from tse where f2 = array[1,2,3];
                        QUERY PLAN
-----------------------------------------------------------------------
  Index Scan using arridx1 on tse  (cost=0.00..4.68 rows=2 width=68)
(actual time=0.07..0.07 rows=1 loops=1)
    Index Cond: (f2 = '{1,2,3}'::integer[])
  Total runtime: 0.15 msec
(3 rows)
Index: doc/src/sgml/array.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
retrieving revision 1.25
diff -c -r1.25 array.sgml
*** doc/src/sgml/array.sgml    13 Mar 2003 01:30:26 -0000    1.25
--- doc/src/sgml/array.sgml    1 Jun 2003 15:41:15 -0000
***************
*** 9,15 ****

   <para>
    <productname>PostgreSQL</productname> allows columns of a table to be
!   defined as variable-length multidimensional arrays. Arrays of any
    built-in type or user-defined type can be created.
   </para>

--- 9,15 ----

   <para>
    <productname>PostgreSQL</productname> allows columns of a table to be
!   defined as variable-length multi-dimensional arrays. Arrays of any
    built-in type or user-defined type can be created.
   </para>

***************
*** 60,73 ****
  </programlisting>
   </para>

   <note>
    <para>
!    A limitation of the present array implementation is that individual
!    elements of an array cannot be SQL null values.  The entire array can be set
!    to null, but you can't have an array with some elements null and some
!    not.  Fixing this is on the to-do list.
    </para>
   </note>
   </sect2>

   <sect2>
--- 60,133 ----
  </programlisting>
   </para>

+  <para>
+   A limitation of the present array implementation is that individual
+   elements of an array cannot be SQL null values.  The entire array can be set
+   to null, but you can't have an array with some elements null and some
+   not.
+  </para>
+  <para>
+   This can lead to surprising results. For example, the result of the
+   previous two inserts looks like this:
+ <programlisting>
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |      schedule
+ -------+---------------------------+--------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting},{""}}
+  Carol | {20000,25000,25000,25000} | {{talk},{meeting}}
+ (2 rows)
+ </programlisting>
+   Because the <literal>[2][2]</literal> element of
+   <structfield>schedule</structfield> is missing in each of the
+   <command>INSERT</command> statements, the <literal>[1][2]</literal>
+   element is discarded.
+  </para>
+
+  <note>
+   <para>
+    Fixing this is on the to-do list.
+   </para>
+  </note>
+
+  <para>
+   The <command>ARRAY</command> expression syntax may also be used:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Bill',
+     ARRAY[10000, 10000, 10000, 10000],
+     ARRAY[['meeting', 'lunch'], ['','']]);
+
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting', '']]);
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |           schedule
+ -------+---------------------------+-------------------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}}
+  Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}}
+ (2 rows)
+ </programlisting>
+   Note that with this syntax, multi-dimensional arrays must have matching
+   extents for each dimension. This eliminates the missing-array-elements
+   problem above. For example:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting']]);
+ ERROR:  Multiple dimension arrays must have array expressions with matching dimensions
+ </programlisting>
+   Also notice that string literals are single quoted instead of double quoted.
+  </para>
+
   <note>
    <para>
!    The examples in the rest of this section are based on the
!    <command>ARRAY</command> expression syntax <command>INSERT</command>s.
    </para>
   </note>
+
   </sect2>

   <sect2>
***************
*** 132,142 ****
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the
!   form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified.
   </para>

   <para>
--- 192,221 ----
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified; another example follows:
! <programlisting>
! SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
!          schedule
! ---------------------------
!  {{meeting,lunch},{"",""}}
! (1 row)
! </programlisting>
!  </para>
!
!  <para>
!   Additionally, we can also access a single arbitrary array element of
!   a one-dimensional array with the <function>array_subscript</function>
!   function:
! <programlisting>
! SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
!  array_subscript
! -----------------
!            10000
! (1 row)
! </programlisting>
   </para>

   <para>
***************
*** 147,153 ****
      WHERE name = 'Carol';
  </programlisting>

!   or updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
--- 226,248 ----
      WHERE name = 'Carol';
  </programlisting>

!   or using the <command>ARRAY</command> expression syntax:
!
! <programlisting>
! UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
!     WHERE name = 'Carol';
! </programlisting>
!
!   <note>
!    <para>
!     Anywhere you can use the <quote>curly braces</quote> array syntax,
!     you can also use the <command>ARRAY</command> expression syntax. The
!     remainder of this section will illustrate only one or the other, but
!     not both.
!    </para>
!   </note>
!
!   An array may also be updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
***************
*** 160,165 ****
--- 255,268 ----
  UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
      WHERE name = 'Carol';
  </programlisting>
+
+   A one-dimensional array may also be updated with the
+   <function>array_assign</function> function:
+
+ <programlisting>
+ UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000)
+     WHERE name = 'Bill';
+ </programListing>
   </para>

   <para>
***************
*** 169,175 ****
    value currently has 4 elements, it will have five elements after an
    update that assigns to <literal>array[5]</>.  Currently, enlargement in
    this fashion is only allowed for one-dimensional arrays, not
!   multidimensional arrays.
   </para>

   <para>
--- 272,278 ----
    value currently has 4 elements, it will have five elements after an
    update that assigns to <literal>array[5]</>.  Currently, enlargement in
    this fashion is only allowed for one-dimensional arrays, not
!   multi-dimensional arrays.
   </para>

   <para>
***************
*** 179,184 ****
--- 282,367 ----
   </para>

   <para>
+   An array can also be enlarged by using the functions
+   <function>array_prepend</function>, <function>array_append</function>,
+   or <function>array_cat</function>. The first two only support one-dimensional
+   arrays, but <function>array_cat</function> supports multi-dimensional arrays.
+   Some examples:
+
+ <programlisting>
+ SELECT array_prepend(1, ARRAY[2,3]);
+  array_prepend
+ ---------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_append(ARRAY[1,2], 3);
+  array_append
+ --------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
+    array_cat
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
+       array_cat
+ ---------------------
+  {{1,2},{3,4},{5,6}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
+       array_cat
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ </programlisting>
+
+   <function>array_prepend</function> and <function>array_append</function>
+   work with a one-dimensional array and a single element to be pushed on
+   to the beginning or end of the array, respectively. The array is extended
+   in the direction of the push. Hence, by pushing onto the beginning of an
+   array with a one-based subscript, a zero-based subscript array is created:
+
+ <programlisting>
+ SELECT array_dims(t.f) FROM (SELECT array_prepend(1, ARRAY[2,3]) AS f) AS t;
+  array_dims
+ ------------
+  [0:2]
+ (1 row)
+ </programlisting>
+
+   <function>array_cat</function> works with either two
+   <replaceable>n</>-dimension arrays, or an <replaceable>n</>-dimension
+   and an <replaceable>n+1</> dimension array. In the former case, the two
+   <replaceable>n</>-dimension arrays become outer elements of an
+   <replaceable>n+1</> dimension array. In the latter, the
+   <replaceable>n</>-dimension array is added as either the first or last
+   outer element of the <replaceable>n+1</> dimension array.
+  </para>
+
+  <para>
+   A final method of enlarging arrays is through the concatenation operator,
+   <command>||</command>, which works exactly as <function>array_cat</function>
+   does.
+ <programlisting>
+ SELECT ARRAY[1,2] || ARRAY[3,4];
+    ?column?
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
+       ?column?
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ (1 row)
+ </programlisting>
+  </para>
+
+  <para>
    The syntax for <command>CREATE TABLE</command> allows fixed-length
    arrays to be defined:

***************
*** 194,199 ****
--- 377,392 ----
   </para>

   <para>
+   An alternative syntax for one-dimensional arrays may be used.
+   <structfield>pay_by_quarter</structfield> could have been defined as:
+ <programlisting>
+     pay_by_quarter  integer ARRAY[4],
+ </programlisting>
+   This syntax may <emphasis>only</emphasis> be used with the integer
+   constant to denote the array size.
+  </para>
+
+  <para>
    Actually, the current implementation does not enforce the declared
    number of dimensions either.  Arrays of a particular element type are
    all considered to be of the same type, regardless of size or number
***************
*** 292,298 ****
     for the array's element type.  (Among the standard data types provided
     in the <productname>PostgreSQL</productname> distribution, type
     <literal>box</> uses a semicolon (<literal>;</>) but all the others
!    use comma.)  In a multidimensional array, each dimension (row, plane,
     cube, etc.) gets its own level of curly braces, and delimiters
     must be written between adjacent curly-braced entities of the same level.
     You may write whitespace before a left brace, after a right
--- 485,491 ----
     for the array's element type.  (Among the standard data types provided
     in the <productname>PostgreSQL</productname> distribution, type
     <literal>box</> uses a semicolon (<literal>;</>) but all the others
!    use comma.)  In a multi-dimensional array, each dimension (row, plane,
     cube, etc.) gets its own level of curly braces, and delimiters
     must be written between adjacent curly-braced entities of the same level.
     You may write whitespace before a left brace, after a right
***************
*** 300,305 ****
--- 493,564 ----
     is not ignored, however: after skipping leading whitespace, everything
     up to the next right brace or delimiter is taken as the item value.
    </para>
+
+   <para>
+    As illustrated earlier in this chapter, arrays may also be represented
+    using the <command>ARRAY</command> expression syntax. This representation
+    of an array value consists of items that are interpreted according to the
+    I/O conversion rules for the array's element type, plus decoration that
+    indicates the array structure. The decoration consists of the keyword
+    <command>ARRAY</command> and square brackets (<literal>[</> and
+    <literal>]</>) around the array values, plus delimiter characters between
+    adjacent items. The delimiter character is always a comma (<literal>,</>).
+    When representing multi-dimensional arrays, the keyword
+    <command>ARRAY</command> is only necessary for the outer level. For example,
+    <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as:
+ <programlisting>
+ SELECT ARRAY[['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   or it also could be written as:
+ <programlisting>
+ SELECT ARRAY[ARRAY['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   </para>
+
+   <para>
+    A final method to represent an array, is through an
+    <command>ARRAY</command> sub-select expression. For example:
+ <programlisting>
+ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
+                           ?column?
+ -------------------------------------------------------------
+  {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
+ (1 row)
+ </programlisting>
+   The sub-select may <emphasis>only</emphasis> return a single column. The
+   resulting one-dimensional array will have an element for each row in the
+   sub-select result, with an element type matching that of the sub-select's
+   target column.
+   </para>
+
+   <para>
+    Arrays may be cast from one type to another in similar fashion to other
+    data types:
+
+ <programlisting>
+ SELECT ARRAY[1,2,3]::oid[];
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+
+ SELECT CAST(ARRAY[1,2,3] AS float8[]);
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+ </programlisting>
+
+   </para>
+
   </sect2>

   <sect2>
***************
*** 316,321 ****
--- 575,588 ----
     Alternatively, you can use backslash-escaping to protect all data characters
     that would otherwise be taken as array syntax or ignorable white space.
    </para>
+
+  <note>
+   <para>
+    The discussion in the preceding paragraph with respect to double quoting does
+    not pertain to the <command>ARRAY</command> expression syntax. In that case,
+    each element is quoted exactly as any other literal value of the element type.
+   </para>
+  </note>

    <para>
     The array output routine will put double quotes around element values
Index: doc/src/sgml/func.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v
retrieving revision 1.154
diff -c -r1.154 func.sgml
*** doc/src/sgml/func.sgml    5 May 2003 15:08:49 -0000    1.154
--- doc/src/sgml/func.sgml    1 Jun 2003 15:41:15 -0000
***************
*** 6962,6967 ****
--- 6962,7209 ----

    </sect1>

+  <sect1 id="functions-array">
+   <title>Array Functions</title>
+
+   <para>
+    <xref linkend="array-operators-table"> shows the operators
+    available for the <type>array</type> types.
+   </para>
+
+     <table id="array-operators-table">
+      <title><type>array</type> Operators</title>
+      <tgroup cols="4">
+       <thead>
+        <row>
+     <entry>Operator</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry> <literal>=</literal> </entry>
+     <entry>equals</entry>
+     <entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry>
+     <entry><literal>t</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>element-to-array concatenation</entry>
+     <entry><literal>3 || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{3,4,5,6}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-element concatenation</entry>
+     <entry><literal>ARRAY[4,5,6] || 7</literal></entry>
+     <entry><literal>{4,5,6,7}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+
+   <para>
+    <xref linkend="array-functions-table"> shows the functions
+    available for use with array types. See <xref linkend="arrays">
+    for more discussion and examples for the use of these functions.
+   </para>
+
+     <table id="array-functions-table">
+      <title><type>array</type> Functions</title>
+      <tgroup cols="5">
+       <thead>
+        <row>
+     <entry>Function</entry>
+     <entry>Return Type</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry>
+      <literal>
+       <function>array_append</function>
+       (<type>anyarray</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the end of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_append(ARRAY[1,2], 3)</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_assign</function>
+       (<type>anyarray</type>, <type>integer</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      assign a value to a specific array element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_assign(ARRAY[1,2,3], 2, 99)</literal></entry>
+     <entry><literal>{1,99,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_cat</function>
+       (<type>anyarray</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      concatenate two arrays, returning <literal>NULL</literal>
+      for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_dims</function>
+       (<type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      returns a text representation of array dimension lower and upper bounds,
+      generating an ERROR for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry>
+     <entry><literal>[1:2][1:3]</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_lower</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns lower bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry>
+     <entry><literal>0</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_prepend</function>
+       (<type>anyelement</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the beginning of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_subscript</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>anyelement</type></entry>
+     <entry>
+      returns requested array element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_subscript(ARRAY[1,2,3], 3)</literal></entry>
+     <entry><literal>3</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_to_str</function>
+       (<type>anyarray</type>, <type>text</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      concatenates array elements using provided delimiter, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_to_str(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry>
+     <entry><literal>1.10~^~2.20~^~3.30</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_upper</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns upper bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry>
+     <entry><literal>4</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>singleton_array</function>
+       (<type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      create an array from the provided element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>singleton_array(1)</literal></entry>
+     <entry><literal>{1}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>str_to_array</function>
+       (<type>text</type>, <type>text</type>)
+      </literal>
+     </entry>
+     <entry><type>text[]</type></entry>
+     <entry>
+      splits string into array elements using provided delimiter, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>str_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry>
+     <entry><literal>{1.1,2.2,3.3}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+   </sect1>

   <sect1 id="functions-aggregate">
    <title>Aggregate Functions</title>
Index: src/backend/catalog/pg_aggregate.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_aggregate.c,v
retrieving revision 1.56
diff -c -r1.56 pg_aggregate.c
*** src/backend/catalog/pg_aggregate.c    18 Sep 2002 21:35:20 -0000    1.56
--- src/backend/catalog/pg_aggregate.c    1 Jun 2003 15:41:15 -0000
***************
*** 50,59 ****
      Oid            finalfn = InvalidOid;    /* can be omitted */
      Oid            finaltype;
      Oid            fnArgs[FUNC_MAX_ARGS];
!     int            nargs;
      Oid            procOid;
      TupleDesc    tupDesc;
      int            i;
      ObjectAddress myself,
                  referenced;

--- 50,65 ----
      Oid            finalfn = InvalidOid;    /* can be omitted */
      Oid            finaltype;
      Oid            fnArgs[FUNC_MAX_ARGS];
!     int            nargs_transfn;
!     int            nargs_finalfn;
      Oid            procOid;
      TupleDesc    tupDesc;
      int            i;
+     Oid            rettype;
+     Oid           *true_oid_array_transfn;
+     Oid           *true_oid_array_finalfn;
+     bool        retset;
+     FuncDetailCode fdresult;
      ObjectAddress myself,
                  referenced;

***************
*** 68,91 ****
      MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
      fnArgs[0] = aggTransType;
      if (aggBaseType == ANYOID)
!         nargs = 1;
      else
      {
          fnArgs[1] = aggBaseType;
!         nargs = 2;
      }
!     transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
      if (!OidIsValid(transfn))
!         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
      tup = SearchSysCache(PROCOID,
                           ObjectIdGetDatum(transfn),
                           0, 0, 0);
      if (!HeapTupleIsValid(tup))
!         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
      proc = (Form_pg_proc) GETSTRUCT(tup);
-     if (proc->prorettype != aggTransType)
-         elog(ERROR, "return type of transition function %s is not %s",
-          NameListToString(aggtransfnName), format_type_be(aggTransType));

      /*
       * If the transfn is strict and the initval is NULL, make sure input
--- 74,122 ----
      MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
      fnArgs[0] = aggTransType;
      if (aggBaseType == ANYOID)
!         nargs_transfn = 1;
      else
      {
          fnArgs[1] = aggBaseType;
!         nargs_transfn = 2;
      }
!
!     /*
!      * func_get_detail looks up the function in the catalogs, does
!      * disambiguation for polymorphic functions, handles inheritance, and
!      * returns the funcid and type and set or singleton status of the
!      * function's return value.  it also returns the true argument types
!      * to the function.
!      */
!     fdresult = func_get_detail(aggtransfnName, NIL, nargs_transfn, fnArgs,
!                                &transfn, &rettype, &retset,
!                                &true_oid_array_transfn);
!
!     /* only valid case is a normal function */
!     if (fdresult != FUNCDETAIL_NORMAL)
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
      if (!OidIsValid(transfn))
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
!     /*
!      * enforce consistency with ANYARRAY and ANYELEMENT argument
!      * and return types, possibly modifying return type along the way
!      */
!     rettype = enforce_generic_type_consistency(fnArgs, true_oid_array_transfn,
!                                                        nargs_transfn, rettype);
!
!     if (rettype != aggTransType)
!         elog(ERROR, "return type of transition function %s is not %s",
!          NameListToString(aggtransfnName), format_type_be(aggTransType));
!
      tup = SearchSysCache(PROCOID,
                           ObjectIdGetDatum(transfn),
                           0, 0, 0);
      if (!HeapTupleIsValid(tup))
!         func_error("AggregateCreate", aggtransfnName,
!                         nargs_transfn, fnArgs, NULL);
      proc = (Form_pg_proc) GETSTRUCT(tup);

      /*
       * If the transfn is strict and the initval is NULL, make sure input
***************
*** 105,121 ****
      {
          MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
          fnArgs[0] = aggTransType;
!         finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
!         if (!OidIsValid(finalfn))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!         tup = SearchSysCache(PROCOID,
!                              ObjectIdGetDatum(finalfn),
!                              0, 0, 0);
!         if (!HeapTupleIsValid(tup))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!         proc = (Form_pg_proc) GETSTRUCT(tup);
!         finaltype = proc->prorettype;
!         ReleaseSysCache(tup);
      }
      else
      {
--- 136,161 ----
      {
          MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
          fnArgs[0] = aggTransType;
!         nargs_finalfn = 1;
!
!         fdresult = func_get_detail(aggfinalfnName, NIL, 1, fnArgs,
!                                    &finalfn, &rettype, &retset,
!                                    &true_oid_array_finalfn);
!
!         /* only valid case is a normal function */
!         if (fdresult != FUNCDETAIL_NORMAL)
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!
!         if (!OidIsValid(finalfn))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!
!         /*
!          * enforce consistency with ANYARRAY and ANYELEMENT argument
!          * and return types, possibly modifying return type along the way
!          */
!         finaltype = enforce_generic_type_consistency(fnArgs,
!                                                      true_oid_array_finalfn,
!                                                      nargs_finalfn, rettype);
      }
      else
      {
***************
*** 125,130 ****
--- 165,191 ----
          finaltype = aggTransType;
      }
      Assert(OidIsValid(finaltype));
+
+     /*
+      * special disallowed cases:
+      * 1)    if finaltype is polymorphic, basetype cannot be ANY
+      * 2)    if finaltype is polymorphic, both args to transfn must be
+      *        polymorphic
+      */
+     if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
+     {
+         if (aggBaseType == ANYOID)
+             elog(ERROR, "aggregate with base type ANY must have a " \
+                         "non-polymorphic return type");
+
+         if (nargs_transfn > 1 && (
+             (true_oid_array_transfn[0] != ANYARRAYOID &&
+              true_oid_array_transfn[0] != ANYELEMENTOID) ||
+             (true_oid_array_transfn[1] != ANYARRAYOID &&
+              true_oid_array_transfn[1] != ANYELEMENTOID)))
+             elog(ERROR, "aggregate with polymorphic return type requires " \
+                         "state function with both arguments polymorphic");
+     }

      /*
       * Everything looks okay.  Try to create the pg_proc entry for the
Index: src/backend/commands/aggregatecmds.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/aggregatecmds.c,v
retrieving revision 1.5
diff -c -r1.5 aggregatecmds.c
*** src/backend/commands/aggregatecmds.c    4 Sep 2002 20:31:14 -0000    1.5
--- src/backend/commands/aggregatecmds.c    1 Jun 2003 15:41:15 -0000
***************
*** 119,125 ****
          baseTypeId = typenameTypeId(baseType);

      transTypeId = typenameTypeId(transType);
!     if (get_typtype(transTypeId) == 'p')
          elog(ERROR, "Aggregate transition datatype cannot be %s",
               format_type_be(transTypeId));

--- 119,127 ----
          baseTypeId = typenameTypeId(baseType);

      transTypeId = typenameTypeId(transType);
!     if (get_typtype(transTypeId) == 'p' &&
!         transTypeId != ANYARRAYOID &&
!         transTypeId != ANYELEMENTOID)
          elog(ERROR, "Aggregate transition datatype cannot be %s",
               format_type_be(transTypeId));

Index: src/backend/commands/analyze.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/analyze.c,v
retrieving revision 1.54
diff -c -r1.54 analyze.c
*** src/backend/commands/analyze.c    27 May 2003 17:49:45 -0000    1.54
--- src/backend/commands/analyze.c    2 Jun 2003 01:06:40 -0000
***************
*** 392,397 ****
--- 392,398 ----
      Oid            eqopr = InvalidOid;
      Oid            eqfunc = InvalidOid;
      Oid            ltopr = InvalidOid;
+     Oid            elem_type = get_element_type(attr->atttypid);
      VacAttrStats *stats;

      /* Don't analyze dropped columns */
***************
*** 413,418 ****
--- 414,433 ----
              eqfunc = oprfuncid(func_operator);
          }
          ReleaseSysCache(func_operator);
+
+         /*
+          * If we have an array type, we also need a suitable
+          * element equality operator. Check to be sure it exists.
+          */
+         if (elem_type != InvalidOid)
+         {
+             Operator    elem_func_operator;
+
+             elem_func_operator = equality_oper(elem_type, true);
+             if (elem_func_operator != NULL)
+                 eqfunc = InvalidOid;
+             ReleaseSysCache(elem_func_operator);
+         }
      }
      if (!OidIsValid(eqfunc))
          return NULL;
***************
*** 448,453 ****
--- 463,482 ----
          if (oprrest == F_SCALARLTSEL)
              ltopr = oprid(func_operator);
          ReleaseSysCache(func_operator);
+
+         /*
+          * If we have an array type, we also need a suitable
+          * element ordering operator. Check to be sure it exists.
+          */
+         if (elem_type != InvalidOid)
+         {
+             Operator    elem_func_operator;
+
+             elem_func_operator = ordering_oper(elem_type, true);
+             if (elem_func_operator != NULL)
+                 ltopr = InvalidOid;
+             ReleaseSysCache(elem_func_operator);
+         }
      }
      stats->ltopr = ltopr;

Index: src/backend/executor/nodeAgg.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeAgg.c,v
retrieving revision 1.105
diff -c -r1.105 nodeAgg.c
*** src/backend/executor/nodeAgg.c    30 May 2003 20:23:10 -0000    1.105
--- src/backend/executor/nodeAgg.c    1 Jun 2003 15:41:15 -0000
***************
*** 58,63 ****
--- 58,64 ----
  #include "executor/executor.h"
  #include "executor/nodeAgg.h"
  #include "miscadmin.h"
+ #include "nodes/makefuncs.h"
  #include "optimizer/clauses.h"
  #include "parser/parse_coerce.h"
  #include "parser/parse_expr.h"
***************
*** 212,218 ****
  static void agg_fill_hash_table(AggState *aggstate);
  static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
  static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
!

  /*
   * Initialize all aggregates for a new group of input values.
--- 213,219 ----
  static void agg_fill_hash_table(AggState *aggstate);
  static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
  static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
! static Oid resolve_type(Oid type_to_resolve, Oid context_type);

  /*
   * Initialize all aggregates for a new group of input values.
***************
*** 351,364 ****
      fcinfo.context = NULL;
      fcinfo.resultinfo = NULL;
      fcinfo.isnull = false;
-
      fcinfo.flinfo = &peraggstate->transfn;
      fcinfo.nargs = 2;
      fcinfo.arg[0] = pergroupstate->transValue;
      fcinfo.argnull[0] = pergroupstate->transValueIsNull;
      fcinfo.arg[1] = newVal;
      fcinfo.argnull[1] = isNull;
-
      newVal = FunctionCallInvoke(&fcinfo);

      /*
--- 352,363 ----
***************
*** 1176,1182 ****
--- 1175,1195 ----
          AclResult    aclresult;
          Oid            transfn_oid,
                      finalfn_oid;
+         FuncExpr   *transfnexpr,
+                    *finalfnexpr;
          Datum        textInitVal;
+         List       *fargs;
+         Oid            agg_rt_type;
+         Oid           *transfn_arg_types;
+         List       *transfn_args = NIL;
+         int            transfn_nargs;
+         Oid            transfn_ret_type;
+         Oid           *finalfn_arg_types = NULL;
+         List       *finalfn_args = NIL;
+         Oid            finalfn_ret_type = InvalidOid;
+         int            finalfn_nargs = 0;
+         Node       *arg0;
+         Node       *arg1;
          int            i;

          /* Look for a previous duplicate aggregate */
***************
*** 1224,1229 ****
--- 1237,1402 ----
                          &peraggstate->transtypeLen,
                          &peraggstate->transtypeByVal);

+         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
+         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
+
+         /* get the runtime aggregate argument type */
+         fargs = aggref->args;
+         agg_rt_type = exprType((Node *) nth(0, fargs));
+
+         /* get the transition function argument and return types */
+         transfn_ret_type = get_func_rettype(transfn_oid);
+         transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs);
+
+         /* resolve any polymorphic types */
+         if (transfn_nargs == 2)
+         /* base type was not ANY */
+         {
+             if (transfn_arg_types[1] == ANYARRAYOID ||
+                 transfn_arg_types[1] == ANYELEMENTOID)
+                 transfn_arg_types[1] = agg_rt_type;
+
+             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+                                                         agg_rt_type);
+
+             /*
+              * Build arg list to use on the transfn FuncExpr node. We really
+              * only care that the node type is correct so that the transfn
+              * can discover the actual argument types at runtime using
+              * get_fn_expr_argtype()
+              */
+             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
+                                                             -1, COERCE_DONTCARE);
+             arg1 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[1],
+                                                             -1, COERCE_DONTCARE);
+             transfn_args = makeList2(arg0, arg1);
+
+             /*
+              * the state transition function always returns the same type
+              * as its first argument
+              */
+             if (transfn_ret_type == ANYARRAYOID ||
+                 transfn_ret_type == ANYELEMENTOID)
+                 transfn_ret_type = transfn_arg_types[0];
+         }
+         else if (transfn_nargs == 1)
+         /*
+          * base type was ANY, therefore the aggregate return type should
+          * be non-polymorphic
+          */
+         {
+             Oid    finaltype = get_func_rettype(aggref->aggfnoid);
+
+             /*
+              * this should have been prevented in AggregateCreate,
+              * but check anyway
+              */
+             if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
+                 elog(ERROR, "aggregate with base type ANY must have a " \
+                             "non-polymorphic return type");
+
+             /* see if we have a final function */
+             if (OidIsValid(finalfn_oid))
+             {
+                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
+                 if (finalfn_nargs != 1)
+                     elog(ERROR, "final function takes unexpected number " \
+                                 "of arguments: %d", finalfn_nargs);
+
+                 /*
+                  * final function argument is always the same as the state
+                  * function return type
+                  */
+                 if (finalfn_arg_types[0] != ANYARRAYOID &&
+                     finalfn_arg_types[0] != ANYELEMENTOID)
+                 {
+                     /* if it is not ambiguous, use it */
+                     transfn_ret_type = finalfn_arg_types[0];
+                 }
+                 else
+                 {
+                     /* if it is ambiguous, try to derive it */
+                     finalfn_ret_type = finaltype;
+                     finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0],
+                                                             finalfn_ret_type);
+                     transfn_ret_type = finalfn_arg_types[0];
+                 }
+             }
+             else
+                 transfn_ret_type = finaltype;
+
+             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+                                                         transfn_ret_type);
+
+             /*
+              * Build arg list to use on the transfn FuncExpr node. We really
+              * only care that the node type is correct so that the transfn
+              * can discover the actual argument types at runtime using
+              * get_fn_expr_argtype()
+              */
+             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
+                                                             -1, COERCE_DONTCARE);
+             transfn_args = makeList1(arg0);
+         }
+         else
+             elog(ERROR, "state transition function takes unexpected number " \
+                         "of arguments: %d", transfn_nargs);
+
+         if (OidIsValid(finalfn_oid))
+         {
+             /* get the final function argument and return types */
+             if (finalfn_ret_type == InvalidOid)
+                 finalfn_ret_type = get_func_rettype(finalfn_oid);
+
+             if (!finalfn_arg_types)
+             {
+                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
+                 if (finalfn_nargs != 1)
+                     elog(ERROR, "final function takes unexpected number " \
+                                 "of arguments: %d", finalfn_nargs);
+             }
+
+             /*
+              * final function argument is always the same as the state
+              * function return type, which by now should have been resolved
+              */
+             if (finalfn_arg_types[0] == ANYARRAYOID ||
+                 finalfn_arg_types[0] == ANYELEMENTOID)
+                 finalfn_arg_types[0] = transfn_ret_type;
+
+             /*
+              * Build arg list to use on the finalfn FuncExpr node. We really
+              * only care that the node type is correct so that the finalfn
+              * can discover the actual argument type at runtime using
+              * get_fn_expr_argtype()
+              */
+             arg0 = (Node *) makeRelabelType((Expr *) NULL, finalfn_arg_types[0],
+                                                             -1, COERCE_DONTCARE);
+             finalfn_args = makeList1(arg0);
+
+             finalfn_ret_type = resolve_type(finalfn_ret_type,
+                                                 finalfn_arg_types[0]);
+         }
+
+         fmgr_info(transfn_oid, &peraggstate->transfn);
+         transfnexpr = (FuncExpr *) make_funcclause(transfn_oid,
+                           transfn_ret_type,
+                           false,            /* cannot be a set */
+                           COERCE_DONTCARE,    /* to match any user expr */
+                           transfn_args);
+         peraggstate->transfn.fn_expr = (Node *) transfnexpr;
+
+         if (OidIsValid(finalfn_oid))
+         {
+             fmgr_info(finalfn_oid, &peraggstate->finalfn);
+             finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid,
+                           finalfn_ret_type,
+                           false,            /* cannot be a set */
+                           COERCE_DONTCARE,    /* to match any user expr */
+                           finalfn_args);
+             peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
+         }
+
          /*
           * initval is potentially null, so don't try to access it as a
           * struct field. Must do it the hard way with SysCacheGetAttr.
***************
*** 1236,1249 ****
              peraggstate->initValue = (Datum) 0;
          else
              peraggstate->initValue = GetAggInitVal(textInitVal,
!                                                    aggform->aggtranstype);
!
!         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
!         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
!
!         fmgr_info(transfn_oid, &peraggstate->transfn);
!         if (OidIsValid(finalfn_oid))
!             fmgr_info(finalfn_oid, &peraggstate->finalfn);

          /*
           * If the transfn is strict and the initval is NULL, make sure
--- 1409,1415 ----
              peraggstate->initValue = (Datum) 0;
          else
              peraggstate->initValue = GetAggInitVal(textInitVal,
!                                                    transfn_arg_types[0]);

          /*
           * If the transfn is strict and the initval is NULL, make sure
***************
*** 1454,1457 ****
--- 1620,1656 ----
      elog(ERROR, "Aggregate function %u called as normal function",
           fcinfo->flinfo->fn_oid);
      return (Datum) 0;            /* keep compiler quiet */
+ }
+
+ static Oid
+ resolve_type(Oid type_to_resolve, Oid context_type)
+ {
+     Oid        resolved_type;
+
+     if (context_type == ANYARRAYOID || context_type == ANYELEMENTOID)
+         resolved_type = type_to_resolve;
+     else if (type_to_resolve == ANYARRAYOID)
+     /* any array */
+     {
+         Oid        context_type_arraytype = get_array_type(context_type);
+
+         if (context_type_arraytype != InvalidOid)
+             resolved_type = context_type_arraytype;
+         else
+             resolved_type = context_type;
+     }
+     else if (type_to_resolve == ANYELEMENTOID)
+     /* any element */
+     {
+         Oid        context_type_elemtype = get_element_type(context_type);
+
+         if (context_type_elemtype != InvalidOid)
+             resolved_type = context_type_elemtype;
+         else
+             resolved_type = context_type;
+     }
+     else
+         resolved_type = type_to_resolve;
+
+     return resolved_type;
  }
Index: src/backend/executor/nodeSubplan.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v
retrieving revision 1.45
diff -c -r1.45 nodeSubplan.c
*** src/backend/executor/nodeSubplan.c    8 Apr 2003 23:20:01 -0000    1.45
--- src/backend/executor/nodeSubplan.c    1 Jun 2003 15:41:47 -0000
***************
*** 28,50 ****
  #include "utils/datum.h"
  #include "utils/lsyscache.h"

-
- typedef struct ArrayBuildState
- {
-     MemoryContext mcontext;        /* where all the temp stuff is kept */
-     Datum       *dvalues;        /* array of accumulated Datums */
-     /*
-      * The allocated size of dvalues[] is always a multiple of
-      * ARRAY_ELEMS_CHUNKSIZE
-      */
- #define ARRAY_ELEMS_CHUNKSIZE    64
-     int            nelems;            /* number of valid Datums in dvalues[] */
-     Oid            element_type;    /* data type of the Datums */
-     int16        typlen;            /* needed info about datatype */
-     bool        typbyval;
-     char        typalign;
- } ArrayBuildState;
-
  static Datum ExecHashSubPlan(SubPlanState *node,
                               ExprContext *econtext,
                               bool *isNull);
--- 28,33 ----
***************
*** 54,66 ****
  static void buildSubPlanHash(SubPlanState *node);
  static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
  static bool tupleAllNulls(HeapTuple tuple);
- static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
-                                          Datum dvalue, bool disnull,
-                                          Oid element_type,
-                                          MemoryContext rcontext);
- static Datum makeArrayResult(ArrayBuildState *astate,
-                              MemoryContext rcontext);
-

  /* ----------------------------------------------------------------
   *        ExecSubPlan
--- 37,42 ----
***************
*** 224,229 ****
--- 200,206 ----
      PlanState  *planstate = node->planstate;
      SubLinkType subLinkType = subplan->subLinkType;
      bool        useOr = subplan->useOr;
+     bool        isExpr = subplan->isExpr;
      MemoryContext oldcontext;
      TupleTableSlot *slot;
      Datum        result;
***************
*** 292,297 ****
--- 269,279 ----
          bool        rownull = false;
          int            col = 1;
          List       *plst;
+         int            numelems;
+         int            elemnum;
+         Datum        dvalue;
+         Datum       *dvalues = NULL;
+         bool        disnull;

          if (subLinkType == EXISTS_SUBLINK)
          {
***************
*** 329,337 ****

          if (subLinkType == ARRAY_SUBLINK)
          {
-             Datum    dvalue;
-             bool    disnull;
-
              found = true;
              /* stash away current value */
              dvalue = heap_getattr(tup, 1, tdesc, &disnull);
--- 311,316 ----
***************
*** 349,446 ****
          found = true;

          /*
!          * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
!          * operators for columns of tuple.
           */
!         plst = subplan->paramIds;
!         foreach(lst, node->exprs)
          {
!             ExprState  *exprstate = (ExprState *) lfirst(lst);
!             int            paramid = lfirsti(plst);
!             ParamExecData *prmdata;
!             Datum        expresult;
!             bool        expnull;
!
!             /*
!              * Load up the Param representing this column of the sub-select.
!              */
!             prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
!             Assert(prmdata->execPlan == NULL);
!             prmdata->value = heap_getattr(tup, col, tdesc,
!                                           &(prmdata->isnull));

!             /*
!              * Now we can eval the combining operator for this column.
!              */
!             expresult = ExecEvalExprSwitchContext(exprstate, econtext,
!                                                   &expnull, NULL);
!
!             /*
!              * Combine the result into the row result as appropriate.
!              */
!             if (col == 1)
              {
!                 rowresult = expresult;
!                 rownull = expnull;
              }
!             else if (useOr)
              {
!                 /* combine within row per OR semantics */
!                 if (expnull)
!                     rownull = true;
!                 else if (DatumGetBool(expresult))
                  {
!                     rowresult = BoolGetDatum(true);
!                     rownull = false;
!                     break;        /* needn't look at any more columns */
                  }
              }
              else
              {
!                 /* combine within row per AND semantics */
!                 if (expnull)
!                     rownull = true;
!                 else if (!DatumGetBool(expresult))
!                 {
!                     rowresult = BoolGetDatum(false);
!                     rownull = false;
!                     break;        /* needn't look at any more columns */
!                 }
              }

-             plst = lnext(plst);
-             col++;
          }

!         if (subLinkType == ANY_SUBLINK)
          {
!             /* combine across rows per OR semantics */
!             if (rownull)
!                 *isNull = true;
!             else if (DatumGetBool(rowresult))
              {
!                 result = BoolGetDatum(true);
!                 *isNull = false;
!                 break;            /* needn't look at any more rows */
              }
!         }
!         else if (subLinkType == ALL_SUBLINK)
!         {
!             /* combine across rows per AND semantics */
!             if (rownull)
!                 *isNull = true;
!             else if (!DatumGetBool(rowresult))
              {
!                 result = BoolGetDatum(false);
!                 *isNull = false;
!                 break;            /* needn't look at any more rows */
              }
-         }
-         else
-         {
-             /* must be MULTIEXPR_SUBLINK */
-             result = rowresult;
-             *isNull = rownull;
          }
      }

--- 328,490 ----
          found = true;

          /*
!          * When isExpr is true, we have either a scalar expression or an
!          * array. In the former case, this is no different than the !isExpr
!          * case. In the latter case, iterate over the elements as if they
!          * were from multiple input tuples.
           */
!         if (!isExpr)
!             numelems = 1;
!         else
          {
!             Oid        expr_typeid = tdesc->attrs[0]->atttypid;

!             if (expr_typeid != subplan->exprtype)
              {
!                 subplan->exprtype = expr_typeid;
!                 subplan->elemtype = get_element_type(expr_typeid);
!
!                 if (subplan->elemtype != InvalidOid)
!                     get_typlenbyvalalign(subplan->elemtype,
!                                          &subplan->elmlen,
!                                          &subplan->elmbyval,
!                                          &subplan->elmalign);
              }
!
!             /* get current value */
!             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
!
!             /* XXX this will need work if/when arrays support NULL elements */
!             if (!disnull)
              {
!                 if (subplan->elemtype != InvalidOid)
!                 {
!                     ArrayType   *v = DatumGetArrayTypeP(dvalue);
!
!                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
!                                       subplan->elmbyval, subplan->elmalign,
!                                         &dvalues, &numelems);
!                 }
!                 else
                  {
!                     numelems = 1;
!                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                     dvalues[0] = dvalue;
                  }
              }
              else
              {
!                 numelems = 1;
!                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                 dvalues[0] = (Datum) 0;
              }

          }

!         for (elemnum = 0; elemnum < numelems; elemnum++)
          {
!             /*
!              * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
!              * operators for columns of tuple.
!              */
!             col = 1;
!             plst = subplan->paramIds;
!             foreach(lst, node->exprs)
              {
!                 ExprState  *exprstate = (ExprState *) lfirst(lst);
!                 int            paramid = lfirsti(plst);
!                 ParamExecData *prmdata;
!                 Datum        expresult;
!                 bool        expnull;
!
!                 /*
!                  * Load up the Param representing this column of the sub-select.
!                  */
!                 prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
!                 Assert(prmdata->execPlan == NULL);
!
!                 if (!isExpr)
!                     prmdata->value = heap_getattr(tup, col, tdesc,
!                                                   &(prmdata->isnull));
!                 else
!                 {
!                     prmdata->value = dvalues[elemnum];
!                     prmdata->isnull = disnull;
!                 }
!
!                 /*
!                  * Now we can eval the combining operator for this column.
!                  */
!                 expresult = ExecEvalExprSwitchContext(exprstate, econtext,
!                                                       &expnull, NULL);
!
!                 /*
!                  * Combine the result into the row result as appropriate.
!                  */
!                 if (col == 1)
!                 {
!                     rowresult = expresult;
!                     rownull = expnull;
!                 }
!                 else if (useOr)
!                 {
!                     /* combine within row per OR semantics */
!                     if (expnull)
!                         rownull = true;
!                     else if (DatumGetBool(expresult))
!                     {
!                         rowresult = BoolGetDatum(true);
!                         rownull = false;
!                         break;        /* needn't look at any more columns */
!                     }
!                 }
!                 else
!                 {
!                     /* combine within row per AND semantics */
!                     if (expnull)
!                         rownull = true;
!                     else if (!DatumGetBool(expresult))
!                     {
!                         rowresult = BoolGetDatum(false);
!                         rownull = false;
!                         break;        /* needn't look at any more columns */
!                     }
!                 }
!
!                 plst = lnext(plst);
!                 col++;
              }
!
!             if (subLinkType == ANY_SUBLINK)
              {
!                 /* combine across rows per OR semantics */
!                 if (rownull)
!                     *isNull = true;
!                 else if (DatumGetBool(rowresult))
!                 {
!                     result = BoolGetDatum(true);
!                     *isNull = false;
!                     break;            /* needn't look at any more rows */
!                 }
!             }
!             else if (subLinkType == ALL_SUBLINK)
!             {
!                 /* combine across rows per AND semantics */
!                 if (rownull)
!                     *isNull = true;
!                 else if (!DatumGetBool(rowresult))
!                 {
!                     result = BoolGetDatum(false);
!                     *isNull = false;
!                     break;            /* needn't look at any more rows */
!                 }
!             }
!             else
!             {
!                 /* must be MULTIEXPR_SUBLINK */
!                 result = rowresult;
!                 *isNull = rownull;
              }
          }
      }

***************
*** 478,483 ****
--- 522,528 ----
  buildSubPlanHash(SubPlanState *node)
  {
      SubPlan       *subplan = (SubPlan *) node->xprstate.expr;
+     bool        isExpr = subplan->isExpr;
      PlanState  *planstate = node->planstate;
      int            ncols = length(node->exprs);
      ExprContext *innerecontext = node->innerecontext;
***************
*** 485,490 ****
--- 530,536 ----
      MemoryContext oldcontext;
      int            nbuckets;
      TupleTableSlot *slot;
+     TupleTableSlot *arrslot = NULL;

      Assert(subplan->subLinkType == ANY_SUBLINK);
      Assert(!subplan->useOr);
***************
*** 562,604 ****
      {
          HeapTuple    tup = slot->val;
          TupleDesc    tdesc = slot->ttc_tupleDescriptor;
!         int            col = 1;
          List       *plst;
          bool        isnew;

          /*
!          * Load up the Params representing the raw sub-select outputs,
!          * then form the projection tuple to store in the hashtable.
           */
!         foreach(plst, subplan->paramIds)
          {
!             int            paramid = lfirsti(plst);
!             ParamExecData *prmdata;

!             prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
!             Assert(prmdata->execPlan == NULL);
!             prmdata->value = heap_getattr(tup, col, tdesc,
!                                           &(prmdata->isnull));
!             col++;
!         }
!         slot = ExecProject(node->projRight, NULL);
!         tup = slot->val;

-         /*
-          * If result contains any nulls, store separately or not at all.
-          * (Since we know the projection tuple has no junk columns, we
-          * can just look at the overall hasnull info bit, instead of
-          * groveling through the columns.)
-          */
-         if (HeapTupleNoNulls(tup))
-         {
-             (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
-             node->havehashrows = true;
          }
!         else if (node->hashnulls)
          {
!             (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
!             node->havenullrows = true;
          }

          /*
--- 608,746 ----
      {
          HeapTuple    tup = slot->val;
          TupleDesc    tdesc = slot->ttc_tupleDescriptor;
!         TupleDesc    arrtdesc = NULL;
          List       *plst;
          bool        isnew;
+         int            numelems;
+         int            elemnum;
+         Datum        dvalue;
+         Datum       *dvalues = NULL;
+         bool        disnull;

          /*
!          * When isExpr is true, we have either a scalar expression or an
!          * array. In the former case, this is no different than the !isExpr
!          * case. In the latter case, iterate over the elements as if they
!          * were from multiple input tuples.
           */
!         if (!isExpr)
!             numelems = 1;
!         else
          {
!             Oid        expr_typeid = tdesc->attrs[0]->atttypid;

!             if (expr_typeid != subplan->exprtype)
!             {
!                 subplan->exprtype = expr_typeid;
!                 subplan->elemtype = get_element_type(expr_typeid);
!
!                 if (subplan->elemtype != InvalidOid)
!                     get_typlenbyvalalign(subplan->elemtype,
!                                          &subplan->elmlen,
!                                          &subplan->elmbyval,
!                                          &subplan->elmalign);
!             }
!
!             /* get current value */
!             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
!
!             if (subplan->elemtype != InvalidOid)
!             {
!                 TupleTable    tupleTable;
!                 ArrayType   *v = DatumGetArrayTypeP(dvalue);
!
!                 arrtdesc = CreateTemplateTupleDesc(1, false);
!                 TupleDescInitEntry(arrtdesc, 1, "elem", subplan->elemtype,
!                                                             -1, 0, false);
!
!                 tupleTable = ExecCreateTupleTable(1);
!                 arrslot = ExecAllocTableSlot(tupleTable);
!                 ExecSetSlotDescriptor(arrslot, arrtdesc, true);
!
!                 /* XXX this will need work if/when arrays support NULL elements */
!                 if (!disnull)
!                 {
!                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
!                                       subplan->elmbyval, subplan->elmalign,
!                                         &dvalues, &numelems);
!                 }
!                 else
!                 {
!                     numelems = 1;
!                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                     dvalues[0] = (Datum) 0;
!                 }
!             }
!             else
!             {
!                 numelems = 1;
!                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                 dvalues[0] = dvalue;
!             }

          }
!
!         for (elemnum = 0; elemnum < numelems; elemnum++)
          {
!             int    col = 1;
!
!             if (!isExpr || subplan->elemtype == InvalidOid)
!             {
!                 /*
!                  * Load up the Params representing the raw sub-select outputs,
!                  * then form the projection tuple to store in the hashtable.
!                  */
!                 foreach(plst, subplan->paramIds)
!                 {
!                     int            paramid = lfirsti(plst);
!                     ParamExecData *prmdata;
!
!                     prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
!                     Assert(prmdata->execPlan == NULL);
!
!                     prmdata->value = heap_getattr(tup, col, tdesc,
!                                                   &(prmdata->isnull));
!
!                     col++;
!                 }
!                 slot = ExecProject(node->projRight, NULL);
!                 tup = slot->val;
!             }
!             else
!             {
!                 /*
!                  * For array type expressions, we need to build up our own
!                  * tuple and slot
!                  */
!                 char        nullflag;
!
!                 nullflag = disnull ? 'n' : ' ';
!                 tup = heap_formtuple(arrtdesc, &dvalues[elemnum], &nullflag);
!                 arrslot = ExecStoreTuple(tup, arrslot, InvalidBuffer, true);
!             }
!
!             /*
!              * If result contains any nulls, store separately or not at all.
!              * (Since we know the projection tuple has no junk columns, we
!              * can just look at the overall hasnull info bit, instead of
!              * groveling through the columns.)
!              */
!             if (HeapTupleNoNulls(tup))
!             {
!                 if (!isExpr)
!                     (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
!                 else
!                     (void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew);
!                 node->havehashrows = true;
!             }
!             else if (node->hashnulls)
!             {
!                 if (!isExpr)
!                     (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
!                 else
!                     (void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew);
!                 node->havenullrows = true;
!             }
          }

          /*
***************
*** 615,620 ****
--- 757,764 ----
       * have the potential for a double free attempt.
       */
      ExecClearTuple(node->projRight->pi_slot);
+     if (arrslot)
+         ExecClearTuple(arrslot);

      MemoryContextSwitchTo(oldcontext);
  }
***************
*** 1084,1185 ****
          prm->execPlan = node;
          parent->chgParam = bms_add_member(parent->chgParam, paramid);
      }
- }
-
- /*
-  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
-  *
-  *    astate is working state (NULL on first call)
-  *    rcontext is where to keep working state
-  */
- static ArrayBuildState *
- accumArrayResult(ArrayBuildState *astate,
-                  Datum dvalue, bool disnull,
-                  Oid element_type,
-                  MemoryContext rcontext)
- {
-     MemoryContext arr_context,
-                   oldcontext;
-
-     if (astate == NULL)
-     {
-         /* First time through --- initialize */
-
-         /* Make a temporary context to hold all the junk */
-         arr_context = AllocSetContextCreate(rcontext,
-                                             "ARRAY_SUBLINK Result",
-                                             ALLOCSET_DEFAULT_MINSIZE,
-                                             ALLOCSET_DEFAULT_INITSIZE,
-                                             ALLOCSET_DEFAULT_MAXSIZE);
-         oldcontext = MemoryContextSwitchTo(arr_context);
-         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
-         astate->mcontext = arr_context;
-         astate->dvalues = (Datum *)
-             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
-         astate->nelems = 0;
-         astate->element_type = element_type;
-         get_typlenbyvalalign(element_type,
-                              &astate->typlen,
-                              &astate->typbyval,
-                              &astate->typalign);
-     }
-     else
-     {
-         oldcontext = MemoryContextSwitchTo(astate->mcontext);
-         Assert(astate->element_type == element_type);
-         /* enlarge dvalues[] if needed */
-         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
-             astate->dvalues = (Datum *)
-                 repalloc(astate->dvalues,
-                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
-     }
-
-     if (disnull)
-         elog(ERROR, "NULL elements not allowed in Arrays");
-
-     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
-     astate->dvalues[astate->nelems++] =
-         datumCopy(dvalue, astate->typbyval, astate->typlen);
-
-     MemoryContextSwitchTo(oldcontext);
-
-     return astate;
- }
-
- /*
-  * makeArrayResult - produce final result of ARRAY_SUBLINK
-  *
-  *    astate is working state (not NULL)
-  *    rcontext is where to construct result
-  */
- static Datum
- makeArrayResult(ArrayBuildState *astate,
-                 MemoryContext rcontext)
- {
-     ArrayType  *result;
-     int            dims[1];
-     int            lbs[1];
-     MemoryContext oldcontext;
-
-     /* Build the final array result in rcontext */
-     oldcontext = MemoryContextSwitchTo(rcontext);
-
-     dims[0] = astate->nelems;
-     lbs[0] = 1;
-
-     result = construct_md_array(astate->dvalues,
-                                 1,
-                                 dims,
-                                 lbs,
-                                 astate->element_type,
-                                 astate->typlen,
-                                 astate->typbyval,
-                                 astate->typalign);
-
-     MemoryContextSwitchTo(oldcontext);
-
-     /* Clean up all the junk */
-     MemoryContextDelete(astate->mcontext);
-
-     return PointerGetDatum(result);
  }
--- 1228,1231 ----
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.251
diff -c -r1.251 copyfuncs.c
*** src/backend/nodes/copyfuncs.c    28 May 2003 16:03:56 -0000    1.251
--- src/backend/nodes/copyfuncs.c    1 Jun 2003 15:41:47 -0000
***************
*** 727,732 ****
--- 727,733 ----
      COPY_NODE_FIELD(target);
      COPY_SCALAR_FIELD(aggstar);
      COPY_SCALAR_FIELD(aggdistinct);
+     COPY_NODE_FIELD(args);

      return newnode;
  }
***************
*** 825,830 ****
--- 826,832 ----

      COPY_SCALAR_FIELD(subLinkType);
      COPY_SCALAR_FIELD(useOr);
+     COPY_SCALAR_FIELD(isExpr);
      COPY_NODE_FIELD(lefthand);
      COPY_NODE_FIELD(operName);
      COPY_OIDLIST_FIELD(operOids);
***************
*** 843,848 ****
--- 845,856 ----

      COPY_SCALAR_FIELD(subLinkType);
      COPY_SCALAR_FIELD(useOr);
+     COPY_SCALAR_FIELD(isExpr);
+     COPY_SCALAR_FIELD(exprtype);
+     COPY_SCALAR_FIELD(elemtype);
+     COPY_SCALAR_FIELD(elmlen);
+     COPY_SCALAR_FIELD(elmbyval);
+     COPY_SCALAR_FIELD(elmalign);
      COPY_NODE_FIELD(exprs);
      COPY_INTLIST_FIELD(paramIds);
      COPY_NODE_FIELD(plan);
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.194
diff -c -r1.194 equalfuncs.c
*** src/backend/nodes/equalfuncs.c    28 May 2003 16:03:56 -0000    1.194
--- src/backend/nodes/equalfuncs.c    1 Jun 2003 15:41:47 -0000
***************
*** 204,209 ****
--- 204,210 ----
      COMPARE_NODE_FIELD(target);
      COMPARE_SCALAR_FIELD(aggstar);
      COMPARE_SCALAR_FIELD(aggdistinct);
+     COMPARE_NODE_FIELD(args);

      return true;
  }
***************
*** 300,305 ****
--- 301,307 ----
  {
      COMPARE_SCALAR_FIELD(subLinkType);
      COMPARE_SCALAR_FIELD(useOr);
+     COMPARE_SCALAR_FIELD(isExpr);
      COMPARE_NODE_FIELD(lefthand);
      COMPARE_NODE_FIELD(operName);
      COMPARE_OIDLIST_FIELD(operOids);
***************
*** 313,318 ****
--- 315,326 ----
  {
      COMPARE_SCALAR_FIELD(subLinkType);
      COMPARE_SCALAR_FIELD(useOr);
+     COMPARE_SCALAR_FIELD(isExpr);
+     COMPARE_SCALAR_FIELD(exprtype);
+     COMPARE_SCALAR_FIELD(elemtype);
+     COMPARE_SCALAR_FIELD(elmlen);
+     COMPARE_SCALAR_FIELD(elmbyval);
+     COMPARE_SCALAR_FIELD(elmalign);
      COMPARE_NODE_FIELD(exprs);
      COMPARE_INTLIST_FIELD(paramIds);
      /* should compare plans, but have to settle for comparing plan IDs */
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v
retrieving revision 1.206
diff -c -r1.206 outfuncs.c
*** src/backend/nodes/outfuncs.c    28 May 2003 16:03:56 -0000    1.206
--- src/backend/nodes/outfuncs.c    1 Jun 2003 15:41:47 -0000
***************
*** 615,620 ****
--- 615,621 ----
      WRITE_NODE_FIELD(target);
      WRITE_BOOL_FIELD(aggstar);
      WRITE_BOOL_FIELD(aggdistinct);
+     WRITE_NODE_FIELD(args);
  }

  static void
***************
*** 700,705 ****
--- 701,707 ----

      WRITE_ENUM_FIELD(subLinkType, SubLinkType);
      WRITE_BOOL_FIELD(useOr);
+     WRITE_BOOL_FIELD(isExpr);
      WRITE_NODE_FIELD(lefthand);
      WRITE_NODE_FIELD(operName);
      WRITE_OIDLIST_FIELD(operOids);
***************
*** 713,718 ****
--- 715,726 ----

      WRITE_ENUM_FIELD(subLinkType, SubLinkType);
      WRITE_BOOL_FIELD(useOr);
+     WRITE_BOOL_FIELD(isExpr);
+     WRITE_OID_FIELD(exprtype);
+     WRITE_OID_FIELD(elemtype);
+     WRITE_INT_FIELD(elmlen);
+     WRITE_BOOL_FIELD(elmbyval);
+     WRITE_CHAR_FIELD(elmalign);
      WRITE_NODE_FIELD(exprs);
      WRITE_INTLIST_FIELD(paramIds);
      WRITE_NODE_FIELD(plan);
Index: src/backend/nodes/readfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v
retrieving revision 1.153
diff -c -r1.153 readfuncs.c
*** src/backend/nodes/readfuncs.c    6 May 2003 00:20:32 -0000    1.153
--- src/backend/nodes/readfuncs.c    1 Jun 2003 15:41:47 -0000
***************
*** 415,420 ****
--- 415,421 ----
      READ_NODE_FIELD(target);
      READ_BOOL_FIELD(aggstar);
      READ_BOOL_FIELD(aggdistinct);
+     READ_NODE_FIELD(args);

      READ_DONE();
  }
***************
*** 544,549 ****
--- 545,551 ----

      READ_ENUM_FIELD(subLinkType, SubLinkType);
      READ_BOOL_FIELD(useOr);
+     READ_BOOL_FIELD(isExpr);
      READ_NODE_FIELD(lefthand);
      READ_NODE_FIELD(operName);
      READ_OIDLIST_FIELD(operOids);
Index: src/backend/optimizer/plan/subselect.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/plan/subselect.c,v
retrieving revision 1.75
diff -c -r1.75 subselect.c
*** src/backend/optimizer/plan/subselect.c    29 Apr 2003 22:13:09 -0000    1.75
--- src/backend/optimizer/plan/subselect.c    1 Jun 2003 15:41:47 -0000
***************
*** 67,73 ****

  static List *convert_sublink_opers(List *lefthand, List *operOids,
                                     List *targetlist, int rtindex,
!                                    List **righthandIds);
  static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
  static Node *replace_correlation_vars_mutator(Node *node, void *context);
  static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
--- 67,73 ----

  static List *convert_sublink_opers(List *lefthand, List *operOids,
                                     List *targetlist, int rtindex,
!                                    bool isExpr, List **righthandIds);
  static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
  static Node *replace_correlation_vars_mutator(Node *node, void *context);
  static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
***************
*** 240,245 ****
--- 240,251 ----
       */
      node->subLinkType = slink->subLinkType;
      node->useOr = slink->useOr;
+     node->isExpr = slink->isExpr;
+     node->exprtype = InvalidOid;
+     node->elemtype = InvalidOid;
+     node->elmlen = 0;
+     node->elmbyval = false;
+     node->elmalign = '\0';
      node->exprs = NIL;
      node->paramIds = NIL;
      node->useHashTable = false;
***************
*** 316,322 ****
          exprs = convert_sublink_opers(lefthand,
                                        slink->operOids,
                                        plan->targetlist,
!                                       0,
                                        &node->paramIds);
          node->setParam = listCopy(node->paramIds);
          PlannerInitPlan = lappend(PlannerInitPlan, node);
--- 322,328 ----
          exprs = convert_sublink_opers(lefthand,
                                        slink->operOids,
                                        plan->targetlist,
!                                       0, node->isExpr,
                                        &node->paramIds);
          node->setParam = listCopy(node->paramIds);
          PlannerInitPlan = lappend(PlannerInitPlan, node);
***************
*** 399,405 ****
          node->exprs = convert_sublink_opers(lefthand,
                                              slink->operOids,
                                              plan->targetlist,
!                                             0,
                                              &node->paramIds);

          /*
--- 405,411 ----
          node->exprs = convert_sublink_opers(lefthand,
                                              slink->operOids,
                                              plan->targetlist,
!                                             0, node->isExpr,
                                              &node->paramIds);

          /*
***************
*** 444,450 ****
  static List *
  convert_sublink_opers(List *lefthand, List *operOids,
                        List *targetlist, int rtindex,
!                       List **righthandIds)
  {
      List       *result = NIL;
      List       *lst;
--- 450,456 ----
  static List *
  convert_sublink_opers(List *lefthand, List *operOids,
                        List *targetlist, int rtindex,
!                       bool isExpr, List **righthandIds)
  {
      List       *result = NIL;
      List       *lst;
***************
*** 499,511 ****
           * are not expecting to have to resolve unknown Params, so
           * it's okay to pass a null pstate.)
           */
!         result = lappend(result,
!                          make_op_expr(NULL,
!                                       tup,
!                                       leftop,
!                                       rightop,
!                                       exprType(leftop),
!                                       te->resdom->restype));

          ReleaseSysCache(tup);

--- 505,542 ----
           * are not expecting to have to resolve unknown Params, so
           * it's okay to pass a null pstate.)
           */
!         if (!isExpr)
!         {
!             result = lappend(result,
!                              make_op_expr(NULL,
!                                           tup,
!                                           leftop,
!                                           rightop,
!                                           exprType(leftop),
!                                           te->resdom->restype));
!         }
!         else
!         {
!             Oid        exprtype = te->resdom->restype;
!             Oid        elemtype = get_element_type(exprtype);
!
!             if (elemtype != InvalidOid)
!                 result = lappend(result,
!                                  make_op_expr(NULL,
!                                               tup,
!                                               leftop,
!                                               rightop,
!                                               exprType(leftop),
!                                               elemtype));
!             else
!                 result = lappend(result,
!                                  make_op_expr(NULL,
!                                               tup,
!                                               leftop,
!                                               rightop,
!                                               exprType(leftop),
!                                               exprtype));
!         }

          ReleaseSysCache(tup);

***************
*** 616,628 ****
      /*
       * The sublink type must be "= ANY" --- that is, an IN operator.
       * (We require the operator name to be unqualified, which may be
!      * overly paranoid, or may not be.)
       */
      if (sublink->subLinkType != ANY_SUBLINK)
          return NULL;
      if (length(sublink->operName) != 1 ||
          strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
          return NULL;
      /*
       * The sub-select must not refer to any Vars of the parent query.
       * (Vars of higher levels should be okay, though.)
--- 647,663 ----
      /*
       * The sublink type must be "= ANY" --- that is, an IN operator.
       * (We require the operator name to be unqualified, which may be
!      * overly paranoid, or may not be.) It must not be an Expression
!      * sublink.
       */
      if (sublink->subLinkType != ANY_SUBLINK)
          return NULL;
      if (length(sublink->operName) != 1 ||
          strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
          return NULL;
+     if (sublink->isExpr)
+         return NULL;
+
      /*
       * The sub-select must not refer to any Vars of the parent query.
       * (Vars of higher levels should be okay, though.)
***************
*** 675,681 ****
      exprs = convert_sublink_opers(sublink->lefthand,
                                    sublink->operOids,
                                    subselect->targetList,
!                                   rtindex,
                                    &ininfo->sub_targetlist);
      return (Node *) make_ands_explicit(exprs);
  }
--- 710,716 ----
      exprs = convert_sublink_opers(sublink->lefthand,
                                    sublink->operOids,
                                    subselect->targetList,
!                                   rtindex, sublink->isExpr,
                                    &ininfo->sub_targetlist);
      return (Node *) make_ands_explicit(exprs);
  }
Index: src/backend/optimizer/util/clauses.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/util/clauses.c,v
retrieving revision 1.138
diff -c -r1.138 clauses.c
*** src/backend/optimizer/util/clauses.c    28 May 2003 22:32:49 -0000    1.138
--- src/backend/optimizer/util/clauses.c    1 Jun 2003 15:41:15 -0000
***************
*** 133,138 ****
--- 133,160 ----
  }

  /*****************************************************************************
+  *              FUNCTION clause functions
+  *****************************************************************************/
+
+ /*
+  * make_funcclause
+  *        Creates a function clause given its function info and argument list.
+  */
+ Expr *
+ make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
+                             CoercionForm funcformat, List *funcargs)
+ {
+     FuncExpr   *expr = makeNode(FuncExpr);
+
+     expr->funcid = funcid;
+     expr->funcresulttype = funcresulttype;
+     expr->funcretset = funcretset;
+     expr->funcformat = funcformat;
+     expr->args = funcargs;
+     return (Expr *) expr;
+ }
+
+ /*****************************************************************************
   *        NOT clause functions
   *****************************************************************************/

Index: src/backend/parser/gram.y
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/gram.y,v
retrieving revision 2.416
diff -c -r2.416 gram.y
*** src/backend/parser/gram.y    29 May 2003 20:40:36 -0000    2.416
--- src/backend/parser/gram.y    1 Jun 2003 15:41:47 -0000
***************
*** 5490,5495 ****
--- 5490,5496 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ANY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = makeList1(makeString("="));
                      n->subselect = $3;
***************
*** 5500,5505 ****
--- 5501,5507 ----
                      /* Make an IN node */
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ANY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = makeList1(makeString("="));
                      n->subselect = $4;
***************
*** 5511,5516 ****
--- 5513,5519 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = $3;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = $2;
                      n->subselect = $4;
***************
*** 5521,5526 ****
--- 5524,5530 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = MULTIEXPR_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = $2;
                      n->subselect = $3;
***************
*** 5904,5909 ****
--- 5908,5914 ----
                      {
                              SubLink *n = (SubLink *)$3;
                              n->subLinkType = ANY_SUBLINK;
+                             n->isExpr = false;
                              n->lefthand = makeList1($1);
                              n->operName = makeList1(makeString("="));
                              $$ = (Node *)n;
***************
*** 5931,5936 ****
--- 5936,5942 ----
                      {
                          /* Make an IN node */
                          SubLink *n = (SubLink *)$4;
+                         n->isExpr = false;
                          n->subLinkType = ANY_SUBLINK;
                          n->lefthand = makeList1($1);
                          n->operName = makeList1(makeString("="));
***************
*** 5957,5967 ****
--- 5963,6000 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = $3;
+                     n->isExpr = false;
                      n->lefthand = makeList1($1);
                      n->operName = $2;
                      n->subselect = $4;
                      $$ = (Node *)n;
                  }
+             | a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op
+                 {
+                     SubLink *n = makeNode(SubLink);
+                     SelectStmt *s = makeNode(SelectStmt);
+                     ResTarget *r = makeNode(ResTarget);
+
+                     r->name = NULL;
+                     r->indirection = NIL;
+                     r->val = (Node *)$5;
+
+                     s->distinctClause = NIL;
+                     s->targetList = makeList1(r);
+                     s->into = NULL;
+                     s->intoColNames = NIL;
+                     s->fromClause = NIL;
+                     s->whereClause = NULL;
+                     s->groupClause = NIL;
+                     s->havingClause = NULL;
+
+                     n->subLinkType = $3;
+                     n->isExpr = true;
+                     n->lefthand = makeList1($1);
+                     n->operName = $2;
+                     n->subselect = (Node *) s;
+                     $$ = (Node *)n;
+                 }
              | UNIQUE select_with_parens %prec Op
                  {
                      /* Not sure how to get rid of the parentheses
***************
*** 6538,6543 ****
--- 6571,6577 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = EXPR_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $1;
***************
*** 6547,6552 ****
--- 6581,6587 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = EXISTS_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $2;
***************
*** 6556,6561 ****
--- 6591,6597 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ARRAY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $2;
***************
*** 6730,6735 ****
--- 6766,6772 ----
  in_expr:    select_with_parens
                  {
                      SubLink *n = makeNode(SubLink);
+                     n->isExpr = false;
                      n->subselect = $1;
                      /* other fields will be filled later */
                      $$ = (Node *)n;
Index: src/backend/parser/parse_coerce.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v
retrieving revision 2.97
diff -c -r2.97 parse_coerce.c
*** src/backend/parser/parse_coerce.c    26 May 2003 00:11:27 -0000    2.97
--- src/backend/parser/parse_coerce.c    1 Jun 2003 16:15:40 -0000
***************
*** 859,865 ****
      /* Get the element type based on the array type, if we have one */
      if (OidIsValid(array_typeid))
      {
!         array_typelem = get_element_type(array_typeid);
          if (!OidIsValid(array_typelem))
              elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
                   format_type_be(array_typeid));
--- 859,869 ----
      /* Get the element type based on the array type, if we have one */
      if (OidIsValid(array_typeid))
      {
!         if (array_typeid != ANYARRAYOID)
!             array_typelem = get_element_type(array_typeid);
!         else
!             array_typelem = ANYELEMENTOID;
!
          if (!OidIsValid(array_typelem))
              elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
                   format_type_be(array_typeid));
***************
*** 919,925 ****
      {
          if (!OidIsValid(array_typeid))
          {
!             array_typeid = get_array_type(elem_typeid);
              if (!OidIsValid(array_typeid))
                  elog(ERROR, "Cannot find array type for datatype %s",
                       format_type_be(elem_typeid));
--- 923,933 ----
      {
          if (!OidIsValid(array_typeid))
          {
!             if (elem_typeid != ANYELEMENTOID)
!                 array_typeid = get_array_type(elem_typeid);
!             else
!                 array_typeid = ANYARRAYOID;
!
              if (!OidIsValid(array_typeid))
                  elog(ERROR, "Cannot find array type for datatype %s",
                       format_type_be(elem_typeid));
***************
*** 1169,1174 ****
--- 1177,1187 ----
      /* Somewhat-fast path for domain -> base type case */
      if (srctype == targettype)
          return true;
+
+     /* Last of the fast-paths: check for matching polymorphic arrays */
+     if (targettype == ANYARRAYOID)
+         if (get_element_type(srctype) != InvalidOid)
+             return true;

      /* Else look in pg_cast */
      tuple = SearchSysCache(CASTSOURCETARGET,
Index: src/backend/parser/parse_expr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_expr.c,v
retrieving revision 1.148
diff -c -r1.148 parse_expr.c
*** src/backend/parser/parse_expr.c    29 Apr 2003 22:13:10 -0000    1.148
--- src/backend/parser/parse_expr.c    1 Jun 2003 15:41:47 -0000
***************
*** 436,441 ****
--- 436,442 ----
                      sublink->operName = NIL;
                      sublink->operOids = NIL;
                      sublink->useOr = FALSE;
+                     sublink->isExpr = FALSE;
                  }
                  else if (sublink->subLinkType == EXPR_SUBLINK ||
                           sublink->subLinkType == ARRAY_SUBLINK)
***************
*** 463,468 ****
--- 464,470 ----
                      sublink->operName = NIL;
                      sublink->operOids = NIL;
                      sublink->useOr = FALSE;
+                     sublink->isExpr = FALSE;
                  }
                  else
                  {
***************
*** 538,547 ****
                           * here, because make_subplan() will insert type
                           * coercion calls if needed.
                           */
!                         optup = oper(op,
!                                      exprType(lexpr),
!                                      exprType((Node *) tent->expr),
!                                      false);
                          opform = (Form_pg_operator) GETSTRUCT(optup);

                          if (opform->oprresult != BOOLOID)
--- 540,569 ----
                           * here, because make_subplan() will insert type
                           * coercion calls if needed.
                           */
!                         if (!sublink->isExpr)
!                         {
!                             optup = oper(op,
!                                          exprType(lexpr),
!                                          exprType((Node *) tent->expr),
!                                          false);
!                         }
!                         else
!                         {
!                             Oid        exprtype = exprType((Node *) tent->expr);
!                             Oid        elemtype = get_element_type(exprtype);
!
!                             if (elemtype != InvalidOid)
!                                 optup = oper(op,
!                                              exprType(lexpr),
!                                              elemtype,
!                                              false);
!                             else
!                                 optup = oper(op,
!                                              exprType(lexpr),
!                                              exprtype,
!                                              false);
!                         }
!
                          opform = (Form_pg_operator) GETSTRUCT(optup);

                          if (opform->oprresult != BOOLOID)
Index: src/backend/parser/parse_func.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_func.c,v
retrieving revision 1.148
diff -c -r1.148 parse_func.c
*** src/backend/parser/parse_func.c    26 May 2003 00:11:27 -0000    1.148
--- src/backend/parser/parse_func.c    1 Jun 2003 15:41:15 -0000
***************
*** 335,340 ****
--- 335,341 ----
          aggref->target = lfirst(fargs);
          aggref->aggstar = agg_star;
          aggref->aggdistinct = agg_distinct;
+         aggref->args = fargs;

          retval = (Node *) aggref;

Index: src/backend/parser/parse_oper.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_oper.c,v
retrieving revision 1.64
diff -c -r1.64 parse_oper.c
*** src/backend/parser/parse_oper.c    26 May 2003 00:11:27 -0000    1.64
--- src/backend/parser/parse_oper.c    1 Jun 2003 23:21:10 -0000
***************
*** 221,226 ****
--- 221,241 ----
  }

  /*
+  * ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper())
+  */
+ Oid
+ ordering_oper_funcid(Oid argtype)
+ {
+     Operator    optup;
+     Oid            result;
+
+     optup = ordering_oper(argtype, false);
+     result = oprfuncid(optup);
+     ReleaseSysCache(optup);
+     return result;
+ }
+
+ /*
   * ordering_oper_opid - convenience routine for oprid(ordering_oper())
   *
   * This was formerly called any_ordering_op()
Index: src/backend/utils/adt/acl.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/acl.c,v
retrieving revision 1.86
diff -c -r1.86 acl.c
*** src/backend/utils/adt/acl.c    24 Jan 2003 21:53:29 -0000    1.86
--- src/backend/utils/adt/acl.c    1 Jun 2003 19:07:00 -0000
***************
*** 720,732 ****
      aidat = ACL_DAT(acl);
      for (i = 0; i < num; ++i)
      {
!         if (aip->ai_grantee == aidat[i].ai_grantee &&
!             aip->ai_privs == aidat[i].ai_privs)
              PG_RETURN_BOOL(true);
      }
      PG_RETURN_BOOL(false);
  }


  /*
   * has_table_privilege variants
--- 720,740 ----
      aidat = ACL_DAT(acl);
      for (i = 0; i < num; ++i)
      {
!         if (aclitemeq(aip, &aidat[i]))
              PG_RETURN_BOOL(true);
      }
      PG_RETURN_BOOL(false);
  }

+ /*
+  * user-facing version of aclitemeq() for use as the
+  * aclitem equality operator
+  */
+ Datum
+ aclitem_eq(PG_FUNCTION_ARGS)
+ {
+     PG_RETURN_BOOL(aclitemeq(PG_GETARG_ACLITEM_P(0), PG_GETARG_ACLITEM_P(1)));
+ }

  /*
   * has_table_privilege variants
Index: src/backend/utils/adt/array_userfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/array_userfuncs.c,v
retrieving revision 1.1
diff -c -r1.1 array_userfuncs.c
*** src/backend/utils/adt/array_userfuncs.c    8 Apr 2003 23:20:02 -0000    1.1
--- src/backend/utils/adt/array_userfuncs.c    1 Jun 2003 15:41:15 -0000
***************
*** 42,48 ****
      else
          ndims = 1;

!     PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
                                                   PG_GETARG_DATUM(0),
                                                   ndims));
  }
--- 42,48 ----
      else
          ndims = 1;

!     PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, elem_type,
                                                   PG_GETARG_DATUM(0),
                                                   ndims));
  }
***************
*** 70,75 ****
--- 70,76 ----
      Oid            arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
      Oid            arg0_elemid;
      Oid            arg1_elemid;
+     ArrayMetaState *my_extra;

      if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
          elog(ERROR, "array_push: cannot determine input data types");
***************
*** 95,122 ****
          PG_RETURN_NULL();        /* keep compiler quiet */
      }

!     /* Sanity check: do we have a one-dimensional array */
!     if (ARR_NDIM(v) != 1)
!         elog(ERROR, "Arrays greater than one-dimension are not supported");
!
!     lb = ARR_LBOUND(v);
!     dimv = ARR_DIMS(v);
!     if (arg0_elemid != InvalidOid)
      {
!         /* append newelem */
!         int    ub = dimv[0] + lb[0] - 1;
!         indx = ub + 1;
      }
      else
      {
!         /* prepend newelem */
!         indx = lb[0] - 1;
      }

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

!     result = array_set(v, 1, &indx, newelem, -1,
!                        typlen, typbyval, typalign, &isNull);

      PG_RETURN_ARRAYTYPE_P(result);
  }
--- 96,156 ----
          PG_RETURN_NULL();        /* keep compiler quiet */
      }

!     if (ARR_NDIM(v) == 1)
      {
!         lb = ARR_LBOUND(v);
!         dimv = ARR_DIMS(v);
!
!         if (arg0_elemid != InvalidOid)
!         {
!             /* append newelem */
!             int    ub = dimv[0] + lb[0] - 1;
!             indx = ub + 1;
!         }
!         else
!         {
!             /* prepend newelem */
!             indx = lb[0] - 1;
!         }
      }
+     else if (ARR_NDIM(v) == 0)
+         indx = 1;
      else
+         elog(ERROR, "only empty and one-dimensional arrays are supported");
+
+
+     /*
+      * We arrange to look up info about element type only once per series
+      * of calls, assuming the element type doesn't change underneath us.
+      */
+     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+     if (my_extra == NULL)
      {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
      }

!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }
!
!     result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval,
!                                                  typalign, &isNull);

      PG_RETURN_ARRAYTYPE_P(result);
  }
***************
*** 145,157 ****

      /*
       * We must have one of the following combinations of inputs:
!      * 1) two arrays with ndims1 == ndims2
!      * 2) ndims1 == ndims2 - 1
!      * 3) ndims1 == ndims2 + 1
       */
      ndims1 = ARR_NDIM(v1);
      ndims2 = ARR_NDIM(v2);

      if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
          elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
                      "%d dimensions", ndims1, ndims2);
--- 179,206 ----

      /*
       * We must have one of the following combinations of inputs:
!      * 1) one empty array, and one non-empty array
!      * 2) both arrays empty
!      * 3) two arrays with ndims1 == ndims2
!      * 4) ndims1 == ndims2 - 1
!      * 5) ndims1 == ndims2 + 1
       */
      ndims1 = ARR_NDIM(v1);
      ndims2 = ARR_NDIM(v2);

+     /*
+      * short circuit - if one input array is empty, and the other is not,
+      * we return the non-empty one as the result
+      *
+      * if both are empty, return the first one
+      */
+     if (ndims1 == 0 && ndims2 > 0)
+         PG_RETURN_ARRAYTYPE_P(v2);
+
+     if (ndims2 == 0)
+         PG_RETURN_ARRAYTYPE_P(v1);
+
+     /* the rest fall into combo 2, 3, or 4 */
      if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
          elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
                      "%d dimensions", ndims1, ndims2);
***************
*** 266,314 ****
      PG_RETURN_ARRAYTYPE_P(result);
  }

- /*----------------------------------------------------------------------------
-  * array_accum :
-  *        accumulator to build a 1-D array from input values -- this can be used
-  *        to create custom aggregates.
-  *
-  * This function is not marked strict, so we have to be careful about nulls.
-  *----------------------------------------------------------------------------
-  */
- Datum
- array_accum(PG_FUNCTION_ARGS)
- {
-     /* return NULL if both arguments are NULL */
-     if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
-         PG_RETURN_NULL();
-
-     /* create a new 1-D array from the new element if the array is NULL */
-     if (PG_ARGISNULL(0))
-     {
-         Oid            tgt_type = get_fn_expr_rettype(fcinfo);
-         Oid            tgt_elem_type;
-
-         if (tgt_type == InvalidOid)
-             elog(ERROR, "Cannot determine target array type");
-         tgt_elem_type = get_element_type(tgt_type);
-         if (tgt_elem_type == InvalidOid)
-             elog(ERROR, "Target type is not an array");
-
-         PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
-                                                      PG_GETARG_DATUM(1),
-                                                      1));
-     }
-
-     /* return the array if the new element is NULL */
-     if (PG_ARGISNULL(1))
-         PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0));
-
-     /*
-      * Otherwise this is equivalent to array_push.  We hack the call a little
-      * so that array_push can see the fn_expr information.
-      */
-     return array_push(fcinfo);
- }
-
  /*-----------------------------------------------------------------------------
   * array_assign :
   *        assign an element of an array to a new value and return the
--- 315,320 ----
***************
*** 329,334 ****
--- 335,341 ----
      int16        typlen;
      bool        typbyval;
      char        typalign;
+     ArrayMetaState *my_extra;

      v = PG_GETARG_ARRAYTYPE_P(0);
      idx_to_chg = PG_GETARG_INT32(1);
***************
*** 349,355 ****
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      result = array_set(v, 1, &idx_to_chg, newelem, -1,
                         typlen, typbyval, typalign, &isNull);
--- 356,390 ----
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      result = array_set(v, 1, &idx_to_chg, newelem, -1,
                         typlen, typbyval, typalign, &isNull);
***************
*** 375,380 ****
--- 410,416 ----
      int16        typlen;
      bool        typbyval;
      char        typalign;
+     ArrayMetaState *my_extra;

      v = PG_GETARG_ARRAYTYPE_P(0);
      idx = PG_GETARG_INT32(1);
***************
*** 394,400 ****
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);

--- 430,464 ----
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);

***************
*** 402,412 ****
  }

  /*
!  * actually does the work for singleton_array(), and array_accum() if it is
!  * given a null input array.
   */
  ArrayType *
! create_singleton_array(Oid element_type, Datum element, int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
--- 466,478 ----
  }

  /*
!  * actually does the work for singleton_array()
   */
  ArrayType *
! create_singleton_array(FunctionCallInfo fcinfo,
!                        Oid element_type,
!                        Datum element,
!                        int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
***************
*** 415,420 ****
--- 481,487 ----
      int        dims[MAXDIM];
      int        lbs[MAXDIM];
      int        i;
+     ArrayMetaState *my_extra;

      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);
***************
*** 429,435 ****
          lbs[i] = 1;
      }

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      return construct_md_array(dvalues, ndims, dims, lbs, element_type,
                                typlen, typbyval, typalign);
--- 496,530 ----
          lbs[i] = 1;
      }

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      return construct_md_array(dvalues, ndims, dims, lbs, element_type,
                                typlen, typbyval, typalign);
Index: src/backend/utils/adt/arrayfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v
retrieving revision 1.89
diff -c -r1.89 arrayfuncs.c
*** src/backend/utils/adt/arrayfuncs.c    9 May 2003 23:01:45 -0000    1.89
--- src/backend/utils/adt/arrayfuncs.c    2 Jun 2003 02:24:39 -0000
***************
*** 21,28 ****
--- 21,30 ----
  #include "catalog/pg_type.h"
  #include "libpq/pqformat.h"
  #include "parser/parse_coerce.h"
+ #include "parser/parse_oper.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
+ #include "utils/datum.h"
  #include "utils/memutils.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
***************
*** 70,85 ****

  #define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)

- /* I/O function selector for system_cache_lookup */
- typedef enum IOFuncSelector
- {
-     IOFunc_input,
-     IOFunc_output,
-     IOFunc_receive,
-     IOFunc_send
- } IOFuncSelector;
-
-
  static int    ArrayCount(char *str, int *dim, char typdelim);
  static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
               FmgrInfo *inputproc, Oid typelem, int32 typmod,
--- 72,77 ----
***************
*** 93,102 ****
  static void CopyArrayEls(char *p, Datum *values, int nitems,
               int typlen, bool typbyval, char typalign,
               bool freedata);
- static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
-                                 int *typlen, bool *typbyval,
-                                 char *typdelim, Oid *typelem,
-                                 Oid *proc, char *typalign);
  static Datum ArrayCast(char *value, bool byval, int len);
  static int ArrayCastAndSet(Datum src,
                  int typlen, bool typbyval, char typalign,
--- 85,90 ----
***************
*** 119,125 ****
                     char *destPtr,
                     int *st, int *endp, char *srcPtr,
                     int typlen, bool typbyval, char typalign);
!

  /*---------------------------------------------------------------------
   * array_in :
--- 107,113 ----
                     char *destPtr,
                     int *st, int *endp, char *srcPtr,
                     int typlen, bool typbyval, char typalign);
! static int array_cmp(FunctionCallInfo fcinfo);

  /*---------------------------------------------------------------------
   * array_in :
***************
*** 154,165 ****
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;

!     /* Get info about element type, including its input conversion proc */
!     system_cache_lookup(element_type, IOFunc_input,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typinput, &typalign);
!     fmgr_info(typinput, &inputproc);

      /* Make a modifiable copy of the input */
      /* XXX why are we allocating an extra 2 bytes here? */
--- 142,190 ----
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;
+     ArrayMetaState *my_extra;

!     /*
!      * We arrange to look up info about element type, including its input
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its input conversion proc */
!         get_type_metadata(element_type, IOFunc_input,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typinput, &typalign);
!         fmgr_info(typinput, &inputproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typinput;
!         my_extra->typalign = typalign;
!         my_extra->proc = inputproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typinput = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         inputproc = my_extra->proc;
!     }

      /* Make a modifiable copy of the input */
      /* XXX why are we allocating an extra 2 bytes here? */
***************
*** 636,647 ****
                  indx[MAXDIM];
      int            ndim,
                 *dim;

      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_output,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typoutput, &typalign);
!     fmgr_info(typoutput, &outputproc);

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
--- 661,711 ----
                  indx[MAXDIM];
      int            ndim,
                 *dim;
+     ArrayMetaState *my_extra;

      element_type = ARR_ELEMTYPE(v);
!
!     /*
!      * We arrange to look up info about element type, including its input
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its output conversion proc */
!         get_type_metadata(element_type, IOFunc_output,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typoutput, &typalign);
!         fmgr_info(typoutput, &outputproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typoutput;
!         my_extra->typalign = typalign;
!         my_extra->proc = outputproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typoutput = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         outputproc = my_extra->proc;
!     }

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
***************
*** 800,805 ****
--- 864,870 ----
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;
+     ArrayMetaState *my_extra;

      /* Get the array header information */
      ndim = pq_getmsgint(buf, 4);
***************
*** 831,844 ****
          PG_RETURN_ARRAYTYPE_P(retval);
      }

!     /* Get info about element type, including its receive conversion proc */
!     system_cache_lookup(element_type, IOFunc_receive,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typreceive, &typalign);
!     if (!OidIsValid(typreceive))
!         elog(ERROR, "No binary input function available for type %s",
!              format_type_be(element_type));
!     fmgr_info(typreceive, &receiveproc);

      dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
                                typlen, typbyval, typalign,
--- 896,945 ----
          PG_RETURN_ARRAYTYPE_P(retval);
      }

!     /*
!      * We arrange to look up info about element type, including its receive
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its receive conversion proc */
!         get_type_metadata(element_type, IOFunc_receive,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typreceive, &typalign);
!         if (!OidIsValid(typreceive))
!             elog(ERROR, "No binary input function available for type %s",
!                  format_type_be(element_type));
!         fmgr_info(typreceive, &receiveproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typreceive;
!         my_extra->typalign = typalign;
!         my_extra->proc = receiveproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typreceive = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         receiveproc = my_extra->proc;
!     }

      dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
                                typlen, typbyval, typalign,
***************
*** 976,990 ****
      int            ndim,
                 *dim;
      StringInfoData buf;

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
!                         &typdelim, &typelem, &typsend, &typalign);
!     if (!OidIsValid(typsend))
!         elog(ERROR, "No binary output function available for type %s",
!              format_type_be(element_type));
!     fmgr_info(typsend, &sendproc);

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
--- 1077,1130 ----
      int            ndim,
                 *dim;
      StringInfoData buf;
+     ArrayMetaState *my_extra;

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!
!     /*
!      * We arrange to look up info about element type, including its send
!      * proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its send proc */
!         get_type_metadata(element_type, IOFunc_send, &typlen, &typbyval,
!                             &typdelim, &typelem, &typsend, &typalign);
!         if (!OidIsValid(typsend))
!             elog(ERROR, "No binary output function available for type %s",
!                  format_type_be(element_type));
!         fmgr_info(typsend, &sendproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typsend;
!         my_extra->typalign = typalign;
!         my_extra->proc = sendproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typsend = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         sendproc = my_extra->proc;
!     }

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
***************
*** 1476,1481 ****
--- 1616,1641 ----
      array = DatumGetArrayTypeP(PointerGetDatum(array));

      ndim = ARR_NDIM(array);
+
+     /*
+      * if number of dims is zero, i.e. an empty array, create an array
+      * with nSubscripts dimensions, and set the lower bounds to the supplied
+      * subscripts
+      */
+     if (ndim == 0)
+     {
+         Oid        elmtype = ARR_ELEMTYPE(array);
+
+         for (i = 0; i < nSubscripts; i++)
+         {
+             dim[i] = 1;
+             lb[i] = indx[i];
+         }
+
+         return construct_md_array(&dataValue, nSubscripts, dim, lb, elmtype,
+                                                 elmlen, elmbyval, elmalign);
+     }
+
      if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
          elog(ERROR, "Invalid array subscripts");

***************
*** 1632,1637 ****
--- 1792,1822 ----
      /* note: we assume srcArray contains no toasted elements */

      ndim = ARR_NDIM(array);
+
+     /*
+      * if number of dims is zero, i.e. an empty array, create an array
+      * with nSubscripts dimensions, and set the upper and lower bounds
+      * to the supplied subscripts
+      */
+     if (ndim == 0)
+     {
+         Datum  *dvalues;
+         int        nelems;
+         Oid        elmtype = ARR_ELEMTYPE(array);
+
+         deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
+                                                         &dvalues, &nelems);
+
+         for (i = 0; i < nSubscripts; i++)
+         {
+             dim[i] = 1 + upperIndx[i] - lowerIndx[i];
+             lb[i] = lowerIndx[i];
+         }
+
+         return construct_md_array(dvalues, nSubscripts, dim, lb, elmtype,
+                                                  elmlen, elmbyval, elmalign);
+     }
+
      if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
          elog(ERROR, "Invalid array subscripts");

***************
*** 1811,1816 ****
--- 1996,2008 ----
      Oid            typelem;
      Oid            proc;
      char       *s;
+     typedef struct {
+         ArrayMetaState *inp_extra;
+         ArrayMetaState *ret_extra;
+     } am_extra;
+     am_extra  *my_extra;
+     ArrayMetaState *inp_extra;
+     ArrayMetaState *ret_extra;

      /* Get input array */
      if (fcinfo->nargs < 1)
***************
*** 1829,1839 ****
      if (nitems <= 0)
          PG_RETURN_ARRAYTYPE_P(v);

!     /* Lookup source and result types. Unneeded variables are reused. */
!     system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
!                         &typdelim, &typelem, &proc, &inp_typalign);
!     system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
!                         &typdelim, &typelem, &proc, &typalign);

      /* Allocate temporary array for new values */
      values = (Datum *) palloc(nitems * sizeof(Datum));
--- 2021,2101 ----
      if (nitems <= 0)
          PG_RETURN_ARRAYTYPE_P(v);

!     /*
!      * We arrange to look up info about input and return element types only
!      * once per series of calls, assuming the element type doesn't change
!      * underneath us.
!      */
!     my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(am_extra));
!         my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
!
!         my_extra->inp_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         inp_extra = my_extra->inp_extra;
!         inp_extra->element_type = InvalidOid;
!
!         my_extra->ret_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         ret_extra = my_extra->ret_extra;
!         ret_extra->element_type = InvalidOid;
!     }
!     else
!     {
!         inp_extra = my_extra->inp_extra;
!         ret_extra = my_extra->ret_extra;
!     }
!
!     if (inp_extra->element_type != inpType)
!     {
!         /* Lookup source and result types. Unneeded variables are reused. */
!         get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
!                             &typdelim, &typelem, &proc, &inp_typalign);
!
!         inp_extra->element_type = inpType;
!         inp_extra->typlen = inp_typlen;
!         inp_extra->typbyval = inp_typbyval;
!         inp_extra->typdelim = typdelim;
!         inp_extra->typelem = typelem;
!         inp_extra->typiofunc = proc;
!         inp_extra->typalign = inp_typalign;
!     }
!     else
!     {
!         inp_typlen = inp_extra->typlen;
!         inp_typbyval = inp_extra->typbyval;
!         typdelim = inp_extra->typdelim;
!         typelem = inp_extra->typelem;
!         proc = inp_extra->typiofunc;
!         inp_typalign = inp_extra->typalign;
!     }
!
!     if (ret_extra->element_type != retType)
!     {
!         /* Lookup source and result types. Unneeded variables are reused. */
!         get_type_metadata(retType, IOFunc_input, &typlen, &typbyval,
!                             &typdelim, &typelem, &proc, &typalign);
!
!         ret_extra->element_type = retType;
!         ret_extra->typlen = typlen;
!         ret_extra->typbyval = typbyval;
!         ret_extra->typdelim = typdelim;
!         ret_extra->typelem = typelem;
!         ret_extra->typiofunc = proc;
!         ret_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = ret_extra->typlen;
!         typbyval = ret_extra->typbyval;
!         typdelim = ret_extra->typdelim;
!         typelem = ret_extra->typelem;
!         proc = ret_extra->typiofunc;
!         typalign = ret_extra->typalign;
!     }

      /* Allocate temporary array for new values */
      values = (Datum *) palloc(nitems * sizeof(Datum));
***************
*** 2049,2056 ****
   *          compares two arrays for equality
   * result :
   *          returns true if the arrays are equal, false otherwise.
-  *
-  * XXX bitwise equality is pretty bogus ...
   *-----------------------------------------------------------------------------
   */
  Datum
--- 2311,2316 ----
***************
*** 2058,2069 ****
  {
      ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
      ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
      bool        result = true;

!     if (ARR_SIZE(array1) != ARR_SIZE(array2))
!         result = false;
!     else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
          result = false;

      /* Avoid leaking memory when handed toasted input. */
      PG_FREE_IF_COPY(array1, 0);
--- 2318,2435 ----
  {
      ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
      ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
+     char       *p1 = (char *) ARR_DATA_PTR(array1);
+     char       *p2 = (char *) ARR_DATA_PTR(array2);
+     int            ndims1 = ARR_NDIM(array1);
+     int            ndims2 = ARR_NDIM(array2);
+     int           *dims1 = ARR_DIMS(array1);
+     int           *dims2 = ARR_DIMS(array2);
+     int            nitems1 = ArrayGetNItems(ndims1, dims1);
+     int            nitems2 = ArrayGetNItems(ndims2, dims2);
+     Oid            element_type = ARR_ELEMTYPE(array1);
+     FmgrInfo   *ae_fmgr_info = fcinfo->flinfo;
      bool        result = true;
+     int            typlen;
+     bool        typbyval;
+     char        typdelim;
+     Oid            typelem;
+     char        typalign;
+     Oid            typiofunc;
+     int            i;
+     ArrayMetaState *my_extra;
+     FunctionCallInfoData locfcinfo;

!     /* fast path if the arrays do not have the same number of elements */
!     if (nitems1 != nitems2)
          result = false;
+     else
+     {
+         /*
+          * We arrange to look up the equality function only once per series of
+          * calls, assuming the element type doesn't change underneath us.
+          */
+         my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
+         if (my_extra == NULL)
+         {
+             ae_fmgr_info->fn_extra = MemoryContextAlloc(ae_fmgr_info->fn_mcxt,
+                                                      sizeof(ArrayMetaState));
+             my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
+             my_extra->element_type = InvalidOid;
+         }
+
+         if (my_extra->element_type != element_type)
+         {
+             Oid        opfuncid = equality_oper_funcid(element_type);
+
+             if (OidIsValid(opfuncid))
+                 fmgr_info_cxt(opfuncid, &my_extra->proc, ae_fmgr_info->fn_mcxt);
+             else
+                 elog(ERROR,
+                      "array_eq: cannot find equality operator for type: %u",
+                      element_type);
+
+             get_type_metadata(element_type, IOFunc_output,
+                               &typlen, &typbyval, &typdelim,
+                               &typelem, &typiofunc, &typalign);
+
+             my_extra->element_type = element_type;
+             my_extra->typlen = typlen;
+             my_extra->typbyval = typbyval;
+             my_extra->typdelim = typdelim;
+             my_extra->typelem = typelem;
+             my_extra->typiofunc = typiofunc;
+             my_extra->typalign = typalign;
+         }
+         else
+         {
+             typlen = my_extra->typlen;
+             typbyval = my_extra->typbyval;
+             typdelim = my_extra->typdelim;
+             typelem = my_extra->typelem;
+             typiofunc = my_extra->typiofunc;
+             typalign = my_extra->typalign;
+         }
+
+         /*
+          * apply the operator to each pair of array elements.
+          */
+         MemSet(&locfcinfo, 0, sizeof(locfcinfo));
+         locfcinfo.flinfo = &my_extra->proc;
+         locfcinfo.nargs = 2;
+
+         /* Loop over source data */
+         for (i = 0; i < nitems1; i++)
+         {
+             Datum    elt1;
+             Datum    elt2;
+             bool    oprresult;
+
+             /* Get element pair */
+             elt1 = fetch_att(p1, typbyval, typlen);
+             elt2 = fetch_att(p2, typbyval, typlen);
+
+             p1 = att_addlength(p1, typlen, PointerGetDatum(p1));
+             p1 = (char *) att_align(p1, typalign);
+
+             p2 = att_addlength(p2, typlen, PointerGetDatum(p2));
+             p2 = (char *) att_align(p2, typalign);
+
+             /*
+              * Apply the operator to the element pair
+              */
+             locfcinfo.arg[0] = elt1;
+             locfcinfo.arg[1] = elt2;
+             locfcinfo.argnull[0] = false;
+             locfcinfo.argnull[1] = false;
+             locfcinfo.isnull = false;
+             oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+             if (!oprresult)
+             {
+                 result = false;
+                 break;
+             }
+         }
+     }

      /* Avoid leaking memory when handed toasted input. */
      PG_FREE_IF_COPY(array1, 0);
***************
*** 2073,2125 ****
  }


! /***************************************************************************/
! /******************|          Support  Routines              |*****************/
! /***************************************************************************/

! static void
! system_cache_lookup(Oid element_type,
!                     IOFuncSelector which_func,
!                     int *typlen,
!                     bool *typbyval,
!                     char *typdelim,
!                     Oid *typelem,
!                     Oid *proc,
!                     char *typalign)
! {
!     HeapTuple    typeTuple;
!     Form_pg_type typeStruct;
!
!     typeTuple = SearchSysCache(TYPEOID,
!                                ObjectIdGetDatum(element_type),
!                                0, 0, 0);
!     if (!HeapTupleIsValid(typeTuple))
!         elog(ERROR, "cache lookup failed for type %u", element_type);
!     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
!
!     *typlen = typeStruct->typlen;
!     *typbyval = typeStruct->typbyval;
!     *typdelim = typeStruct->typdelim;
!     *typelem = typeStruct->typelem;
!     *typalign = typeStruct->typalign;
!     switch (which_func)
!     {
!         case IOFunc_input:
!             *proc = typeStruct->typinput;
!             break;
!         case IOFunc_output:
!             *proc = typeStruct->typoutput;
!             break;
!         case IOFunc_receive:
!             *proc = typeStruct->typreceive;
!             break;
!         case IOFunc_send:
!             *proc = typeStruct->typsend;
!             break;
      }
!     ReleaseSysCache(typeTuple);
  }

  /*
   * Fetch array element at pointer, converted correctly to a Datum
   */
--- 2439,2628 ----
  }


! /*-----------------------------------------------------------------------------
!  * array-array bool operators:
!  *        Given two arrays, iterate comparison operators
!  *        over the array. Uses logic similar to text comparison
!  *        functions, except element-by-element instead of
!  *        character-by-character.
!  *----------------------------------------------------------------------------
!  */
! Datum
! array_ne(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
! }

! Datum
! array_lt(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
! }
!
! Datum
! array_gt(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
! }
!
! Datum
! array_le(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
! }
!
! Datum
! array_ge(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
! }
!
! Datum
! btarraycmp(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_INT32(array_cmp(fcinfo));
! }
!
! /*
!  * array_cmp()
!  * Internal comparison function for arrays.
!  *
!  * Returns -1, 0 or 1
!  */
! static int
! array_cmp(FunctionCallInfo fcinfo)
! {
!     ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
!     ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
!     FmgrInfo   *ac_fmgr_info = fcinfo->flinfo;
!     Datum        opresult;
!     int            result = 0;
!     Oid            element_type = InvalidOid;
!     int            typlen;
!     bool        typbyval;
!     char        typdelim;
!     Oid            typelem;
!     char        typalign;
!     Oid            typiofunc;
!     Datum       *dvalues1;
!     int            nelems1;
!     Datum       *dvalues2;
!     int            nelems2;
!     int            min_nelems;
!     int            i;
!     typedef struct
!     {
!         Oid                element_type;
!         int                typlen;
!         bool            typbyval;
!         char            typdelim;
!         Oid                typelem;
!         Oid                typiofunc;
!         char            typalign;
!         FmgrInfo        eqproc;
!         FmgrInfo        ordproc;
!     } ac_extra;
!     ac_extra *my_extra;
!
!     element_type = ARR_ELEMTYPE(array1);
!
!     /*
!      * We arrange to look up the element type operator function only once
!      * per series of calls, assuming the element type and opname don't
!      * change underneath us.
!      */
!     my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
!     if (my_extra == NULL)
!     {
!         ac_fmgr_info->fn_extra = MemoryContextAlloc(ac_fmgr_info->fn_mcxt,
!                                                          sizeof(ac_extra));
!         my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         Oid        eqfuncid = equality_oper_funcid(element_type);
!         Oid        ordfuncid = ordering_oper_funcid(element_type);
!
!         fmgr_info_cxt(eqfuncid, &my_extra->eqproc, ac_fmgr_info->fn_mcxt);
!         fmgr_info_cxt(ordfuncid, &my_extra->ordproc, ac_fmgr_info->fn_mcxt);
!
!         if (my_extra->eqproc.fn_nargs != 2)
!             elog(ERROR, "Equality operator does not take 2 arguments: %u",
!                                                                  eqfuncid);
!         if (my_extra->ordproc.fn_nargs != 2)
!             elog(ERROR, "Ordering operator does not take 2 arguments: %u",
!                                                                  ordfuncid);
!
!         get_type_metadata(element_type, IOFunc_output,
!                           &typlen, &typbyval, &typdelim,
!                           &typelem, &typiofunc, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = InvalidOid;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }
!
!     /* extract a C array of arg array datums */
!     deconstruct_array(array1, element_type, typlen, typbyval, typalign,
!                                                     &dvalues1, &nelems1);
!
!     deconstruct_array(array2, element_type, typlen, typbyval, typalign,
!                                                     &dvalues2, &nelems2);
!
!     min_nelems = Min(nelems1, nelems2);
!     for (i = 0; i < min_nelems; i++)
!     {
!         /* are they equal */
!         opresult = FunctionCall2(&my_extra->eqproc,
!                                  dvalues1[i], dvalues2[i]);
!
!         if (!DatumGetBool(opresult))
!         {
!             /* nope, see if arg1 is less than arg2 */
!             opresult = FunctionCall2(&my_extra->ordproc,
!                                      dvalues1[i], dvalues2[i]);
!             if (DatumGetBool(opresult))
!             {
!                 /* arg1 is less than arg2 */
!                 result = -1;
!                 break;
!             }
!             else
!             {
!                 /* arg1 is greater than arg2 */
!                 result = 1;
!                 break;
!             }
!         }
      }
!
!     if ((result == 0) && (nelems1 != nelems2))
!         result = (nelems1 < nelems2) ? -1 : 1;
!
!     /* Avoid leaking memory when handed toasted input. */
!     PG_FREE_IF_COPY(array1, 0);
!     PG_FREE_IF_COPY(array2, 1);
!
!     return result;
  }

+
+ /***************************************************************************/
+ /******************|          Support  Routines              |*****************/
+ /***************************************************************************/
+
  /*
   * Fetch array element at pointer, converted correctly to a Datum
   */
***************
*** 2423,2428 ****
--- 2926,2943 ----
          if (tgt_elem_type == InvalidOid)
              elog(ERROR, "Target type is not an array");

+         /*
+          * We don't deal with domain constraints yet, so bail out.
+          * This isn't currently a problem, because we also don't
+          * support arrays of domain type elements either. But in the
+          * future we might. At that point consideration should be given
+          * to removing the check below and adding a domain constraints
+          * check to the coercion.
+          */
+         if (getBaseType(tgt_elem_type) != tgt_elem_type)
+             elog(ERROR, "array coercion to domain type elements not " \
+                         "currently supported");
+
          if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
                                     COERCION_EXPLICIT, &funcId))
          {
***************
*** 2439,2448 ****
      }

      /*
!      * If it's binary-compatible, return the array unmodified.
       */
      if (my_extra->coerce_finfo.fn_oid == InvalidOid)
!         PG_RETURN_ARRAYTYPE_P(src);

      /*
       * Use array_map to apply the function to each array element.
--- 2954,2969 ----
      }

      /*
!      * If it's binary-compatible, modify the element type in the array header,
!      * but otherwise leave the array as we received it.
       */
      if (my_extra->coerce_finfo.fn_oid == InvalidOid)
!     {
!         ArrayType  *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
!
!         ARR_ELEMTYPE(result) = my_extra->desttype;
!         PG_RETURN_ARRAYTYPE_P(result);
!     }

      /*
       * Use array_map to apply the function to each array element.
***************
*** 2453,2456 ****
--- 2974,3092 ----
      locfcinfo.arg[0] = PointerGetDatum(src);

      return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
+ }
+
+ /*
+  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
+  *
+  *    astate is working state (NULL on first call)
+  *    rcontext is where to keep working state
+  */
+ ArrayBuildState *
+ accumArrayResult(ArrayBuildState *astate,
+                  Datum dvalue, bool disnull,
+                  Oid element_type,
+                  MemoryContext rcontext)
+ {
+     MemoryContext arr_context,
+                   oldcontext;
+
+     if (astate == NULL)
+     {
+         /* First time through --- initialize */
+
+         /* Make a temporary context to hold all the junk */
+         arr_context = AllocSetContextCreate(rcontext,
+                                             "accumArrayResult",
+                                             ALLOCSET_DEFAULT_MINSIZE,
+                                             ALLOCSET_DEFAULT_INITSIZE,
+                                             ALLOCSET_DEFAULT_MAXSIZE);
+         oldcontext = MemoryContextSwitchTo(arr_context);
+         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
+         astate->mcontext = arr_context;
+         astate->dvalues = (Datum *)
+             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
+         astate->nelems = 0;
+         astate->element_type = element_type;
+         get_typlenbyvalalign(element_type,
+                              &astate->typlen,
+                              &astate->typbyval,
+                              &astate->typalign);
+     }
+     else
+     {
+         oldcontext = MemoryContextSwitchTo(astate->mcontext);
+         Assert(astate->element_type == element_type);
+         /* enlarge dvalues[] if needed */
+         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
+             astate->dvalues = (Datum *)
+                 repalloc(astate->dvalues,
+                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
+     }
+
+     if (disnull)
+         elog(ERROR, "NULL elements not allowed in Arrays");
+
+     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
+     astate->dvalues[astate->nelems++] =
+         datumCopy(dvalue, astate->typbyval, astate->typlen);
+
+     MemoryContextSwitchTo(oldcontext);
+
+     return astate;
+ }
+
+ /*
+  * makeArrayResult - produce final result of accumArrayResult
+  *
+  *    astate is working state (not NULL)
+  *    rcontext is where to construct result
+  */
+ Datum
+ makeArrayResult(ArrayBuildState *astate,
+                 MemoryContext rcontext)
+ {
+     int            dims[1];
+     int            lbs[1];
+
+     dims[0] = astate->nelems;
+     lbs[0] = 1;
+
+     return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
+ }
+
+ /*
+  * makeMdArrayResult - produce md final result of accumArrayResult
+  *
+  *    astate is working state (not NULL)
+  *    rcontext is where to construct result
+  */
+ Datum
+ makeMdArrayResult(ArrayBuildState *astate,
+                 int ndims,
+                 int *dims,
+                 int *lbs,
+                 MemoryContext rcontext)
+ {
+     ArrayType  *result;
+     MemoryContext oldcontext;
+
+     /* Build the final array result in rcontext */
+     oldcontext = MemoryContextSwitchTo(rcontext);
+
+     result = construct_md_array(astate->dvalues,
+                                 ndims,
+                                 dims,
+                                 lbs,
+                                 astate->element_type,
+                                 astate->typlen,
+                                 astate->typbyval,
+                                 astate->typalign);
+
+     MemoryContextSwitchTo(oldcontext);
+
+     /* Clean up all the junk */
+     MemoryContextDelete(astate->mcontext);
+
+     return PointerGetDatum(result);
  }
Index: src/backend/utils/adt/varlena.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/varlena.c,v
retrieving revision 1.98
diff -c -r1.98 varlena.c
*** src/backend/utils/adt/varlena.c    15 May 2003 15:50:19 -0000    1.98
--- src/backend/utils/adt/varlena.c    1 Jun 2003 15:41:15 -0000
***************
*** 19,29 ****
--- 19,32 ----
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
  #include "access/tuptoaster.h"
+ #include "catalog/pg_type.h"
  #include "lib/stringinfo.h"
  #include "libpq/crypt.h"
  #include "libpq/pqformat.h"
+ #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/pg_locale.h"
+ #include "utils/lsyscache.h"


  typedef struct varlena unknown;
***************
*** 1983,1990 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

--- 1986,1992 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

***************
*** 2004,2011 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
--- 2006,2012 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
***************
*** 2026,2031 ****
--- 2027,2217 ----
          result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn -
fldsep_len,false); 
          PG_RETURN_TEXT_P(result_text);
      }
+ }
+
+ /*
+  * text_to_array
+  * parse input string
+  * return text array of elements
+  * based on provided field separator
+  */
+ Datum
+ text_to_array(PG_FUNCTION_ARGS)
+ {
+     text       *inputstring = PG_GETARG_TEXT_P(0);
+     int            inputstring_len = TEXTLEN(inputstring);
+     text       *fldsep = PG_GETARG_TEXT_P(1);
+     int            fldsep_len = TEXTLEN(fldsep);
+     int            fldnum;
+     int            start_posn = 0;
+     int            end_posn = 0;
+     text       *result_text = NULL;
+     ArrayBuildState *astate = NULL;
+     MemoryContext oldcontext = CurrentMemoryContext;
+
+     /* return NULL for empty input string */
+     if (inputstring_len < 1)
+         PG_RETURN_NULL();
+
+     /* empty field separator
+      * return one element, 1D, array using the input string */
+     if (fldsep_len < 1)
+         PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+                               CStringGetDatum(inputstring), 1));
+
+     /* start with end position holding the initial start position */
+     end_posn = 0;
+     for (fldnum=1;;fldnum++)    /* field number is 1 based */
+     {
+         Datum    dvalue;
+         bool    disnull = false;
+
+         start_posn = end_posn;
+         end_posn = text_position(PointerGetDatum(inputstring),
+                                  PointerGetDatum(fldsep),
+                                  fldnum);
+
+         if ((start_posn == 0) && (end_posn == 0))    /* fldsep not found */
+         {
+             if (fldnum == 1)
+             {
+                 /* first element
+                  * return one element, 1D, array using the input string */
+                 PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+                                       CStringGetDatum(inputstring), 1));
+             }
+             else
+             {
+                 /* otherwise create array and exit */
+                 PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
+             }
+         }
+         else if ((start_posn != 0) && (end_posn == 0))
+         {
+             /* last field requested */
+             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
+         }
+         else if ((start_posn == 0) && (end_posn != 0))
+         {
+             /* first field requested */
+             result_text = LEFT(inputstring, fldsep);
+         }
+         else
+         {
+             /* prior to last field requested */
+             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn
-fldsep_len, false); 
+         }
+
+         /* stash away current value */
+         dvalue = PointerGetDatum(result_text);
+         astate = accumArrayResult(astate, dvalue,
+                                   disnull, TEXTOID, oldcontext);
+
+     }
+
+     /* never reached -- keep compiler quiet */
+     PG_RETURN_NULL();
+ }
+
+ /*
+  * array_to_text
+  * concatenate Cstring representation of input array elements
+  * using provided field separator
+  */
+ Datum
+ array_to_text(PG_FUNCTION_ARGS)
+ {
+     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
+     char       *fldsep = PG_TEXTARG_GET_STR(1);
+     int            nitems, *dims, ndims;
+     char       *p;
+     Oid            element_type;
+     int            typlen;
+     bool        typbyval;
+     char        typdelim;
+     Oid            typoutput,
+                 typelem;
+     FmgrInfo    outputproc;
+     char        typalign;
+     StringInfo    result_str = makeStringInfo();
+     int            i;
+     ArrayMetaState *my_extra;
+
+     p = ARR_DATA_PTR(v);
+     ndims = ARR_NDIM(v);
+     dims = ARR_DIMS(v);
+     nitems = ArrayGetNItems(ndims, dims);
+
+     /* if there are no elements, return an empty string */
+     if (nitems == 0)
+         PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
+
+     element_type = ARR_ELEMTYPE(v);
+
+     /*
+      * We arrange to look up info about element type, including its output
+      * conversion proc only once per series of calls, assuming the element
+      * type doesn't change underneath us.
+      */
+     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+     if (my_extra == NULL)
+     {
+         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+                                                      sizeof(ArrayMetaState));
+         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+         my_extra->element_type = InvalidOid;
+     }
+
+     if (my_extra->element_type != element_type)
+     {
+         /* Get info about element type, including its output conversion proc */
+         get_type_metadata(element_type, IOFunc_output,
+                             &typlen, &typbyval, &typdelim,
+                             &typelem, &typoutput, &typalign);
+         fmgr_info(typoutput, &outputproc);
+
+         my_extra->element_type = element_type;
+         my_extra->typlen = typlen;
+         my_extra->typbyval = typbyval;
+         my_extra->typdelim = typdelim;
+         my_extra->typelem = typelem;
+         my_extra->typiofunc = typoutput;
+         my_extra->typalign = typalign;
+         my_extra->proc = outputproc;
+     }
+     else
+     {
+         typlen = my_extra->typlen;
+         typbyval = my_extra->typbyval;
+         typdelim = my_extra->typdelim;
+         typelem = my_extra->typelem;
+         typoutput = my_extra->typiofunc;
+         typalign = my_extra->typalign;
+         outputproc = my_extra->proc;
+     }
+
+     for (i = 0; i < nitems; i++)
+     {
+         Datum        itemvalue;
+         char       *value;
+
+         itemvalue = fetch_att(p, typbyval, typlen);
+
+         value = DatumGetCString(FunctionCall3(&outputproc,
+                                               itemvalue,
+                                               ObjectIdGetDatum(typelem),
+                                               Int32GetDatum(-1)));
+
+         if (i > 0)
+             appendStringInfo(result_str, "%s%s", fldsep, value);
+         else
+             appendStringInfo(result_str, "%s", value);
+
+         p = att_addlength(p, typlen, PointerGetDatum(p));
+         p = (char *) att_align(p, typalign);
+     }
+
+     PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
  }

  #define HEXBASE 16
Index: src/backend/utils/cache/lsyscache.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v
retrieving revision 1.95
diff -c -r1.95 lsyscache.c
*** src/backend/utils/cache/lsyscache.c    26 May 2003 00:11:27 -0000    1.95
--- src/backend/utils/cache/lsyscache.c    1 Jun 2003 15:41:15 -0000
***************
*** 625,630 ****
--- 625,664 ----
  }

  /*
+  * get_func_argtypes
+  *        Given procedure id, return the function's argument types.
+  *        Also pass back the number of arguments.
+  */
+ Oid *
+ get_func_argtypes(Oid funcid, int *nargs)
+ {
+     HeapTuple        tp;
+     Form_pg_proc    procstruct;
+     Oid               *result = NULL;
+     int                i;
+
+     tp = SearchSysCache(PROCOID,
+                         ObjectIdGetDatum(funcid),
+                         0, 0, 0);
+     if (!HeapTupleIsValid(tp))
+         elog(ERROR, "Function OID %u does not exist", funcid);
+
+     procstruct = (Form_pg_proc) GETSTRUCT(tp);
+     *nargs = (int) procstruct->pronargs;
+
+     if (*nargs > 0)
+     {
+         result = (Oid *) palloc(*nargs * sizeof(Oid));
+
+         for (i = 0; i < *nargs; i++)
+             result[i] = procstruct->proargtypes[i];
+     }
+
+     ReleaseSysCache(tp);
+     return result;
+ }
+
+ /*
   * get_func_retset
   *        Given procedure id, return the function's proretset flag.
   */
***************
*** 994,999 ****
--- 1028,1083 ----
      *typbyval = typtup->typbyval;
      *typalign = typtup->typalign;
      ReleaseSysCache(tp);
+ }
+
+ /*
+  * get_type_metadata
+  *
+  *        A six-fer:    given the type OID, return typlen, typbyval, typalign,
+  *                    typdelim, typelem, IO function Oid. The IO function
+  *                    returned is controlled by IOFuncSelector
+  */
+ void
+ get_type_metadata(Oid element_type,
+                     IOFuncSelector which_func,
+                     int *typlen,
+                     bool *typbyval,
+                     char *typdelim,
+                     Oid *typelem,
+                     Oid *proc,
+                     char *typalign)
+ {
+     HeapTuple    typeTuple;
+     Form_pg_type typeStruct;
+
+     typeTuple = SearchSysCache(TYPEOID,
+                                ObjectIdGetDatum(element_type),
+                                0, 0, 0);
+     if (!HeapTupleIsValid(typeTuple))
+         elog(ERROR, "cache lookup failed for type %u", element_type);
+     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+
+     *typlen = typeStruct->typlen;
+     *typbyval = typeStruct->typbyval;
+     *typdelim = typeStruct->typdelim;
+     *typelem = typeStruct->typelem;
+     *typalign = typeStruct->typalign;
+     switch (which_func)
+     {
+         case IOFunc_input:
+             *proc = typeStruct->typinput;
+             break;
+         case IOFunc_output:
+             *proc = typeStruct->typoutput;
+             break;
+         case IOFunc_receive:
+             *proc = typeStruct->typreceive;
+             break;
+         case IOFunc_send:
+             *proc = typeStruct->typsend;
+             break;
+     }
+     ReleaseSysCache(typeTuple);
  }

  #ifdef NOT_USED
Index: src/backend/utils/fmgr/fmgr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/fmgr/fmgr.c,v
retrieving revision 1.68
diff -c -r1.68 fmgr.c
*** src/backend/utils/fmgr/fmgr.c    8 Apr 2003 23:20:02 -0000    1.68
--- src/backend/utils/fmgr/fmgr.c    1 Jun 2003 15:41:15 -0000
***************
*** 1673,1675 ****
--- 1673,1701 ----

      return exprType((Node *) nth(argnum, args));
  }
+
+ /*
+  * Get the OID of the function or operator
+  *
+  * Returns InvalidOid if information is not available
+  */
+ Oid
+ get_fn_expr_functype(FunctionCallInfo fcinfo)
+ {
+     Node   *expr;
+
+     /*
+      * can't return anything useful if we have no FmgrInfo or if
+      * its fn_expr node has not been initialized
+      */
+     if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
+         return InvalidOid;
+
+     expr = fcinfo->flinfo->fn_expr;
+     if (IsA(expr, FuncExpr))
+         return ((FuncExpr *) expr)->funcid;
+     else if (IsA(expr, OpExpr))
+         return ((OpExpr *) expr)->opno;
+     else
+         return InvalidOid;
+ }
Index: src/include/fmgr.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/fmgr.h,v
retrieving revision 1.27
diff -c -r1.27 fmgr.h
*** src/include/fmgr.h    8 Apr 2003 23:20:04 -0000    1.27
--- src/include/fmgr.h    1 Jun 2003 15:41:15 -0000
***************
*** 18,23 ****
--- 18,24 ----
  #ifndef FMGR_H
  #define FMGR_H

+ #include "nodes/nodes.h"

  /*
   * All functions that can be called directly by fmgr must have this signature.
***************
*** 372,385 ****
                   Datum arg6, Datum arg7, Datum arg8,
                   Datum arg9);

-
  /*
   * Routines in fmgr.c
   */
  extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
! extern Oid    fmgr_internal_function(const char *proname);
! extern Oid    get_fn_expr_rettype(FunctionCallInfo fcinfo);
! extern Oid    get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);

  /*
   * Routines in dfmgr.c
--- 373,386 ----
                   Datum arg6, Datum arg7, Datum arg8,
                   Datum arg9);

  /*
   * Routines in fmgr.c
   */
  extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
! extern Oid fmgr_internal_function(const char *proname);
! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo);
! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
! extern Oid get_fn_expr_functype(FunctionCallInfo fcinfo);

  /*
   * Routines in dfmgr.c
Index: src/include/catalog/pg_amop.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amop.h,v
retrieving revision 1.49
diff -c -r1.49 pg_amop.h
*** src/include/catalog/pg_amop.h    26 May 2003 00:11:27 -0000    1.49
--- src/include/catalog/pg_amop.h    2 Jun 2003 02:00:05 -0000
***************
*** 418,423 ****
--- 418,432 ----
  DATA(insert (    2098 4 f 2335 ));
  DATA(insert (    2098 5 f 2336 ));

+ /*
+  *    btree array_ops
+  */
+
+ DATA(insert (     397 1 f 1072 ));
+ DATA(insert (     397 2 f 1074 ));
+ DATA(insert (     397 3 f 1070 ));
+ DATA(insert (     397 4 f 1075 ));
+ DATA(insert (     397 5 f 1073 ));

  /*
   *    hash index _ops
Index: src/include/catalog/pg_amproc.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amproc.h,v
retrieving revision 1.37
diff -c -r1.37 pg_amproc.h
*** src/include/catalog/pg_amproc.h    26 May 2003 00:11:27 -0000    1.37
--- src/include/catalog/pg_amproc.h    2 Jun 2003 02:25:34 -0000
***************
*** 78,83 ****
--- 78,84 ----


  /* btree */
+ DATA(insert (     397 1  398 ));
  DATA(insert (     421 1    357 ));
  DATA(insert (     423 1 1596 ));
  DATA(insert (     424 1 1693 ));
Index: src/include/catalog/pg_opclass.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_opclass.h,v
retrieving revision 1.50
diff -c -r1.50 pg_opclass.h
*** src/include/catalog/pg_opclass.h    28 May 2003 16:04:00 -0000    1.50
--- src/include/catalog/pg_opclass.h    2 Jun 2003 01:47:07 -0000
***************
*** 87,92 ****
--- 87,93 ----
   */

  DATA(insert OID =  421 (    403        abstime_ops        PGNSP PGUID  702 t 0 ));
+ DATA(insert OID =  397 (    403        array_ops        PGNSP PGUID 2277 t 0 ));
  DATA(insert OID =  422 (    402        bigbox_ops        PGNSP PGUID  603 f 0 ));
  DATA(insert OID =  423 (    403        bit_ops            PGNSP PGUID 1560 t 0 ));
  DATA(insert OID =  424 (    403        bool_ops        PGNSP PGUID   16 t 0 ));
Index: src/include/catalog/pg_operator.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_operator.h,v
retrieving revision 1.114
diff -c -r1.114 pg_operator.h
*** src/include/catalog/pg_operator.h    26 May 2003 00:11:27 -0000    1.114
--- src/include/catalog/pg_operator.h    1 Jun 2003 23:10:35 -0000
***************
*** 116,125 ****
  DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
  DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 

! DATA(insert OID = 329 (  "="       PGNSP PGUID b f 2277 2277 16   329 0 0 0    0 0 array_eq     eqsel eqjoinsel ));
! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277   0 0 0 0    0 0 array_append   -       -     ));
! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277   0 0 0 0    0 0 array_prepend  -       -     ));
! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277   0 0 0 0    0 0 array_cat      -       -     ));

  DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
  DATA(insert OID = 353 (  "="       PGNSP PGUID b t    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
--- 116,130 ----
  DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
  DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 

! DATA(insert OID = 1070 (  "="       PGNSP PGUID b f 2277 2277 16 1070 1071  1072 1072 1072 1073 array_eq eqsel
eqjoinsel)); 
! DATA(insert OID = 1071 (  "<>"       PGNSP PGUID b f 2277 2277 16 1071 1070  0 0 0 0 array_ne neqsel neqjoinsel ));
! DATA(insert OID = 1072 (  "<"       PGNSP PGUID b f 2277 2277 16 1073 1075  0 0 0 0 array_lt scalarltsel
scalarltjoinsel)); 
! DATA(insert OID = 1073 (  ">"       PGNSP PGUID b f 2277 2277 16 1072 1074  0 0 0 0 array_gt scalargtsel
scalargtjoinsel)); 
! DATA(insert OID = 1074 (  "<="       PGNSP PGUID b f 2277 2277 16 1075 1073  0 0 0 0 array_le scalarltsel
scalarltjoinsel)); 
! DATA(insert OID = 1075 (  ">="       PGNSP PGUID b f 2277 2277 16 1074 1072  0 0 0 0 array_ge scalargtsel
scalargtjoinsel)); 
! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277 0 0  0 0 0 0 array_append   -       -     ));
! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277 0 0  0 0 0 0 array_prepend  -       -     ));
! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277 0 0  0 0 0 0 array_cat      -       -     ));

  DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
  DATA(insert OID = 353 (  "="       PGNSP PGUID b t    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
***************
*** 425,430 ****
--- 430,436 ----
  DATA(insert OID = 966 (  "+"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - ));
  DATA(insert OID = 967 (  "-"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclremove - - ));
  DATA(insert OID = 968 (  "~"       PGNSP PGUID b f 1034 1033     16 0 0 0 0 0 0 aclcontains - - ));
+ DATA(insert OID = 974 (  "="       PGNSP PGUID b f 1033 1033     16 0 0 0 0 0 0 aclitemeq eqsel eqjoinsel ));

  /* additional geometric operators - thomas 1997-07-09 */
  DATA(insert OID =  969 (  "@@"       PGNSP PGUID l f    0  601    600    0  0 0 0 0 0 lseg_center - - ));
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v
retrieving revision 1.302
diff -c -r1.302 pg_proc.h
*** src/include/catalog/pg_proc.h    26 May 2003 00:11:27 -0000    1.302
--- src/include/catalog/pg_proc.h    2 Jun 2003 02:21:05 -0000
***************
*** 758,763 ****
--- 758,765 ----
  DESCR("btree less-equal-greater");
  DATA(insert OID = 360 (  bttextcmp           PGNSP PGUID 12 f f t f i 2 23 "25 25"    bttextcmp - _null_ ));
  DESCR("btree less-equal-greater");
+ DATA(insert OID = 398 (  btarraycmp           PGNSP PGUID 12 f f t f i 2 23 "2277 2277"    btarraycmp - _null_ ));
+ DESCR("btree less-equal-greater");

  DATA(insert OID = 361 (  lseg_distance       PGNSP PGUID 12 f f t f i 2 701 "601 601"  lseg_distance - _null_ ));
  DESCR("distance between");
***************
*** 984,997 ****
  DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
  DESCR("greater-than-or-equal");

- DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
- DESCR("array equal");
-
  DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
  DESCR("current user name");
  DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
  DESCR("session user name");

  DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
  DESCR("array dimensions");
  DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
--- 986,1008 ----
  DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
  DESCR("greater-than-or-equal");

  DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
  DESCR("current user name");
  DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
  DESCR("session user name");

+ DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
+ DESCR("array equal");
+ DATA(insert OID = 390 (  array_ne           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ne - _null_ ));
+ DESCR("array not equal");
+ DATA(insert OID = 391 (  array_lt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_lt - _null_ ));
+ DESCR("array less than");
+ DATA(insert OID = 392 (  array_gt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_gt - _null_ ));
+ DESCR("array greater than");
+ DATA(insert OID = 393 (  array_le           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_le - _null_ ));
+ DESCR("array less than or equal");
+ DATA(insert OID = 396 (  array_ge           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ge - _null_ ));
+ DESCR("array greater than or equal");
  DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
  DESCR("array dimensions");
  DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
***************
*** 1008,1015 ****
  DESCR("append element onto end of array");
  DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
  DESCR("prepend element onto front of array");
- DATA(insert OID = 380 (  array_accum       PGNSP PGUID 12 f f f f i 2 2277 "2277 2283" array_accum - _null_ ));
- DESCR("push element onto end of array, creating array if needed");
  DATA(insert OID = 381 (  array_assign       PGNSP PGUID 12 f f t f i 3 2277 "2277 23 2283" array_assign - _null_ ));
  DESCR("assign specific array element");
  DATA(insert OID = 382 (  array_subscript  PGNSP PGUID 12 f f t f i 2 2283 "2277 23" array_subscript - _null_ ));
--- 1019,1024 ----
***************
*** 1018,1023 ****
--- 1027,1036 ----
  DESCR("concatenate two arrays");
  DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
  DESCR("coerce array type to another array type");
+ DATA(insert OID = 394 (  str_to_array       PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ ));
+ DESCR("split delimited text into text[]");
+ DATA(insert OID = 395 (  array_to_str       PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ ));
+ DESCR("concatenate array elements, using delimiter, into text");

  DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
  DESCR("I/O");
***************
*** 1318,1323 ****
--- 1331,1338 ----
  DESCR("remove ACL item");
  DATA(insert OID = 1037 (  aclcontains       PGNSP PGUID 12 f f t f s 2 16 "1034 1033"    aclcontains - _null_ ));
  DESCR("does ACL contain item?");
+ DATA(insert OID = 1062 (  aclitemeq           PGNSP PGUID 12 f f t f s 2 16 "1033 1033"    aclitem_eq - _null_ ));
+ DESCR("equality operator for ACL items");
  DATA(insert OID = 1038 (  seteval           PGNSP PGUID 12 f f t t v 1 23 "26"  seteval - _null_ ));
  DESCR("internal function supporting PostQuel-style sets");
  DATA(insert OID = 1044 (  bpcharin           PGNSP PGUID 12 f f t f i 3 1042 "2275 26 23" bpcharin - _null_ ));
Index: src/include/nodes/primnodes.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v
retrieving revision 1.82
diff -c -r1.82 primnodes.h
*** src/include/nodes/primnodes.h    6 May 2003 00:20:33 -0000    1.82
--- src/include/nodes/primnodes.h    1 Jun 2003 15:41:47 -0000
***************
*** 225,230 ****
--- 225,231 ----
      Expr       *target;            /* expression we are aggregating on */
      bool        aggstar;        /* TRUE if argument was really '*' */
      bool        aggdistinct;    /* TRUE if it's agg(DISTINCT ...) */
+     List       *args;            /* arguments to the aggregate */
  } Aggref;

  /* ----------------
***************
*** 357,371 ****
  /* ----------------
   * SubLink
   *
!  * A SubLink represents a subselect appearing in an expression, and in some
!  * cases also the combining operator(s) just above it.    The subLinkType
!  * indicates the form of the expression represented:
   *    EXISTS_SUBLINK        EXISTS(SELECT ...)
   *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
   *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
   *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
   *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
   *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
   * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
   * same length as the subselect's targetlist.  MULTIEXPR will *always* have
   * a list with more than one entry; if the subselect has just one target
--- 358,376 ----
  /* ----------------
   * SubLink
   *
!  * A SubLink represents a subselect, or an expression, appearing in an
!  * expression, and in some cases also the combining operator(s) just above
!  * it.    The subLinkType indicates the form of the expression represented:
   *    EXISTS_SUBLINK        EXISTS(SELECT ...)
   *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
   *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
   *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
   *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
   *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
+  * If an expression is used in place of the subselect, it is transformed
+  * into a simple "(SELECT expr)" in gram.y. This is to allow arrays to be
+  * used as if they were the result of a single column subselect. If the
+  * expression is scalar, it is treated as a one element array.
   * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
   * same length as the subselect's targetlist.  MULTIEXPR will *always* have
   * a list with more than one entry; if the subselect has just one target
***************
*** 414,419 ****
--- 419,426 ----
      SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
      bool        useOr;            /* TRUE to combine column results with
                                   * "OR" not "AND" */
+     bool        isExpr;            /* TRUE if the subselect is really derived
+                                  * from a single expression */
      List       *lefthand;        /* list of outer-query expressions on the
                                   * left */
      List       *operName;        /* originally specified operator name */
***************
*** 455,460 ****
--- 462,476 ----
      SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
      bool        useOr;            /* TRUE to combine column results with
                                   * "OR" not "AND" */
+     bool        isExpr;            /* TRUE if the subselect is really derived
+                                  * from a single expression */
+     /* runtime cache for single array expressions */
+     Oid            exprtype;        /* array and element type, and other info
+                                  * needed deconstruct the array */
+     Oid            elemtype;
+     int16        elmlen;
+     bool        elmbyval;
+     char        elmalign;
      /* The combining operators, transformed to executable expressions: */
      List       *exprs;            /* list of OpExpr expression trees */
      List       *paramIds;        /* IDs of Params embedded in the above */
Index: src/include/optimizer/clauses.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/optimizer/clauses.h,v
retrieving revision 1.63
diff -c -r1.63 clauses.h
*** src/include/optimizer/clauses.h    28 May 2003 16:04:02 -0000    1.63
--- src/include/optimizer/clauses.h    1 Jun 2003 15:41:15 -0000
***************
*** 28,33 ****
--- 28,36 ----
  extern Node *get_leftop(Expr *clause);
  extern Node *get_rightop(Expr *clause);

+ extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
+                                     CoercionForm funcformat, List *funcargs);
+
  extern bool not_clause(Node *clause);
  extern Expr *make_notclause(Expr *notclause);
  extern Expr *get_notclausearg(Expr *notclause);
Index: src/include/parser/parse_oper.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_oper.h,v
retrieving revision 1.25
diff -c -r1.25 parse_oper.h
*** src/include/parser/parse_oper.h    29 Apr 2003 22:13:11 -0000    1.25
--- src/include/parser/parse_oper.h    1 Jun 2003 23:21:48 -0000
***************
*** 44,49 ****
--- 44,50 ----
  /* Convenience routines for common calls on the above */
  extern Oid    compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError);
  extern Oid    equality_oper_funcid(Oid argtype);
+ extern Oid  ordering_oper_funcid(Oid argtype);
  extern Oid    ordering_oper_opid(Oid argtype);

  /* Extract operator OID or underlying-function OID from an Operator tuple */
Index: src/include/utils/acl.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/acl.h,v
retrieving revision 1.51
diff -c -r1.51 acl.h
*** src/include/utils/acl.h    23 Jan 2003 23:39:07 -0000    1.51
--- src/include/utils/acl.h    1 Jun 2003 19:03:25 -0000
***************
*** 188,193 ****
--- 188,194 ----
  extern Datum aclinsert(PG_FUNCTION_ARGS);
  extern Datum aclremove(PG_FUNCTION_ARGS);
  extern Datum aclcontains(PG_FUNCTION_ARGS);
+ extern Datum aclitem_eq(PG_FUNCTION_ARGS);

  /*
   * prototypes for functions in aclchk.c
Index: src/include/utils/array.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v
retrieving revision 1.38
diff -c -r1.38 array.h
*** src/include/utils/array.h    8 May 2003 22:19:57 -0000    1.38
--- src/include/utils/array.h    2 Jun 2003 02:25:03 -0000
***************
*** 32,37 ****
--- 32,68 ----
      Oid            elemtype;        /* element type OID */
  } ArrayType;

+ typedef struct ArrayBuildState
+ {
+     MemoryContext mcontext;        /* where all the temp stuff is kept */
+     Datum       *dvalues;        /* array of accumulated Datums */
+     /*
+      * The allocated size of dvalues[] is always a multiple of
+      * ARRAY_ELEMS_CHUNKSIZE
+      */
+ #define ARRAY_ELEMS_CHUNKSIZE    64
+     int            nelems;            /* number of valid Datums in dvalues[] */
+     Oid            element_type;    /* data type of the Datums */
+     int16        typlen;            /* needed info about datatype */
+     bool        typbyval;
+     char        typalign;
+ } ArrayBuildState;
+
+ /*
+  * structure to cache type metadata needed for array manipulation
+  */
+ typedef struct ArrayMetaState
+ {
+     Oid                element_type;
+     int                typlen;
+     bool            typbyval;
+     char            typdelim;
+     Oid                typelem;
+     Oid                typiofunc;
+     char            typalign;
+     FmgrInfo        proc;
+ } ArrayMetaState;
+
  /*
   * fmgr macros for array objects
   */
***************
*** 86,91 ****
--- 117,128 ----
  extern Datum array_send(PG_FUNCTION_ARGS);
  extern Datum array_length_coerce(PG_FUNCTION_ARGS);
  extern Datum array_eq(PG_FUNCTION_ARGS);
+ extern Datum array_ne(PG_FUNCTION_ARGS);
+ extern Datum array_lt(PG_FUNCTION_ARGS);
+ extern Datum array_gt(PG_FUNCTION_ARGS);
+ extern Datum array_le(PG_FUNCTION_ARGS);
+ extern Datum array_ge(PG_FUNCTION_ARGS);
+ extern Datum btarraycmp(PG_FUNCTION_ARGS);
  extern Datum array_dims(PG_FUNCTION_ARGS);
  extern Datum array_lower(PG_FUNCTION_ARGS);
  extern Datum array_upper(PG_FUNCTION_ARGS);
***************
*** 124,130 ****
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
!

  /*
   * prototypes for functions defined in arrayutils.c
--- 161,174 ----
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
! extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
!                                          Datum dvalue, bool disnull,
!                                          Oid element_type,
!                                          MemoryContext rcontext);
! extern Datum makeArrayResult(ArrayBuildState *astate,
!                              MemoryContext rcontext);
! extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
!                                int *dims, int *lbs, MemoryContext rcontext);

  /*
   * prototypes for functions defined in arrayutils.c
***************
*** 143,152 ****
   */
  extern Datum singleton_array(PG_FUNCTION_ARGS);
  extern Datum array_push(PG_FUNCTION_ARGS);
- extern Datum array_accum(PG_FUNCTION_ARGS);
  extern Datum array_cat(PG_FUNCTION_ARGS);

! extern ArrayType *create_singleton_array(Oid element_type,
                                           Datum element,
                                           int ndims);

--- 187,196 ----
   */
  extern Datum singleton_array(PG_FUNCTION_ARGS);
  extern Datum array_push(PG_FUNCTION_ARGS);
  extern Datum array_cat(PG_FUNCTION_ARGS);

! extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
!                                          Oid element_type,
                                           Datum element,
                                           int ndims);

Index: src/include/utils/builtins.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/builtins.h,v
retrieving revision 1.219
diff -c -r1.219 builtins.h
*** src/include/utils/builtins.h    26 May 2003 00:11:28 -0000    1.219
--- src/include/utils/builtins.h    1 Jun 2003 15:41:15 -0000
***************
*** 530,535 ****
--- 530,537 ----
                        List **namelist);
  extern Datum replace_text(PG_FUNCTION_ARGS);
  extern Datum split_text(PG_FUNCTION_ARGS);
+ extern Datum text_to_array(PG_FUNCTION_ARGS);
+ extern Datum array_to_text(PG_FUNCTION_ARGS);
  extern Datum to_hex32(PG_FUNCTION_ARGS);
  extern Datum to_hex64(PG_FUNCTION_ARGS);
  extern Datum md5_text(PG_FUNCTION_ARGS);
Index: src/include/utils/lsyscache.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v
retrieving revision 1.70
diff -c -r1.70 lsyscache.h
*** src/include/utils/lsyscache.h    26 May 2003 00:11:28 -0000    1.70
--- src/include/utils/lsyscache.h    1 Jun 2003 15:41:15 -0000
***************
*** 15,20 ****
--- 15,29 ----

  #include "access/htup.h"

+ /* I/O function selector for system_cache_lookup */
+ typedef enum IOFuncSelector
+ {
+     IOFunc_input,
+     IOFunc_output,
+     IOFunc_receive,
+     IOFunc_send
+ } IOFuncSelector;
+
  extern bool op_in_opclass(Oid opno, Oid opclass);
  extern bool op_requires_recheck(Oid opno, Oid opclass);
  extern Oid    get_opclass_member(Oid opclass, int16 strategy);
***************
*** 39,44 ****
--- 48,54 ----
  extern RegProcedure get_oprjoin(Oid opno);
  extern char *get_func_name(Oid funcid);
  extern Oid    get_func_rettype(Oid funcid);
+ extern Oid *get_func_argtypes(Oid funcid, int *nargs);
  extern bool get_func_retset(Oid funcid);
  extern bool func_strict(Oid funcid);
  extern char func_volatile(Oid funcid);
***************
*** 54,59 ****
--- 64,77 ----
  extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
  extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
                       char *typalign);
+ extern void get_type_metadata(Oid element_type,
+                                 IOFuncSelector which_func,
+                                 int *typlen,
+                                 bool *typbyval,
+                                 char *typdelim,
+                                 Oid *typelem,
+                                 Oid *proc,
+                                 char *typalign);
  extern char get_typstorage(Oid typid);
  extern int32 get_typtypmod(Oid typid);
  extern Node *get_typdefault(Oid typid);

Re: array support patch phase 1 patch

From
Bruce Momjian
Date:
(I assume this includes all your outstanding unapplied array patches.)

Your patch has been added to the PostgreSQL unapplied patches list at:

    http://momjian.postgresql.org/cgi-bin/pgpatches

I will try to apply it within the next 48 hours.

---------------------------------------------------------------------------


Joe Conway wrote:
> Tom Lane wrote:
> > Joe Conway <mail@joeconway.com> writes:
> >>Next question: should I roll the three array related patches floating
> >>around (phase2, phase3, polycoerce) into one big patch again? It's
> >>difficult for me to continue to make progress without doing that.
> >
> > If it's easier at your end.
>
> Attached is the "super combo" array patch ;-)
>
> It includes the following previously sent patches:
> array-phase2.19.patch
> array-phase3.05.patch
> array-polycoerce.3.patch
>
> It also includes the following additional changes:
> 1) aclitem type now has an equality operator
> 2) examine_attribute() now checks for the element type's equality and
>     ordering operators when dealing with a varlena array type
> 3) There are now "<>, ">", "<", "<=", and ">=" operators for
>     array-to-array comparison, using semantics similar to text's
>     character-to-character comparison.
> 4) There is also now a btarraycmp() function and other required
>     btree support. BTree indexes can now be created on varlena arrays.
>
> Below are some examples of the new (since earlier patches)
> functionality. CVS tip from this morning plus this patch compiles
> cleanly and passes all 90 regression tests on my dev box. If there are
> no objections, please apply.
>
> Thanks,
>
> Joe
>
>
> Examples:
> ------------------------------------------
> create table tse(f1 int, f2 int[], f3 text[]);
> insert into tse values(1,array[69,42,54], array['g','d','e']);
> insert into tse values(2,array[1,2,3], array['x','y','z']);
> insert into tse values(3,array[2,99,0], array['Tom','Bruce']);
> insert into tse values(4,array[1,1,1], array['a','a','a']);
> insert into tse values(5,array[5,6,7], array['a','b','c']);
>
> regression=# select distinct array(select f1 from tse);
>    ?column?
> -------------
>   {1,2,3,4,5}
> (1 row)
>
> regression=# select * from tse where f2 < array[1,2,3,4];
>   f1 |   f2    |   f3
> ----+---------+---------
>    2 | {1,2,3} | {x,y,z}
>    4 | {1,1,1} | {a,a,a}
> (2 rows)
>
> regression=# select * from tse where f2 < array[1,2,3];
>   f1 |   f2    |   f3
> ----+---------+---------
>    4 | {1,1,1} | {a,a,a}
> (1 row)
>
> regression=# select * from tse where f2 <= array[1,2,3];
>   f1 |   f2    |   f3
> ----+---------+---------
>    2 | {1,2,3} | {x,y,z}
>    4 | {1,1,1} | {a,a,a}
> (2 rows)
>
> regression=# select * from tse where f2 > array[1,2,3];
>   f1 |     f2     |     f3
> ----+------------+-------------
>    1 | {69,42,54} | {g,d,e}
>    3 | {2,99,0}   | {Tom,Bruce}
>    5 | {5,6,7}    | {a,b,c}
> (3 rows)
>
> regression=# select * from tse where f2 >= array[1,2,3];
>   f1 |     f2     |     f3
> ----+------------+-------------
>    1 | {69,42,54} | {g,d,e}
>    2 | {1,2,3}    | {x,y,z}
>    3 | {2,99,0}   | {Tom,Bruce}
>    5 | {5,6,7}    | {a,b,c}
> (4 rows)
>
> regression=# select * from tse where f2 != array[1,2,3];
>   f1 |     f2     |     f3
> ----+------------+-------------
>    1 | {69,42,54} | {g,d,e}
>    3 | {2,99,0}   | {Tom,Bruce}
>    4 | {1,1,1}    | {a,a,a}
>    5 | {5,6,7}    | {a,b,c}
> (4 rows)
>
> regression=# select * from tse where f2 = array[1,2,3];
>   f1 |   f2    |   f3
> ----+---------+---------
>    2 | {1,2,3} | {x,y,z}
> (1 row)
>
> regression=# select * from tse where f3 >= array['Tom'] and f3 < array['a'];
>   f1 |    f2    |     f3
> ----+----------+-------------
>    3 | {2,99,0} | {Tom,Bruce}
> (1 row)
>
> regression=# create index arridx1 on tse(f2);
> CREATE INDEX
> regression=# set enable_seqscan to off;
> SET
> regression=# explain analyze select * from tse where f2 = array[1,2,3];
>                         QUERY PLAN
> -----------------------------------------------------------------------
>   Index Scan using arridx1 on tse  (cost=0.00..4.68 rows=2 width=68)
> (actual time=0.07..0.07 rows=1 loops=1)
>     Index Cond: (f2 = '{1,2,3}'::integer[])
>   Total runtime: 0.15 msec
> (3 rows)

> Index: doc/src/sgml/array.sgml
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
> retrieving revision 1.25
> diff -c -r1.25 array.sgml
> *** doc/src/sgml/array.sgml    13 Mar 2003 01:30:26 -0000    1.25
> --- doc/src/sgml/array.sgml    1 Jun 2003 15:41:15 -0000
> ***************
> *** 9,15 ****
>
>    <para>
>     <productname>PostgreSQL</productname> allows columns of a table to be
> !   defined as variable-length multidimensional arrays. Arrays of any
>     built-in type or user-defined type can be created.
>    </para>
>
> --- 9,15 ----
>
>    <para>
>     <productname>PostgreSQL</productname> allows columns of a table to be
> !   defined as variable-length multi-dimensional arrays. Arrays of any
>     built-in type or user-defined type can be created.
>    </para>
>
> ***************
> *** 60,73 ****
>   </programlisting>
>    </para>
>
>    <note>
>     <para>
> !    A limitation of the present array implementation is that individual
> !    elements of an array cannot be SQL null values.  The entire array can be set
> !    to null, but you can't have an array with some elements null and some
> !    not.  Fixing this is on the to-do list.
>     </para>
>    </note>
>    </sect2>
>
>    <sect2>
> --- 60,133 ----
>   </programlisting>
>    </para>
>
> +  <para>
> +   A limitation of the present array implementation is that individual
> +   elements of an array cannot be SQL null values.  The entire array can be set
> +   to null, but you can't have an array with some elements null and some
> +   not.
> +  </para>
> +  <para>
> +   This can lead to surprising results. For example, the result of the
> +   previous two inserts looks like this:
> + <programlisting>
> + SELECT * FROM sal_emp;
> +  name  |      pay_by_quarter       |      schedule
> + -------+---------------------------+--------------------
> +  Bill  | {10000,10000,10000,10000} | {{meeting},{""}}
> +  Carol | {20000,25000,25000,25000} | {{talk},{meeting}}
> + (2 rows)
> + </programlisting>
> +   Because the <literal>[2][2]</literal> element of
> +   <structfield>schedule</structfield> is missing in each of the
> +   <command>INSERT</command> statements, the <literal>[1][2]</literal>
> +   element is discarded.
> +  </para>
> +
> +  <note>
> +   <para>
> +    Fixing this is on the to-do list.
> +   </para>
> +  </note>
> +
> +  <para>
> +   The <command>ARRAY</command> expression syntax may also be used:
> + <programlisting>
> + INSERT INTO sal_emp
> +     VALUES ('Bill',
> +     ARRAY[10000, 10000, 10000, 10000],
> +     ARRAY[['meeting', 'lunch'], ['','']]);
> +
> + INSERT INTO sal_emp
> +     VALUES ('Carol',
> +     ARRAY[20000, 25000, 25000, 25000],
> +     ARRAY[['talk', 'consult'], ['meeting', '']]);
> + SELECT * FROM sal_emp;
> +  name  |      pay_by_quarter       |           schedule
> + -------+---------------------------+-------------------------------
> +  Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}}
> +  Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}}
> + (2 rows)
> + </programlisting>
> +   Note that with this syntax, multi-dimensional arrays must have matching
> +   extents for each dimension. This eliminates the missing-array-elements
> +   problem above. For example:
> + <programlisting>
> + INSERT INTO sal_emp
> +     VALUES ('Carol',
> +     ARRAY[20000, 25000, 25000, 25000],
> +     ARRAY[['talk', 'consult'], ['meeting']]);
> + ERROR:  Multiple dimension arrays must have array expressions with matching dimensions
> + </programlisting>
> +   Also notice that string literals are single quoted instead of double quoted.
> +  </para>
> +
>    <note>
>     <para>
> !    The examples in the rest of this section are based on the
> !    <command>ARRAY</command> expression syntax <command>INSERT</command>s.
>     </para>
>    </note>
> +
>    </sect2>
>
>    <sect2>
> ***************
> *** 132,142 ****
>   </programlisting>
>
>     with the same result.  An array subscripting operation is always taken to
> !   represent an array slice if any of the subscripts are written in the
> !   form
>     <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
>     A lower bound of 1 is assumed for any subscript where only one value
> !   is specified.
>    </para>
>
>    <para>
> --- 192,221 ----
>   </programlisting>
>
>     with the same result.  An array subscripting operation is always taken to
> !   represent an array slice if any of the subscripts are written in the form
>     <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
>     A lower bound of 1 is assumed for any subscript where only one value
> !   is specified; another example follows:
> ! <programlisting>
> ! SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
> !          schedule
> ! ---------------------------
> !  {{meeting,lunch},{"",""}}
> ! (1 row)
> ! </programlisting>
> !  </para>
> !
> !  <para>
> !   Additionally, we can also access a single arbitrary array element of
> !   a one-dimensional array with the <function>array_subscript</function>
> !   function:
> ! <programlisting>
> ! SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
> !  array_subscript
> ! -----------------
> !            10000
> ! (1 row)
> ! </programlisting>
>    </para>
>
>    <para>
> ***************
> *** 147,153 ****
>       WHERE name = 'Carol';
>   </programlisting>
>
> !   or updated at a single element:
>
>   <programlisting>
>   UPDATE sal_emp SET pay_by_quarter[4] = 15000
> --- 226,248 ----
>       WHERE name = 'Carol';
>   </programlisting>
>
> !   or using the <command>ARRAY</command> expression syntax:
> !
> ! <programlisting>
> ! UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
> !     WHERE name = 'Carol';
> ! </programlisting>
> !
> !   <note>
> !    <para>
> !     Anywhere you can use the <quote>curly braces</quote> array syntax,
> !     you can also use the <command>ARRAY</command> expression syntax. The
> !     remainder of this section will illustrate only one or the other, but
> !     not both.
> !    </para>
> !   </note>
> !
> !   An array may also be updated at a single element:
>
>   <programlisting>
>   UPDATE sal_emp SET pay_by_quarter[4] = 15000
> ***************
> *** 160,165 ****
> --- 255,268 ----
>   UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
>       WHERE name = 'Carol';
>   </programlisting>
> +
> +   A one-dimensional array may also be updated with the
> +   <function>array_assign</function> function:
> +
> + <programlisting>
> + UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000)
> +     WHERE name = 'Bill';
> + </programListing>
>    </para>
>
>    <para>
> ***************
> *** 169,175 ****
>     value currently has 4 elements, it will have five elements after an
>     update that assigns to <literal>array[5]</>.  Currently, enlargement in
>     this fashion is only allowed for one-dimensional arrays, not
> !   multidimensional arrays.
>    </para>
>
>    <para>
> --- 272,278 ----
>     value currently has 4 elements, it will have five elements after an
>     update that assigns to <literal>array[5]</>.  Currently, enlargement in
>     this fashion is only allowed for one-dimensional arrays, not
> !   multi-dimensional arrays.
>    </para>
>
>    <para>
> ***************
> *** 179,184 ****
> --- 282,367 ----
>    </para>
>
>    <para>
> +   An array can also be enlarged by using the functions
> +   <function>array_prepend</function>, <function>array_append</function>,
> +   or <function>array_cat</function>. The first two only support one-dimensional
> +   arrays, but <function>array_cat</function> supports multi-dimensional arrays.
> +   Some examples:
> +
> + <programlisting>
> + SELECT array_prepend(1, ARRAY[2,3]);
> +  array_prepend
> + ---------------
> +  {1,2,3}
> + (1 row)
> +
> + SELECT array_append(ARRAY[1,2], 3);
> +  array_append
> + --------------
> +  {1,2,3}
> + (1 row)
> +
> + SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
> +    array_cat
> + ---------------
> +  {{1,2},{3,4}}
> + (1 row)
> +
> + SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
> +       array_cat
> + ---------------------
> +  {{1,2},{3,4},{5,6}}
> + (1 row)
> +
> + SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
> +       array_cat
> + ---------------------
> +  {{5,6},{1,2},{3,4}}
> + </programlisting>
> +
> +   <function>array_prepend</function> and <function>array_append</function>
> +   work with a one-dimensional array and a single element to be pushed on
> +   to the beginning or end of the array, respectively. The array is extended
> +   in the direction of the push. Hence, by pushing onto the beginning of an
> +   array with a one-based subscript, a zero-based subscript array is created:
> +
> + <programlisting>
> + SELECT array_dims(t.f) FROM (SELECT array_prepend(1, ARRAY[2,3]) AS f) AS t;
> +  array_dims
> + ------------
> +  [0:2]
> + (1 row)
> + </programlisting>
> +
> +   <function>array_cat</function> works with either two
> +   <replaceable>n</>-dimension arrays, or an <replaceable>n</>-dimension
> +   and an <replaceable>n+1</> dimension array. In the former case, the two
> +   <replaceable>n</>-dimension arrays become outer elements of an
> +   <replaceable>n+1</> dimension array. In the latter, the
> +   <replaceable>n</>-dimension array is added as either the first or last
> +   outer element of the <replaceable>n+1</> dimension array.
> +  </para>
> +
> +  <para>
> +   A final method of enlarging arrays is through the concatenation operator,
> +   <command>||</command>, which works exactly as <function>array_cat</function>
> +   does.
> + <programlisting>
> + SELECT ARRAY[1,2] || ARRAY[3,4];
> +    ?column?
> + ---------------
> +  {{1,2},{3,4}}
> + (1 row)
> +
> + SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
> +       ?column?
> + ---------------------
> +  {{5,6},{1,2},{3,4}}
> + (1 row)
> + </programlisting>
> +  </para>
> +
> +  <para>
>     The syntax for <command>CREATE TABLE</command> allows fixed-length
>     arrays to be defined:
>
> ***************
> *** 194,199 ****
> --- 377,392 ----
>    </para>
>
>    <para>
> +   An alternative syntax for one-dimensional arrays may be used.
> +   <structfield>pay_by_quarter</structfield> could have been defined as:
> + <programlisting>
> +     pay_by_quarter  integer ARRAY[4],
> + </programlisting>
> +   This syntax may <emphasis>only</emphasis> be used with the integer
> +   constant to denote the array size.
> +  </para>
> +
> +  <para>
>     Actually, the current implementation does not enforce the declared
>     number of dimensions either.  Arrays of a particular element type are
>     all considered to be of the same type, regardless of size or number
> ***************
> *** 292,298 ****
>      for the array's element type.  (Among the standard data types provided
>      in the <productname>PostgreSQL</productname> distribution, type
>      <literal>box</> uses a semicolon (<literal>;</>) but all the others
> !    use comma.)  In a multidimensional array, each dimension (row, plane,
>      cube, etc.) gets its own level of curly braces, and delimiters
>      must be written between adjacent curly-braced entities of the same level.
>      You may write whitespace before a left brace, after a right
> --- 485,491 ----
>      for the array's element type.  (Among the standard data types provided
>      in the <productname>PostgreSQL</productname> distribution, type
>      <literal>box</> uses a semicolon (<literal>;</>) but all the others
> !    use comma.)  In a multi-dimensional array, each dimension (row, plane,
>      cube, etc.) gets its own level of curly braces, and delimiters
>      must be written between adjacent curly-braced entities of the same level.
>      You may write whitespace before a left brace, after a right
> ***************
> *** 300,305 ****
> --- 493,564 ----
>      is not ignored, however: after skipping leading whitespace, everything
>      up to the next right brace or delimiter is taken as the item value.
>     </para>
> +
> +   <para>
> +    As illustrated earlier in this chapter, arrays may also be represented
> +    using the <command>ARRAY</command> expression syntax. This representation
> +    of an array value consists of items that are interpreted according to the
> +    I/O conversion rules for the array's element type, plus decoration that
> +    indicates the array structure. The decoration consists of the keyword
> +    <command>ARRAY</command> and square brackets (<literal>[</> and
> +    <literal>]</>) around the array values, plus delimiter characters between
> +    adjacent items. The delimiter character is always a comma (<literal>,</>).
> +    When representing multi-dimensional arrays, the keyword
> +    <command>ARRAY</command> is only necessary for the outer level. For example,
> +    <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as:
> + <programlisting>
> + SELECT ARRAY[['hello world', 'happy birthday']];
> +                array
> + ------------------------------------
> +  {{"hello world","happy birthday"}}
> + (1 row)
> + </programlisting>
> +   or it also could be written as:
> + <programlisting>
> + SELECT ARRAY[ARRAY['hello world', 'happy birthday']];
> +                array
> + ------------------------------------
> +  {{"hello world","happy birthday"}}
> + (1 row)
> + </programlisting>
> +   </para>
> +
> +   <para>
> +    A final method to represent an array, is through an
> +    <command>ARRAY</command> sub-select expression. For example:
> + <programlisting>
> + SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
> +                           ?column?
> + -------------------------------------------------------------
> +  {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
> + (1 row)
> + </programlisting>
> +   The sub-select may <emphasis>only</emphasis> return a single column. The
> +   resulting one-dimensional array will have an element for each row in the
> +   sub-select result, with an element type matching that of the sub-select's
> +   target column.
> +   </para>
> +
> +   <para>
> +    Arrays may be cast from one type to another in similar fashion to other
> +    data types:
> +
> + <programlisting>
> + SELECT ARRAY[1,2,3]::oid[];
> +   array
> + ---------
> +  {1,2,3}
> + (1 row)
> +
> + SELECT CAST(ARRAY[1,2,3] AS float8[]);
> +   array
> + ---------
> +  {1,2,3}
> + (1 row)
> + </programlisting>
> +
> +   </para>
> +
>    </sect2>
>
>    <sect2>
> ***************
> *** 316,321 ****
> --- 575,588 ----
>      Alternatively, you can use backslash-escaping to protect all data characters
>      that would otherwise be taken as array syntax or ignorable white space.
>     </para>
> +
> +  <note>
> +   <para>
> +    The discussion in the preceding paragraph with respect to double quoting does
> +    not pertain to the <command>ARRAY</command> expression syntax. In that case,
> +    each element is quoted exactly as any other literal value of the element type.
> +   </para>
> +  </note>
>
>     <para>
>      The array output routine will put double quotes around element values
> Index: doc/src/sgml/func.sgml
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v
> retrieving revision 1.154
> diff -c -r1.154 func.sgml
> *** doc/src/sgml/func.sgml    5 May 2003 15:08:49 -0000    1.154
> --- doc/src/sgml/func.sgml    1 Jun 2003 15:41:15 -0000
> ***************
> *** 6962,6967 ****
> --- 6962,7209 ----
>
>     </sect1>
>
> +  <sect1 id="functions-array">
> +   <title>Array Functions</title>
> +
> +   <para>
> +    <xref linkend="array-operators-table"> shows the operators
> +    available for the <type>array</type> types.
> +   </para>
> +
> +     <table id="array-operators-table">
> +      <title><type>array</type> Operators</title>
> +      <tgroup cols="4">
> +       <thead>
> +        <row>
> +     <entry>Operator</entry>
> +     <entry>Description</entry>
> +     <entry>Example</entry>
> +     <entry>Result</entry>
> +        </row>
> +       </thead>
> +       <tbody>
> +        <row>
> +     <entry> <literal>=</literal> </entry>
> +     <entry>equals</entry>
> +     <entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry>
> +     <entry><literal>t</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>array-to-array concatenation</entry>
> +     <entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry>
> +     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>array-to-array concatenation</entry>
> +     <entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry>
> +     <entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>element-to-array concatenation</entry>
> +     <entry><literal>3 || ARRAY[4,5,6]</literal></entry>
> +     <entry><literal>{3,4,5,6}</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>array-to-element concatenation</entry>
> +     <entry><literal>ARRAY[4,5,6] || 7</literal></entry>
> +     <entry><literal>{4,5,6,7}</literal></entry>
> +        </row>
> +       </tbody>
> +      </tgroup>
> +     </table>
> +
> +   <para>
> +    <xref linkend="array-functions-table"> shows the functions
> +    available for use with array types. See <xref linkend="arrays">
> +    for more discussion and examples for the use of these functions.
> +   </para>
> +
> +     <table id="array-functions-table">
> +      <title><type>array</type> Functions</title>
> +      <tgroup cols="5">
> +       <thead>
> +        <row>
> +     <entry>Function</entry>
> +     <entry>Return Type</entry>
> +     <entry>Description</entry>
> +     <entry>Example</entry>
> +     <entry>Result</entry>
> +        </row>
> +       </thead>
> +       <tbody>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_append</function>
> +       (<type>anyarray</type>, <type>anyelement</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>anyarray</type></entry>
> +     <entry>
> +      append an element to the end of an array, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_append(ARRAY[1,2], 3)</literal></entry>
> +     <entry><literal>{1,2,3}</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_assign</function>
> +       (<type>anyarray</type>, <type>integer</type>, <type>anyelement</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>anyarray</type></entry>
> +     <entry>
> +      assign a value to a specific array element, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_assign(ARRAY[1,2,3], 2, 99)</literal></entry>
> +     <entry><literal>{1,99,3}</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_cat</function>
> +       (<type>anyarray</type>, <type>anyarray</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>anyarray</type></entry>
> +     <entry>
> +      concatenate two arrays, returning <literal>NULL</literal>
> +      for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry>
> +     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_dims</function>
> +       (<type>anyarray</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>text</type></entry>
> +     <entry>
> +      returns a text representation of array dimension lower and upper bounds,
> +      generating an ERROR for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry>
> +     <entry><literal>[1:2][1:3]</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_lower</function>
> +       (<type>anyarray</type>, <type>integer</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>integer</type></entry>
> +     <entry>
> +      returns lower bound of the requested array dimension, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry>
> +     <entry><literal>0</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_prepend</function>
> +       (<type>anyelement</type>, <type>anyarray</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>anyarray</type></entry>
> +     <entry>
> +      append an element to the beginning of an array, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry>
> +     <entry><literal>{1,2,3}</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_subscript</function>
> +       (<type>anyarray</type>, <type>integer</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>anyelement</type></entry>
> +     <entry>
> +      returns requested array element, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_subscript(ARRAY[1,2,3], 3)</literal></entry>
> +     <entry><literal>3</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_to_str</function>
> +       (<type>anyarray</type>, <type>text</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>text</type></entry>
> +     <entry>
> +      concatenates array elements using provided delimiter, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_to_str(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry>
> +     <entry><literal>1.10~^~2.20~^~3.30</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_upper</function>
> +       (<type>anyarray</type>, <type>integer</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>integer</type></entry>
> +     <entry>
> +      returns upper bound of the requested array dimension, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry>
> +     <entry><literal>4</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>singleton_array</function>
> +       (<type>anyelement</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>anyarray</type></entry>
> +     <entry>
> +      create an array from the provided element, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>singleton_array(1)</literal></entry>
> +     <entry><literal>{1}</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>str_to_array</function>
> +       (<type>text</type>, <type>text</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>text[]</type></entry>
> +     <entry>
> +      splits string into array elements using provided delimiter, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>str_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry>
> +     <entry><literal>{1.1,2.2,3.3}</literal></entry>
> +        </row>
> +       </tbody>
> +      </tgroup>
> +     </table>
> +   </sect1>
>
>    <sect1 id="functions-aggregate">
>     <title>Aggregate Functions</title>
> Index: src/backend/catalog/pg_aggregate.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_aggregate.c,v
> retrieving revision 1.56
> diff -c -r1.56 pg_aggregate.c
> *** src/backend/catalog/pg_aggregate.c    18 Sep 2002 21:35:20 -0000    1.56
> --- src/backend/catalog/pg_aggregate.c    1 Jun 2003 15:41:15 -0000
> ***************
> *** 50,59 ****
>       Oid            finalfn = InvalidOid;    /* can be omitted */
>       Oid            finaltype;
>       Oid            fnArgs[FUNC_MAX_ARGS];
> !     int            nargs;
>       Oid            procOid;
>       TupleDesc    tupDesc;
>       int            i;
>       ObjectAddress myself,
>                   referenced;
>
> --- 50,65 ----
>       Oid            finalfn = InvalidOid;    /* can be omitted */
>       Oid            finaltype;
>       Oid            fnArgs[FUNC_MAX_ARGS];
> !     int            nargs_transfn;
> !     int            nargs_finalfn;
>       Oid            procOid;
>       TupleDesc    tupDesc;
>       int            i;
> +     Oid            rettype;
> +     Oid           *true_oid_array_transfn;
> +     Oid           *true_oid_array_finalfn;
> +     bool        retset;
> +     FuncDetailCode fdresult;
>       ObjectAddress myself,
>                   referenced;
>
> ***************
> *** 68,91 ****
>       MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>       fnArgs[0] = aggTransType;
>       if (aggBaseType == ANYOID)
> !         nargs = 1;
>       else
>       {
>           fnArgs[1] = aggBaseType;
> !         nargs = 2;
>       }
> !     transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
>       if (!OidIsValid(transfn))
> !         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
>       tup = SearchSysCache(PROCOID,
>                            ObjectIdGetDatum(transfn),
>                            0, 0, 0);
>       if (!HeapTupleIsValid(tup))
> !         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
>       proc = (Form_pg_proc) GETSTRUCT(tup);
> -     if (proc->prorettype != aggTransType)
> -         elog(ERROR, "return type of transition function %s is not %s",
> -          NameListToString(aggtransfnName), format_type_be(aggTransType));
>
>       /*
>        * If the transfn is strict and the initval is NULL, make sure input
> --- 74,122 ----
>       MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>       fnArgs[0] = aggTransType;
>       if (aggBaseType == ANYOID)
> !         nargs_transfn = 1;
>       else
>       {
>           fnArgs[1] = aggBaseType;
> !         nargs_transfn = 2;
>       }
> !
> !     /*
> !      * func_get_detail looks up the function in the catalogs, does
> !      * disambiguation for polymorphic functions, handles inheritance, and
> !      * returns the funcid and type and set or singleton status of the
> !      * function's return value.  it also returns the true argument types
> !      * to the function.
> !      */
> !     fdresult = func_get_detail(aggtransfnName, NIL, nargs_transfn, fnArgs,
> !                                &transfn, &rettype, &retset,
> !                                &true_oid_array_transfn);
> !
> !     /* only valid case is a normal function */
> !     if (fdresult != FUNCDETAIL_NORMAL)
> !         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
> !
>       if (!OidIsValid(transfn))
> !         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
> !
> !     /*
> !      * enforce consistency with ANYARRAY and ANYELEMENT argument
> !      * and return types, possibly modifying return type along the way
> !      */
> !     rettype = enforce_generic_type_consistency(fnArgs, true_oid_array_transfn,
> !                                                        nargs_transfn, rettype);
> !
> !     if (rettype != aggTransType)
> !         elog(ERROR, "return type of transition function %s is not %s",
> !          NameListToString(aggtransfnName), format_type_be(aggTransType));
> !
>       tup = SearchSysCache(PROCOID,
>                            ObjectIdGetDatum(transfn),
>                            0, 0, 0);
>       if (!HeapTupleIsValid(tup))
> !         func_error("AggregateCreate", aggtransfnName,
> !                         nargs_transfn, fnArgs, NULL);
>       proc = (Form_pg_proc) GETSTRUCT(tup);
>
>       /*
>        * If the transfn is strict and the initval is NULL, make sure input
> ***************
> *** 105,121 ****
>       {
>           MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>           fnArgs[0] = aggTransType;
> !         finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
> !         if (!OidIsValid(finalfn))
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !         tup = SearchSysCache(PROCOID,
> !                              ObjectIdGetDatum(finalfn),
> !                              0, 0, 0);
> !         if (!HeapTupleIsValid(tup))
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !         proc = (Form_pg_proc) GETSTRUCT(tup);
> !         finaltype = proc->prorettype;
> !         ReleaseSysCache(tup);
>       }
>       else
>       {
> --- 136,161 ----
>       {
>           MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>           fnArgs[0] = aggTransType;
> !         nargs_finalfn = 1;
> !
> !         fdresult = func_get_detail(aggfinalfnName, NIL, 1, fnArgs,
> !                                    &finalfn, &rettype, &retset,
> !                                    &true_oid_array_finalfn);
> !
> !         /* only valid case is a normal function */
> !         if (fdresult != FUNCDETAIL_NORMAL)
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !
> !         if (!OidIsValid(finalfn))
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !
> !         /*
> !          * enforce consistency with ANYARRAY and ANYELEMENT argument
> !          * and return types, possibly modifying return type along the way
> !          */
> !         finaltype = enforce_generic_type_consistency(fnArgs,
> !                                                      true_oid_array_finalfn,
> !                                                      nargs_finalfn, rettype);
>       }
>       else
>       {
> ***************
> *** 125,130 ****
> --- 165,191 ----
>           finaltype = aggTransType;
>       }
>       Assert(OidIsValid(finaltype));
> +
> +     /*
> +      * special disallowed cases:
> +      * 1)    if finaltype is polymorphic, basetype cannot be ANY
> +      * 2)    if finaltype is polymorphic, both args to transfn must be
> +      *        polymorphic
> +      */
> +     if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
> +     {
> +         if (aggBaseType == ANYOID)
> +             elog(ERROR, "aggregate with base type ANY must have a " \
> +                         "non-polymorphic return type");
> +
> +         if (nargs_transfn > 1 && (
> +             (true_oid_array_transfn[0] != ANYARRAYOID &&
> +              true_oid_array_transfn[0] != ANYELEMENTOID) ||
> +             (true_oid_array_transfn[1] != ANYARRAYOID &&
> +              true_oid_array_transfn[1] != ANYELEMENTOID)))
> +             elog(ERROR, "aggregate with polymorphic return type requires " \
> +                         "state function with both arguments polymorphic");
> +     }
>
>       /*
>        * Everything looks okay.  Try to create the pg_proc entry for the
> Index: src/backend/commands/aggregatecmds.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/aggregatecmds.c,v
> retrieving revision 1.5
> diff -c -r1.5 aggregatecmds.c
> *** src/backend/commands/aggregatecmds.c    4 Sep 2002 20:31:14 -0000    1.5
> --- src/backend/commands/aggregatecmds.c    1 Jun 2003 15:41:15 -0000
> ***************
> *** 119,125 ****
>           baseTypeId = typenameTypeId(baseType);
>
>       transTypeId = typenameTypeId(transType);
> !     if (get_typtype(transTypeId) == 'p')
>           elog(ERROR, "Aggregate transition datatype cannot be %s",
>                format_type_be(transTypeId));
>
> --- 119,127 ----
>           baseTypeId = typenameTypeId(baseType);
>
>       transTypeId = typenameTypeId(transType);
> !     if (get_typtype(transTypeId) == 'p' &&
> !         transTypeId != ANYARRAYOID &&
> !         transTypeId != ANYELEMENTOID)
>           elog(ERROR, "Aggregate transition datatype cannot be %s",
>                format_type_be(transTypeId));
>
> Index: src/backend/commands/analyze.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/analyze.c,v
> retrieving revision 1.54
> diff -c -r1.54 analyze.c
> *** src/backend/commands/analyze.c    27 May 2003 17:49:45 -0000    1.54
> --- src/backend/commands/analyze.c    2 Jun 2003 01:06:40 -0000
> ***************
> *** 392,397 ****
> --- 392,398 ----
>       Oid            eqopr = InvalidOid;
>       Oid            eqfunc = InvalidOid;
>       Oid            ltopr = InvalidOid;
> +     Oid            elem_type = get_element_type(attr->atttypid);
>       VacAttrStats *stats;
>
>       /* Don't analyze dropped columns */
> ***************
> *** 413,418 ****
> --- 414,433 ----
>               eqfunc = oprfuncid(func_operator);
>           }
>           ReleaseSysCache(func_operator);
> +
> +         /*
> +          * If we have an array type, we also need a suitable
> +          * element equality operator. Check to be sure it exists.
> +          */
> +         if (elem_type != InvalidOid)
> +         {
> +             Operator    elem_func_operator;
> +
> +             elem_func_operator = equality_oper(elem_type, true);
> +             if (elem_func_operator != NULL)
> +                 eqfunc = InvalidOid;
> +             ReleaseSysCache(elem_func_operator);
> +         }
>       }
>       if (!OidIsValid(eqfunc))
>           return NULL;
> ***************
> *** 448,453 ****
> --- 463,482 ----
>           if (oprrest == F_SCALARLTSEL)
>               ltopr = oprid(func_operator);
>           ReleaseSysCache(func_operator);
> +
> +         /*
> +          * If we have an array type, we also need a suitable
> +          * element ordering operator. Check to be sure it exists.
> +          */
> +         if (elem_type != InvalidOid)
> +         {
> +             Operator    elem_func_operator;
> +
> +             elem_func_operator = ordering_oper(elem_type, true);
> +             if (elem_func_operator != NULL)
> +                 ltopr = InvalidOid;
> +             ReleaseSysCache(elem_func_operator);
> +         }
>       }
>       stats->ltopr = ltopr;
>
> Index: src/backend/executor/nodeAgg.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeAgg.c,v
> retrieving revision 1.105
> diff -c -r1.105 nodeAgg.c
> *** src/backend/executor/nodeAgg.c    30 May 2003 20:23:10 -0000    1.105
> --- src/backend/executor/nodeAgg.c    1 Jun 2003 15:41:15 -0000
> ***************
> *** 58,63 ****
> --- 58,64 ----
>   #include "executor/executor.h"
>   #include "executor/nodeAgg.h"
>   #include "miscadmin.h"
> + #include "nodes/makefuncs.h"
>   #include "optimizer/clauses.h"
>   #include "parser/parse_coerce.h"
>   #include "parser/parse_expr.h"
> ***************
> *** 212,218 ****
>   static void agg_fill_hash_table(AggState *aggstate);
>   static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
>   static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
> !
>
>   /*
>    * Initialize all aggregates for a new group of input values.
> --- 213,219 ----
>   static void agg_fill_hash_table(AggState *aggstate);
>   static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
>   static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
> ! static Oid resolve_type(Oid type_to_resolve, Oid context_type);
>
>   /*
>    * Initialize all aggregates for a new group of input values.
> ***************
> *** 351,364 ****
>       fcinfo.context = NULL;
>       fcinfo.resultinfo = NULL;
>       fcinfo.isnull = false;
> -
>       fcinfo.flinfo = &peraggstate->transfn;
>       fcinfo.nargs = 2;
>       fcinfo.arg[0] = pergroupstate->transValue;
>       fcinfo.argnull[0] = pergroupstate->transValueIsNull;
>       fcinfo.arg[1] = newVal;
>       fcinfo.argnull[1] = isNull;
> -
>       newVal = FunctionCallInvoke(&fcinfo);
>
>       /*
> --- 352,363 ----
> ***************
> *** 1176,1182 ****
> --- 1175,1195 ----
>           AclResult    aclresult;
>           Oid            transfn_oid,
>                       finalfn_oid;
> +         FuncExpr   *transfnexpr,
> +                    *finalfnexpr;
>           Datum        textInitVal;
> +         List       *fargs;
> +         Oid            agg_rt_type;
> +         Oid           *transfn_arg_types;
> +         List       *transfn_args = NIL;
> +         int            transfn_nargs;
> +         Oid            transfn_ret_type;
> +         Oid           *finalfn_arg_types = NULL;
> +         List       *finalfn_args = NIL;
> +         Oid            finalfn_ret_type = InvalidOid;
> +         int            finalfn_nargs = 0;
> +         Node       *arg0;
> +         Node       *arg1;
>           int            i;
>
>           /* Look for a previous duplicate aggregate */
> ***************
> *** 1224,1229 ****
> --- 1237,1402 ----
>                           &peraggstate->transtypeLen,
>                           &peraggstate->transtypeByVal);
>
> +         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
> +         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
> +
> +         /* get the runtime aggregate argument type */
> +         fargs = aggref->args;
> +         agg_rt_type = exprType((Node *) nth(0, fargs));
> +
> +         /* get the transition function argument and return types */
> +         transfn_ret_type = get_func_rettype(transfn_oid);
> +         transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs);
> +
> +         /* resolve any polymorphic types */
> +         if (transfn_nargs == 2)
> +         /* base type was not ANY */
> +         {
> +             if (transfn_arg_types[1] == ANYARRAYOID ||
> +                 transfn_arg_types[1] == ANYELEMENTOID)
> +                 transfn_arg_types[1] = agg_rt_type;
> +
> +             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
> +                                                         agg_rt_type);
> +
> +             /*
> +              * Build arg list to use on the transfn FuncExpr node. We really
> +              * only care that the node type is correct so that the transfn
> +              * can discover the actual argument types at runtime using
> +              * get_fn_expr_argtype()
> +              */
> +             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
> +                                                             -1, COERCE_DONTCARE);
> +             arg1 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[1],
> +                                                             -1, COERCE_DONTCARE);
> +             transfn_args = makeList2(arg0, arg1);
> +
> +             /*
> +              * the state transition function always returns the same type
> +              * as its first argument
> +              */
> +             if (transfn_ret_type == ANYARRAYOID ||
> +                 transfn_ret_type == ANYELEMENTOID)
> +                 transfn_ret_type = transfn_arg_types[0];
> +         }
> +         else if (transfn_nargs == 1)
> +         /*
> +          * base type was ANY, therefore the aggregate return type should
> +          * be non-polymorphic
> +          */
> +         {
> +             Oid    finaltype = get_func_rettype(aggref->aggfnoid);
> +
> +             /*
> +              * this should have been prevented in AggregateCreate,
> +              * but check anyway
> +              */
> +             if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
> +                 elog(ERROR, "aggregate with base type ANY must have a " \
> +                             "non-polymorphic return type");
> +
> +             /* see if we have a final function */
> +             if (OidIsValid(finalfn_oid))
> +             {
> +                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
> +                 if (finalfn_nargs != 1)
> +                     elog(ERROR, "final function takes unexpected number " \
> +                                 "of arguments: %d", finalfn_nargs);
> +
> +                 /*
> +                  * final function argument is always the same as the state
> +                  * function return type
> +                  */
> +                 if (finalfn_arg_types[0] != ANYARRAYOID &&
> +                     finalfn_arg_types[0] != ANYELEMENTOID)
> +                 {
> +                     /* if it is not ambiguous, use it */
> +                     transfn_ret_type = finalfn_arg_types[0];
> +                 }
> +                 else
> +                 {
> +                     /* if it is ambiguous, try to derive it */
> +                     finalfn_ret_type = finaltype;
> +                     finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0],
> +                                                             finalfn_ret_type);
> +                     transfn_ret_type = finalfn_arg_types[0];
> +                 }
> +             }
> +             else
> +                 transfn_ret_type = finaltype;
> +
> +             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
> +                                                         transfn_ret_type);
> +
> +             /*
> +              * Build arg list to use on the transfn FuncExpr node. We really
> +              * only care that the node type is correct so that the transfn
> +              * can discover the actual argument types at runtime using
> +              * get_fn_expr_argtype()
> +              */
> +             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
> +                                                             -1, COERCE_DONTCARE);
> +             transfn_args = makeList1(arg0);
> +         }
> +         else
> +             elog(ERROR, "state transition function takes unexpected number " \
> +                         "of arguments: %d", transfn_nargs);
> +
> +         if (OidIsValid(finalfn_oid))
> +         {
> +             /* get the final function argument and return types */
> +             if (finalfn_ret_type == InvalidOid)
> +                 finalfn_ret_type = get_func_rettype(finalfn_oid);
> +
> +             if (!finalfn_arg_types)
> +             {
> +                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
> +                 if (finalfn_nargs != 1)
> +                     elog(ERROR, "final function takes unexpected number " \
> +                                 "of arguments: %d", finalfn_nargs);
> +             }
> +
> +             /*
> +              * final function argument is always the same as the state
> +              * function return type, which by now should have been resolved
> +              */
> +             if (finalfn_arg_types[0] == ANYARRAYOID ||
> +                 finalfn_arg_types[0] == ANYELEMENTOID)
> +                 finalfn_arg_types[0] = transfn_ret_type;
> +
> +             /*
> +              * Build arg list to use on the finalfn FuncExpr node. We really
> +              * only care that the node type is correct so that the finalfn
> +              * can discover the actual argument type at runtime using
> +              * get_fn_expr_argtype()
> +              */
> +             arg0 = (Node *) makeRelabelType((Expr *) NULL, finalfn_arg_types[0],
> +                                                             -1, COERCE_DONTCARE);
> +             finalfn_args = makeList1(arg0);
> +
> +             finalfn_ret_type = resolve_type(finalfn_ret_type,
> +                                                 finalfn_arg_types[0]);
> +         }
> +
> +         fmgr_info(transfn_oid, &peraggstate->transfn);
> +         transfnexpr = (FuncExpr *) make_funcclause(transfn_oid,
> +                           transfn_ret_type,
> +                           false,            /* cannot be a set */
> +                           COERCE_DONTCARE,    /* to match any user expr */
> +                           transfn_args);
> +         peraggstate->transfn.fn_expr = (Node *) transfnexpr;
> +
> +         if (OidIsValid(finalfn_oid))
> +         {
> +             fmgr_info(finalfn_oid, &peraggstate->finalfn);
> +             finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid,
> +                           finalfn_ret_type,
> +                           false,            /* cannot be a set */
> +                           COERCE_DONTCARE,    /* to match any user expr */
> +                           finalfn_args);
> +             peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
> +         }
> +
>           /*
>            * initval is potentially null, so don't try to access it as a
>            * struct field. Must do it the hard way with SysCacheGetAttr.
> ***************
> *** 1236,1249 ****
>               peraggstate->initValue = (Datum) 0;
>           else
>               peraggstate->initValue = GetAggInitVal(textInitVal,
> !                                                    aggform->aggtranstype);
> !
> !         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
> !         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
> !
> !         fmgr_info(transfn_oid, &peraggstate->transfn);
> !         if (OidIsValid(finalfn_oid))
> !             fmgr_info(finalfn_oid, &peraggstate->finalfn);
>
>           /*
>            * If the transfn is strict and the initval is NULL, make sure
> --- 1409,1415 ----
>               peraggstate->initValue = (Datum) 0;
>           else
>               peraggstate->initValue = GetAggInitVal(textInitVal,
> !                                                    transfn_arg_types[0]);
>
>           /*
>            * If the transfn is strict and the initval is NULL, make sure
> ***************
> *** 1454,1457 ****
> --- 1620,1656 ----
>       elog(ERROR, "Aggregate function %u called as normal function",
>            fcinfo->flinfo->fn_oid);
>       return (Datum) 0;            /* keep compiler quiet */
> + }
> +
> + static Oid
> + resolve_type(Oid type_to_resolve, Oid context_type)
> + {
> +     Oid        resolved_type;
> +
> +     if (context_type == ANYARRAYOID || context_type == ANYELEMENTOID)
> +         resolved_type = type_to_resolve;
> +     else if (type_to_resolve == ANYARRAYOID)
> +     /* any array */
> +     {
> +         Oid        context_type_arraytype = get_array_type(context_type);
> +
> +         if (context_type_arraytype != InvalidOid)
> +             resolved_type = context_type_arraytype;
> +         else
> +             resolved_type = context_type;
> +     }
> +     else if (type_to_resolve == ANYELEMENTOID)
> +     /* any element */
> +     {
> +         Oid        context_type_elemtype = get_element_type(context_type);
> +
> +         if (context_type_elemtype != InvalidOid)
> +             resolved_type = context_type_elemtype;
> +         else
> +             resolved_type = context_type;
> +     }
> +     else
> +         resolved_type = type_to_resolve;
> +
> +     return resolved_type;
>   }
> Index: src/backend/executor/nodeSubplan.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v
> retrieving revision 1.45
> diff -c -r1.45 nodeSubplan.c
> *** src/backend/executor/nodeSubplan.c    8 Apr 2003 23:20:01 -0000    1.45
> --- src/backend/executor/nodeSubplan.c    1 Jun 2003 15:41:47 -0000
> ***************
> *** 28,50 ****
>   #include "utils/datum.h"
>   #include "utils/lsyscache.h"
>
> -
> - typedef struct ArrayBuildState
> - {
> -     MemoryContext mcontext;        /* where all the temp stuff is kept */
> -     Datum       *dvalues;        /* array of accumulated Datums */
> -     /*
> -      * The allocated size of dvalues[] is always a multiple of
> -      * ARRAY_ELEMS_CHUNKSIZE
> -      */
> - #define ARRAY_ELEMS_CHUNKSIZE    64
> -     int            nelems;            /* number of valid Datums in dvalues[] */
> -     Oid            element_type;    /* data type of the Datums */
> -     int16        typlen;            /* needed info about datatype */
> -     bool        typbyval;
> -     char        typalign;
> - } ArrayBuildState;
> -
>   static Datum ExecHashSubPlan(SubPlanState *node,
>                                ExprContext *econtext,
>                                bool *isNull);
> --- 28,33 ----
> ***************
> *** 54,66 ****
>   static void buildSubPlanHash(SubPlanState *node);
>   static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
>   static bool tupleAllNulls(HeapTuple tuple);
> - static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
> -                                          Datum dvalue, bool disnull,
> -                                          Oid element_type,
> -                                          MemoryContext rcontext);
> - static Datum makeArrayResult(ArrayBuildState *astate,
> -                              MemoryContext rcontext);
> -
>
>   /* ----------------------------------------------------------------
>    *        ExecSubPlan
> --- 37,42 ----
> ***************
> *** 224,229 ****
> --- 200,206 ----
>       PlanState  *planstate = node->planstate;
>       SubLinkType subLinkType = subplan->subLinkType;
>       bool        useOr = subplan->useOr;
> +     bool        isExpr = subplan->isExpr;
>       MemoryContext oldcontext;
>       TupleTableSlot *slot;
>       Datum        result;
> ***************
> *** 292,297 ****
> --- 269,279 ----
>           bool        rownull = false;
>           int            col = 1;
>           List       *plst;
> +         int            numelems;
> +         int            elemnum;
> +         Datum        dvalue;
> +         Datum       *dvalues = NULL;
> +         bool        disnull;
>
>           if (subLinkType == EXISTS_SUBLINK)
>           {
> ***************
> *** 329,337 ****
>
>           if (subLinkType == ARRAY_SUBLINK)
>           {
> -             Datum    dvalue;
> -             bool    disnull;
> -
>               found = true;
>               /* stash away current value */
>               dvalue = heap_getattr(tup, 1, tdesc, &disnull);
> --- 311,316 ----
> ***************
> *** 349,446 ****
>           found = true;
>
>           /*
> !          * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
> !          * operators for columns of tuple.
>            */
> !         plst = subplan->paramIds;
> !         foreach(lst, node->exprs)
>           {
> !             ExprState  *exprstate = (ExprState *) lfirst(lst);
> !             int            paramid = lfirsti(plst);
> !             ParamExecData *prmdata;
> !             Datum        expresult;
> !             bool        expnull;
> !
> !             /*
> !              * Load up the Param representing this column of the sub-select.
> !              */
> !             prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
> !             Assert(prmdata->execPlan == NULL);
> !             prmdata->value = heap_getattr(tup, col, tdesc,
> !                                           &(prmdata->isnull));
>
> !             /*
> !              * Now we can eval the combining operator for this column.
> !              */
> !             expresult = ExecEvalExprSwitchContext(exprstate, econtext,
> !                                                   &expnull, NULL);
> !
> !             /*
> !              * Combine the result into the row result as appropriate.
> !              */
> !             if (col == 1)
>               {
> !                 rowresult = expresult;
> !                 rownull = expnull;
>               }
> !             else if (useOr)
>               {
> !                 /* combine within row per OR semantics */
> !                 if (expnull)
> !                     rownull = true;
> !                 else if (DatumGetBool(expresult))
>                   {
> !                     rowresult = BoolGetDatum(true);
> !                     rownull = false;
> !                     break;        /* needn't look at any more columns */
>                   }
>               }
>               else
>               {
> !                 /* combine within row per AND semantics */
> !                 if (expnull)
> !                     rownull = true;
> !                 else if (!DatumGetBool(expresult))
> !                 {
> !                     rowresult = BoolGetDatum(false);
> !                     rownull = false;
> !                     break;        /* needn't look at any more columns */
> !                 }
>               }
>
> -             plst = lnext(plst);
> -             col++;
>           }
>
> !         if (subLinkType == ANY_SUBLINK)
>           {
> !             /* combine across rows per OR semantics */
> !             if (rownull)
> !                 *isNull = true;
> !             else if (DatumGetBool(rowresult))
>               {
> !                 result = BoolGetDatum(true);
> !                 *isNull = false;
> !                 break;            /* needn't look at any more rows */
>               }
> !         }
> !         else if (subLinkType == ALL_SUBLINK)
> !         {
> !             /* combine across rows per AND semantics */
> !             if (rownull)
> !                 *isNull = true;
> !             else if (!DatumGetBool(rowresult))
>               {
> !                 result = BoolGetDatum(false);
> !                 *isNull = false;
> !                 break;            /* needn't look at any more rows */
>               }
> -         }
> -         else
> -         {
> -             /* must be MULTIEXPR_SUBLINK */
> -             result = rowresult;
> -             *isNull = rownull;
>           }
>       }
>
> --- 328,490 ----
>           found = true;
>
>           /*
> !          * When isExpr is true, we have either a scalar expression or an
> !          * array. In the former case, this is no different than the !isExpr
> !          * case. In the latter case, iterate over the elements as if they
> !          * were from multiple input tuples.
>            */
> !         if (!isExpr)
> !             numelems = 1;
> !         else
>           {
> !             Oid        expr_typeid = tdesc->attrs[0]->atttypid;
>
> !             if (expr_typeid != subplan->exprtype)
>               {
> !                 subplan->exprtype = expr_typeid;
> !                 subplan->elemtype = get_element_type(expr_typeid);
> !
> !                 if (subplan->elemtype != InvalidOid)
> !                     get_typlenbyvalalign(subplan->elemtype,
> !                                          &subplan->elmlen,
> !                                          &subplan->elmbyval,
> !                                          &subplan->elmalign);
>               }
> !
> !             /* get current value */
> !             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
> !
> !             /* XXX this will need work if/when arrays support NULL elements */
> !             if (!disnull)
>               {
> !                 if (subplan->elemtype != InvalidOid)
> !                 {
> !                     ArrayType   *v = DatumGetArrayTypeP(dvalue);
> !
> !                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
> !                                       subplan->elmbyval, subplan->elmalign,
> !                                         &dvalues, &numelems);
> !                 }
> !                 else
>                   {
> !                     numelems = 1;
> !                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                     dvalues[0] = dvalue;
>                   }
>               }
>               else
>               {
> !                 numelems = 1;
> !                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                 dvalues[0] = (Datum) 0;
>               }
>
>           }
>
> !         for (elemnum = 0; elemnum < numelems; elemnum++)
>           {
> !             /*
> !              * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
> !              * operators for columns of tuple.
> !              */
> !             col = 1;
> !             plst = subplan->paramIds;
> !             foreach(lst, node->exprs)
>               {
> !                 ExprState  *exprstate = (ExprState *) lfirst(lst);
> !                 int            paramid = lfirsti(plst);
> !                 ParamExecData *prmdata;
> !                 Datum        expresult;
> !                 bool        expnull;
> !
> !                 /*
> !                  * Load up the Param representing this column of the sub-select.
> !                  */
> !                 prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
> !                 Assert(prmdata->execPlan == NULL);
> !
> !                 if (!isExpr)
> !                     prmdata->value = heap_getattr(tup, col, tdesc,
> !                                                   &(prmdata->isnull));
> !                 else
> !                 {
> !                     prmdata->value = dvalues[elemnum];
> !                     prmdata->isnull = disnull;
> !                 }
> !
> !                 /*
> !                  * Now we can eval the combining operator for this column.
> !                  */
> !                 expresult = ExecEvalExprSwitchContext(exprstate, econtext,
> !                                                       &expnull, NULL);
> !
> !                 /*
> !                  * Combine the result into the row result as appropriate.
> !                  */
> !                 if (col == 1)
> !                 {
> !                     rowresult = expresult;
> !                     rownull = expnull;
> !                 }
> !                 else if (useOr)
> !                 {
> !                     /* combine within row per OR semantics */
> !                     if (expnull)
> !                         rownull = true;
> !                     else if (DatumGetBool(expresult))
> !                     {
> !                         rowresult = BoolGetDatum(true);
> !                         rownull = false;
> !                         break;        /* needn't look at any more columns */
> !                     }
> !                 }
> !                 else
> !                 {
> !                     /* combine within row per AND semantics */
> !                     if (expnull)
> !                         rownull = true;
> !                     else if (!DatumGetBool(expresult))
> !                     {
> !                         rowresult = BoolGetDatum(false);
> !                         rownull = false;
> !                         break;        /* needn't look at any more columns */
> !                     }
> !                 }
> !
> !                 plst = lnext(plst);
> !                 col++;
>               }
> !
> !             if (subLinkType == ANY_SUBLINK)
>               {
> !                 /* combine across rows per OR semantics */
> !                 if (rownull)
> !                     *isNull = true;
> !                 else if (DatumGetBool(rowresult))
> !                 {
> !                     result = BoolGetDatum(true);
> !                     *isNull = false;
> !                     break;            /* needn't look at any more rows */
> !                 }
> !             }
> !             else if (subLinkType == ALL_SUBLINK)
> !             {
> !                 /* combine across rows per AND semantics */
> !                 if (rownull)
> !                     *isNull = true;
> !                 else if (!DatumGetBool(rowresult))
> !                 {
> !                     result = BoolGetDatum(false);
> !                     *isNull = false;
> !                     break;            /* needn't look at any more rows */
> !                 }
> !             }
> !             else
> !             {
> !                 /* must be MULTIEXPR_SUBLINK */
> !                 result = rowresult;
> !                 *isNull = rownull;
>               }
>           }
>       }
>
> ***************
> *** 478,483 ****
> --- 522,528 ----
>   buildSubPlanHash(SubPlanState *node)
>   {
>       SubPlan       *subplan = (SubPlan *) node->xprstate.expr;
> +     bool        isExpr = subplan->isExpr;
>       PlanState  *planstate = node->planstate;
>       int            ncols = length(node->exprs);
>       ExprContext *innerecontext = node->innerecontext;
> ***************
> *** 485,490 ****
> --- 530,536 ----
>       MemoryContext oldcontext;
>       int            nbuckets;
>       TupleTableSlot *slot;
> +     TupleTableSlot *arrslot = NULL;
>
>       Assert(subplan->subLinkType == ANY_SUBLINK);
>       Assert(!subplan->useOr);
> ***************
> *** 562,604 ****
>       {
>           HeapTuple    tup = slot->val;
>           TupleDesc    tdesc = slot->ttc_tupleDescriptor;
> !         int            col = 1;
>           List       *plst;
>           bool        isnew;
>
>           /*
> !          * Load up the Params representing the raw sub-select outputs,
> !          * then form the projection tuple to store in the hashtable.
>            */
> !         foreach(plst, subplan->paramIds)
>           {
> !             int            paramid = lfirsti(plst);
> !             ParamExecData *prmdata;
>
> !             prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
> !             Assert(prmdata->execPlan == NULL);
> !             prmdata->value = heap_getattr(tup, col, tdesc,
> !                                           &(prmdata->isnull));
> !             col++;
> !         }
> !         slot = ExecProject(node->projRight, NULL);
> !         tup = slot->val;
>
> -         /*
> -          * If result contains any nulls, store separately or not at all.
> -          * (Since we know the projection tuple has no junk columns, we
> -          * can just look at the overall hasnull info bit, instead of
> -          * groveling through the columns.)
> -          */
> -         if (HeapTupleNoNulls(tup))
> -         {
> -             (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
> -             node->havehashrows = true;
>           }
> !         else if (node->hashnulls)
>           {
> !             (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
> !             node->havenullrows = true;
>           }
>
>           /*
> --- 608,746 ----
>       {
>           HeapTuple    tup = slot->val;
>           TupleDesc    tdesc = slot->ttc_tupleDescriptor;
> !         TupleDesc    arrtdesc = NULL;
>           List       *plst;
>           bool        isnew;
> +         int            numelems;
> +         int            elemnum;
> +         Datum        dvalue;
> +         Datum       *dvalues = NULL;
> +         bool        disnull;
>
>           /*
> !          * When isExpr is true, we have either a scalar expression or an
> !          * array. In the former case, this is no different than the !isExpr
> !          * case. In the latter case, iterate over the elements as if they
> !          * were from multiple input tuples.
>            */
> !         if (!isExpr)
> !             numelems = 1;
> !         else
>           {
> !             Oid        expr_typeid = tdesc->attrs[0]->atttypid;
>
> !             if (expr_typeid != subplan->exprtype)
> !             {
> !                 subplan->exprtype = expr_typeid;
> !                 subplan->elemtype = get_element_type(expr_typeid);
> !
> !                 if (subplan->elemtype != InvalidOid)
> !                     get_typlenbyvalalign(subplan->elemtype,
> !                                          &subplan->elmlen,
> !                                          &subplan->elmbyval,
> !                                          &subplan->elmalign);
> !             }
> !
> !             /* get current value */
> !             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
> !
> !             if (subplan->elemtype != InvalidOid)
> !             {
> !                 TupleTable    tupleTable;
> !                 ArrayType   *v = DatumGetArrayTypeP(dvalue);
> !
> !                 arrtdesc = CreateTemplateTupleDesc(1, false);
> !                 TupleDescInitEntry(arrtdesc, 1, "elem", subplan->elemtype,
> !                                                             -1, 0, false);
> !
> !                 tupleTable = ExecCreateTupleTable(1);
> !                 arrslot = ExecAllocTableSlot(tupleTable);
> !                 ExecSetSlotDescriptor(arrslot, arrtdesc, true);
> !
> !                 /* XXX this will need work if/when arrays support NULL elements */
> !                 if (!disnull)
> !                 {
> !                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
> !                                       subplan->elmbyval, subplan->elmalign,
> !                                         &dvalues, &numelems);
> !                 }
> !                 else
> !                 {
> !                     numelems = 1;
> !                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                     dvalues[0] = (Datum) 0;
> !                 }
> !             }
> !             else
> !             {
> !                 numelems = 1;
> !                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                 dvalues[0] = dvalue;
> !             }
>
>           }
> !
> !         for (elemnum = 0; elemnum < numelems; elemnum++)
>           {
> !             int    col = 1;
> !
> !             if (!isExpr || subplan->elemtype == InvalidOid)
> !             {
> !                 /*
> !                  * Load up the Params representing the raw sub-select outputs,
> !                  * then form the projection tuple to store in the hashtable.
> !                  */
> !                 foreach(plst, subplan->paramIds)
> !                 {
> !                     int            paramid = lfirsti(plst);
> !                     ParamExecData *prmdata;
> !
> !                     prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
> !                     Assert(prmdata->execPlan == NULL);
> !
> !                     prmdata->value = heap_getattr(tup, col, tdesc,
> !                                                   &(prmdata->isnull));
> !
> !                     col++;
> !                 }
> !                 slot = ExecProject(node->projRight, NULL);
> !                 tup = slot->val;
> !             }
> !             else
> !             {
> !                 /*
> !                  * For array type expressions, we need to build up our own
> !                  * tuple and slot
> !                  */
> !                 char        nullflag;
> !
> !                 nullflag = disnull ? 'n' : ' ';
> !                 tup = heap_formtuple(arrtdesc, &dvalues[elemnum], &nullflag);
> !                 arrslot = ExecStoreTuple(tup, arrslot, InvalidBuffer, true);
> !             }
> !
> !             /*
> !              * If result contains any nulls, store separately or not at all.
> !              * (Since we know the projection tuple has no junk columns, we
> !              * can just look at the overall hasnull info bit, instead of
> !              * groveling through the columns.)
> !              */
> !             if (HeapTupleNoNulls(tup))
> !             {
> !                 if (!isExpr)
> !                     (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
> !                 else
> !                     (void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew);
> !                 node->havehashrows = true;
> !             }
> !             else if (node->hashnulls)
> !             {
> !                 if (!isExpr)
> !                     (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
> !                 else
> !                     (void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew);
> !                 node->havenullrows = true;
> !             }
>           }
>
>           /*
> ***************
> *** 615,620 ****
> --- 757,764 ----
>        * have the potential for a double free attempt.
>        */
>       ExecClearTuple(node->projRight->pi_slot);
> +     if (arrslot)
> +         ExecClearTuple(arrslot);
>
>       MemoryContextSwitchTo(oldcontext);
>   }
> ***************
> *** 1084,1185 ****
>           prm->execPlan = node;
>           parent->chgParam = bms_add_member(parent->chgParam, paramid);
>       }
> - }
> -
> - /*
> -  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
> -  *
> -  *    astate is working state (NULL on first call)
> -  *    rcontext is where to keep working state
> -  */
> - static ArrayBuildState *
> - accumArrayResult(ArrayBuildState *astate,
> -                  Datum dvalue, bool disnull,
> -                  Oid element_type,
> -                  MemoryContext rcontext)
> - {
> -     MemoryContext arr_context,
> -                   oldcontext;
> -
> -     if (astate == NULL)
> -     {
> -         /* First time through --- initialize */
> -
> -         /* Make a temporary context to hold all the junk */
> -         arr_context = AllocSetContextCreate(rcontext,
> -                                             "ARRAY_SUBLINK Result",
> -                                             ALLOCSET_DEFAULT_MINSIZE,
> -                                             ALLOCSET_DEFAULT_INITSIZE,
> -                                             ALLOCSET_DEFAULT_MAXSIZE);
> -         oldcontext = MemoryContextSwitchTo(arr_context);
> -         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
> -         astate->mcontext = arr_context;
> -         astate->dvalues = (Datum *)
> -             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
> -         astate->nelems = 0;
> -         astate->element_type = element_type;
> -         get_typlenbyvalalign(element_type,
> -                              &astate->typlen,
> -                              &astate->typbyval,
> -                              &astate->typalign);
> -     }
> -     else
> -     {
> -         oldcontext = MemoryContextSwitchTo(astate->mcontext);
> -         Assert(astate->element_type == element_type);
> -         /* enlarge dvalues[] if needed */
> -         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
> -             astate->dvalues = (Datum *)
> -                 repalloc(astate->dvalues,
> -                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
> -     }
> -
> -     if (disnull)
> -         elog(ERROR, "NULL elements not allowed in Arrays");
> -
> -     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
> -     astate->dvalues[astate->nelems++] =
> -         datumCopy(dvalue, astate->typbyval, astate->typlen);
> -
> -     MemoryContextSwitchTo(oldcontext);
> -
> -     return astate;
> - }
> -
> - /*
> -  * makeArrayResult - produce final result of ARRAY_SUBLINK
> -  *
> -  *    astate is working state (not NULL)
> -  *    rcontext is where to construct result
> -  */
> - static Datum
> - makeArrayResult(ArrayBuildState *astate,
> -                 MemoryContext rcontext)
> - {
> -     ArrayType  *result;
> -     int            dims[1];
> -     int            lbs[1];
> -     MemoryContext oldcontext;
> -
> -     /* Build the final array result in rcontext */
> -     oldcontext = MemoryContextSwitchTo(rcontext);
> -
> -     dims[0] = astate->nelems;
> -     lbs[0] = 1;
> -
> -     result = construct_md_array(astate->dvalues,
> -                                 1,
> -                                 dims,
> -                                 lbs,
> -                                 astate->element_type,
> -                                 astate->typlen,
> -                                 astate->typbyval,
> -                                 astate->typalign);
> -
> -     MemoryContextSwitchTo(oldcontext);
> -
> -     /* Clean up all the junk */
> -     MemoryContextDelete(astate->mcontext);
> -
> -     return PointerGetDatum(result);
>   }
> --- 1228,1231 ----
> Index: src/backend/nodes/copyfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
> retrieving revision 1.251
> diff -c -r1.251 copyfuncs.c
> *** src/backend/nodes/copyfuncs.c    28 May 2003 16:03:56 -0000    1.251
> --- src/backend/nodes/copyfuncs.c    1 Jun 2003 15:41:47 -0000
> ***************
> *** 727,732 ****
> --- 727,733 ----
>       COPY_NODE_FIELD(target);
>       COPY_SCALAR_FIELD(aggstar);
>       COPY_SCALAR_FIELD(aggdistinct);
> +     COPY_NODE_FIELD(args);
>
>       return newnode;
>   }
> ***************
> *** 825,830 ****
> --- 826,832 ----
>
>       COPY_SCALAR_FIELD(subLinkType);
>       COPY_SCALAR_FIELD(useOr);
> +     COPY_SCALAR_FIELD(isExpr);
>       COPY_NODE_FIELD(lefthand);
>       COPY_NODE_FIELD(operName);
>       COPY_OIDLIST_FIELD(operOids);
> ***************
> *** 843,848 ****
> --- 845,856 ----
>
>       COPY_SCALAR_FIELD(subLinkType);
>       COPY_SCALAR_FIELD(useOr);
> +     COPY_SCALAR_FIELD(isExpr);
> +     COPY_SCALAR_FIELD(exprtype);
> +     COPY_SCALAR_FIELD(elemtype);
> +     COPY_SCALAR_FIELD(elmlen);
> +     COPY_SCALAR_FIELD(elmbyval);
> +     COPY_SCALAR_FIELD(elmalign);
>       COPY_NODE_FIELD(exprs);
>       COPY_INTLIST_FIELD(paramIds);
>       COPY_NODE_FIELD(plan);
> Index: src/backend/nodes/equalfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v
> retrieving revision 1.194
> diff -c -r1.194 equalfuncs.c
> *** src/backend/nodes/equalfuncs.c    28 May 2003 16:03:56 -0000    1.194
> --- src/backend/nodes/equalfuncs.c    1 Jun 2003 15:41:47 -0000
> ***************
> *** 204,209 ****
> --- 204,210 ----
>       COMPARE_NODE_FIELD(target);
>       COMPARE_SCALAR_FIELD(aggstar);
>       COMPARE_SCALAR_FIELD(aggdistinct);
> +     COMPARE_NODE_FIELD(args);
>
>       return true;
>   }
> ***************
> *** 300,305 ****
> --- 301,307 ----
>   {
>       COMPARE_SCALAR_FIELD(subLinkType);
>       COMPARE_SCALAR_FIELD(useOr);
> +     COMPARE_SCALAR_FIELD(isExpr);
>       COMPARE_NODE_FIELD(lefthand);
>       COMPARE_NODE_FIELD(operName);
>       COMPARE_OIDLIST_FIELD(operOids);
> ***************
> *** 313,318 ****
> --- 315,326 ----
>   {
>       COMPARE_SCALAR_FIELD(subLinkType);
>       COMPARE_SCALAR_FIELD(useOr);
> +     COMPARE_SCALAR_FIELD(isExpr);
> +     COMPARE_SCALAR_FIELD(exprtype);
> +     COMPARE_SCALAR_FIELD(elemtype);
> +     COMPARE_SCALAR_FIELD(elmlen);
> +     COMPARE_SCALAR_FIELD(elmbyval);
> +     COMPARE_SCALAR_FIELD(elmalign);
>       COMPARE_NODE_FIELD(exprs);
>       COMPARE_INTLIST_FIELD(paramIds);
>       /* should compare plans, but have to settle for comparing plan IDs */
> Index: src/backend/nodes/outfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v
> retrieving revision 1.206
> diff -c -r1.206 outfuncs.c
> *** src/backend/nodes/outfuncs.c    28 May 2003 16:03:56 -0000    1.206
> --- src/backend/nodes/outfuncs.c    1 Jun 2003 15:41:47 -0000
> ***************
> *** 615,620 ****
> --- 615,621 ----
>       WRITE_NODE_FIELD(target);
>       WRITE_BOOL_FIELD(aggstar);
>       WRITE_BOOL_FIELD(aggdistinct);
> +     WRITE_NODE_FIELD(args);
>   }
>
>   static void
> ***************
> *** 700,705 ****
> --- 701,707 ----
>
>       WRITE_ENUM_FIELD(subLinkType, SubLinkType);
>       WRITE_BOOL_FIELD(useOr);
> +     WRITE_BOOL_FIELD(isExpr);
>       WRITE_NODE_FIELD(lefthand);
>       WRITE_NODE_FIELD(operName);
>       WRITE_OIDLIST_FIELD(operOids);
> ***************
> *** 713,718 ****
> --- 715,726 ----
>
>       WRITE_ENUM_FIELD(subLinkType, SubLinkType);
>       WRITE_BOOL_FIELD(useOr);
> +     WRITE_BOOL_FIELD(isExpr);
> +     WRITE_OID_FIELD(exprtype);
> +     WRITE_OID_FIELD(elemtype);
> +     WRITE_INT_FIELD(elmlen);
> +     WRITE_BOOL_FIELD(elmbyval);
> +     WRITE_CHAR_FIELD(elmalign);
>       WRITE_NODE_FIELD(exprs);
>       WRITE_INTLIST_FIELD(paramIds);
>       WRITE_NODE_FIELD(plan);
> Index: src/backend/nodes/readfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v
> retrieving revision 1.153
> diff -c -r1.153 readfuncs.c
> *** src/backend/nodes/readfuncs.c    6 May 2003 00:20:32 -0000    1.153
> --- src/backend/nodes/readfuncs.c    1 Jun 2003 15:41:47 -0000
> ***************
> *** 415,420 ****
> --- 415,421 ----
>       READ_NODE_FIELD(target);
>       READ_BOOL_FIELD(aggstar);
>       READ_BOOL_FIELD(aggdistinct);
> +     READ_NODE_FIELD(args);
>
>       READ_DONE();
>   }
> ***************
> *** 544,549 ****
> --- 545,551 ----
>
>       READ_ENUM_FIELD(subLinkType, SubLinkType);
>       READ_BOOL_FIELD(useOr);
> +     READ_BOOL_FIELD(isExpr);
>       READ_NODE_FIELD(lefthand);
>       READ_NODE_FIELD(operName);
>       READ_OIDLIST_FIELD(operOids);
> Index: src/backend/optimizer/plan/subselect.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/plan/subselect.c,v
> retrieving revision 1.75
> diff -c -r1.75 subselect.c
> *** src/backend/optimizer/plan/subselect.c    29 Apr 2003 22:13:09 -0000    1.75
> --- src/backend/optimizer/plan/subselect.c    1 Jun 2003 15:41:47 -0000
> ***************
> *** 67,73 ****
>
>   static List *convert_sublink_opers(List *lefthand, List *operOids,
>                                      List *targetlist, int rtindex,
> !                                    List **righthandIds);
>   static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
>   static Node *replace_correlation_vars_mutator(Node *node, void *context);
>   static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
> --- 67,73 ----
>
>   static List *convert_sublink_opers(List *lefthand, List *operOids,
>                                      List *targetlist, int rtindex,
> !                                    bool isExpr, List **righthandIds);
>   static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
>   static Node *replace_correlation_vars_mutator(Node *node, void *context);
>   static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
> ***************
> *** 240,245 ****
> --- 240,251 ----
>        */
>       node->subLinkType = slink->subLinkType;
>       node->useOr = slink->useOr;
> +     node->isExpr = slink->isExpr;
> +     node->exprtype = InvalidOid;
> +     node->elemtype = InvalidOid;
> +     node->elmlen = 0;
> +     node->elmbyval = false;
> +     node->elmalign = '\0';
>       node->exprs = NIL;
>       node->paramIds = NIL;
>       node->useHashTable = false;
> ***************
> *** 316,322 ****
>           exprs = convert_sublink_opers(lefthand,
>                                         slink->operOids,
>                                         plan->targetlist,
> !                                       0,
>                                         &node->paramIds);
>           node->setParam = listCopy(node->paramIds);
>           PlannerInitPlan = lappend(PlannerInitPlan, node);
> --- 322,328 ----
>           exprs = convert_sublink_opers(lefthand,
>                                         slink->operOids,
>                                         plan->targetlist,
> !                                       0, node->isExpr,
>                                         &node->paramIds);
>           node->setParam = listCopy(node->paramIds);
>           PlannerInitPlan = lappend(PlannerInitPlan, node);
> ***************
> *** 399,405 ****
>           node->exprs = convert_sublink_opers(lefthand,
>                                               slink->operOids,
>                                               plan->targetlist,
> !                                             0,
>                                               &node->paramIds);
>
>           /*
> --- 405,411 ----
>           node->exprs = convert_sublink_opers(lefthand,
>                                               slink->operOids,
>                                               plan->targetlist,
> !                                             0, node->isExpr,
>                                               &node->paramIds);
>
>           /*
> ***************
> *** 444,450 ****
>   static List *
>   convert_sublink_opers(List *lefthand, List *operOids,
>                         List *targetlist, int rtindex,
> !                       List **righthandIds)
>   {
>       List       *result = NIL;
>       List       *lst;
> --- 450,456 ----
>   static List *
>   convert_sublink_opers(List *lefthand, List *operOids,
>                         List *targetlist, int rtindex,
> !                       bool isExpr, List **righthandIds)
>   {
>       List       *result = NIL;
>       List       *lst;
> ***************
> *** 499,511 ****
>            * are not expecting to have to resolve unknown Params, so
>            * it's okay to pass a null pstate.)
>            */
> !         result = lappend(result,
> !                          make_op_expr(NULL,
> !                                       tup,
> !                                       leftop,
> !                                       rightop,
> !                                       exprType(leftop),
> !                                       te->resdom->restype));
>
>           ReleaseSysCache(tup);
>
> --- 505,542 ----
>            * are not expecting to have to resolve unknown Params, so
>            * it's okay to pass a null pstate.)
>            */
> !         if (!isExpr)
> !         {
> !             result = lappend(result,
> !                              make_op_expr(NULL,
> !                                           tup,
> !                                           leftop,
> !                                           rightop,
> !                                           exprType(leftop),
> !                                           te->resdom->restype));
> !         }
> !         else
> !         {
> !             Oid        exprtype = te->resdom->restype;
> !             Oid        elemtype = get_element_type(exprtype);
> !
> !             if (elemtype != InvalidOid)
> !                 result = lappend(result,
> !                                  make_op_expr(NULL,
> !                                               tup,
> !                                               leftop,
> !                                               rightop,
> !                                               exprType(leftop),
> !                                               elemtype));
> !             else
> !                 result = lappend(result,
> !                                  make_op_expr(NULL,
> !                                               tup,
> !                                               leftop,
> !                                               rightop,
> !                                               exprType(leftop),
> !                                               exprtype));
> !         }
>
>           ReleaseSysCache(tup);
>
> ***************
> *** 616,628 ****
>       /*
>        * The sublink type must be "= ANY" --- that is, an IN operator.
>        * (We require the operator name to be unqualified, which may be
> !      * overly paranoid, or may not be.)
>        */
>       if (sublink->subLinkType != ANY_SUBLINK)
>           return NULL;
>       if (length(sublink->operName) != 1 ||
>           strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
>           return NULL;
>       /*
>        * The sub-select must not refer to any Vars of the parent query.
>        * (Vars of higher levels should be okay, though.)
> --- 647,663 ----
>       /*
>        * The sublink type must be "= ANY" --- that is, an IN operator.
>        * (We require the operator name to be unqualified, which may be
> !      * overly paranoid, or may not be.) It must not be an Expression
> !      * sublink.
>        */
>       if (sublink->subLinkType != ANY_SUBLINK)
>           return NULL;
>       if (length(sublink->operName) != 1 ||
>           strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
>           return NULL;
> +     if (sublink->isExpr)
> +         return NULL;
> +
>       /*
>        * The sub-select must not refer to any Vars of the parent query.
>        * (Vars of higher levels should be okay, though.)
> ***************
> *** 675,681 ****
>       exprs = convert_sublink_opers(sublink->lefthand,
>                                     sublink->operOids,
>                                     subselect->targetList,
> !                                   rtindex,
>                                     &ininfo->sub_targetlist);
>       return (Node *) make_ands_explicit(exprs);
>   }
> --- 710,716 ----
>       exprs = convert_sublink_opers(sublink->lefthand,
>                                     sublink->operOids,
>                                     subselect->targetList,
> !                                   rtindex, sublink->isExpr,
>                                     &ininfo->sub_targetlist);
>       return (Node *) make_ands_explicit(exprs);
>   }
> Index: src/backend/optimizer/util/clauses.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/util/clauses.c,v
> retrieving revision 1.138
> diff -c -r1.138 clauses.c
> *** src/backend/optimizer/util/clauses.c    28 May 2003 22:32:49 -0000    1.138
> --- src/backend/optimizer/util/clauses.c    1 Jun 2003 15:41:15 -0000
> ***************
> *** 133,138 ****
> --- 133,160 ----
>   }
>
>   /*****************************************************************************
> +  *              FUNCTION clause functions
> +  *****************************************************************************/
> +
> + /*
> +  * make_funcclause
> +  *        Creates a function clause given its function info and argument list.
> +  */
> + Expr *
> + make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
> +                             CoercionForm funcformat, List *funcargs)
> + {
> +     FuncExpr   *expr = makeNode(FuncExpr);
> +
> +     expr->funcid = funcid;
> +     expr->funcresulttype = funcresulttype;
> +     expr->funcretset = funcretset;
> +     expr->funcformat = funcformat;
> +     expr->args = funcargs;
> +     return (Expr *) expr;
> + }
> +
> + /*****************************************************************************
>    *        NOT clause functions
>    *****************************************************************************/
>
> Index: src/backend/parser/gram.y
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/gram.y,v
> retrieving revision 2.416
> diff -c -r2.416 gram.y
> *** src/backend/parser/gram.y    29 May 2003 20:40:36 -0000    2.416
> --- src/backend/parser/gram.y    1 Jun 2003 15:41:47 -0000
> ***************
> *** 5490,5495 ****
> --- 5490,5496 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = ANY_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = makeList1(makeString("="));
>                       n->subselect = $3;
> ***************
> *** 5500,5505 ****
> --- 5501,5507 ----
>                       /* Make an IN node */
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = ANY_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = makeList1(makeString("="));
>                       n->subselect = $4;
> ***************
> *** 5511,5516 ****
> --- 5513,5519 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = $3;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = $2;
>                       n->subselect = $4;
> ***************
> *** 5521,5526 ****
> --- 5524,5530 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = MULTIEXPR_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = $2;
>                       n->subselect = $3;
> ***************
> *** 5904,5909 ****
> --- 5908,5914 ----
>                       {
>                               SubLink *n = (SubLink *)$3;
>                               n->subLinkType = ANY_SUBLINK;
> +                             n->isExpr = false;
>                               n->lefthand = makeList1($1);
>                               n->operName = makeList1(makeString("="));
>                               $$ = (Node *)n;
> ***************
> *** 5931,5936 ****
> --- 5936,5942 ----
>                       {
>                           /* Make an IN node */
>                           SubLink *n = (SubLink *)$4;
> +                         n->isExpr = false;
>                           n->subLinkType = ANY_SUBLINK;
>                           n->lefthand = makeList1($1);
>                           n->operName = makeList1(makeString("="));
> ***************
> *** 5957,5967 ****
> --- 5963,6000 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = $3;
> +                     n->isExpr = false;
>                       n->lefthand = makeList1($1);
>                       n->operName = $2;
>                       n->subselect = $4;
>                       $$ = (Node *)n;
>                   }
> +             | a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op
> +                 {
> +                     SubLink *n = makeNode(SubLink);
> +                     SelectStmt *s = makeNode(SelectStmt);
> +                     ResTarget *r = makeNode(ResTarget);
> +
> +                     r->name = NULL;
> +                     r->indirection = NIL;
> +                     r->val = (Node *)$5;
> +
> +                     s->distinctClause = NIL;
> +                     s->targetList = makeList1(r);
> +                     s->into = NULL;
> +                     s->intoColNames = NIL;
> +                     s->fromClause = NIL;
> +                     s->whereClause = NULL;
> +                     s->groupClause = NIL;
> +                     s->havingClause = NULL;
> +
> +                     n->subLinkType = $3;
> +                     n->isExpr = true;
> +                     n->lefthand = makeList1($1);
> +                     n->operName = $2;
> +                     n->subselect = (Node *) s;
> +                     $$ = (Node *)n;
> +                 }
>               | UNIQUE select_with_parens %prec Op
>                   {
>                       /* Not sure how to get rid of the parentheses
> ***************
> *** 6538,6543 ****
> --- 6571,6577 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = EXPR_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = NIL;
>                       n->operName = NIL;
>                       n->subselect = $1;
> ***************
> *** 6547,6552 ****
> --- 6581,6587 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = EXISTS_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = NIL;
>                       n->operName = NIL;
>                       n->subselect = $2;
> ***************
> *** 6556,6561 ****
> --- 6591,6597 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = ARRAY_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = NIL;
>                       n->operName = NIL;
>                       n->subselect = $2;
> ***************
> *** 6730,6735 ****
> --- 6766,6772 ----
>   in_expr:    select_with_parens
>                   {
>                       SubLink *n = makeNode(SubLink);
> +                     n->isExpr = false;
>                       n->subselect = $1;
>                       /* other fields will be filled later */
>                       $$ = (Node *)n;
> Index: src/backend/parser/parse_coerce.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v
> retrieving revision 2.97
> diff -c -r2.97 parse_coerce.c
> *** src/backend/parser/parse_coerce.c    26 May 2003 00:11:27 -0000    2.97
> --- src/backend/parser/parse_coerce.c    1 Jun 2003 16:15:40 -0000
> ***************
> *** 859,865 ****
>       /* Get the element type based on the array type, if we have one */
>       if (OidIsValid(array_typeid))
>       {
> !         array_typelem = get_element_type(array_typeid);
>           if (!OidIsValid(array_typelem))
>               elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
>                    format_type_be(array_typeid));
> --- 859,869 ----
>       /* Get the element type based on the array type, if we have one */
>       if (OidIsValid(array_typeid))
>       {
> !         if (array_typeid != ANYARRAYOID)
> !             array_typelem = get_element_type(array_typeid);
> !         else
> !             array_typelem = ANYELEMENTOID;
> !
>           if (!OidIsValid(array_typelem))
>               elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
>                    format_type_be(array_typeid));
> ***************
> *** 919,925 ****
>       {
>           if (!OidIsValid(array_typeid))
>           {
> !             array_typeid = get_array_type(elem_typeid);
>               if (!OidIsValid(array_typeid))
>                   elog(ERROR, "Cannot find array type for datatype %s",
>                        format_type_be(elem_typeid));
> --- 923,933 ----
>       {
>           if (!OidIsValid(array_typeid))
>           {
> !             if (elem_typeid != ANYELEMENTOID)
> !                 array_typeid = get_array_type(elem_typeid);
> !             else
> !                 array_typeid = ANYARRAYOID;
> !
>               if (!OidIsValid(array_typeid))
>                   elog(ERROR, "Cannot find array type for datatype %s",
>                        format_type_be(elem_typeid));
> ***************
> *** 1169,1174 ****
> --- 1177,1187 ----
>       /* Somewhat-fast path for domain -> base type case */
>       if (srctype == targettype)
>           return true;
> +
> +     /* Last of the fast-paths: check for matching polymorphic arrays */
> +     if (targettype == ANYARRAYOID)
> +         if (get_element_type(srctype) != InvalidOid)
> +             return true;
>
>       /* Else look in pg_cast */
>       tuple = SearchSysCache(CASTSOURCETARGET,
> Index: src/backend/parser/parse_expr.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_expr.c,v
> retrieving revision 1.148
> diff -c -r1.148 parse_expr.c
> *** src/backend/parser/parse_expr.c    29 Apr 2003 22:13:10 -0000    1.148
> --- src/backend/parser/parse_expr.c    1 Jun 2003 15:41:47 -0000
> ***************
> *** 436,441 ****
> --- 436,442 ----
>                       sublink->operName = NIL;
>                       sublink->operOids = NIL;
>                       sublink->useOr = FALSE;
> +                     sublink->isExpr = FALSE;
>                   }
>                   else if (sublink->subLinkType == EXPR_SUBLINK ||
>                            sublink->subLinkType == ARRAY_SUBLINK)
> ***************
> *** 463,468 ****
> --- 464,470 ----
>                       sublink->operName = NIL;
>                       sublink->operOids = NIL;
>                       sublink->useOr = FALSE;
> +                     sublink->isExpr = FALSE;
>                   }
>                   else
>                   {
> ***************
> *** 538,547 ****
>                            * here, because make_subplan() will insert type
>                            * coercion calls if needed.
>                            */
> !                         optup = oper(op,
> !                                      exprType(lexpr),
> !                                      exprType((Node *) tent->expr),
> !                                      false);
>                           opform = (Form_pg_operator) GETSTRUCT(optup);
>
>                           if (opform->oprresult != BOOLOID)
> --- 540,569 ----
>                            * here, because make_subplan() will insert type
>                            * coercion calls if needed.
>                            */
> !                         if (!sublink->isExpr)
> !                         {
> !                             optup = oper(op,
> !                                          exprType(lexpr),
> !                                          exprType((Node *) tent->expr),
> !                                          false);
> !                         }
> !                         else
> !                         {
> !                             Oid        exprtype = exprType((Node *) tent->expr);
> !                             Oid        elemtype = get_element_type(exprtype);
> !
> !                             if (elemtype != InvalidOid)
> !                                 optup = oper(op,
> !                                              exprType(lexpr),
> !                                              elemtype,
> !                                              false);
> !                             else
> !                                 optup = oper(op,
> !                                              exprType(lexpr),
> !                                              exprtype,
> !                                              false);
> !                         }
> !
>                           opform = (Form_pg_operator) GETSTRUCT(optup);
>
>                           if (opform->oprresult != BOOLOID)
> Index: src/backend/parser/parse_func.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_func.c,v
> retrieving revision 1.148
> diff -c -r1.148 parse_func.c
> *** src/backend/parser/parse_func.c    26 May 2003 00:11:27 -0000    1.148
> --- src/backend/parser/parse_func.c    1 Jun 2003 15:41:15 -0000
> ***************
> *** 335,340 ****
> --- 335,341 ----
>           aggref->target = lfirst(fargs);
>           aggref->aggstar = agg_star;
>           aggref->aggdistinct = agg_distinct;
> +         aggref->args = fargs;
>
>           retval = (Node *) aggref;
>
> Index: src/backend/parser/parse_oper.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_oper.c,v
> retrieving revision 1.64
> diff -c -r1.64 parse_oper.c
> *** src/backend/parser/parse_oper.c    26 May 2003 00:11:27 -0000    1.64
> --- src/backend/parser/parse_oper.c    1 Jun 2003 23:21:10 -0000
> ***************
> *** 221,226 ****
> --- 221,241 ----
>   }
>
>   /*
> +  * ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper())
> +  */
> + Oid
> + ordering_oper_funcid(Oid argtype)
> + {
> +     Operator    optup;
> +     Oid            result;
> +
> +     optup = ordering_oper(argtype, false);
> +     result = oprfuncid(optup);
> +     ReleaseSysCache(optup);
> +     return result;
> + }
> +
> + /*
>    * ordering_oper_opid - convenience routine for oprid(ordering_oper())
>    *
>    * This was formerly called any_ordering_op()
> Index: src/backend/utils/adt/acl.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/acl.c,v
> retrieving revision 1.86
> diff -c -r1.86 acl.c
> *** src/backend/utils/adt/acl.c    24 Jan 2003 21:53:29 -0000    1.86
> --- src/backend/utils/adt/acl.c    1 Jun 2003 19:07:00 -0000
> ***************
> *** 720,732 ****
>       aidat = ACL_DAT(acl);
>       for (i = 0; i < num; ++i)
>       {
> !         if (aip->ai_grantee == aidat[i].ai_grantee &&
> !             aip->ai_privs == aidat[i].ai_privs)
>               PG_RETURN_BOOL(true);
>       }
>       PG_RETURN_BOOL(false);
>   }
>
>
>   /*
>    * has_table_privilege variants
> --- 720,740 ----
>       aidat = ACL_DAT(acl);
>       for (i = 0; i < num; ++i)
>       {
> !         if (aclitemeq(aip, &aidat[i]))
>               PG_RETURN_BOOL(true);
>       }
>       PG_RETURN_BOOL(false);
>   }
>
> + /*
> +  * user-facing version of aclitemeq() for use as the
> +  * aclitem equality operator
> +  */
> + Datum
> + aclitem_eq(PG_FUNCTION_ARGS)
> + {
> +     PG_RETURN_BOOL(aclitemeq(PG_GETARG_ACLITEM_P(0), PG_GETARG_ACLITEM_P(1)));
> + }
>
>   /*
>    * has_table_privilege variants
> Index: src/backend/utils/adt/array_userfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/array_userfuncs.c,v
> retrieving revision 1.1
> diff -c -r1.1 array_userfuncs.c
> *** src/backend/utils/adt/array_userfuncs.c    8 Apr 2003 23:20:02 -0000    1.1
> --- src/backend/utils/adt/array_userfuncs.c    1 Jun 2003 15:41:15 -0000
> ***************
> *** 42,48 ****
>       else
>           ndims = 1;
>
> !     PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
>                                                    PG_GETARG_DATUM(0),
>                                                    ndims));
>   }
> --- 42,48 ----
>       else
>           ndims = 1;
>
> !     PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, elem_type,
>                                                    PG_GETARG_DATUM(0),
>                                                    ndims));
>   }
> ***************
> *** 70,75 ****
> --- 70,76 ----
>       Oid            arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
>       Oid            arg0_elemid;
>       Oid            arg1_elemid;
> +     ArrayMetaState *my_extra;
>
>       if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
>           elog(ERROR, "array_push: cannot determine input data types");
> ***************
> *** 95,122 ****
>           PG_RETURN_NULL();        /* keep compiler quiet */
>       }
>
> !     /* Sanity check: do we have a one-dimensional array */
> !     if (ARR_NDIM(v) != 1)
> !         elog(ERROR, "Arrays greater than one-dimension are not supported");
> !
> !     lb = ARR_LBOUND(v);
> !     dimv = ARR_DIMS(v);
> !     if (arg0_elemid != InvalidOid)
>       {
> !         /* append newelem */
> !         int    ub = dimv[0] + lb[0] - 1;
> !         indx = ub + 1;
>       }
>       else
>       {
> !         /* prepend newelem */
> !         indx = lb[0] - 1;
>       }
>
> !     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
> !     result = array_set(v, 1, &indx, newelem, -1,
> !                        typlen, typbyval, typalign, &isNull);
>
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
> --- 96,156 ----
>           PG_RETURN_NULL();        /* keep compiler quiet */
>       }
>
> !     if (ARR_NDIM(v) == 1)
>       {
> !         lb = ARR_LBOUND(v);
> !         dimv = ARR_DIMS(v);
> !
> !         if (arg0_elemid != InvalidOid)
> !         {
> !             /* append newelem */
> !             int    ub = dimv[0] + lb[0] - 1;
> !             indx = ub + 1;
> !         }
> !         else
> !         {
> !             /* prepend newelem */
> !             indx = lb[0] - 1;
> !         }
>       }
> +     else if (ARR_NDIM(v) == 0)
> +         indx = 1;
>       else
> +         elog(ERROR, "only empty and one-dimensional arrays are supported");
> +
> +
> +     /*
> +      * We arrange to look up info about element type only once per series
> +      * of calls, assuming the element type doesn't change underneath us.
> +      */
> +     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> +     if (my_extra == NULL)
>       {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
>       }
>
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type */
> !         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typalign = my_extra->typalign;
> !     }
> !
> !     result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval,
> !                                                  typalign, &isNull);
>
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
> ***************
> *** 145,157 ****
>
>       /*
>        * We must have one of the following combinations of inputs:
> !      * 1) two arrays with ndims1 == ndims2
> !      * 2) ndims1 == ndims2 - 1
> !      * 3) ndims1 == ndims2 + 1
>        */
>       ndims1 = ARR_NDIM(v1);
>       ndims2 = ARR_NDIM(v2);
>
>       if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
>           elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
>                       "%d dimensions", ndims1, ndims2);
> --- 179,206 ----
>
>       /*
>        * We must have one of the following combinations of inputs:
> !      * 1) one empty array, and one non-empty array
> !      * 2) both arrays empty
> !      * 3) two arrays with ndims1 == ndims2
> !      * 4) ndims1 == ndims2 - 1
> !      * 5) ndims1 == ndims2 + 1
>        */
>       ndims1 = ARR_NDIM(v1);
>       ndims2 = ARR_NDIM(v2);
>
> +     /*
> +      * short circuit - if one input array is empty, and the other is not,
> +      * we return the non-empty one as the result
> +      *
> +      * if both are empty, return the first one
> +      */
> +     if (ndims1 == 0 && ndims2 > 0)
> +         PG_RETURN_ARRAYTYPE_P(v2);
> +
> +     if (ndims2 == 0)
> +         PG_RETURN_ARRAYTYPE_P(v1);
> +
> +     /* the rest fall into combo 2, 3, or 4 */
>       if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
>           elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
>                       "%d dimensions", ndims1, ndims2);
> ***************
> *** 266,314 ****
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
>
> - /*----------------------------------------------------------------------------
> -  * array_accum :
> -  *        accumulator to build a 1-D array from input values -- this can be used
> -  *        to create custom aggregates.
> -  *
> -  * This function is not marked strict, so we have to be careful about nulls.
> -  *----------------------------------------------------------------------------
> -  */
> - Datum
> - array_accum(PG_FUNCTION_ARGS)
> - {
> -     /* return NULL if both arguments are NULL */
> -     if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
> -         PG_RETURN_NULL();
> -
> -     /* create a new 1-D array from the new element if the array is NULL */
> -     if (PG_ARGISNULL(0))
> -     {
> -         Oid            tgt_type = get_fn_expr_rettype(fcinfo);
> -         Oid            tgt_elem_type;
> -
> -         if (tgt_type == InvalidOid)
> -             elog(ERROR, "Cannot determine target array type");
> -         tgt_elem_type = get_element_type(tgt_type);
> -         if (tgt_elem_type == InvalidOid)
> -             elog(ERROR, "Target type is not an array");
> -
> -         PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
> -                                                      PG_GETARG_DATUM(1),
> -                                                      1));
> -     }
> -
> -     /* return the array if the new element is NULL */
> -     if (PG_ARGISNULL(1))
> -         PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0));
> -
> -     /*
> -      * Otherwise this is equivalent to array_push.  We hack the call a little
> -      * so that array_push can see the fn_expr information.
> -      */
> -     return array_push(fcinfo);
> - }
> -
>   /*-----------------------------------------------------------------------------
>    * array_assign :
>    *        assign an element of an array to a new value and return the
> --- 315,320 ----
> ***************
> *** 329,334 ****
> --- 335,341 ----
>       int16        typlen;
>       bool        typbyval;
>       char        typalign;
> +     ArrayMetaState *my_extra;
>
>       v = PG_GETARG_ARRAYTYPE_P(0);
>       idx_to_chg = PG_GETARG_INT32(1);
> ***************
> *** 349,355 ****
>       if (element_type == 0)
>           elog(ERROR, "Invalid array element type: %u", element_type);
>
> !     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
>       result = array_set(v, 1, &idx_to_chg, newelem, -1,
>                          typlen, typbyval, typalign, &isNull);
> --- 356,390 ----
>       if (element_type == 0)
>           elog(ERROR, "Invalid array element type: %u", element_type);
>
> !     /*
> !      * We arrange to look up info about element type only once per series
> !      * of calls, assuming the element type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type */
> !         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typalign = my_extra->typalign;
> !     }
>
>       result = array_set(v, 1, &idx_to_chg, newelem, -1,
>                          typlen, typbyval, typalign, &isNull);
> ***************
> *** 375,380 ****
> --- 410,416 ----
>       int16        typlen;
>       bool        typbyval;
>       char        typalign;
> +     ArrayMetaState *my_extra;
>
>       v = PG_GETARG_ARRAYTYPE_P(0);
>       idx = PG_GETARG_INT32(1);
> ***************
> *** 394,400 ****
>       if (element_type == 0)
>           elog(ERROR, "Invalid array element type: %u", element_type);
>
> !     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
>       result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);
>
> --- 430,464 ----
>       if (element_type == 0)
>           elog(ERROR, "Invalid array element type: %u", element_type);
>
> !     /*
> !      * We arrange to look up info about element type only once per series
> !      * of calls, assuming the element type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type */
> !         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typalign = my_extra->typalign;
> !     }
>
>       result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);
>
> ***************
> *** 402,412 ****
>   }
>
>   /*
> !  * actually does the work for singleton_array(), and array_accum() if it is
> !  * given a null input array.
>    */
>   ArrayType *
> ! create_singleton_array(Oid element_type, Datum element, int ndims)
>   {
>       Datum    dvalues[1];
>       int16    typlen;
> --- 466,478 ----
>   }
>
>   /*
> !  * actually does the work for singleton_array()
>    */
>   ArrayType *
> ! create_singleton_array(FunctionCallInfo fcinfo,
> !                        Oid element_type,
> !                        Datum element,
> !                        int ndims)
>   {
>       Datum    dvalues[1];
>       int16    typlen;
> ***************
> *** 415,420 ****
> --- 481,487 ----
>       int        dims[MAXDIM];
>       int        lbs[MAXDIM];
>       int        i;
> +     ArrayMetaState *my_extra;
>
>       if (element_type == 0)
>           elog(ERROR, "Invalid array element type: %u", element_type);
> ***************
> *** 429,435 ****
>           lbs[i] = 1;
>       }
>
> !     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
>       return construct_md_array(dvalues, ndims, dims, lbs, element_type,
>                                 typlen, typbyval, typalign);
> --- 496,530 ----
>           lbs[i] = 1;
>       }
>
> !     /*
> !      * We arrange to look up info about element type only once per series
> !      * of calls, assuming the element type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type */
> !         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typalign = my_extra->typalign;
> !     }
>
>       return construct_md_array(dvalues, ndims, dims, lbs, element_type,
>                                 typlen, typbyval, typalign);
> Index: src/backend/utils/adt/arrayfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v
> retrieving revision 1.89
> diff -c -r1.89 arrayfuncs.c
> *** src/backend/utils/adt/arrayfuncs.c    9 May 2003 23:01:45 -0000    1.89
> --- src/backend/utils/adt/arrayfuncs.c    2 Jun 2003 02:24:39 -0000
> ***************
> *** 21,28 ****
> --- 21,30 ----
>   #include "catalog/pg_type.h"
>   #include "libpq/pqformat.h"
>   #include "parser/parse_coerce.h"
> + #include "parser/parse_oper.h"
>   #include "utils/array.h"
>   #include "utils/builtins.h"
> + #include "utils/datum.h"
>   #include "utils/memutils.h"
>   #include "utils/lsyscache.h"
>   #include "utils/syscache.h"
> ***************
> *** 70,85 ****
>
>   #define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)
>
> - /* I/O function selector for system_cache_lookup */
> - typedef enum IOFuncSelector
> - {
> -     IOFunc_input,
> -     IOFunc_output,
> -     IOFunc_receive,
> -     IOFunc_send
> - } IOFuncSelector;
> -
> -
>   static int    ArrayCount(char *str, int *dim, char typdelim);
>   static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
>                FmgrInfo *inputproc, Oid typelem, int32 typmod,
> --- 72,77 ----
> ***************
> *** 93,102 ****
>   static void CopyArrayEls(char *p, Datum *values, int nitems,
>                int typlen, bool typbyval, char typalign,
>                bool freedata);
> - static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
> -                                 int *typlen, bool *typbyval,
> -                                 char *typdelim, Oid *typelem,
> -                                 Oid *proc, char *typalign);
>   static Datum ArrayCast(char *value, bool byval, int len);
>   static int ArrayCastAndSet(Datum src,
>                   int typlen, bool typbyval, char typalign,
> --- 85,90 ----
> ***************
> *** 119,125 ****
>                      char *destPtr,
>                      int *st, int *endp, char *srcPtr,
>                      int typlen, bool typbyval, char typalign);
> !
>
>   /*---------------------------------------------------------------------
>    * array_in :
> --- 107,113 ----
>                      char *destPtr,
>                      int *st, int *endp, char *srcPtr,
>                      int typlen, bool typbyval, char typalign);
> ! static int array_cmp(FunctionCallInfo fcinfo);
>
>   /*---------------------------------------------------------------------
>    * array_in :
> ***************
> *** 154,165 ****
>                   dim[MAXDIM],
>                   lBound[MAXDIM];
>       char        typalign;
>
> !     /* Get info about element type, including its input conversion proc */
> !     system_cache_lookup(element_type, IOFunc_input,
> !                         &typlen, &typbyval, &typdelim,
> !                         &typelem, &typinput, &typalign);
> !     fmgr_info(typinput, &inputproc);
>
>       /* Make a modifiable copy of the input */
>       /* XXX why are we allocating an extra 2 bytes here? */
> --- 142,190 ----
>                   dim[MAXDIM],
>                   lBound[MAXDIM];
>       char        typalign;
> +     ArrayMetaState *my_extra;
>
> !     /*
> !      * We arrange to look up info about element type, including its input
> !      * conversion proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its input conversion proc */
> !         get_type_metadata(element_type, IOFunc_input,
> !                             &typlen, &typbyval, &typdelim,
> !                             &typelem, &typinput, &typalign);
> !         fmgr_info(typinput, &inputproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typinput;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = inputproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typinput = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         inputproc = my_extra->proc;
> !     }
>
>       /* Make a modifiable copy of the input */
>       /* XXX why are we allocating an extra 2 bytes here? */
> ***************
> *** 636,647 ****
>                   indx[MAXDIM];
>       int            ndim,
>                  *dim;
>
>       element_type = ARR_ELEMTYPE(v);
> !     system_cache_lookup(element_type, IOFunc_output,
> !                         &typlen, &typbyval, &typdelim,
> !                         &typelem, &typoutput, &typalign);
> !     fmgr_info(typoutput, &outputproc);
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> --- 661,711 ----
>                   indx[MAXDIM];
>       int            ndim,
>                  *dim;
> +     ArrayMetaState *my_extra;
>
>       element_type = ARR_ELEMTYPE(v);
> !
> !     /*
> !      * We arrange to look up info about element type, including its input
> !      * conversion proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its output conversion proc */
> !         get_type_metadata(element_type, IOFunc_output,
> !                             &typlen, &typbyval, &typdelim,
> !                             &typelem, &typoutput, &typalign);
> !         fmgr_info(typoutput, &outputproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typoutput;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = outputproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typoutput = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         outputproc = my_extra->proc;
> !     }
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> ***************
> *** 800,805 ****
> --- 864,870 ----
>                   dim[MAXDIM],
>                   lBound[MAXDIM];
>       char        typalign;
> +     ArrayMetaState *my_extra;
>
>       /* Get the array header information */
>       ndim = pq_getmsgint(buf, 4);
> ***************
> *** 831,844 ****
>           PG_RETURN_ARRAYTYPE_P(retval);
>       }
>
> !     /* Get info about element type, including its receive conversion proc */
> !     system_cache_lookup(element_type, IOFunc_receive,
> !                         &typlen, &typbyval, &typdelim,
> !                         &typelem, &typreceive, &typalign);
> !     if (!OidIsValid(typreceive))
> !         elog(ERROR, "No binary input function available for type %s",
> !              format_type_be(element_type));
> !     fmgr_info(typreceive, &receiveproc);
>
>       dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
>                                 typlen, typbyval, typalign,
> --- 896,945 ----
>           PG_RETURN_ARRAYTYPE_P(retval);
>       }
>
> !     /*
> !      * We arrange to look up info about element type, including its receive
> !      * conversion proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its receive conversion proc */
> !         get_type_metadata(element_type, IOFunc_receive,
> !                             &typlen, &typbyval, &typdelim,
> !                             &typelem, &typreceive, &typalign);
> !         if (!OidIsValid(typreceive))
> !             elog(ERROR, "No binary input function available for type %s",
> !                  format_type_be(element_type));
> !         fmgr_info(typreceive, &receiveproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typreceive;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = receiveproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typreceive = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         receiveproc = my_extra->proc;
> !     }
>
>       dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
>                                 typlen, typbyval, typalign,
> ***************
> *** 976,990 ****
>       int            ndim,
>                  *dim;
>       StringInfoData buf;
>
>       /* Get information about the element type and the array dimensions */
>       element_type = ARR_ELEMTYPE(v);
> !     system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
> !                         &typdelim, &typelem, &typsend, &typalign);
> !     if (!OidIsValid(typsend))
> !         elog(ERROR, "No binary output function available for type %s",
> !              format_type_be(element_type));
> !     fmgr_info(typsend, &sendproc);
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> --- 1077,1130 ----
>       int            ndim,
>                  *dim;
>       StringInfoData buf;
> +     ArrayMetaState *my_extra;
>
>       /* Get information about the element type and the array dimensions */
>       element_type = ARR_ELEMTYPE(v);
> !
> !     /*
> !      * We arrange to look up info about element type, including its send
> !      * proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its send proc */
> !         get_type_metadata(element_type, IOFunc_send, &typlen, &typbyval,
> !                             &typdelim, &typelem, &typsend, &typalign);
> !         if (!OidIsValid(typsend))
> !             elog(ERROR, "No binary output function available for type %s",
> !                  format_type_be(element_type));
> !         fmgr_info(typsend, &sendproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typsend;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = sendproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typsend = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         sendproc = my_extra->proc;
> !     }
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> ***************
> *** 1476,1481 ****
> --- 1616,1641 ----
>       array = DatumGetArrayTypeP(PointerGetDatum(array));
>
>       ndim = ARR_NDIM(array);
> +
> +     /*
> +      * if number of dims is zero, i.e. an empty array, create an array
> +      * with nSubscripts dimensions, and set the lower bounds to the supplied
> +      * subscripts
> +      */
> +     if (ndim == 0)
> +     {
> +         Oid        elmtype = ARR_ELEMTYPE(array);
> +
> +         for (i = 0; i < nSubscripts; i++)
> +         {
> +             dim[i] = 1;
> +             lb[i] = indx[i];
> +         }
> +
> +         return construct_md_array(&dataValue, nSubscripts, dim, lb, elmtype,
> +                                                 elmlen, elmbyval, elmalign);
> +     }
> +
>       if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
>           elog(ERROR, "Invalid array subscripts");
>
> ***************
> *** 1632,1637 ****
> --- 1792,1822 ----
>       /* note: we assume srcArray contains no toasted elements */
>
>       ndim = ARR_NDIM(array);
> +
> +     /*
> +      * if number of dims is zero, i.e. an empty array, create an array
> +      * with nSubscripts dimensions, and set the upper and lower bounds
> +      * to the supplied subscripts
> +      */
> +     if (ndim == 0)
> +     {
> +         Datum  *dvalues;
> +         int        nelems;
> +         Oid        elmtype = ARR_ELEMTYPE(array);
> +
> +         deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
> +                                                         &dvalues, &nelems);
> +
> +         for (i = 0; i < nSubscripts; i++)
> +         {
> +             dim[i] = 1 + upperIndx[i] - lowerIndx[i];
> +             lb[i] = lowerIndx[i];
> +         }
> +
> +         return construct_md_array(dvalues, nSubscripts, dim, lb, elmtype,
> +                                                  elmlen, elmbyval, elmalign);
> +     }
> +
>       if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
>           elog(ERROR, "Invalid array subscripts");
>
> ***************
> *** 1811,1816 ****
> --- 1996,2008 ----
>       Oid            typelem;
>       Oid            proc;
>       char       *s;
> +     typedef struct {
> +         ArrayMetaState *inp_extra;
> +         ArrayMetaState *ret_extra;
> +     } am_extra;
> +     am_extra  *my_extra;
> +     ArrayMetaState *inp_extra;
> +     ArrayMetaState *ret_extra;
>
>       /* Get input array */
>       if (fcinfo->nargs < 1)
> ***************
> *** 1829,1839 ****
>       if (nitems <= 0)
>           PG_RETURN_ARRAYTYPE_P(v);
>
> !     /* Lookup source and result types. Unneeded variables are reused. */
> !     system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
> !                         &typdelim, &typelem, &proc, &inp_typalign);
> !     system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
> !                         &typdelim, &typelem, &proc, &typalign);
>
>       /* Allocate temporary array for new values */
>       values = (Datum *) palloc(nitems * sizeof(Datum));
> --- 2021,2101 ----
>       if (nitems <= 0)
>           PG_RETURN_ARRAYTYPE_P(v);
>
> !     /*
> !      * We arrange to look up info about input and return element types only
> !      * once per series of calls, assuming the element type doesn't change
> !      * underneath us.
> !      */
> !     my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(am_extra));
> !         my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
> !
> !         my_extra->inp_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         inp_extra = my_extra->inp_extra;
> !         inp_extra->element_type = InvalidOid;
> !
> !         my_extra->ret_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         ret_extra = my_extra->ret_extra;
> !         ret_extra->element_type = InvalidOid;
> !     }
> !     else
> !     {
> !         inp_extra = my_extra->inp_extra;
> !         ret_extra = my_extra->ret_extra;
> !     }
> !
> !     if (inp_extra->element_type != inpType)
> !     {
> !         /* Lookup source and result types. Unneeded variables are reused. */
> !         get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
> !                             &typdelim, &typelem, &proc, &inp_typalign);
> !
> !         inp_extra->element_type = inpType;
> !         inp_extra->typlen = inp_typlen;
> !         inp_extra->typbyval = inp_typbyval;
> !         inp_extra->typdelim = typdelim;
> !         inp_extra->typelem = typelem;
> !         inp_extra->typiofunc = proc;
> !         inp_extra->typalign = inp_typalign;
> !     }
> !     else
> !     {
> !         inp_typlen = inp_extra->typlen;
> !         inp_typbyval = inp_extra->typbyval;
> !         typdelim = inp_extra->typdelim;
> !         typelem = inp_extra->typelem;
> !         proc = inp_extra->typiofunc;
> !         inp_typalign = inp_extra->typalign;
> !     }
> !
> !     if (ret_extra->element_type != retType)
> !     {
> !         /* Lookup source and result types. Unneeded variables are reused. */
> !         get_type_metadata(retType, IOFunc_input, &typlen, &typbyval,
> !                             &typdelim, &typelem, &proc, &typalign);
> !
> !         ret_extra->element_type = retType;
> !         ret_extra->typlen = typlen;
> !         ret_extra->typbyval = typbyval;
> !         ret_extra->typdelim = typdelim;
> !         ret_extra->typelem = typelem;
> !         ret_extra->typiofunc = proc;
> !         ret_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = ret_extra->typlen;
> !         typbyval = ret_extra->typbyval;
> !         typdelim = ret_extra->typdelim;
> !         typelem = ret_extra->typelem;
> !         proc = ret_extra->typiofunc;
> !         typalign = ret_extra->typalign;
> !     }
>
>       /* Allocate temporary array for new values */
>       values = (Datum *) palloc(nitems * sizeof(Datum));
> ***************
> *** 2049,2056 ****
>    *          compares two arrays for equality
>    * result :
>    *          returns true if the arrays are equal, false otherwise.
> -  *
> -  * XXX bitwise equality is pretty bogus ...
>    *-----------------------------------------------------------------------------
>    */
>   Datum
> --- 2311,2316 ----
> ***************
> *** 2058,2069 ****
>   {
>       ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
>       ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
>       bool        result = true;
>
> !     if (ARR_SIZE(array1) != ARR_SIZE(array2))
> !         result = false;
> !     else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
>           result = false;
>
>       /* Avoid leaking memory when handed toasted input. */
>       PG_FREE_IF_COPY(array1, 0);
> --- 2318,2435 ----
>   {
>       ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
>       ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
> +     char       *p1 = (char *) ARR_DATA_PTR(array1);
> +     char       *p2 = (char *) ARR_DATA_PTR(array2);
> +     int            ndims1 = ARR_NDIM(array1);
> +     int            ndims2 = ARR_NDIM(array2);
> +     int           *dims1 = ARR_DIMS(array1);
> +     int           *dims2 = ARR_DIMS(array2);
> +     int            nitems1 = ArrayGetNItems(ndims1, dims1);
> +     int            nitems2 = ArrayGetNItems(ndims2, dims2);
> +     Oid            element_type = ARR_ELEMTYPE(array1);
> +     FmgrInfo   *ae_fmgr_info = fcinfo->flinfo;
>       bool        result = true;
> +     int            typlen;
> +     bool        typbyval;
> +     char        typdelim;
> +     Oid            typelem;
> +     char        typalign;
> +     Oid            typiofunc;
> +     int            i;
> +     ArrayMetaState *my_extra;
> +     FunctionCallInfoData locfcinfo;
>
> !     /* fast path if the arrays do not have the same number of elements */
> !     if (nitems1 != nitems2)
>           result = false;
> +     else
> +     {
> +         /*
> +          * We arrange to look up the equality function only once per series of
> +          * calls, assuming the element type doesn't change underneath us.
> +          */
> +         my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
> +         if (my_extra == NULL)
> +         {
> +             ae_fmgr_info->fn_extra = MemoryContextAlloc(ae_fmgr_info->fn_mcxt,
> +                                                      sizeof(ArrayMetaState));
> +             my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
> +             my_extra->element_type = InvalidOid;
> +         }
> +
> +         if (my_extra->element_type != element_type)
> +         {
> +             Oid        opfuncid = equality_oper_funcid(element_type);
> +
> +             if (OidIsValid(opfuncid))
> +                 fmgr_info_cxt(opfuncid, &my_extra->proc, ae_fmgr_info->fn_mcxt);
> +             else
> +                 elog(ERROR,
> +                      "array_eq: cannot find equality operator for type: %u",
> +                      element_type);
> +
> +             get_type_metadata(element_type, IOFunc_output,
> +                               &typlen, &typbyval, &typdelim,
> +                               &typelem, &typiofunc, &typalign);
> +
> +             my_extra->element_type = element_type;
> +             my_extra->typlen = typlen;
> +             my_extra->typbyval = typbyval;
> +             my_extra->typdelim = typdelim;
> +             my_extra->typelem = typelem;
> +             my_extra->typiofunc = typiofunc;
> +             my_extra->typalign = typalign;
> +         }
> +         else
> +         {
> +             typlen = my_extra->typlen;
> +             typbyval = my_extra->typbyval;
> +             typdelim = my_extra->typdelim;
> +             typelem = my_extra->typelem;
> +             typiofunc = my_extra->typiofunc;
> +             typalign = my_extra->typalign;
> +         }
> +
> +         /*
> +          * apply the operator to each pair of array elements.
> +          */
> +         MemSet(&locfcinfo, 0, sizeof(locfcinfo));
> +         locfcinfo.flinfo = &my_extra->proc;
> +         locfcinfo.nargs = 2;
> +
> +         /* Loop over source data */
> +         for (i = 0; i < nitems1; i++)
> +         {
> +             Datum    elt1;
> +             Datum    elt2;
> +             bool    oprresult;
> +
> +             /* Get element pair */
> +             elt1 = fetch_att(p1, typbyval, typlen);
> +             elt2 = fetch_att(p2, typbyval, typlen);
> +
> +             p1 = att_addlength(p1, typlen, PointerGetDatum(p1));
> +             p1 = (char *) att_align(p1, typalign);
> +
> +             p2 = att_addlength(p2, typlen, PointerGetDatum(p2));
> +             p2 = (char *) att_align(p2, typalign);
> +
> +             /*
> +              * Apply the operator to the element pair
> +              */
> +             locfcinfo.arg[0] = elt1;
> +             locfcinfo.arg[1] = elt2;
> +             locfcinfo.argnull[0] = false;
> +             locfcinfo.argnull[1] = false;
> +             locfcinfo.isnull = false;
> +             oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
> +             if (!oprresult)
> +             {
> +                 result = false;
> +                 break;
> +             }
> +         }
> +     }
>
>       /* Avoid leaking memory when handed toasted input. */
>       PG_FREE_IF_COPY(array1, 0);
> ***************
> *** 2073,2125 ****
>   }
>
>
> ! /***************************************************************************/
> ! /******************|          Support  Routines              |*****************/
> ! /***************************************************************************/
>
> ! static void
> ! system_cache_lookup(Oid element_type,
> !                     IOFuncSelector which_func,
> !                     int *typlen,
> !                     bool *typbyval,
> !                     char *typdelim,
> !                     Oid *typelem,
> !                     Oid *proc,
> !                     char *typalign)
> ! {
> !     HeapTuple    typeTuple;
> !     Form_pg_type typeStruct;
> !
> !     typeTuple = SearchSysCache(TYPEOID,
> !                                ObjectIdGetDatum(element_type),
> !                                0, 0, 0);
> !     if (!HeapTupleIsValid(typeTuple))
> !         elog(ERROR, "cache lookup failed for type %u", element_type);
> !     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
> !
> !     *typlen = typeStruct->typlen;
> !     *typbyval = typeStruct->typbyval;
> !     *typdelim = typeStruct->typdelim;
> !     *typelem = typeStruct->typelem;
> !     *typalign = typeStruct->typalign;
> !     switch (which_func)
> !     {
> !         case IOFunc_input:
> !             *proc = typeStruct->typinput;
> !             break;
> !         case IOFunc_output:
> !             *proc = typeStruct->typoutput;
> !             break;
> !         case IOFunc_receive:
> !             *proc = typeStruct->typreceive;
> !             break;
> !         case IOFunc_send:
> !             *proc = typeStruct->typsend;
> !             break;
>       }
> !     ReleaseSysCache(typeTuple);
>   }
>
>   /*
>    * Fetch array element at pointer, converted correctly to a Datum
>    */
> --- 2439,2628 ----
>   }
>
>
> ! /*-----------------------------------------------------------------------------
> !  * array-array bool operators:
> !  *        Given two arrays, iterate comparison operators
> !  *        over the array. Uses logic similar to text comparison
> !  *        functions, except element-by-element instead of
> !  *        character-by-character.
> !  *----------------------------------------------------------------------------
> !  */
> ! Datum
> ! array_ne(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
> ! }
>
> ! Datum
> ! array_lt(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
> ! }
> !
> ! Datum
> ! array_gt(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
> ! }
> !
> ! Datum
> ! array_le(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
> ! }
> !
> ! Datum
> ! array_ge(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
> ! }
> !
> ! Datum
> ! btarraycmp(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_INT32(array_cmp(fcinfo));
> ! }
> !
> ! /*
> !  * array_cmp()
> !  * Internal comparison function for arrays.
> !  *
> !  * Returns -1, 0 or 1
> !  */
> ! static int
> ! array_cmp(FunctionCallInfo fcinfo)
> ! {
> !     ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
> !     ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
> !     FmgrInfo   *ac_fmgr_info = fcinfo->flinfo;
> !     Datum        opresult;
> !     int            result = 0;
> !     Oid            element_type = InvalidOid;
> !     int            typlen;
> !     bool        typbyval;
> !     char        typdelim;
> !     Oid            typelem;
> !     char        typalign;
> !     Oid            typiofunc;
> !     Datum       *dvalues1;
> !     int            nelems1;
> !     Datum       *dvalues2;
> !     int            nelems2;
> !     int            min_nelems;
> !     int            i;
> !     typedef struct
> !     {
> !         Oid                element_type;
> !         int                typlen;
> !         bool            typbyval;
> !         char            typdelim;
> !         Oid                typelem;
> !         Oid                typiofunc;
> !         char            typalign;
> !         FmgrInfo        eqproc;
> !         FmgrInfo        ordproc;
> !     } ac_extra;
> !     ac_extra *my_extra;
> !
> !     element_type = ARR_ELEMTYPE(array1);
> !
> !     /*
> !      * We arrange to look up the element type operator function only once
> !      * per series of calls, assuming the element type and opname don't
> !      * change underneath us.
> !      */
> !     my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         ac_fmgr_info->fn_extra = MemoryContextAlloc(ac_fmgr_info->fn_mcxt,
> !                                                          sizeof(ac_extra));
> !         my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         Oid        eqfuncid = equality_oper_funcid(element_type);
> !         Oid        ordfuncid = ordering_oper_funcid(element_type);
> !
> !         fmgr_info_cxt(eqfuncid, &my_extra->eqproc, ac_fmgr_info->fn_mcxt);
> !         fmgr_info_cxt(ordfuncid, &my_extra->ordproc, ac_fmgr_info->fn_mcxt);
> !
> !         if (my_extra->eqproc.fn_nargs != 2)
> !             elog(ERROR, "Equality operator does not take 2 arguments: %u",
> !                                                                  eqfuncid);
> !         if (my_extra->ordproc.fn_nargs != 2)
> !             elog(ERROR, "Ordering operator does not take 2 arguments: %u",
> !                                                                  ordfuncid);
> !
> !         get_type_metadata(element_type, IOFunc_output,
> !                           &typlen, &typbyval, &typdelim,
> !                           &typelem, &typiofunc, &typalign);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = InvalidOid;
> !         my_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typalign = my_extra->typalign;
> !     }
> !
> !     /* extract a C array of arg array datums */
> !     deconstruct_array(array1, element_type, typlen, typbyval, typalign,
> !                                                     &dvalues1, &nelems1);
> !
> !     deconstruct_array(array2, element_type, typlen, typbyval, typalign,
> !                                                     &dvalues2, &nelems2);
> !
> !     min_nelems = Min(nelems1, nelems2);
> !     for (i = 0; i < min_nelems; i++)
> !     {
> !         /* are they equal */
> !         opresult = FunctionCall2(&my_extra->eqproc,
> !                                  dvalues1[i], dvalues2[i]);
> !
> !         if (!DatumGetBool(opresult))
> !         {
> !             /* nope, see if arg1 is less than arg2 */
> !             opresult = FunctionCall2(&my_extra->ordproc,
> !                                      dvalues1[i], dvalues2[i]);
> !             if (DatumGetBool(opresult))
> !             {
> !                 /* arg1 is less than arg2 */
> !                 result = -1;
> !                 break;
> !             }
> !             else
> !             {
> !                 /* arg1 is greater than arg2 */
> !                 result = 1;
> !                 break;
> !             }
> !         }
>       }
> !
> !     if ((result == 0) && (nelems1 != nelems2))
> !         result = (nelems1 < nelems2) ? -1 : 1;
> !
> !     /* Avoid leaking memory when handed toasted input. */
> !     PG_FREE_IF_COPY(array1, 0);
> !     PG_FREE_IF_COPY(array2, 1);
> !
> !     return result;
>   }
>
> +
> + /***************************************************************************/
> + /******************|          Support  Routines              |*****************/
> + /***************************************************************************/
> +
>   /*
>    * Fetch array element at pointer, converted correctly to a Datum
>    */
> ***************
> *** 2423,2428 ****
> --- 2926,2943 ----
>           if (tgt_elem_type == InvalidOid)
>               elog(ERROR, "Target type is not an array");
>
> +         /*
> +          * We don't deal with domain constraints yet, so bail out.
> +          * This isn't currently a problem, because we also don't
> +          * support arrays of domain type elements either. But in the
> +          * future we might. At that point consideration should be given
> +          * to removing the check below and adding a domain constraints
> +          * check to the coercion.
> +          */
> +         if (getBaseType(tgt_elem_type) != tgt_elem_type)
> +             elog(ERROR, "array coercion to domain type elements not " \
> +                         "currently supported");
> +
>           if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
>                                      COERCION_EXPLICIT, &funcId))
>           {
> ***************
> *** 2439,2448 ****
>       }
>
>       /*
> !      * If it's binary-compatible, return the array unmodified.
>        */
>       if (my_extra->coerce_finfo.fn_oid == InvalidOid)
> !         PG_RETURN_ARRAYTYPE_P(src);
>
>       /*
>        * Use array_map to apply the function to each array element.
> --- 2954,2969 ----
>       }
>
>       /*
> !      * If it's binary-compatible, modify the element type in the array header,
> !      * but otherwise leave the array as we received it.
>        */
>       if (my_extra->coerce_finfo.fn_oid == InvalidOid)
> !     {
> !         ArrayType  *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
> !
> !         ARR_ELEMTYPE(result) = my_extra->desttype;
> !         PG_RETURN_ARRAYTYPE_P(result);
> !     }
>
>       /*
>        * Use array_map to apply the function to each array element.
> ***************
> *** 2453,2456 ****
> --- 2974,3092 ----
>       locfcinfo.arg[0] = PointerGetDatum(src);
>
>       return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
> + }
> +
> + /*
> +  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
> +  *
> +  *    astate is working state (NULL on first call)
> +  *    rcontext is where to keep working state
> +  */
> + ArrayBuildState *
> + accumArrayResult(ArrayBuildState *astate,
> +                  Datum dvalue, bool disnull,
> +                  Oid element_type,
> +                  MemoryContext rcontext)
> + {
> +     MemoryContext arr_context,
> +                   oldcontext;
> +
> +     if (astate == NULL)
> +     {
> +         /* First time through --- initialize */
> +
> +         /* Make a temporary context to hold all the junk */
> +         arr_context = AllocSetContextCreate(rcontext,
> +                                             "accumArrayResult",
> +                                             ALLOCSET_DEFAULT_MINSIZE,
> +                                             ALLOCSET_DEFAULT_INITSIZE,
> +                                             ALLOCSET_DEFAULT_MAXSIZE);
> +         oldcontext = MemoryContextSwitchTo(arr_context);
> +         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
> +         astate->mcontext = arr_context;
> +         astate->dvalues = (Datum *)
> +             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
> +         astate->nelems = 0;
> +         astate->element_type = element_type;
> +         get_typlenbyvalalign(element_type,
> +                              &astate->typlen,
> +                              &astate->typbyval,
> +                              &astate->typalign);
> +     }
> +     else
> +     {
> +         oldcontext = MemoryContextSwitchTo(astate->mcontext);
> +         Assert(astate->element_type == element_type);
> +         /* enlarge dvalues[] if needed */
> +         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
> +             astate->dvalues = (Datum *)
> +                 repalloc(astate->dvalues,
> +                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
> +     }
> +
> +     if (disnull)
> +         elog(ERROR, "NULL elements not allowed in Arrays");
> +
> +     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
> +     astate->dvalues[astate->nelems++] =
> +         datumCopy(dvalue, astate->typbyval, astate->typlen);
> +
> +     MemoryContextSwitchTo(oldcontext);
> +
> +     return astate;
> + }
> +
> + /*
> +  * makeArrayResult - produce final result of accumArrayResult
> +  *
> +  *    astate is working state (not NULL)
> +  *    rcontext is where to construct result
> +  */
> + Datum
> + makeArrayResult(ArrayBuildState *astate,
> +                 MemoryContext rcontext)
> + {
> +     int            dims[1];
> +     int            lbs[1];
> +
> +     dims[0] = astate->nelems;
> +     lbs[0] = 1;
> +
> +     return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
> + }
> +
> + /*
> +  * makeMdArrayResult - produce md final result of accumArrayResult
> +  *
> +  *    astate is working state (not NULL)
> +  *    rcontext is where to construct result
> +  */
> + Datum
> + makeMdArrayResult(ArrayBuildState *astate,
> +                 int ndims,
> +                 int *dims,
> +                 int *lbs,
> +                 MemoryContext rcontext)
> + {
> +     ArrayType  *result;
> +     MemoryContext oldcontext;
> +
> +     /* Build the final array result in rcontext */
> +     oldcontext = MemoryContextSwitchTo(rcontext);
> +
> +     result = construct_md_array(astate->dvalues,
> +                                 ndims,
> +                                 dims,
> +                                 lbs,
> +                                 astate->element_type,
> +                                 astate->typlen,
> +                                 astate->typbyval,
> +                                 astate->typalign);
> +
> +     MemoryContextSwitchTo(oldcontext);
> +
> +     /* Clean up all the junk */
> +     MemoryContextDelete(astate->mcontext);
> +
> +     return PointerGetDatum(result);
>   }
> Index: src/backend/utils/adt/varlena.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/varlena.c,v
> retrieving revision 1.98
> diff -c -r1.98 varlena.c
> *** src/backend/utils/adt/varlena.c    15 May 2003 15:50:19 -0000    1.98
> --- src/backend/utils/adt/varlena.c    1 Jun 2003 15:41:15 -0000
> ***************
> *** 19,29 ****
> --- 19,32 ----
>   #include "mb/pg_wchar.h"
>   #include "miscadmin.h"
>   #include "access/tuptoaster.h"
> + #include "catalog/pg_type.h"
>   #include "lib/stringinfo.h"
>   #include "libpq/crypt.h"
>   #include "libpq/pqformat.h"
> + #include "utils/array.h"
>   #include "utils/builtins.h"
>   #include "utils/pg_locale.h"
> + #include "utils/lsyscache.h"
>
>
>   typedef struct varlena unknown;
> ***************
> *** 1983,1990 ****
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else
> ! /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>
> --- 1986,1992 ----
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else                    /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>
> ***************
> *** 2004,2011 ****
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else
> ! /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>       else if ((start_posn != 0) && (end_posn == 0))
> --- 2006,2012 ----
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else                    /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>       else if ((start_posn != 0) && (end_posn == 0))
> ***************
> *** 2026,2031 ****
> --- 2027,2217 ----
>           result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn -
fldsep_len,false); 
>           PG_RETURN_TEXT_P(result_text);
>       }
> + }
> +
> + /*
> +  * text_to_array
> +  * parse input string
> +  * return text array of elements
> +  * based on provided field separator
> +  */
> + Datum
> + text_to_array(PG_FUNCTION_ARGS)
> + {
> +     text       *inputstring = PG_GETARG_TEXT_P(0);
> +     int            inputstring_len = TEXTLEN(inputstring);
> +     text       *fldsep = PG_GETARG_TEXT_P(1);
> +     int            fldsep_len = TEXTLEN(fldsep);
> +     int            fldnum;
> +     int            start_posn = 0;
> +     int            end_posn = 0;
> +     text       *result_text = NULL;
> +     ArrayBuildState *astate = NULL;
> +     MemoryContext oldcontext = CurrentMemoryContext;
> +
> +     /* return NULL for empty input string */
> +     if (inputstring_len < 1)
> +         PG_RETURN_NULL();
> +
> +     /* empty field separator
> +      * return one element, 1D, array using the input string */
> +     if (fldsep_len < 1)
> +         PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
> +                               CStringGetDatum(inputstring), 1));
> +
> +     /* start with end position holding the initial start position */
> +     end_posn = 0;
> +     for (fldnum=1;;fldnum++)    /* field number is 1 based */
> +     {
> +         Datum    dvalue;
> +         bool    disnull = false;
> +
> +         start_posn = end_posn;
> +         end_posn = text_position(PointerGetDatum(inputstring),
> +                                  PointerGetDatum(fldsep),
> +                                  fldnum);
> +
> +         if ((start_posn == 0) && (end_posn == 0))    /* fldsep not found */
> +         {
> +             if (fldnum == 1)
> +             {
> +                 /* first element
> +                  * return one element, 1D, array using the input string */
> +                 PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
> +                                       CStringGetDatum(inputstring), 1));
> +             }
> +             else
> +             {
> +                 /* otherwise create array and exit */
> +                 PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
> +             }
> +         }
> +         else if ((start_posn != 0) && (end_posn == 0))
> +         {
> +             /* last field requested */
> +             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
> +         }
> +         else if ((start_posn == 0) && (end_posn != 0))
> +         {
> +             /* first field requested */
> +             result_text = LEFT(inputstring, fldsep);
> +         }
> +         else
> +         {
> +             /* prior to last field requested */
> +             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn -
start_posn- fldsep_len, false); 
> +         }
> +
> +         /* stash away current value */
> +         dvalue = PointerGetDatum(result_text);
> +         astate = accumArrayResult(astate, dvalue,
> +                                   disnull, TEXTOID, oldcontext);
> +
> +     }
> +
> +     /* never reached -- keep compiler quiet */
> +     PG_RETURN_NULL();
> + }
> +
> + /*
> +  * array_to_text
> +  * concatenate Cstring representation of input array elements
> +  * using provided field separator
> +  */
> + Datum
> + array_to_text(PG_FUNCTION_ARGS)
> + {
> +     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
> +     char       *fldsep = PG_TEXTARG_GET_STR(1);
> +     int            nitems, *dims, ndims;
> +     char       *p;
> +     Oid            element_type;
> +     int            typlen;
> +     bool        typbyval;
> +     char        typdelim;
> +     Oid            typoutput,
> +                 typelem;
> +     FmgrInfo    outputproc;
> +     char        typalign;
> +     StringInfo    result_str = makeStringInfo();
> +     int            i;
> +     ArrayMetaState *my_extra;
> +
> +     p = ARR_DATA_PTR(v);
> +     ndims = ARR_NDIM(v);
> +     dims = ARR_DIMS(v);
> +     nitems = ArrayGetNItems(ndims, dims);
> +
> +     /* if there are no elements, return an empty string */
> +     if (nitems == 0)
> +         PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
> +
> +     element_type = ARR_ELEMTYPE(v);
> +
> +     /*
> +      * We arrange to look up info about element type, including its output
> +      * conversion proc only once per series of calls, assuming the element
> +      * type doesn't change underneath us.
> +      */
> +     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> +     if (my_extra == NULL)
> +     {
> +         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> +                                                      sizeof(ArrayMetaState));
> +         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> +         my_extra->element_type = InvalidOid;
> +     }
> +
> +     if (my_extra->element_type != element_type)
> +     {
> +         /* Get info about element type, including its output conversion proc */
> +         get_type_metadata(element_type, IOFunc_output,
> +                             &typlen, &typbyval, &typdelim,
> +                             &typelem, &typoutput, &typalign);
> +         fmgr_info(typoutput, &outputproc);
> +
> +         my_extra->element_type = element_type;
> +         my_extra->typlen = typlen;
> +         my_extra->typbyval = typbyval;
> +         my_extra->typdelim = typdelim;
> +         my_extra->typelem = typelem;
> +         my_extra->typiofunc = typoutput;
> +         my_extra->typalign = typalign;
> +         my_extra->proc = outputproc;
> +     }
> +     else
> +     {
> +         typlen = my_extra->typlen;
> +         typbyval = my_extra->typbyval;
> +         typdelim = my_extra->typdelim;
> +         typelem = my_extra->typelem;
> +         typoutput = my_extra->typiofunc;
> +         typalign = my_extra->typalign;
> +         outputproc = my_extra->proc;
> +     }
> +
> +     for (i = 0; i < nitems; i++)
> +     {
> +         Datum        itemvalue;
> +         char       *value;
> +
> +         itemvalue = fetch_att(p, typbyval, typlen);
> +
> +         value = DatumGetCString(FunctionCall3(&outputproc,
> +                                               itemvalue,
> +                                               ObjectIdGetDatum(typelem),
> +                                               Int32GetDatum(-1)));
> +
> +         if (i > 0)
> +             appendStringInfo(result_str, "%s%s", fldsep, value);
> +         else
> +             appendStringInfo(result_str, "%s", value);
> +
> +         p = att_addlength(p, typlen, PointerGetDatum(p));
> +         p = (char *) att_align(p, typalign);
> +     }
> +
> +     PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
>   }
>
>   #define HEXBASE 16
> Index: src/backend/utils/cache/lsyscache.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v
> retrieving revision 1.95
> diff -c -r1.95 lsyscache.c
> *** src/backend/utils/cache/lsyscache.c    26 May 2003 00:11:27 -0000    1.95
> --- src/backend/utils/cache/lsyscache.c    1 Jun 2003 15:41:15 -0000
> ***************
> *** 625,630 ****
> --- 625,664 ----
>   }
>
>   /*
> +  * get_func_argtypes
> +  *        Given procedure id, return the function's argument types.
> +  *        Also pass back the number of arguments.
> +  */
> + Oid *
> + get_func_argtypes(Oid funcid, int *nargs)
> + {
> +     HeapTuple        tp;
> +     Form_pg_proc    procstruct;
> +     Oid               *result = NULL;
> +     int                i;
> +
> +     tp = SearchSysCache(PROCOID,
> +                         ObjectIdGetDatum(funcid),
> +                         0, 0, 0);
> +     if (!HeapTupleIsValid(tp))
> +         elog(ERROR, "Function OID %u does not exist", funcid);
> +
> +     procstruct = (Form_pg_proc) GETSTRUCT(tp);
> +     *nargs = (int) procstruct->pronargs;
> +
> +     if (*nargs > 0)
> +     {
> +         result = (Oid *) palloc(*nargs * sizeof(Oid));
> +
> +         for (i = 0; i < *nargs; i++)
> +             result[i] = procstruct->proargtypes[i];
> +     }
> +
> +     ReleaseSysCache(tp);
> +     return result;
> + }
> +
> + /*
>    * get_func_retset
>    *        Given procedure id, return the function's proretset flag.
>    */
> ***************
> *** 994,999 ****
> --- 1028,1083 ----
>       *typbyval = typtup->typbyval;
>       *typalign = typtup->typalign;
>       ReleaseSysCache(tp);
> + }
> +
> + /*
> +  * get_type_metadata
> +  *
> +  *        A six-fer:    given the type OID, return typlen, typbyval, typalign,
> +  *                    typdelim, typelem, IO function Oid. The IO function
> +  *                    returned is controlled by IOFuncSelector
> +  */
> + void
> + get_type_metadata(Oid element_type,
> +                     IOFuncSelector which_func,
> +                     int *typlen,
> +                     bool *typbyval,
> +                     char *typdelim,
> +                     Oid *typelem,
> +                     Oid *proc,
> +                     char *typalign)
> + {
> +     HeapTuple    typeTuple;
> +     Form_pg_type typeStruct;
> +
> +     typeTuple = SearchSysCache(TYPEOID,
> +                                ObjectIdGetDatum(element_type),
> +                                0, 0, 0);
> +     if (!HeapTupleIsValid(typeTuple))
> +         elog(ERROR, "cache lookup failed for type %u", element_type);
> +     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
> +
> +     *typlen = typeStruct->typlen;
> +     *typbyval = typeStruct->typbyval;
> +     *typdelim = typeStruct->typdelim;
> +     *typelem = typeStruct->typelem;
> +     *typalign = typeStruct->typalign;
> +     switch (which_func)
> +     {
> +         case IOFunc_input:
> +             *proc = typeStruct->typinput;
> +             break;
> +         case IOFunc_output:
> +             *proc = typeStruct->typoutput;
> +             break;
> +         case IOFunc_receive:
> +             *proc = typeStruct->typreceive;
> +             break;
> +         case IOFunc_send:
> +             *proc = typeStruct->typsend;
> +             break;
> +     }
> +     ReleaseSysCache(typeTuple);
>   }
>
>   #ifdef NOT_USED
> Index: src/backend/utils/fmgr/fmgr.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/fmgr/fmgr.c,v
> retrieving revision 1.68
> diff -c -r1.68 fmgr.c
> *** src/backend/utils/fmgr/fmgr.c    8 Apr 2003 23:20:02 -0000    1.68
> --- src/backend/utils/fmgr/fmgr.c    1 Jun 2003 15:41:15 -0000
> ***************
> *** 1673,1675 ****
> --- 1673,1701 ----
>
>       return exprType((Node *) nth(argnum, args));
>   }
> +
> + /*
> +  * Get the OID of the function or operator
> +  *
> +  * Returns InvalidOid if information is not available
> +  */
> + Oid
> + get_fn_expr_functype(FunctionCallInfo fcinfo)
> + {
> +     Node   *expr;
> +
> +     /*
> +      * can't return anything useful if we have no FmgrInfo or if
> +      * its fn_expr node has not been initialized
> +      */
> +     if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
> +         return InvalidOid;
> +
> +     expr = fcinfo->flinfo->fn_expr;
> +     if (IsA(expr, FuncExpr))
> +         return ((FuncExpr *) expr)->funcid;
> +     else if (IsA(expr, OpExpr))
> +         return ((OpExpr *) expr)->opno;
> +     else
> +         return InvalidOid;
> + }
> Index: src/include/fmgr.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/fmgr.h,v
> retrieving revision 1.27
> diff -c -r1.27 fmgr.h
> *** src/include/fmgr.h    8 Apr 2003 23:20:04 -0000    1.27
> --- src/include/fmgr.h    1 Jun 2003 15:41:15 -0000
> ***************
> *** 18,23 ****
> --- 18,24 ----
>   #ifndef FMGR_H
>   #define FMGR_H
>
> + #include "nodes/nodes.h"
>
>   /*
>    * All functions that can be called directly by fmgr must have this signature.
> ***************
> *** 372,385 ****
>                    Datum arg6, Datum arg7, Datum arg8,
>                    Datum arg9);
>
> -
>   /*
>    * Routines in fmgr.c
>    */
>   extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
> ! extern Oid    fmgr_internal_function(const char *proname);
> ! extern Oid    get_fn_expr_rettype(FunctionCallInfo fcinfo);
> ! extern Oid    get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
>
>   /*
>    * Routines in dfmgr.c
> --- 373,386 ----
>                    Datum arg6, Datum arg7, Datum arg8,
>                    Datum arg9);
>
>   /*
>    * Routines in fmgr.c
>    */
>   extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
> ! extern Oid fmgr_internal_function(const char *proname);
> ! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo);
> ! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
> ! extern Oid get_fn_expr_functype(FunctionCallInfo fcinfo);
>
>   /*
>    * Routines in dfmgr.c
> Index: src/include/catalog/pg_amop.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amop.h,v
> retrieving revision 1.49
> diff -c -r1.49 pg_amop.h
> *** src/include/catalog/pg_amop.h    26 May 2003 00:11:27 -0000    1.49
> --- src/include/catalog/pg_amop.h    2 Jun 2003 02:00:05 -0000
> ***************
> *** 418,423 ****
> --- 418,432 ----
>   DATA(insert (    2098 4 f 2335 ));
>   DATA(insert (    2098 5 f 2336 ));
>
> + /*
> +  *    btree array_ops
> +  */
> +
> + DATA(insert (     397 1 f 1072 ));
> + DATA(insert (     397 2 f 1074 ));
> + DATA(insert (     397 3 f 1070 ));
> + DATA(insert (     397 4 f 1075 ));
> + DATA(insert (     397 5 f 1073 ));
>
>   /*
>    *    hash index _ops
> Index: src/include/catalog/pg_amproc.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amproc.h,v
> retrieving revision 1.37
> diff -c -r1.37 pg_amproc.h
> *** src/include/catalog/pg_amproc.h    26 May 2003 00:11:27 -0000    1.37
> --- src/include/catalog/pg_amproc.h    2 Jun 2003 02:25:34 -0000
> ***************
> *** 78,83 ****
> --- 78,84 ----
>
>
>   /* btree */
> + DATA(insert (     397 1  398 ));
>   DATA(insert (     421 1    357 ));
>   DATA(insert (     423 1 1596 ));
>   DATA(insert (     424 1 1693 ));
> Index: src/include/catalog/pg_opclass.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_opclass.h,v
> retrieving revision 1.50
> diff -c -r1.50 pg_opclass.h
> *** src/include/catalog/pg_opclass.h    28 May 2003 16:04:00 -0000    1.50
> --- src/include/catalog/pg_opclass.h    2 Jun 2003 01:47:07 -0000
> ***************
> *** 87,92 ****
> --- 87,93 ----
>    */
>
>   DATA(insert OID =  421 (    403        abstime_ops        PGNSP PGUID  702 t 0 ));
> + DATA(insert OID =  397 (    403        array_ops        PGNSP PGUID 2277 t 0 ));
>   DATA(insert OID =  422 (    402        bigbox_ops        PGNSP PGUID  603 f 0 ));
>   DATA(insert OID =  423 (    403        bit_ops            PGNSP PGUID 1560 t 0 ));
>   DATA(insert OID =  424 (    403        bool_ops        PGNSP PGUID   16 t 0 ));
> Index: src/include/catalog/pg_operator.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_operator.h,v
> retrieving revision 1.114
> diff -c -r1.114 pg_operator.h
> *** src/include/catalog/pg_operator.h    26 May 2003 00:11:27 -0000    1.114
> --- src/include/catalog/pg_operator.h    1 Jun 2003 23:10:35 -0000
> ***************
> *** 116,125 ****
>   DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
>   DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 
>
> ! DATA(insert OID = 329 (  "="       PGNSP PGUID b f 2277 2277 16   329 0 0 0    0 0 array_eq     eqsel eqjoinsel ));
> ! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277   0 0 0 0    0 0 array_append   -       -
));
> ! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277   0 0 0 0    0 0 array_prepend  -       -
));
> ! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277   0 0 0 0    0 0 array_cat      -       -
));
>
>   DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
>   DATA(insert OID = 353 (  "="       PGNSP PGUID b t    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
> --- 116,130 ----
>   DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
>   DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 
>
> ! DATA(insert OID = 1070 (  "="       PGNSP PGUID b f 2277 2277 16 1070 1071  1072 1072 1072 1073 array_eq eqsel
eqjoinsel)); 
> ! DATA(insert OID = 1071 (  "<>"       PGNSP PGUID b f 2277 2277 16 1071 1070  0 0 0 0 array_ne neqsel neqjoinsel ));
> ! DATA(insert OID = 1072 (  "<"       PGNSP PGUID b f 2277 2277 16 1073 1075  0 0 0 0 array_lt scalarltsel
scalarltjoinsel)); 
> ! DATA(insert OID = 1073 (  ">"       PGNSP PGUID b f 2277 2277 16 1072 1074  0 0 0 0 array_gt scalargtsel
scalargtjoinsel)); 
> ! DATA(insert OID = 1074 (  "<="       PGNSP PGUID b f 2277 2277 16 1075 1073  0 0 0 0 array_le scalarltsel
scalarltjoinsel)); 
> ! DATA(insert OID = 1075 (  ">="       PGNSP PGUID b f 2277 2277 16 1074 1072  0 0 0 0 array_ge scalargtsel
scalargtjoinsel)); 
> ! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277 0 0  0 0 0 0 array_append   -       -     ));
> ! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277 0 0  0 0 0 0 array_prepend  -       -     ));
> ! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277 0 0  0 0 0 0 array_cat      -       -     ));
>
>   DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
>   DATA(insert OID = 353 (  "="       PGNSP PGUID b t    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
> ***************
> *** 425,430 ****
> --- 430,436 ----
>   DATA(insert OID = 966 (  "+"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - ));
>   DATA(insert OID = 967 (  "-"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclremove - - ));
>   DATA(insert OID = 968 (  "~"       PGNSP PGUID b f 1034 1033     16 0 0 0 0 0 0 aclcontains - - ));
> + DATA(insert OID = 974 (  "="       PGNSP PGUID b f 1033 1033     16 0 0 0 0 0 0 aclitemeq eqsel eqjoinsel ));
>
>   /* additional geometric operators - thomas 1997-07-09 */
>   DATA(insert OID =  969 (  "@@"       PGNSP PGUID l f    0  601    600    0  0 0 0 0 0 lseg_center - - ));
> Index: src/include/catalog/pg_proc.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v
> retrieving revision 1.302
> diff -c -r1.302 pg_proc.h
> *** src/include/catalog/pg_proc.h    26 May 2003 00:11:27 -0000    1.302
> --- src/include/catalog/pg_proc.h    2 Jun 2003 02:21:05 -0000
> ***************
> *** 758,763 ****
> --- 758,765 ----
>   DESCR("btree less-equal-greater");
>   DATA(insert OID = 360 (  bttextcmp           PGNSP PGUID 12 f f t f i 2 23 "25 25"    bttextcmp - _null_ ));
>   DESCR("btree less-equal-greater");
> + DATA(insert OID = 398 (  btarraycmp           PGNSP PGUID 12 f f t f i 2 23 "2277 2277"    btarraycmp - _null_ ));
> + DESCR("btree less-equal-greater");
>
>   DATA(insert OID = 361 (  lseg_distance       PGNSP PGUID 12 f f t f i 2 701 "601 601"  lseg_distance - _null_ ));
>   DESCR("distance between");
> ***************
> *** 984,997 ****
>   DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
>   DESCR("greater-than-or-equal");
>
> - DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
> - DESCR("array equal");
> -
>   DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
>   DESCR("current user name");
>   DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
>   DESCR("session user name");
>
>   DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
>   DESCR("array dimensions");
>   DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
> --- 986,1008 ----
>   DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
>   DESCR("greater-than-or-equal");
>
>   DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
>   DESCR("current user name");
>   DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
>   DESCR("session user name");
>
> + DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
> + DESCR("array equal");
> + DATA(insert OID = 390 (  array_ne           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ne - _null_ ));
> + DESCR("array not equal");
> + DATA(insert OID = 391 (  array_lt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_lt - _null_ ));
> + DESCR("array less than");
> + DATA(insert OID = 392 (  array_gt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_gt - _null_ ));
> + DESCR("array greater than");
> + DATA(insert OID = 393 (  array_le           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_le - _null_ ));
> + DESCR("array less than or equal");
> + DATA(insert OID = 396 (  array_ge           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ge - _null_ ));
> + DESCR("array greater than or equal");
>   DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
>   DESCR("array dimensions");
>   DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
> ***************
> *** 1008,1015 ****
>   DESCR("append element onto end of array");
>   DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
>   DESCR("prepend element onto front of array");
> - DATA(insert OID = 380 (  array_accum       PGNSP PGUID 12 f f f f i 2 2277 "2277 2283" array_accum - _null_ ));
> - DESCR("push element onto end of array, creating array if needed");
>   DATA(insert OID = 381 (  array_assign       PGNSP PGUID 12 f f t f i 3 2277 "2277 23 2283" array_assign - _null_
));
>   DESCR("assign specific array element");
>   DATA(insert OID = 382 (  array_subscript  PGNSP PGUID 12 f f t f i 2 2283 "2277 23" array_subscript - _null_ ));
> --- 1019,1024 ----
> ***************
> *** 1018,1023 ****
> --- 1027,1036 ----
>   DESCR("concatenate two arrays");
>   DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
>   DESCR("coerce array type to another array type");
> + DATA(insert OID = 394 (  str_to_array       PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ ));
> + DESCR("split delimited text into text[]");
> + DATA(insert OID = 395 (  array_to_str       PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ ));
> + DESCR("concatenate array elements, using delimiter, into text");
>
>   DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
>   DESCR("I/O");
> ***************
> *** 1318,1323 ****
> --- 1331,1338 ----
>   DESCR("remove ACL item");
>   DATA(insert OID = 1037 (  aclcontains       PGNSP PGUID 12 f f t f s 2 16 "1034 1033"    aclcontains - _null_ ));
>   DESCR("does ACL contain item?");
> + DATA(insert OID = 1062 (  aclitemeq           PGNSP PGUID 12 f f t f s 2 16 "1033 1033"    aclitem_eq - _null_ ));
> + DESCR("equality operator for ACL items");
>   DATA(insert OID = 1038 (  seteval           PGNSP PGUID 12 f f t t v 1 23 "26"  seteval - _null_ ));
>   DESCR("internal function supporting PostQuel-style sets");
>   DATA(insert OID = 1044 (  bpcharin           PGNSP PGUID 12 f f t f i 3 1042 "2275 26 23" bpcharin - _null_ ));
> Index: src/include/nodes/primnodes.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v
> retrieving revision 1.82
> diff -c -r1.82 primnodes.h
> *** src/include/nodes/primnodes.h    6 May 2003 00:20:33 -0000    1.82
> --- src/include/nodes/primnodes.h    1 Jun 2003 15:41:47 -0000
> ***************
> *** 225,230 ****
> --- 225,231 ----
>       Expr       *target;            /* expression we are aggregating on */
>       bool        aggstar;        /* TRUE if argument was really '*' */
>       bool        aggdistinct;    /* TRUE if it's agg(DISTINCT ...) */
> +     List       *args;            /* arguments to the aggregate */
>   } Aggref;
>
>   /* ----------------
> ***************
> *** 357,371 ****
>   /* ----------------
>    * SubLink
>    *
> !  * A SubLink represents a subselect appearing in an expression, and in some
> !  * cases also the combining operator(s) just above it.    The subLinkType
> !  * indicates the form of the expression represented:
>    *    EXISTS_SUBLINK        EXISTS(SELECT ...)
>    *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
>    *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
>    *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
>    *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
>    *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
>    * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
>    * same length as the subselect's targetlist.  MULTIEXPR will *always* have
>    * a list with more than one entry; if the subselect has just one target
> --- 358,376 ----
>   /* ----------------
>    * SubLink
>    *
> !  * A SubLink represents a subselect, or an expression, appearing in an
> !  * expression, and in some cases also the combining operator(s) just above
> !  * it.    The subLinkType indicates the form of the expression represented:
>    *    EXISTS_SUBLINK        EXISTS(SELECT ...)
>    *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
>    *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
>    *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
>    *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
>    *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
> +  * If an expression is used in place of the subselect, it is transformed
> +  * into a simple "(SELECT expr)" in gram.y. This is to allow arrays to be
> +  * used as if they were the result of a single column subselect. If the
> +  * expression is scalar, it is treated as a one element array.
>    * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
>    * same length as the subselect's targetlist.  MULTIEXPR will *always* have
>    * a list with more than one entry; if the subselect has just one target
> ***************
> *** 414,419 ****
> --- 419,426 ----
>       SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
>       bool        useOr;            /* TRUE to combine column results with
>                                    * "OR" not "AND" */
> +     bool        isExpr;            /* TRUE if the subselect is really derived
> +                                  * from a single expression */
>       List       *lefthand;        /* list of outer-query expressions on the
>                                    * left */
>       List       *operName;        /* originally specified operator name */
> ***************
> *** 455,460 ****
> --- 462,476 ----
>       SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
>       bool        useOr;            /* TRUE to combine column results with
>                                    * "OR" not "AND" */
> +     bool        isExpr;            /* TRUE if the subselect is really derived
> +                                  * from a single expression */
> +     /* runtime cache for single array expressions */
> +     Oid            exprtype;        /* array and element type, and other info
> +                                  * needed deconstruct the array */
> +     Oid            elemtype;
> +     int16        elmlen;
> +     bool        elmbyval;
> +     char        elmalign;
>       /* The combining operators, transformed to executable expressions: */
>       List       *exprs;            /* list of OpExpr expression trees */
>       List       *paramIds;        /* IDs of Params embedded in the above */
> Index: src/include/optimizer/clauses.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/optimizer/clauses.h,v
> retrieving revision 1.63
> diff -c -r1.63 clauses.h
> *** src/include/optimizer/clauses.h    28 May 2003 16:04:02 -0000    1.63
> --- src/include/optimizer/clauses.h    1 Jun 2003 15:41:15 -0000
> ***************
> *** 28,33 ****
> --- 28,36 ----
>   extern Node *get_leftop(Expr *clause);
>   extern Node *get_rightop(Expr *clause);
>
> + extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
> +                                     CoercionForm funcformat, List *funcargs);
> +
>   extern bool not_clause(Node *clause);
>   extern Expr *make_notclause(Expr *notclause);
>   extern Expr *get_notclausearg(Expr *notclause);
> Index: src/include/parser/parse_oper.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_oper.h,v
> retrieving revision 1.25
> diff -c -r1.25 parse_oper.h
> *** src/include/parser/parse_oper.h    29 Apr 2003 22:13:11 -0000    1.25
> --- src/include/parser/parse_oper.h    1 Jun 2003 23:21:48 -0000
> ***************
> *** 44,49 ****
> --- 44,50 ----
>   /* Convenience routines for common calls on the above */
>   extern Oid    compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError);
>   extern Oid    equality_oper_funcid(Oid argtype);
> + extern Oid  ordering_oper_funcid(Oid argtype);
>   extern Oid    ordering_oper_opid(Oid argtype);
>
>   /* Extract operator OID or underlying-function OID from an Operator tuple */
> Index: src/include/utils/acl.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/acl.h,v
> retrieving revision 1.51
> diff -c -r1.51 acl.h
> *** src/include/utils/acl.h    23 Jan 2003 23:39:07 -0000    1.51
> --- src/include/utils/acl.h    1 Jun 2003 19:03:25 -0000
> ***************
> *** 188,193 ****
> --- 188,194 ----
>   extern Datum aclinsert(PG_FUNCTION_ARGS);
>   extern Datum aclremove(PG_FUNCTION_ARGS);
>   extern Datum aclcontains(PG_FUNCTION_ARGS);
> + extern Datum aclitem_eq(PG_FUNCTION_ARGS);
>
>   /*
>    * prototypes for functions in aclchk.c
> Index: src/include/utils/array.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v
> retrieving revision 1.38
> diff -c -r1.38 array.h
> *** src/include/utils/array.h    8 May 2003 22:19:57 -0000    1.38
> --- src/include/utils/array.h    2 Jun 2003 02:25:03 -0000
> ***************
> *** 32,37 ****
> --- 32,68 ----
>       Oid            elemtype;        /* element type OID */
>   } ArrayType;
>
> + typedef struct ArrayBuildState
> + {
> +     MemoryContext mcontext;        /* where all the temp stuff is kept */
> +     Datum       *dvalues;        /* array of accumulated Datums */
> +     /*
> +      * The allocated size of dvalues[] is always a multiple of
> +      * ARRAY_ELEMS_CHUNKSIZE
> +      */
> + #define ARRAY_ELEMS_CHUNKSIZE    64
> +     int            nelems;            /* number of valid Datums in dvalues[] */
> +     Oid            element_type;    /* data type of the Datums */
> +     int16        typlen;            /* needed info about datatype */
> +     bool        typbyval;
> +     char        typalign;
> + } ArrayBuildState;
> +
> + /*
> +  * structure to cache type metadata needed for array manipulation
> +  */
> + typedef struct ArrayMetaState
> + {
> +     Oid                element_type;
> +     int                typlen;
> +     bool            typbyval;
> +     char            typdelim;
> +     Oid                typelem;
> +     Oid                typiofunc;
> +     char            typalign;
> +     FmgrInfo        proc;
> + } ArrayMetaState;
> +
>   /*
>    * fmgr macros for array objects
>    */
> ***************
> *** 86,91 ****
> --- 117,128 ----
>   extern Datum array_send(PG_FUNCTION_ARGS);
>   extern Datum array_length_coerce(PG_FUNCTION_ARGS);
>   extern Datum array_eq(PG_FUNCTION_ARGS);
> + extern Datum array_ne(PG_FUNCTION_ARGS);
> + extern Datum array_lt(PG_FUNCTION_ARGS);
> + extern Datum array_gt(PG_FUNCTION_ARGS);
> + extern Datum array_le(PG_FUNCTION_ARGS);
> + extern Datum array_ge(PG_FUNCTION_ARGS);
> + extern Datum btarraycmp(PG_FUNCTION_ARGS);
>   extern Datum array_dims(PG_FUNCTION_ARGS);
>   extern Datum array_lower(PG_FUNCTION_ARGS);
>   extern Datum array_upper(PG_FUNCTION_ARGS);
> ***************
> *** 124,130 ****
>                     Oid elmtype,
>                     int elmlen, bool elmbyval, char elmalign,
>                     Datum **elemsp, int *nelemsp);
> !
>
>   /*
>    * prototypes for functions defined in arrayutils.c
> --- 161,174 ----
>                     Oid elmtype,
>                     int elmlen, bool elmbyval, char elmalign,
>                     Datum **elemsp, int *nelemsp);
> ! extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
> !                                          Datum dvalue, bool disnull,
> !                                          Oid element_type,
> !                                          MemoryContext rcontext);
> ! extern Datum makeArrayResult(ArrayBuildState *astate,
> !                              MemoryContext rcontext);
> ! extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
> !                                int *dims, int *lbs, MemoryContext rcontext);
>
>   /*
>    * prototypes for functions defined in arrayutils.c
> ***************
> *** 143,152 ****
>    */
>   extern Datum singleton_array(PG_FUNCTION_ARGS);
>   extern Datum array_push(PG_FUNCTION_ARGS);
> - extern Datum array_accum(PG_FUNCTION_ARGS);
>   extern Datum array_cat(PG_FUNCTION_ARGS);
>
> ! extern ArrayType *create_singleton_array(Oid element_type,
>                                            Datum element,
>                                            int ndims);
>
> --- 187,196 ----
>    */
>   extern Datum singleton_array(PG_FUNCTION_ARGS);
>   extern Datum array_push(PG_FUNCTION_ARGS);
>   extern Datum array_cat(PG_FUNCTION_ARGS);
>
> ! extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
> !                                          Oid element_type,
>                                            Datum element,
>                                            int ndims);
>
> Index: src/include/utils/builtins.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/builtins.h,v
> retrieving revision 1.219
> diff -c -r1.219 builtins.h
> *** src/include/utils/builtins.h    26 May 2003 00:11:28 -0000    1.219
> --- src/include/utils/builtins.h    1 Jun 2003 15:41:15 -0000
> ***************
> *** 530,535 ****
> --- 530,537 ----
>                         List **namelist);
>   extern Datum replace_text(PG_FUNCTION_ARGS);
>   extern Datum split_text(PG_FUNCTION_ARGS);
> + extern Datum text_to_array(PG_FUNCTION_ARGS);
> + extern Datum array_to_text(PG_FUNCTION_ARGS);
>   extern Datum to_hex32(PG_FUNCTION_ARGS);
>   extern Datum to_hex64(PG_FUNCTION_ARGS);
>   extern Datum md5_text(PG_FUNCTION_ARGS);
> Index: src/include/utils/lsyscache.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v
> retrieving revision 1.70
> diff -c -r1.70 lsyscache.h
> *** src/include/utils/lsyscache.h    26 May 2003 00:11:28 -0000    1.70
> --- src/include/utils/lsyscache.h    1 Jun 2003 15:41:15 -0000
> ***************
> *** 15,20 ****
> --- 15,29 ----
>
>   #include "access/htup.h"
>
> + /* I/O function selector for system_cache_lookup */
> + typedef enum IOFuncSelector
> + {
> +     IOFunc_input,
> +     IOFunc_output,
> +     IOFunc_receive,
> +     IOFunc_send
> + } IOFuncSelector;
> +
>   extern bool op_in_opclass(Oid opno, Oid opclass);
>   extern bool op_requires_recheck(Oid opno, Oid opclass);
>   extern Oid    get_opclass_member(Oid opclass, int16 strategy);
> ***************
> *** 39,44 ****
> --- 48,54 ----
>   extern RegProcedure get_oprjoin(Oid opno);
>   extern char *get_func_name(Oid funcid);
>   extern Oid    get_func_rettype(Oid funcid);
> + extern Oid *get_func_argtypes(Oid funcid, int *nargs);
>   extern bool get_func_retset(Oid funcid);
>   extern bool func_strict(Oid funcid);
>   extern char func_volatile(Oid funcid);
> ***************
> *** 54,59 ****
> --- 64,77 ----
>   extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
>   extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
>                        char *typalign);
> + extern void get_type_metadata(Oid element_type,
> +                                 IOFuncSelector which_func,
> +                                 int *typlen,
> +                                 bool *typbyval,
> +                                 char *typdelim,
> +                                 Oid *typelem,
> +                                 Oid *proc,
> +                                 char *typalign);
>   extern char get_typstorage(Oid typid);
>   extern int32 get_typtypmod(Oid typid);
>   extern Node *get_typdefault(Oid typid);

>
> ---------------------------(end of broadcast)---------------------------
> TIP 1: subscribe and unsubscribe commands go to majordomo@postgresql.org

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Re: array support patch phase 1 patch

From
Joe Conway
Date:
Bruce Momjian wrote:
> (I assume this includes all your outstanding unapplied array patches.)
>

Yes, I believe it does.

I still owe a further documentation update, and should probably add to
the array regression test, but I'll wait to see how much of this
actually gets applied. Also, I want to wait until after June 15th for
those, if that's OK.

Joe




Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> on ANALYZE (and therefore on initdb) I get this:
> template1=# analyze;
> ERROR:  Unable to identify an ordering operator for type aclitem
>          Use an explicit ordering operator or modify the query
> This is where I ran into the problem with an equality operator earlier.
> Problem this time is that an ordering operator makes no sense for
> aclitems -- does it? Any thoughts on how to work around this?

Seems like you'll have to invent one.  ANALYZE can hardly be expected
not to try to develop histogram stats for an array-of-aclitem column
when the array-of-aclitem datatype appears to be offering a '<'
operator.

I'm a bit troubled by the implications though.  If anyone creates a
datatype foo, they'd better not try to make an array-of-foo column
unless they've made '=' and '<' operators for foo.  This seems a bit
evil, especially for types like "point" which don't have obvious '<'
semantics, but *do* have uses for arrays.  Maybe we'd better think
twice about how to handle this.  How could the lack of an underlying
'<' be reflected back to the array-type level?

            regards, tom lane

Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> 3) There are now "<>, ">", "<", "<=", and ">=" operators for
>     array-to-array comparison, using semantics similar to text's
>     character-to-character comparison.

> Below are some examples of the new (since earlier patches)
> functionality. CVS tip from this morning plus this patch compiles
> cleanly and passes all 90 regression tests on my dev box.

Didn't you just say this makes initdb fail?

            regards, tom lane

Re: array support patch phase 1 patch

From
Joe Conway
Date:
Tom Lane wrote:
> Joe Conway <mail@joeconway.com> writes:
>
>>3) There are now "<>, ">", "<", "<=", and ">=" operators for
>>    array-to-array comparison, using semantics similar to text's
>>    character-to-character comparison.
>
>
>>Below are some examples of the new (since earlier patches)
>>functionality. CVS tip from this morning plus this patch compiles
>>cleanly and passes all 90 regression tests on my dev box.
>
>
> Didn't you just say this makes initdb fail?

Sorry -- in the meantime I fixed (or hacked, depending on your POV) the
issue by:

2) examine_attribute() now checks for the element type's equality and
    ordering operators when dealing with a varlena array type

This seems to take care of the issue. Now initdb works fine and all
tests pass.

Joe


Re: array support patch phase 1 patch

From
Joe Conway
Date:
Tom Lane wrote:
> I'm a bit troubled by the implications though.  If anyone creates a
> datatype foo, they'd better not try to make an array-of-foo column
> unless they've made '=' and '<' operators for foo.  This seems a bit
> evil, especially for types like "point" which don't have obvious '<'
> semantics, but *do* have uses for arrays.  Maybe we'd better think
> twice about how to handle this.  How could the lack of an underlying
> '<' be reflected back to the array-type level?

See my last reply. This issue seemed isolated to analyze.

Now there is no requirement to have either '=' or '<' operators for foo
in order to make a foo[] datatype. You'll only need them if you want
statistics. And even at that, if you just create the '=', you'll still
get the reduced level of support as for any other data type that has no
ordering operator. This is how aclitem is currently working with this patch.

Joe



Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> Tom Lane wrote:
>> Didn't you just say this makes initdb fail?

> Sorry -- in the meantime I fixed (or hacked, depending on your POV) the
> issue by:

> 2) examine_attribute() now checks for the element type's equality and
>     ordering operators when dealing with a varlena array type

That would mask the problem for ANALYZE, but not for anything else.

Whether there is anything else I'm not sure, but I can't say I feel
comfortable about that answer.

            regards, tom lane

Re: array support patch phase 1 patch

From
Kris Jurka
Date:
I've been looking at using the new array support with the JDBC driver to
retrieve foreign key information and I've kind of gotten stuck.

The basic query I want to run is

SELECT
        pkn.nspname AS PKTABLE_SCHEM,
        pkt.relname AS PKTABLE_NAME,
        pka.attname AS PKCOLUMN_NAME,
        fkn.nspname AS FKTABLE_SCHEM,
        fkt.relname AS FKTABLE_NAME,
        fka.attname AS FKCOLUMN_NAME,
        c.conname AS FK_NAME,
        pkc.conname AS PK_NAME

FROM pg_namespace pkn, pg_class pkt, pg_attribute pka,
        pg_namespace fkn, pg_class fkt, pg_attribute fka,
        pg_constraint c, pg_constraint pkc

WHERE
        pkn.oid = pkt.relnamespace
        AND pkt.oid = pka.attrelid
        AND fkn.oid = fkt.relnamespace
        AND fkt.oid = fka.attrelid
        AND c.conrelid = fkt.oid
        AND c.confrelid = pkt.oid
        AND pka.attnum = ANY (c.confkey)
        AND fka.attnum = ANY (c.conkey)
        AND c.confrelid = pkc.conrelid
--      AND pkc.conkey = c.confkey
;

So I'm getting back the right column and table names, but for a
multi-column key you get a cartesian product because you can't join on
index(conkey) = index(confkey).

I was trying formulate a way to make a function which will explode an
array into a resultset composed of the index and value.  So '{3,4,7}'
would become

index    value
1    3
2    4
3    7

I suppose you'd really want something like:

CREATE TABLE t (
    a int primary key,
    b int[]
);

SELECT * FROM explode_index(t,a,b);

returning rows of a, b-index, b-value

Another unrelated issue I ran into was that I wanted an equality operator
that was not ordered, so [1,2,3] = [2,1,3] because they contain the same
elements.


Just one user's thoughts,
Kris Jurka



Re: array support patch phase 1 patch

From
Joe Conway
Date:
Kris Jurka wrote:
> I was trying formulate a way to make a function which will explode an
> array into a resultset composed of the index and value.  So '{3,4,7}'
> would become
>
> index    value
> 1    3
> 2    4
> 3    7
>

I submitted a function that would do this, array_values(), but it was
rejected. See:
http://archives.postgresql.org/pgsql-hackers/2002-12/msg00453.php

Perhaps we should revisit that decision before feature freeze for 7.4?

Joe


Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> Kris Jurka wrote:
>> I was trying formulate a way to make a function which will explode an
>> array into a resultset composed of the index and value.

> I submitted a function that would do this, array_values(), but it was
> rejected. See:
> http://archives.postgresql.org/pgsql-hackers/2002-12/msg00453.php
> Perhaps we should revisit that decision before feature freeze for 7.4?

Kris' example didn't seem at all compelling to me, since the basic query
design was wrong anyway if he hopes to deal with multiple-column foreign
keys.  You can't have just one instance of pg_attribute being joined to,
unless you are planning to execute the query again for each column ---
and in that case the tests might as well be like "attnum = conkey[1]",
next time "attnum = conkey[2]", etc.

            regards, tom lane

Re: array support patch phase 1 patch

From
Kris Jurka
Date:

On Mon, 2 Jun 2003, Joe Conway wrote:

> Kris Jurka wrote:
> > I was trying formulate a way to make a function which will explode an
> > array into a resultset composed of the index and value.  So '{3,4,7}'
> > would become
> >
> > index    value
> > 1    3
> > 2    4
> > 3    7
> >
>
> I submitted a function that would do this, array_values(), but it was
> rejected. See:
> http://archives.postgresql.org/pgsql-hackers/2002-12/msg00453.php
>
> Perhaps we should revisit that decision before feature freeze for 7.4?

For the group listing example given in your original post what I want to
is produce a query which could return:

group_name    member_name
g1        user1
g1        user2
g2        user1
g2        user2
g2        user3

This can be done with the current array functionality:

SELECT g.groname, s.usename
FROM pg_group g, pg_shadow s
WHERE s.usesysid = ANY (g.grolist);

The problem arises when trying to join arrays as if they were tables.


Re: array support patch phase 1 patch

From
Joe Conway
Date:
Tom Lane wrote:
> I'm not sure though that any of these cases really are of concern.  Do
> we care in any of them whether failure to find the operator is postponed
> till runtime?  ANALYZE needs to know because it has a fallback path if
> there's no "<" operator, but ORDER BY does not.
>

I was thinking the same thing and hoping you'd say that. I'll send in an
updated patch later this morning once I've done a bit more testing.

Thanks,

Joe




Re: array support patch phase 1 patch

From
Joe Conway
Date:
Joe Conway wrote:
> Tom Lane wrote:
>> I'm not sure though that any of these cases really are of concern.  Do
>> we care in any of them whether failure to find the operator is postponed
>> till runtime?  ANALYZE needs to know because it has a fallback path if
>> there's no "<" operator, but ORDER BY does not.
>
> I was thinking the same thing and hoping you'd say that. I'll send in an
> updated patch later this morning once I've done a bit more testing.

Here is an updated array-combo patch. Only change is that this one moves
the check for missing array element type equality and ordering operators
into equality_oper() and ordering_oper(), per recent discussion.

The patch applys cleanly on cvs as of this morning, compiles without any
(new) warnings, initdb's fine, and passes all 90 regression tests.

Please apply.

Thanks,

Joe
Index: doc/src/sgml/array.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
retrieving revision 1.25
diff -c -r1.25 array.sgml
*** doc/src/sgml/array.sgml    13 Mar 2003 01:30:26 -0000    1.25
--- doc/src/sgml/array.sgml    1 Jun 2003 15:41:15 -0000
***************
*** 9,15 ****

   <para>
    <productname>PostgreSQL</productname> allows columns of a table to be
!   defined as variable-length multidimensional arrays. Arrays of any
    built-in type or user-defined type can be created.
   </para>

--- 9,15 ----

   <para>
    <productname>PostgreSQL</productname> allows columns of a table to be
!   defined as variable-length multi-dimensional arrays. Arrays of any
    built-in type or user-defined type can be created.
   </para>

***************
*** 60,73 ****
  </programlisting>
   </para>

   <note>
    <para>
!    A limitation of the present array implementation is that individual
!    elements of an array cannot be SQL null values.  The entire array can be set
!    to null, but you can't have an array with some elements null and some
!    not.  Fixing this is on the to-do list.
    </para>
   </note>
   </sect2>

   <sect2>
--- 60,133 ----
  </programlisting>
   </para>

+  <para>
+   A limitation of the present array implementation is that individual
+   elements of an array cannot be SQL null values.  The entire array can be set
+   to null, but you can't have an array with some elements null and some
+   not.
+  </para>
+  <para>
+   This can lead to surprising results. For example, the result of the
+   previous two inserts looks like this:
+ <programlisting>
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |      schedule
+ -------+---------------------------+--------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting},{""}}
+  Carol | {20000,25000,25000,25000} | {{talk},{meeting}}
+ (2 rows)
+ </programlisting>
+   Because the <literal>[2][2]</literal> element of
+   <structfield>schedule</structfield> is missing in each of the
+   <command>INSERT</command> statements, the <literal>[1][2]</literal>
+   element is discarded.
+  </para>
+
+  <note>
+   <para>
+    Fixing this is on the to-do list.
+   </para>
+  </note>
+
+  <para>
+   The <command>ARRAY</command> expression syntax may also be used:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Bill',
+     ARRAY[10000, 10000, 10000, 10000],
+     ARRAY[['meeting', 'lunch'], ['','']]);
+
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting', '']]);
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |           schedule
+ -------+---------------------------+-------------------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}}
+  Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}}
+ (2 rows)
+ </programlisting>
+   Note that with this syntax, multi-dimensional arrays must have matching
+   extents for each dimension. This eliminates the missing-array-elements
+   problem above. For example:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting']]);
+ ERROR:  Multiple dimension arrays must have array expressions with matching dimensions
+ </programlisting>
+   Also notice that string literals are single quoted instead of double quoted.
+  </para>
+
   <note>
    <para>
!    The examples in the rest of this section are based on the
!    <command>ARRAY</command> expression syntax <command>INSERT</command>s.
    </para>
   </note>
+
   </sect2>

   <sect2>
***************
*** 132,142 ****
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the
!   form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified.
   </para>

   <para>
--- 192,221 ----
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified; another example follows:
! <programlisting>
! SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
!          schedule
! ---------------------------
!  {{meeting,lunch},{"",""}}
! (1 row)
! </programlisting>
!  </para>
!
!  <para>
!   Additionally, we can also access a single arbitrary array element of
!   a one-dimensional array with the <function>array_subscript</function>
!   function:
! <programlisting>
! SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
!  array_subscript
! -----------------
!            10000
! (1 row)
! </programlisting>
   </para>

   <para>
***************
*** 147,153 ****
      WHERE name = 'Carol';
  </programlisting>

!   or updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
--- 226,248 ----
      WHERE name = 'Carol';
  </programlisting>

!   or using the <command>ARRAY</command> expression syntax:
!
! <programlisting>
! UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
!     WHERE name = 'Carol';
! </programlisting>
!
!   <note>
!    <para>
!     Anywhere you can use the <quote>curly braces</quote> array syntax,
!     you can also use the <command>ARRAY</command> expression syntax. The
!     remainder of this section will illustrate only one or the other, but
!     not both.
!    </para>
!   </note>
!
!   An array may also be updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
***************
*** 160,165 ****
--- 255,268 ----
  UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
      WHERE name = 'Carol';
  </programlisting>
+
+   A one-dimensional array may also be updated with the
+   <function>array_assign</function> function:
+
+ <programlisting>
+ UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000)
+     WHERE name = 'Bill';
+ </programListing>
   </para>

   <para>
***************
*** 169,175 ****
    value currently has 4 elements, it will have five elements after an
    update that assigns to <literal>array[5]</>.  Currently, enlargement in
    this fashion is only allowed for one-dimensional arrays, not
!   multidimensional arrays.
   </para>

   <para>
--- 272,278 ----
    value currently has 4 elements, it will have five elements after an
    update that assigns to <literal>array[5]</>.  Currently, enlargement in
    this fashion is only allowed for one-dimensional arrays, not
!   multi-dimensional arrays.
   </para>

   <para>
***************
*** 179,184 ****
--- 282,367 ----
   </para>

   <para>
+   An array can also be enlarged by using the functions
+   <function>array_prepend</function>, <function>array_append</function>,
+   or <function>array_cat</function>. The first two only support one-dimensional
+   arrays, but <function>array_cat</function> supports multi-dimensional arrays.
+   Some examples:
+
+ <programlisting>
+ SELECT array_prepend(1, ARRAY[2,3]);
+  array_prepend
+ ---------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_append(ARRAY[1,2], 3);
+  array_append
+ --------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
+    array_cat
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
+       array_cat
+ ---------------------
+  {{1,2},{3,4},{5,6}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
+       array_cat
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ </programlisting>
+
+   <function>array_prepend</function> and <function>array_append</function>
+   work with a one-dimensional array and a single element to be pushed on
+   to the beginning or end of the array, respectively. The array is extended
+   in the direction of the push. Hence, by pushing onto the beginning of an
+   array with a one-based subscript, a zero-based subscript array is created:
+
+ <programlisting>
+ SELECT array_dims(t.f) FROM (SELECT array_prepend(1, ARRAY[2,3]) AS f) AS t;
+  array_dims
+ ------------
+  [0:2]
+ (1 row)
+ </programlisting>
+
+   <function>array_cat</function> works with either two
+   <replaceable>n</>-dimension arrays, or an <replaceable>n</>-dimension
+   and an <replaceable>n+1</> dimension array. In the former case, the two
+   <replaceable>n</>-dimension arrays become outer elements of an
+   <replaceable>n+1</> dimension array. In the latter, the
+   <replaceable>n</>-dimension array is added as either the first or last
+   outer element of the <replaceable>n+1</> dimension array.
+  </para>
+
+  <para>
+   A final method of enlarging arrays is through the concatenation operator,
+   <command>||</command>, which works exactly as <function>array_cat</function>
+   does.
+ <programlisting>
+ SELECT ARRAY[1,2] || ARRAY[3,4];
+    ?column?
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
+       ?column?
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ (1 row)
+ </programlisting>
+  </para>
+
+  <para>
    The syntax for <command>CREATE TABLE</command> allows fixed-length
    arrays to be defined:

***************
*** 194,199 ****
--- 377,392 ----
   </para>

   <para>
+   An alternative syntax for one-dimensional arrays may be used.
+   <structfield>pay_by_quarter</structfield> could have been defined as:
+ <programlisting>
+     pay_by_quarter  integer ARRAY[4],
+ </programlisting>
+   This syntax may <emphasis>only</emphasis> be used with the integer
+   constant to denote the array size.
+  </para>
+
+  <para>
    Actually, the current implementation does not enforce the declared
    number of dimensions either.  Arrays of a particular element type are
    all considered to be of the same type, regardless of size or number
***************
*** 292,298 ****
     for the array's element type.  (Among the standard data types provided
     in the <productname>PostgreSQL</productname> distribution, type
     <literal>box</> uses a semicolon (<literal>;</>) but all the others
!    use comma.)  In a multidimensional array, each dimension (row, plane,
     cube, etc.) gets its own level of curly braces, and delimiters
     must be written between adjacent curly-braced entities of the same level.
     You may write whitespace before a left brace, after a right
--- 485,491 ----
     for the array's element type.  (Among the standard data types provided
     in the <productname>PostgreSQL</productname> distribution, type
     <literal>box</> uses a semicolon (<literal>;</>) but all the others
!    use comma.)  In a multi-dimensional array, each dimension (row, plane,
     cube, etc.) gets its own level of curly braces, and delimiters
     must be written between adjacent curly-braced entities of the same level.
     You may write whitespace before a left brace, after a right
***************
*** 300,305 ****
--- 493,564 ----
     is not ignored, however: after skipping leading whitespace, everything
     up to the next right brace or delimiter is taken as the item value.
    </para>
+
+   <para>
+    As illustrated earlier in this chapter, arrays may also be represented
+    using the <command>ARRAY</command> expression syntax. This representation
+    of an array value consists of items that are interpreted according to the
+    I/O conversion rules for the array's element type, plus decoration that
+    indicates the array structure. The decoration consists of the keyword
+    <command>ARRAY</command> and square brackets (<literal>[</> and
+    <literal>]</>) around the array values, plus delimiter characters between
+    adjacent items. The delimiter character is always a comma (<literal>,</>).
+    When representing multi-dimensional arrays, the keyword
+    <command>ARRAY</command> is only necessary for the outer level. For example,
+    <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as:
+ <programlisting>
+ SELECT ARRAY[['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   or it also could be written as:
+ <programlisting>
+ SELECT ARRAY[ARRAY['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   </para>
+
+   <para>
+    A final method to represent an array, is through an
+    <command>ARRAY</command> sub-select expression. For example:
+ <programlisting>
+ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
+                           ?column?
+ -------------------------------------------------------------
+  {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
+ (1 row)
+ </programlisting>
+   The sub-select may <emphasis>only</emphasis> return a single column. The
+   resulting one-dimensional array will have an element for each row in the
+   sub-select result, with an element type matching that of the sub-select's
+   target column.
+   </para>
+
+   <para>
+    Arrays may be cast from one type to another in similar fashion to other
+    data types:
+
+ <programlisting>
+ SELECT ARRAY[1,2,3]::oid[];
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+
+ SELECT CAST(ARRAY[1,2,3] AS float8[]);
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+ </programlisting>
+
+   </para>
+
   </sect2>

   <sect2>
***************
*** 316,321 ****
--- 575,588 ----
     Alternatively, you can use backslash-escaping to protect all data characters
     that would otherwise be taken as array syntax or ignorable white space.
    </para>
+
+  <note>
+   <para>
+    The discussion in the preceding paragraph with respect to double quoting does
+    not pertain to the <command>ARRAY</command> expression syntax. In that case,
+    each element is quoted exactly as any other literal value of the element type.
+   </para>
+  </note>

    <para>
     The array output routine will put double quotes around element values
Index: doc/src/sgml/func.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v
retrieving revision 1.154
diff -c -r1.154 func.sgml
*** doc/src/sgml/func.sgml    5 May 2003 15:08:49 -0000    1.154
--- doc/src/sgml/func.sgml    1 Jun 2003 15:41:15 -0000
***************
*** 6962,6967 ****
--- 6962,7209 ----

    </sect1>

+  <sect1 id="functions-array">
+   <title>Array Functions</title>
+
+   <para>
+    <xref linkend="array-operators-table"> shows the operators
+    available for the <type>array</type> types.
+   </para>
+
+     <table id="array-operators-table">
+      <title><type>array</type> Operators</title>
+      <tgroup cols="4">
+       <thead>
+        <row>
+     <entry>Operator</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry> <literal>=</literal> </entry>
+     <entry>equals</entry>
+     <entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry>
+     <entry><literal>t</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>element-to-array concatenation</entry>
+     <entry><literal>3 || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{3,4,5,6}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-element concatenation</entry>
+     <entry><literal>ARRAY[4,5,6] || 7</literal></entry>
+     <entry><literal>{4,5,6,7}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+
+   <para>
+    <xref linkend="array-functions-table"> shows the functions
+    available for use with array types. See <xref linkend="arrays">
+    for more discussion and examples for the use of these functions.
+   </para>
+
+     <table id="array-functions-table">
+      <title><type>array</type> Functions</title>
+      <tgroup cols="5">
+       <thead>
+        <row>
+     <entry>Function</entry>
+     <entry>Return Type</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry>
+      <literal>
+       <function>array_append</function>
+       (<type>anyarray</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the end of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_append(ARRAY[1,2], 3)</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_assign</function>
+       (<type>anyarray</type>, <type>integer</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      assign a value to a specific array element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_assign(ARRAY[1,2,3], 2, 99)</literal></entry>
+     <entry><literal>{1,99,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_cat</function>
+       (<type>anyarray</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      concatenate two arrays, returning <literal>NULL</literal>
+      for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_dims</function>
+       (<type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      returns a text representation of array dimension lower and upper bounds,
+      generating an ERROR for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry>
+     <entry><literal>[1:2][1:3]</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_lower</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns lower bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry>
+     <entry><literal>0</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_prepend</function>
+       (<type>anyelement</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the beginning of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_subscript</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>anyelement</type></entry>
+     <entry>
+      returns requested array element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_subscript(ARRAY[1,2,3], 3)</literal></entry>
+     <entry><literal>3</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_to_str</function>
+       (<type>anyarray</type>, <type>text</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      concatenates array elements using provided delimiter, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_to_str(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry>
+     <entry><literal>1.10~^~2.20~^~3.30</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_upper</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns upper bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry>
+     <entry><literal>4</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>singleton_array</function>
+       (<type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      create an array from the provided element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>singleton_array(1)</literal></entry>
+     <entry><literal>{1}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>str_to_array</function>
+       (<type>text</type>, <type>text</type>)
+      </literal>
+     </entry>
+     <entry><type>text[]</type></entry>
+     <entry>
+      splits string into array elements using provided delimiter, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>str_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry>
+     <entry><literal>{1.1,2.2,3.3}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+   </sect1>

   <sect1 id="functions-aggregate">
    <title>Aggregate Functions</title>
Index: src/backend/catalog/pg_aggregate.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_aggregate.c,v
retrieving revision 1.56
diff -c -r1.56 pg_aggregate.c
*** src/backend/catalog/pg_aggregate.c    18 Sep 2002 21:35:20 -0000    1.56
--- src/backend/catalog/pg_aggregate.c    1 Jun 2003 15:41:15 -0000
***************
*** 50,59 ****
      Oid            finalfn = InvalidOid;    /* can be omitted */
      Oid            finaltype;
      Oid            fnArgs[FUNC_MAX_ARGS];
!     int            nargs;
      Oid            procOid;
      TupleDesc    tupDesc;
      int            i;
      ObjectAddress myself,
                  referenced;

--- 50,65 ----
      Oid            finalfn = InvalidOid;    /* can be omitted */
      Oid            finaltype;
      Oid            fnArgs[FUNC_MAX_ARGS];
!     int            nargs_transfn;
!     int            nargs_finalfn;
      Oid            procOid;
      TupleDesc    tupDesc;
      int            i;
+     Oid            rettype;
+     Oid           *true_oid_array_transfn;
+     Oid           *true_oid_array_finalfn;
+     bool        retset;
+     FuncDetailCode fdresult;
      ObjectAddress myself,
                  referenced;

***************
*** 68,91 ****
      MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
      fnArgs[0] = aggTransType;
      if (aggBaseType == ANYOID)
!         nargs = 1;
      else
      {
          fnArgs[1] = aggBaseType;
!         nargs = 2;
      }
!     transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
      if (!OidIsValid(transfn))
!         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
      tup = SearchSysCache(PROCOID,
                           ObjectIdGetDatum(transfn),
                           0, 0, 0);
      if (!HeapTupleIsValid(tup))
!         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
      proc = (Form_pg_proc) GETSTRUCT(tup);
-     if (proc->prorettype != aggTransType)
-         elog(ERROR, "return type of transition function %s is not %s",
-          NameListToString(aggtransfnName), format_type_be(aggTransType));

      /*
       * If the transfn is strict and the initval is NULL, make sure input
--- 74,122 ----
      MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
      fnArgs[0] = aggTransType;
      if (aggBaseType == ANYOID)
!         nargs_transfn = 1;
      else
      {
          fnArgs[1] = aggBaseType;
!         nargs_transfn = 2;
      }
!
!     /*
!      * func_get_detail looks up the function in the catalogs, does
!      * disambiguation for polymorphic functions, handles inheritance, and
!      * returns the funcid and type and set or singleton status of the
!      * function's return value.  it also returns the true argument types
!      * to the function.
!      */
!     fdresult = func_get_detail(aggtransfnName, NIL, nargs_transfn, fnArgs,
!                                &transfn, &rettype, &retset,
!                                &true_oid_array_transfn);
!
!     /* only valid case is a normal function */
!     if (fdresult != FUNCDETAIL_NORMAL)
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
      if (!OidIsValid(transfn))
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
!     /*
!      * enforce consistency with ANYARRAY and ANYELEMENT argument
!      * and return types, possibly modifying return type along the way
!      */
!     rettype = enforce_generic_type_consistency(fnArgs, true_oid_array_transfn,
!                                                        nargs_transfn, rettype);
!
!     if (rettype != aggTransType)
!         elog(ERROR, "return type of transition function %s is not %s",
!          NameListToString(aggtransfnName), format_type_be(aggTransType));
!
      tup = SearchSysCache(PROCOID,
                           ObjectIdGetDatum(transfn),
                           0, 0, 0);
      if (!HeapTupleIsValid(tup))
!         func_error("AggregateCreate", aggtransfnName,
!                         nargs_transfn, fnArgs, NULL);
      proc = (Form_pg_proc) GETSTRUCT(tup);

      /*
       * If the transfn is strict and the initval is NULL, make sure input
***************
*** 105,121 ****
      {
          MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
          fnArgs[0] = aggTransType;
!         finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
!         if (!OidIsValid(finalfn))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!         tup = SearchSysCache(PROCOID,
!                              ObjectIdGetDatum(finalfn),
!                              0, 0, 0);
!         if (!HeapTupleIsValid(tup))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!         proc = (Form_pg_proc) GETSTRUCT(tup);
!         finaltype = proc->prorettype;
!         ReleaseSysCache(tup);
      }
      else
      {
--- 136,161 ----
      {
          MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
          fnArgs[0] = aggTransType;
!         nargs_finalfn = 1;
!
!         fdresult = func_get_detail(aggfinalfnName, NIL, 1, fnArgs,
!                                    &finalfn, &rettype, &retset,
!                                    &true_oid_array_finalfn);
!
!         /* only valid case is a normal function */
!         if (fdresult != FUNCDETAIL_NORMAL)
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!
!         if (!OidIsValid(finalfn))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!
!         /*
!          * enforce consistency with ANYARRAY and ANYELEMENT argument
!          * and return types, possibly modifying return type along the way
!          */
!         finaltype = enforce_generic_type_consistency(fnArgs,
!                                                      true_oid_array_finalfn,
!                                                      nargs_finalfn, rettype);
      }
      else
      {
***************
*** 125,130 ****
--- 165,191 ----
          finaltype = aggTransType;
      }
      Assert(OidIsValid(finaltype));
+
+     /*
+      * special disallowed cases:
+      * 1)    if finaltype is polymorphic, basetype cannot be ANY
+      * 2)    if finaltype is polymorphic, both args to transfn must be
+      *        polymorphic
+      */
+     if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
+     {
+         if (aggBaseType == ANYOID)
+             elog(ERROR, "aggregate with base type ANY must have a " \
+                         "non-polymorphic return type");
+
+         if (nargs_transfn > 1 && (
+             (true_oid_array_transfn[0] != ANYARRAYOID &&
+              true_oid_array_transfn[0] != ANYELEMENTOID) ||
+             (true_oid_array_transfn[1] != ANYARRAYOID &&
+              true_oid_array_transfn[1] != ANYELEMENTOID)))
+             elog(ERROR, "aggregate with polymorphic return type requires " \
+                         "state function with both arguments polymorphic");
+     }

      /*
       * Everything looks okay.  Try to create the pg_proc entry for the
Index: src/backend/commands/aggregatecmds.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/aggregatecmds.c,v
retrieving revision 1.5
diff -c -r1.5 aggregatecmds.c
*** src/backend/commands/aggregatecmds.c    4 Sep 2002 20:31:14 -0000    1.5
--- src/backend/commands/aggregatecmds.c    1 Jun 2003 15:41:15 -0000
***************
*** 119,125 ****
          baseTypeId = typenameTypeId(baseType);

      transTypeId = typenameTypeId(transType);
!     if (get_typtype(transTypeId) == 'p')
          elog(ERROR, "Aggregate transition datatype cannot be %s",
               format_type_be(transTypeId));

--- 119,127 ----
          baseTypeId = typenameTypeId(baseType);

      transTypeId = typenameTypeId(transType);
!     if (get_typtype(transTypeId) == 'p' &&
!         transTypeId != ANYARRAYOID &&
!         transTypeId != ANYELEMENTOID)
          elog(ERROR, "Aggregate transition datatype cannot be %s",
               format_type_be(transTypeId));

Index: src/backend/executor/nodeAgg.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeAgg.c,v
retrieving revision 1.105
diff -c -r1.105 nodeAgg.c
*** src/backend/executor/nodeAgg.c    30 May 2003 20:23:10 -0000    1.105
--- src/backend/executor/nodeAgg.c    1 Jun 2003 15:41:15 -0000
***************
*** 58,63 ****
--- 58,64 ----
  #include "executor/executor.h"
  #include "executor/nodeAgg.h"
  #include "miscadmin.h"
+ #include "nodes/makefuncs.h"
  #include "optimizer/clauses.h"
  #include "parser/parse_coerce.h"
  #include "parser/parse_expr.h"
***************
*** 212,218 ****
  static void agg_fill_hash_table(AggState *aggstate);
  static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
  static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
!

  /*
   * Initialize all aggregates for a new group of input values.
--- 213,219 ----
  static void agg_fill_hash_table(AggState *aggstate);
  static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
  static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
! static Oid resolve_type(Oid type_to_resolve, Oid context_type);

  /*
   * Initialize all aggregates for a new group of input values.
***************
*** 351,364 ****
      fcinfo.context = NULL;
      fcinfo.resultinfo = NULL;
      fcinfo.isnull = false;
-
      fcinfo.flinfo = &peraggstate->transfn;
      fcinfo.nargs = 2;
      fcinfo.arg[0] = pergroupstate->transValue;
      fcinfo.argnull[0] = pergroupstate->transValueIsNull;
      fcinfo.arg[1] = newVal;
      fcinfo.argnull[1] = isNull;
-
      newVal = FunctionCallInvoke(&fcinfo);

      /*
--- 352,363 ----
***************
*** 1176,1182 ****
--- 1175,1195 ----
          AclResult    aclresult;
          Oid            transfn_oid,
                      finalfn_oid;
+         FuncExpr   *transfnexpr,
+                    *finalfnexpr;
          Datum        textInitVal;
+         List       *fargs;
+         Oid            agg_rt_type;
+         Oid           *transfn_arg_types;
+         List       *transfn_args = NIL;
+         int            transfn_nargs;
+         Oid            transfn_ret_type;
+         Oid           *finalfn_arg_types = NULL;
+         List       *finalfn_args = NIL;
+         Oid            finalfn_ret_type = InvalidOid;
+         int            finalfn_nargs = 0;
+         Node       *arg0;
+         Node       *arg1;
          int            i;

          /* Look for a previous duplicate aggregate */
***************
*** 1224,1229 ****
--- 1237,1402 ----
                          &peraggstate->transtypeLen,
                          &peraggstate->transtypeByVal);

+         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
+         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
+
+         /* get the runtime aggregate argument type */
+         fargs = aggref->args;
+         agg_rt_type = exprType((Node *) nth(0, fargs));
+
+         /* get the transition function argument and return types */
+         transfn_ret_type = get_func_rettype(transfn_oid);
+         transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs);
+
+         /* resolve any polymorphic types */
+         if (transfn_nargs == 2)
+         /* base type was not ANY */
+         {
+             if (transfn_arg_types[1] == ANYARRAYOID ||
+                 transfn_arg_types[1] == ANYELEMENTOID)
+                 transfn_arg_types[1] = agg_rt_type;
+
+             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+                                                         agg_rt_type);
+
+             /*
+              * Build arg list to use on the transfn FuncExpr node. We really
+              * only care that the node type is correct so that the transfn
+              * can discover the actual argument types at runtime using
+              * get_fn_expr_argtype()
+              */
+             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
+                                                             -1, COERCE_DONTCARE);
+             arg1 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[1],
+                                                             -1, COERCE_DONTCARE);
+             transfn_args = makeList2(arg0, arg1);
+
+             /*
+              * the state transition function always returns the same type
+              * as its first argument
+              */
+             if (transfn_ret_type == ANYARRAYOID ||
+                 transfn_ret_type == ANYELEMENTOID)
+                 transfn_ret_type = transfn_arg_types[0];
+         }
+         else if (transfn_nargs == 1)
+         /*
+          * base type was ANY, therefore the aggregate return type should
+          * be non-polymorphic
+          */
+         {
+             Oid    finaltype = get_func_rettype(aggref->aggfnoid);
+
+             /*
+              * this should have been prevented in AggregateCreate,
+              * but check anyway
+              */
+             if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
+                 elog(ERROR, "aggregate with base type ANY must have a " \
+                             "non-polymorphic return type");
+
+             /* see if we have a final function */
+             if (OidIsValid(finalfn_oid))
+             {
+                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
+                 if (finalfn_nargs != 1)
+                     elog(ERROR, "final function takes unexpected number " \
+                                 "of arguments: %d", finalfn_nargs);
+
+                 /*
+                  * final function argument is always the same as the state
+                  * function return type
+                  */
+                 if (finalfn_arg_types[0] != ANYARRAYOID &&
+                     finalfn_arg_types[0] != ANYELEMENTOID)
+                 {
+                     /* if it is not ambiguous, use it */
+                     transfn_ret_type = finalfn_arg_types[0];
+                 }
+                 else
+                 {
+                     /* if it is ambiguous, try to derive it */
+                     finalfn_ret_type = finaltype;
+                     finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0],
+                                                             finalfn_ret_type);
+                     transfn_ret_type = finalfn_arg_types[0];
+                 }
+             }
+             else
+                 transfn_ret_type = finaltype;
+
+             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+                                                         transfn_ret_type);
+
+             /*
+              * Build arg list to use on the transfn FuncExpr node. We really
+              * only care that the node type is correct so that the transfn
+              * can discover the actual argument types at runtime using
+              * get_fn_expr_argtype()
+              */
+             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
+                                                             -1, COERCE_DONTCARE);
+             transfn_args = makeList1(arg0);
+         }
+         else
+             elog(ERROR, "state transition function takes unexpected number " \
+                         "of arguments: %d", transfn_nargs);
+
+         if (OidIsValid(finalfn_oid))
+         {
+             /* get the final function argument and return types */
+             if (finalfn_ret_type == InvalidOid)
+                 finalfn_ret_type = get_func_rettype(finalfn_oid);
+
+             if (!finalfn_arg_types)
+             {
+                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
+                 if (finalfn_nargs != 1)
+                     elog(ERROR, "final function takes unexpected number " \
+                                 "of arguments: %d", finalfn_nargs);
+             }
+
+             /*
+              * final function argument is always the same as the state
+              * function return type, which by now should have been resolved
+              */
+             if (finalfn_arg_types[0] == ANYARRAYOID ||
+                 finalfn_arg_types[0] == ANYELEMENTOID)
+                 finalfn_arg_types[0] = transfn_ret_type;
+
+             /*
+              * Build arg list to use on the finalfn FuncExpr node. We really
+              * only care that the node type is correct so that the finalfn
+              * can discover the actual argument type at runtime using
+              * get_fn_expr_argtype()
+              */
+             arg0 = (Node *) makeRelabelType((Expr *) NULL, finalfn_arg_types[0],
+                                                             -1, COERCE_DONTCARE);
+             finalfn_args = makeList1(arg0);
+
+             finalfn_ret_type = resolve_type(finalfn_ret_type,
+                                                 finalfn_arg_types[0]);
+         }
+
+         fmgr_info(transfn_oid, &peraggstate->transfn);
+         transfnexpr = (FuncExpr *) make_funcclause(transfn_oid,
+                           transfn_ret_type,
+                           false,            /* cannot be a set */
+                           COERCE_DONTCARE,    /* to match any user expr */
+                           transfn_args);
+         peraggstate->transfn.fn_expr = (Node *) transfnexpr;
+
+         if (OidIsValid(finalfn_oid))
+         {
+             fmgr_info(finalfn_oid, &peraggstate->finalfn);
+             finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid,
+                           finalfn_ret_type,
+                           false,            /* cannot be a set */
+                           COERCE_DONTCARE,    /* to match any user expr */
+                           finalfn_args);
+             peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
+         }
+
          /*
           * initval is potentially null, so don't try to access it as a
           * struct field. Must do it the hard way with SysCacheGetAttr.
***************
*** 1236,1249 ****
              peraggstate->initValue = (Datum) 0;
          else
              peraggstate->initValue = GetAggInitVal(textInitVal,
!                                                    aggform->aggtranstype);
!
!         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
!         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
!
!         fmgr_info(transfn_oid, &peraggstate->transfn);
!         if (OidIsValid(finalfn_oid))
!             fmgr_info(finalfn_oid, &peraggstate->finalfn);

          /*
           * If the transfn is strict and the initval is NULL, make sure
--- 1409,1415 ----
              peraggstate->initValue = (Datum) 0;
          else
              peraggstate->initValue = GetAggInitVal(textInitVal,
!                                                    transfn_arg_types[0]);

          /*
           * If the transfn is strict and the initval is NULL, make sure
***************
*** 1454,1457 ****
--- 1620,1656 ----
      elog(ERROR, "Aggregate function %u called as normal function",
           fcinfo->flinfo->fn_oid);
      return (Datum) 0;            /* keep compiler quiet */
+ }
+
+ static Oid
+ resolve_type(Oid type_to_resolve, Oid context_type)
+ {
+     Oid        resolved_type;
+
+     if (context_type == ANYARRAYOID || context_type == ANYELEMENTOID)
+         resolved_type = type_to_resolve;
+     else if (type_to_resolve == ANYARRAYOID)
+     /* any array */
+     {
+         Oid        context_type_arraytype = get_array_type(context_type);
+
+         if (context_type_arraytype != InvalidOid)
+             resolved_type = context_type_arraytype;
+         else
+             resolved_type = context_type;
+     }
+     else if (type_to_resolve == ANYELEMENTOID)
+     /* any element */
+     {
+         Oid        context_type_elemtype = get_element_type(context_type);
+
+         if (context_type_elemtype != InvalidOid)
+             resolved_type = context_type_elemtype;
+         else
+             resolved_type = context_type;
+     }
+     else
+         resolved_type = type_to_resolve;
+
+     return resolved_type;
  }
Index: src/backend/executor/nodeSubplan.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v
retrieving revision 1.45
diff -c -r1.45 nodeSubplan.c
*** src/backend/executor/nodeSubplan.c    8 Apr 2003 23:20:01 -0000    1.45
--- src/backend/executor/nodeSubplan.c    1 Jun 2003 15:41:47 -0000
***************
*** 28,50 ****
  #include "utils/datum.h"
  #include "utils/lsyscache.h"

-
- typedef struct ArrayBuildState
- {
-     MemoryContext mcontext;        /* where all the temp stuff is kept */
-     Datum       *dvalues;        /* array of accumulated Datums */
-     /*
-      * The allocated size of dvalues[] is always a multiple of
-      * ARRAY_ELEMS_CHUNKSIZE
-      */
- #define ARRAY_ELEMS_CHUNKSIZE    64
-     int            nelems;            /* number of valid Datums in dvalues[] */
-     Oid            element_type;    /* data type of the Datums */
-     int16        typlen;            /* needed info about datatype */
-     bool        typbyval;
-     char        typalign;
- } ArrayBuildState;
-
  static Datum ExecHashSubPlan(SubPlanState *node,
                               ExprContext *econtext,
                               bool *isNull);
--- 28,33 ----
***************
*** 54,66 ****
  static void buildSubPlanHash(SubPlanState *node);
  static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
  static bool tupleAllNulls(HeapTuple tuple);
- static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
-                                          Datum dvalue, bool disnull,
-                                          Oid element_type,
-                                          MemoryContext rcontext);
- static Datum makeArrayResult(ArrayBuildState *astate,
-                              MemoryContext rcontext);
-

  /* ----------------------------------------------------------------
   *        ExecSubPlan
--- 37,42 ----
***************
*** 224,229 ****
--- 200,206 ----
      PlanState  *planstate = node->planstate;
      SubLinkType subLinkType = subplan->subLinkType;
      bool        useOr = subplan->useOr;
+     bool        isExpr = subplan->isExpr;
      MemoryContext oldcontext;
      TupleTableSlot *slot;
      Datum        result;
***************
*** 292,297 ****
--- 269,279 ----
          bool        rownull = false;
          int            col = 1;
          List       *plst;
+         int            numelems;
+         int            elemnum;
+         Datum        dvalue;
+         Datum       *dvalues = NULL;
+         bool        disnull;

          if (subLinkType == EXISTS_SUBLINK)
          {
***************
*** 329,337 ****

          if (subLinkType == ARRAY_SUBLINK)
          {
-             Datum    dvalue;
-             bool    disnull;
-
              found = true;
              /* stash away current value */
              dvalue = heap_getattr(tup, 1, tdesc, &disnull);
--- 311,316 ----
***************
*** 349,446 ****
          found = true;

          /*
!          * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
!          * operators for columns of tuple.
           */
!         plst = subplan->paramIds;
!         foreach(lst, node->exprs)
          {
!             ExprState  *exprstate = (ExprState *) lfirst(lst);
!             int            paramid = lfirsti(plst);
!             ParamExecData *prmdata;
!             Datum        expresult;
!             bool        expnull;
!
!             /*
!              * Load up the Param representing this column of the sub-select.
!              */
!             prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
!             Assert(prmdata->execPlan == NULL);
!             prmdata->value = heap_getattr(tup, col, tdesc,
!                                           &(prmdata->isnull));

!             /*
!              * Now we can eval the combining operator for this column.
!              */
!             expresult = ExecEvalExprSwitchContext(exprstate, econtext,
!                                                   &expnull, NULL);
!
!             /*
!              * Combine the result into the row result as appropriate.
!              */
!             if (col == 1)
              {
!                 rowresult = expresult;
!                 rownull = expnull;
              }
!             else if (useOr)
              {
!                 /* combine within row per OR semantics */
!                 if (expnull)
!                     rownull = true;
!                 else if (DatumGetBool(expresult))
                  {
!                     rowresult = BoolGetDatum(true);
!                     rownull = false;
!                     break;        /* needn't look at any more columns */
                  }
              }
              else
              {
!                 /* combine within row per AND semantics */
!                 if (expnull)
!                     rownull = true;
!                 else if (!DatumGetBool(expresult))
!                 {
!                     rowresult = BoolGetDatum(false);
!                     rownull = false;
!                     break;        /* needn't look at any more columns */
!                 }
              }

-             plst = lnext(plst);
-             col++;
          }

!         if (subLinkType == ANY_SUBLINK)
          {
!             /* combine across rows per OR semantics */
!             if (rownull)
!                 *isNull = true;
!             else if (DatumGetBool(rowresult))
              {
!                 result = BoolGetDatum(true);
!                 *isNull = false;
!                 break;            /* needn't look at any more rows */
              }
!         }
!         else if (subLinkType == ALL_SUBLINK)
!         {
!             /* combine across rows per AND semantics */
!             if (rownull)
!                 *isNull = true;
!             else if (!DatumGetBool(rowresult))
              {
!                 result = BoolGetDatum(false);
!                 *isNull = false;
!                 break;            /* needn't look at any more rows */
              }
-         }
-         else
-         {
-             /* must be MULTIEXPR_SUBLINK */
-             result = rowresult;
-             *isNull = rownull;
          }
      }

--- 328,490 ----
          found = true;

          /*
!          * When isExpr is true, we have either a scalar expression or an
!          * array. In the former case, this is no different than the !isExpr
!          * case. In the latter case, iterate over the elements as if they
!          * were from multiple input tuples.
           */
!         if (!isExpr)
!             numelems = 1;
!         else
          {
!             Oid        expr_typeid = tdesc->attrs[0]->atttypid;

!             if (expr_typeid != subplan->exprtype)
              {
!                 subplan->exprtype = expr_typeid;
!                 subplan->elemtype = get_element_type(expr_typeid);
!
!                 if (subplan->elemtype != InvalidOid)
!                     get_typlenbyvalalign(subplan->elemtype,
!                                          &subplan->elmlen,
!                                          &subplan->elmbyval,
!                                          &subplan->elmalign);
              }
!
!             /* get current value */
!             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
!
!             /* XXX this will need work if/when arrays support NULL elements */
!             if (!disnull)
              {
!                 if (subplan->elemtype != InvalidOid)
!                 {
!                     ArrayType   *v = DatumGetArrayTypeP(dvalue);
!
!                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
!                                       subplan->elmbyval, subplan->elmalign,
!                                         &dvalues, &numelems);
!                 }
!                 else
                  {
!                     numelems = 1;
!                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                     dvalues[0] = dvalue;
                  }
              }
              else
              {
!                 numelems = 1;
!                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                 dvalues[0] = (Datum) 0;
              }

          }

!         for (elemnum = 0; elemnum < numelems; elemnum++)
          {
!             /*
!              * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
!              * operators for columns of tuple.
!              */
!             col = 1;
!             plst = subplan->paramIds;
!             foreach(lst, node->exprs)
              {
!                 ExprState  *exprstate = (ExprState *) lfirst(lst);
!                 int            paramid = lfirsti(plst);
!                 ParamExecData *prmdata;
!                 Datum        expresult;
!                 bool        expnull;
!
!                 /*
!                  * Load up the Param representing this column of the sub-select.
!                  */
!                 prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
!                 Assert(prmdata->execPlan == NULL);
!
!                 if (!isExpr)
!                     prmdata->value = heap_getattr(tup, col, tdesc,
!                                                   &(prmdata->isnull));
!                 else
!                 {
!                     prmdata->value = dvalues[elemnum];
!                     prmdata->isnull = disnull;
!                 }
!
!                 /*
!                  * Now we can eval the combining operator for this column.
!                  */
!                 expresult = ExecEvalExprSwitchContext(exprstate, econtext,
!                                                       &expnull, NULL);
!
!                 /*
!                  * Combine the result into the row result as appropriate.
!                  */
!                 if (col == 1)
!                 {
!                     rowresult = expresult;
!                     rownull = expnull;
!                 }
!                 else if (useOr)
!                 {
!                     /* combine within row per OR semantics */
!                     if (expnull)
!                         rownull = true;
!                     else if (DatumGetBool(expresult))
!                     {
!                         rowresult = BoolGetDatum(true);
!                         rownull = false;
!                         break;        /* needn't look at any more columns */
!                     }
!                 }
!                 else
!                 {
!                     /* combine within row per AND semantics */
!                     if (expnull)
!                         rownull = true;
!                     else if (!DatumGetBool(expresult))
!                     {
!                         rowresult = BoolGetDatum(false);
!                         rownull = false;
!                         break;        /* needn't look at any more columns */
!                     }
!                 }
!
!                 plst = lnext(plst);
!                 col++;
              }
!
!             if (subLinkType == ANY_SUBLINK)
              {
!                 /* combine across rows per OR semantics */
!                 if (rownull)
!                     *isNull = true;
!                 else if (DatumGetBool(rowresult))
!                 {
!                     result = BoolGetDatum(true);
!                     *isNull = false;
!                     break;            /* needn't look at any more rows */
!                 }
!             }
!             else if (subLinkType == ALL_SUBLINK)
!             {
!                 /* combine across rows per AND semantics */
!                 if (rownull)
!                     *isNull = true;
!                 else if (!DatumGetBool(rowresult))
!                 {
!                     result = BoolGetDatum(false);
!                     *isNull = false;
!                     break;            /* needn't look at any more rows */
!                 }
!             }
!             else
!             {
!                 /* must be MULTIEXPR_SUBLINK */
!                 result = rowresult;
!                 *isNull = rownull;
              }
          }
      }

***************
*** 478,483 ****
--- 522,528 ----
  buildSubPlanHash(SubPlanState *node)
  {
      SubPlan       *subplan = (SubPlan *) node->xprstate.expr;
+     bool        isExpr = subplan->isExpr;
      PlanState  *planstate = node->planstate;
      int            ncols = length(node->exprs);
      ExprContext *innerecontext = node->innerecontext;
***************
*** 485,490 ****
--- 530,536 ----
      MemoryContext oldcontext;
      int            nbuckets;
      TupleTableSlot *slot;
+     TupleTableSlot *arrslot = NULL;

      Assert(subplan->subLinkType == ANY_SUBLINK);
      Assert(!subplan->useOr);
***************
*** 562,604 ****
      {
          HeapTuple    tup = slot->val;
          TupleDesc    tdesc = slot->ttc_tupleDescriptor;
!         int            col = 1;
          List       *plst;
          bool        isnew;

          /*
!          * Load up the Params representing the raw sub-select outputs,
!          * then form the projection tuple to store in the hashtable.
           */
!         foreach(plst, subplan->paramIds)
          {
!             int            paramid = lfirsti(plst);
!             ParamExecData *prmdata;

!             prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
!             Assert(prmdata->execPlan == NULL);
!             prmdata->value = heap_getattr(tup, col, tdesc,
!                                           &(prmdata->isnull));
!             col++;
!         }
!         slot = ExecProject(node->projRight, NULL);
!         tup = slot->val;

-         /*
-          * If result contains any nulls, store separately or not at all.
-          * (Since we know the projection tuple has no junk columns, we
-          * can just look at the overall hasnull info bit, instead of
-          * groveling through the columns.)
-          */
-         if (HeapTupleNoNulls(tup))
-         {
-             (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
-             node->havehashrows = true;
          }
!         else if (node->hashnulls)
          {
!             (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
!             node->havenullrows = true;
          }

          /*
--- 608,746 ----
      {
          HeapTuple    tup = slot->val;
          TupleDesc    tdesc = slot->ttc_tupleDescriptor;
!         TupleDesc    arrtdesc = NULL;
          List       *plst;
          bool        isnew;
+         int            numelems;
+         int            elemnum;
+         Datum        dvalue;
+         Datum       *dvalues = NULL;
+         bool        disnull;

          /*
!          * When isExpr is true, we have either a scalar expression or an
!          * array. In the former case, this is no different than the !isExpr
!          * case. In the latter case, iterate over the elements as if they
!          * were from multiple input tuples.
           */
!         if (!isExpr)
!             numelems = 1;
!         else
          {
!             Oid        expr_typeid = tdesc->attrs[0]->atttypid;

!             if (expr_typeid != subplan->exprtype)
!             {
!                 subplan->exprtype = expr_typeid;
!                 subplan->elemtype = get_element_type(expr_typeid);
!
!                 if (subplan->elemtype != InvalidOid)
!                     get_typlenbyvalalign(subplan->elemtype,
!                                          &subplan->elmlen,
!                                          &subplan->elmbyval,
!                                          &subplan->elmalign);
!             }
!
!             /* get current value */
!             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
!
!             if (subplan->elemtype != InvalidOid)
!             {
!                 TupleTable    tupleTable;
!                 ArrayType   *v = DatumGetArrayTypeP(dvalue);
!
!                 arrtdesc = CreateTemplateTupleDesc(1, false);
!                 TupleDescInitEntry(arrtdesc, 1, "elem", subplan->elemtype,
!                                                             -1, 0, false);
!
!                 tupleTable = ExecCreateTupleTable(1);
!                 arrslot = ExecAllocTableSlot(tupleTable);
!                 ExecSetSlotDescriptor(arrslot, arrtdesc, true);
!
!                 /* XXX this will need work if/when arrays support NULL elements */
!                 if (!disnull)
!                 {
!                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
!                                       subplan->elmbyval, subplan->elmalign,
!                                         &dvalues, &numelems);
!                 }
!                 else
!                 {
!                     numelems = 1;
!                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                     dvalues[0] = (Datum) 0;
!                 }
!             }
!             else
!             {
!                 numelems = 1;
!                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                 dvalues[0] = dvalue;
!             }

          }
!
!         for (elemnum = 0; elemnum < numelems; elemnum++)
          {
!             int    col = 1;
!
!             if (!isExpr || subplan->elemtype == InvalidOid)
!             {
!                 /*
!                  * Load up the Params representing the raw sub-select outputs,
!                  * then form the projection tuple to store in the hashtable.
!                  */
!                 foreach(plst, subplan->paramIds)
!                 {
!                     int            paramid = lfirsti(plst);
!                     ParamExecData *prmdata;
!
!                     prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
!                     Assert(prmdata->execPlan == NULL);
!
!                     prmdata->value = heap_getattr(tup, col, tdesc,
!                                                   &(prmdata->isnull));
!
!                     col++;
!                 }
!                 slot = ExecProject(node->projRight, NULL);
!                 tup = slot->val;
!             }
!             else
!             {
!                 /*
!                  * For array type expressions, we need to build up our own
!                  * tuple and slot
!                  */
!                 char        nullflag;
!
!                 nullflag = disnull ? 'n' : ' ';
!                 tup = heap_formtuple(arrtdesc, &dvalues[elemnum], &nullflag);
!                 arrslot = ExecStoreTuple(tup, arrslot, InvalidBuffer, true);
!             }
!
!             /*
!              * If result contains any nulls, store separately or not at all.
!              * (Since we know the projection tuple has no junk columns, we
!              * can just look at the overall hasnull info bit, instead of
!              * groveling through the columns.)
!              */
!             if (HeapTupleNoNulls(tup))
!             {
!                 if (!isExpr)
!                     (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
!                 else
!                     (void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew);
!                 node->havehashrows = true;
!             }
!             else if (node->hashnulls)
!             {
!                 if (!isExpr)
!                     (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
!                 else
!                     (void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew);
!                 node->havenullrows = true;
!             }
          }

          /*
***************
*** 615,620 ****
--- 757,764 ----
       * have the potential for a double free attempt.
       */
      ExecClearTuple(node->projRight->pi_slot);
+     if (arrslot)
+         ExecClearTuple(arrslot);

      MemoryContextSwitchTo(oldcontext);
  }
***************
*** 1084,1185 ****
          prm->execPlan = node;
          parent->chgParam = bms_add_member(parent->chgParam, paramid);
      }
- }
-
- /*
-  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
-  *
-  *    astate is working state (NULL on first call)
-  *    rcontext is where to keep working state
-  */
- static ArrayBuildState *
- accumArrayResult(ArrayBuildState *astate,
-                  Datum dvalue, bool disnull,
-                  Oid element_type,
-                  MemoryContext rcontext)
- {
-     MemoryContext arr_context,
-                   oldcontext;
-
-     if (astate == NULL)
-     {
-         /* First time through --- initialize */
-
-         /* Make a temporary context to hold all the junk */
-         arr_context = AllocSetContextCreate(rcontext,
-                                             "ARRAY_SUBLINK Result",
-                                             ALLOCSET_DEFAULT_MINSIZE,
-                                             ALLOCSET_DEFAULT_INITSIZE,
-                                             ALLOCSET_DEFAULT_MAXSIZE);
-         oldcontext = MemoryContextSwitchTo(arr_context);
-         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
-         astate->mcontext = arr_context;
-         astate->dvalues = (Datum *)
-             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
-         astate->nelems = 0;
-         astate->element_type = element_type;
-         get_typlenbyvalalign(element_type,
-                              &astate->typlen,
-                              &astate->typbyval,
-                              &astate->typalign);
-     }
-     else
-     {
-         oldcontext = MemoryContextSwitchTo(astate->mcontext);
-         Assert(astate->element_type == element_type);
-         /* enlarge dvalues[] if needed */
-         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
-             astate->dvalues = (Datum *)
-                 repalloc(astate->dvalues,
-                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
-     }
-
-     if (disnull)
-         elog(ERROR, "NULL elements not allowed in Arrays");
-
-     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
-     astate->dvalues[astate->nelems++] =
-         datumCopy(dvalue, astate->typbyval, astate->typlen);
-
-     MemoryContextSwitchTo(oldcontext);
-
-     return astate;
- }
-
- /*
-  * makeArrayResult - produce final result of ARRAY_SUBLINK
-  *
-  *    astate is working state (not NULL)
-  *    rcontext is where to construct result
-  */
- static Datum
- makeArrayResult(ArrayBuildState *astate,
-                 MemoryContext rcontext)
- {
-     ArrayType  *result;
-     int            dims[1];
-     int            lbs[1];
-     MemoryContext oldcontext;
-
-     /* Build the final array result in rcontext */
-     oldcontext = MemoryContextSwitchTo(rcontext);
-
-     dims[0] = astate->nelems;
-     lbs[0] = 1;
-
-     result = construct_md_array(astate->dvalues,
-                                 1,
-                                 dims,
-                                 lbs,
-                                 astate->element_type,
-                                 astate->typlen,
-                                 astate->typbyval,
-                                 astate->typalign);
-
-     MemoryContextSwitchTo(oldcontext);
-
-     /* Clean up all the junk */
-     MemoryContextDelete(astate->mcontext);
-
-     return PointerGetDatum(result);
  }
--- 1228,1231 ----
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.251
diff -c -r1.251 copyfuncs.c
*** src/backend/nodes/copyfuncs.c    28 May 2003 16:03:56 -0000    1.251
--- src/backend/nodes/copyfuncs.c    1 Jun 2003 15:41:47 -0000
***************
*** 727,732 ****
--- 727,733 ----
      COPY_NODE_FIELD(target);
      COPY_SCALAR_FIELD(aggstar);
      COPY_SCALAR_FIELD(aggdistinct);
+     COPY_NODE_FIELD(args);

      return newnode;
  }
***************
*** 825,830 ****
--- 826,832 ----

      COPY_SCALAR_FIELD(subLinkType);
      COPY_SCALAR_FIELD(useOr);
+     COPY_SCALAR_FIELD(isExpr);
      COPY_NODE_FIELD(lefthand);
      COPY_NODE_FIELD(operName);
      COPY_OIDLIST_FIELD(operOids);
***************
*** 843,848 ****
--- 845,856 ----

      COPY_SCALAR_FIELD(subLinkType);
      COPY_SCALAR_FIELD(useOr);
+     COPY_SCALAR_FIELD(isExpr);
+     COPY_SCALAR_FIELD(exprtype);
+     COPY_SCALAR_FIELD(elemtype);
+     COPY_SCALAR_FIELD(elmlen);
+     COPY_SCALAR_FIELD(elmbyval);
+     COPY_SCALAR_FIELD(elmalign);
      COPY_NODE_FIELD(exprs);
      COPY_INTLIST_FIELD(paramIds);
      COPY_NODE_FIELD(plan);
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.194
diff -c -r1.194 equalfuncs.c
*** src/backend/nodes/equalfuncs.c    28 May 2003 16:03:56 -0000    1.194
--- src/backend/nodes/equalfuncs.c    1 Jun 2003 15:41:47 -0000
***************
*** 204,209 ****
--- 204,210 ----
      COMPARE_NODE_FIELD(target);
      COMPARE_SCALAR_FIELD(aggstar);
      COMPARE_SCALAR_FIELD(aggdistinct);
+     COMPARE_NODE_FIELD(args);

      return true;
  }
***************
*** 300,305 ****
--- 301,307 ----
  {
      COMPARE_SCALAR_FIELD(subLinkType);
      COMPARE_SCALAR_FIELD(useOr);
+     COMPARE_SCALAR_FIELD(isExpr);
      COMPARE_NODE_FIELD(lefthand);
      COMPARE_NODE_FIELD(operName);
      COMPARE_OIDLIST_FIELD(operOids);
***************
*** 313,318 ****
--- 315,326 ----
  {
      COMPARE_SCALAR_FIELD(subLinkType);
      COMPARE_SCALAR_FIELD(useOr);
+     COMPARE_SCALAR_FIELD(isExpr);
+     COMPARE_SCALAR_FIELD(exprtype);
+     COMPARE_SCALAR_FIELD(elemtype);
+     COMPARE_SCALAR_FIELD(elmlen);
+     COMPARE_SCALAR_FIELD(elmbyval);
+     COMPARE_SCALAR_FIELD(elmalign);
      COMPARE_NODE_FIELD(exprs);
      COMPARE_INTLIST_FIELD(paramIds);
      /* should compare plans, but have to settle for comparing plan IDs */
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v
retrieving revision 1.206
diff -c -r1.206 outfuncs.c
*** src/backend/nodes/outfuncs.c    28 May 2003 16:03:56 -0000    1.206
--- src/backend/nodes/outfuncs.c    1 Jun 2003 15:41:47 -0000
***************
*** 615,620 ****
--- 615,621 ----
      WRITE_NODE_FIELD(target);
      WRITE_BOOL_FIELD(aggstar);
      WRITE_BOOL_FIELD(aggdistinct);
+     WRITE_NODE_FIELD(args);
  }

  static void
***************
*** 700,705 ****
--- 701,707 ----

      WRITE_ENUM_FIELD(subLinkType, SubLinkType);
      WRITE_BOOL_FIELD(useOr);
+     WRITE_BOOL_FIELD(isExpr);
      WRITE_NODE_FIELD(lefthand);
      WRITE_NODE_FIELD(operName);
      WRITE_OIDLIST_FIELD(operOids);
***************
*** 713,718 ****
--- 715,726 ----

      WRITE_ENUM_FIELD(subLinkType, SubLinkType);
      WRITE_BOOL_FIELD(useOr);
+     WRITE_BOOL_FIELD(isExpr);
+     WRITE_OID_FIELD(exprtype);
+     WRITE_OID_FIELD(elemtype);
+     WRITE_INT_FIELD(elmlen);
+     WRITE_BOOL_FIELD(elmbyval);
+     WRITE_CHAR_FIELD(elmalign);
      WRITE_NODE_FIELD(exprs);
      WRITE_INTLIST_FIELD(paramIds);
      WRITE_NODE_FIELD(plan);
Index: src/backend/nodes/readfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v
retrieving revision 1.153
diff -c -r1.153 readfuncs.c
*** src/backend/nodes/readfuncs.c    6 May 2003 00:20:32 -0000    1.153
--- src/backend/nodes/readfuncs.c    1 Jun 2003 15:41:47 -0000
***************
*** 415,420 ****
--- 415,421 ----
      READ_NODE_FIELD(target);
      READ_BOOL_FIELD(aggstar);
      READ_BOOL_FIELD(aggdistinct);
+     READ_NODE_FIELD(args);

      READ_DONE();
  }
***************
*** 544,549 ****
--- 545,551 ----

      READ_ENUM_FIELD(subLinkType, SubLinkType);
      READ_BOOL_FIELD(useOr);
+     READ_BOOL_FIELD(isExpr);
      READ_NODE_FIELD(lefthand);
      READ_NODE_FIELD(operName);
      READ_OIDLIST_FIELD(operOids);
Index: src/backend/optimizer/plan/subselect.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/plan/subselect.c,v
retrieving revision 1.75
diff -c -r1.75 subselect.c
*** src/backend/optimizer/plan/subselect.c    29 Apr 2003 22:13:09 -0000    1.75
--- src/backend/optimizer/plan/subselect.c    1 Jun 2003 15:41:47 -0000
***************
*** 67,73 ****

  static List *convert_sublink_opers(List *lefthand, List *operOids,
                                     List *targetlist, int rtindex,
!                                    List **righthandIds);
  static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
  static Node *replace_correlation_vars_mutator(Node *node, void *context);
  static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
--- 67,73 ----

  static List *convert_sublink_opers(List *lefthand, List *operOids,
                                     List *targetlist, int rtindex,
!                                    bool isExpr, List **righthandIds);
  static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
  static Node *replace_correlation_vars_mutator(Node *node, void *context);
  static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
***************
*** 240,245 ****
--- 240,251 ----
       */
      node->subLinkType = slink->subLinkType;
      node->useOr = slink->useOr;
+     node->isExpr = slink->isExpr;
+     node->exprtype = InvalidOid;
+     node->elemtype = InvalidOid;
+     node->elmlen = 0;
+     node->elmbyval = false;
+     node->elmalign = '\0';
      node->exprs = NIL;
      node->paramIds = NIL;
      node->useHashTable = false;
***************
*** 316,322 ****
          exprs = convert_sublink_opers(lefthand,
                                        slink->operOids,
                                        plan->targetlist,
!                                       0,
                                        &node->paramIds);
          node->setParam = listCopy(node->paramIds);
          PlannerInitPlan = lappend(PlannerInitPlan, node);
--- 322,328 ----
          exprs = convert_sublink_opers(lefthand,
                                        slink->operOids,
                                        plan->targetlist,
!                                       0, node->isExpr,
                                        &node->paramIds);
          node->setParam = listCopy(node->paramIds);
          PlannerInitPlan = lappend(PlannerInitPlan, node);
***************
*** 399,405 ****
          node->exprs = convert_sublink_opers(lefthand,
                                              slink->operOids,
                                              plan->targetlist,
!                                             0,
                                              &node->paramIds);

          /*
--- 405,411 ----
          node->exprs = convert_sublink_opers(lefthand,
                                              slink->operOids,
                                              plan->targetlist,
!                                             0, node->isExpr,
                                              &node->paramIds);

          /*
***************
*** 444,450 ****
  static List *
  convert_sublink_opers(List *lefthand, List *operOids,
                        List *targetlist, int rtindex,
!                       List **righthandIds)
  {
      List       *result = NIL;
      List       *lst;
--- 450,456 ----
  static List *
  convert_sublink_opers(List *lefthand, List *operOids,
                        List *targetlist, int rtindex,
!                       bool isExpr, List **righthandIds)
  {
      List       *result = NIL;
      List       *lst;
***************
*** 499,511 ****
           * are not expecting to have to resolve unknown Params, so
           * it's okay to pass a null pstate.)
           */
!         result = lappend(result,
!                          make_op_expr(NULL,
!                                       tup,
!                                       leftop,
!                                       rightop,
!                                       exprType(leftop),
!                                       te->resdom->restype));

          ReleaseSysCache(tup);

--- 505,542 ----
           * are not expecting to have to resolve unknown Params, so
           * it's okay to pass a null pstate.)
           */
!         if (!isExpr)
!         {
!             result = lappend(result,
!                              make_op_expr(NULL,
!                                           tup,
!                                           leftop,
!                                           rightop,
!                                           exprType(leftop),
!                                           te->resdom->restype));
!         }
!         else
!         {
!             Oid        exprtype = te->resdom->restype;
!             Oid        elemtype = get_element_type(exprtype);
!
!             if (elemtype != InvalidOid)
!                 result = lappend(result,
!                                  make_op_expr(NULL,
!                                               tup,
!                                               leftop,
!                                               rightop,
!                                               exprType(leftop),
!                                               elemtype));
!             else
!                 result = lappend(result,
!                                  make_op_expr(NULL,
!                                               tup,
!                                               leftop,
!                                               rightop,
!                                               exprType(leftop),
!                                               exprtype));
!         }

          ReleaseSysCache(tup);

***************
*** 616,628 ****
      /*
       * The sublink type must be "= ANY" --- that is, an IN operator.
       * (We require the operator name to be unqualified, which may be
!      * overly paranoid, or may not be.)
       */
      if (sublink->subLinkType != ANY_SUBLINK)
          return NULL;
      if (length(sublink->operName) != 1 ||
          strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
          return NULL;
      /*
       * The sub-select must not refer to any Vars of the parent query.
       * (Vars of higher levels should be okay, though.)
--- 647,663 ----
      /*
       * The sublink type must be "= ANY" --- that is, an IN operator.
       * (We require the operator name to be unqualified, which may be
!      * overly paranoid, or may not be.) It must not be an Expression
!      * sublink.
       */
      if (sublink->subLinkType != ANY_SUBLINK)
          return NULL;
      if (length(sublink->operName) != 1 ||
          strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
          return NULL;
+     if (sublink->isExpr)
+         return NULL;
+
      /*
       * The sub-select must not refer to any Vars of the parent query.
       * (Vars of higher levels should be okay, though.)
***************
*** 675,681 ****
      exprs = convert_sublink_opers(sublink->lefthand,
                                    sublink->operOids,
                                    subselect->targetList,
!                                   rtindex,
                                    &ininfo->sub_targetlist);
      return (Node *) make_ands_explicit(exprs);
  }
--- 710,716 ----
      exprs = convert_sublink_opers(sublink->lefthand,
                                    sublink->operOids,
                                    subselect->targetList,
!                                   rtindex, sublink->isExpr,
                                    &ininfo->sub_targetlist);
      return (Node *) make_ands_explicit(exprs);
  }
Index: src/backend/optimizer/util/clauses.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/util/clauses.c,v
retrieving revision 1.138
diff -c -r1.138 clauses.c
*** src/backend/optimizer/util/clauses.c    28 May 2003 22:32:49 -0000    1.138
--- src/backend/optimizer/util/clauses.c    1 Jun 2003 15:41:15 -0000
***************
*** 133,138 ****
--- 133,160 ----
  }

  /*****************************************************************************
+  *              FUNCTION clause functions
+  *****************************************************************************/
+
+ /*
+  * make_funcclause
+  *        Creates a function clause given its function info and argument list.
+  */
+ Expr *
+ make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
+                             CoercionForm funcformat, List *funcargs)
+ {
+     FuncExpr   *expr = makeNode(FuncExpr);
+
+     expr->funcid = funcid;
+     expr->funcresulttype = funcresulttype;
+     expr->funcretset = funcretset;
+     expr->funcformat = funcformat;
+     expr->args = funcargs;
+     return (Expr *) expr;
+ }
+
+ /*****************************************************************************
   *        NOT clause functions
   *****************************************************************************/

Index: src/backend/parser/gram.y
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/gram.y,v
retrieving revision 2.416
diff -c -r2.416 gram.y
*** src/backend/parser/gram.y    29 May 2003 20:40:36 -0000    2.416
--- src/backend/parser/gram.y    1 Jun 2003 15:41:47 -0000
***************
*** 5490,5495 ****
--- 5490,5496 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ANY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = makeList1(makeString("="));
                      n->subselect = $3;
***************
*** 5500,5505 ****
--- 5501,5507 ----
                      /* Make an IN node */
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ANY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = makeList1(makeString("="));
                      n->subselect = $4;
***************
*** 5511,5516 ****
--- 5513,5519 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = $3;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = $2;
                      n->subselect = $4;
***************
*** 5521,5526 ****
--- 5524,5530 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = MULTIEXPR_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = $2;
                      n->subselect = $3;
***************
*** 5904,5909 ****
--- 5908,5914 ----
                      {
                              SubLink *n = (SubLink *)$3;
                              n->subLinkType = ANY_SUBLINK;
+                             n->isExpr = false;
                              n->lefthand = makeList1($1);
                              n->operName = makeList1(makeString("="));
                              $$ = (Node *)n;
***************
*** 5931,5936 ****
--- 5936,5942 ----
                      {
                          /* Make an IN node */
                          SubLink *n = (SubLink *)$4;
+                         n->isExpr = false;
                          n->subLinkType = ANY_SUBLINK;
                          n->lefthand = makeList1($1);
                          n->operName = makeList1(makeString("="));
***************
*** 5957,5967 ****
--- 5963,6000 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = $3;
+                     n->isExpr = false;
                      n->lefthand = makeList1($1);
                      n->operName = $2;
                      n->subselect = $4;
                      $$ = (Node *)n;
                  }
+             | a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op
+                 {
+                     SubLink *n = makeNode(SubLink);
+                     SelectStmt *s = makeNode(SelectStmt);
+                     ResTarget *r = makeNode(ResTarget);
+
+                     r->name = NULL;
+                     r->indirection = NIL;
+                     r->val = (Node *)$5;
+
+                     s->distinctClause = NIL;
+                     s->targetList = makeList1(r);
+                     s->into = NULL;
+                     s->intoColNames = NIL;
+                     s->fromClause = NIL;
+                     s->whereClause = NULL;
+                     s->groupClause = NIL;
+                     s->havingClause = NULL;
+
+                     n->subLinkType = $3;
+                     n->isExpr = true;
+                     n->lefthand = makeList1($1);
+                     n->operName = $2;
+                     n->subselect = (Node *) s;
+                     $$ = (Node *)n;
+                 }
              | UNIQUE select_with_parens %prec Op
                  {
                      /* Not sure how to get rid of the parentheses
***************
*** 6538,6543 ****
--- 6571,6577 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = EXPR_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $1;
***************
*** 6547,6552 ****
--- 6581,6587 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = EXISTS_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $2;
***************
*** 6556,6561 ****
--- 6591,6597 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ARRAY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $2;
***************
*** 6730,6735 ****
--- 6766,6772 ----
  in_expr:    select_with_parens
                  {
                      SubLink *n = makeNode(SubLink);
+                     n->isExpr = false;
                      n->subselect = $1;
                      /* other fields will be filled later */
                      $$ = (Node *)n;
Index: src/backend/parser/parse_coerce.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v
retrieving revision 2.97
diff -c -r2.97 parse_coerce.c
*** src/backend/parser/parse_coerce.c    26 May 2003 00:11:27 -0000    2.97
--- src/backend/parser/parse_coerce.c    1 Jun 2003 16:15:40 -0000
***************
*** 859,865 ****
      /* Get the element type based on the array type, if we have one */
      if (OidIsValid(array_typeid))
      {
!         array_typelem = get_element_type(array_typeid);
          if (!OidIsValid(array_typelem))
              elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
                   format_type_be(array_typeid));
--- 859,869 ----
      /* Get the element type based on the array type, if we have one */
      if (OidIsValid(array_typeid))
      {
!         if (array_typeid != ANYARRAYOID)
!             array_typelem = get_element_type(array_typeid);
!         else
!             array_typelem = ANYELEMENTOID;
!
          if (!OidIsValid(array_typelem))
              elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
                   format_type_be(array_typeid));
***************
*** 919,925 ****
      {
          if (!OidIsValid(array_typeid))
          {
!             array_typeid = get_array_type(elem_typeid);
              if (!OidIsValid(array_typeid))
                  elog(ERROR, "Cannot find array type for datatype %s",
                       format_type_be(elem_typeid));
--- 923,933 ----
      {
          if (!OidIsValid(array_typeid))
          {
!             if (elem_typeid != ANYELEMENTOID)
!                 array_typeid = get_array_type(elem_typeid);
!             else
!                 array_typeid = ANYARRAYOID;
!
              if (!OidIsValid(array_typeid))
                  elog(ERROR, "Cannot find array type for datatype %s",
                       format_type_be(elem_typeid));
***************
*** 1169,1174 ****
--- 1177,1187 ----
      /* Somewhat-fast path for domain -> base type case */
      if (srctype == targettype)
          return true;
+
+     /* Last of the fast-paths: check for matching polymorphic arrays */
+     if (targettype == ANYARRAYOID)
+         if (get_element_type(srctype) != InvalidOid)
+             return true;

      /* Else look in pg_cast */
      tuple = SearchSysCache(CASTSOURCETARGET,
Index: src/backend/parser/parse_expr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_expr.c,v
retrieving revision 1.148
diff -c -r1.148 parse_expr.c
*** src/backend/parser/parse_expr.c    29 Apr 2003 22:13:10 -0000    1.148
--- src/backend/parser/parse_expr.c    1 Jun 2003 15:41:47 -0000
***************
*** 436,441 ****
--- 436,442 ----
                      sublink->operName = NIL;
                      sublink->operOids = NIL;
                      sublink->useOr = FALSE;
+                     sublink->isExpr = FALSE;
                  }
                  else if (sublink->subLinkType == EXPR_SUBLINK ||
                           sublink->subLinkType == ARRAY_SUBLINK)
***************
*** 463,468 ****
--- 464,470 ----
                      sublink->operName = NIL;
                      sublink->operOids = NIL;
                      sublink->useOr = FALSE;
+                     sublink->isExpr = FALSE;
                  }
                  else
                  {
***************
*** 538,547 ****
                           * here, because make_subplan() will insert type
                           * coercion calls if needed.
                           */
!                         optup = oper(op,
!                                      exprType(lexpr),
!                                      exprType((Node *) tent->expr),
!                                      false);
                          opform = (Form_pg_operator) GETSTRUCT(optup);

                          if (opform->oprresult != BOOLOID)
--- 540,569 ----
                           * here, because make_subplan() will insert type
                           * coercion calls if needed.
                           */
!                         if (!sublink->isExpr)
!                         {
!                             optup = oper(op,
!                                          exprType(lexpr),
!                                          exprType((Node *) tent->expr),
!                                          false);
!                         }
!                         else
!                         {
!                             Oid        exprtype = exprType((Node *) tent->expr);
!                             Oid        elemtype = get_element_type(exprtype);
!
!                             if (elemtype != InvalidOid)
!                                 optup = oper(op,
!                                              exprType(lexpr),
!                                              elemtype,
!                                              false);
!                             else
!                                 optup = oper(op,
!                                              exprType(lexpr),
!                                              exprtype,
!                                              false);
!                         }
!
                          opform = (Form_pg_operator) GETSTRUCT(optup);

                          if (opform->oprresult != BOOLOID)
Index: src/backend/parser/parse_func.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_func.c,v
retrieving revision 1.148
diff -c -r1.148 parse_func.c
*** src/backend/parser/parse_func.c    26 May 2003 00:11:27 -0000    1.148
--- src/backend/parser/parse_func.c    1 Jun 2003 15:41:15 -0000
***************
*** 335,340 ****
--- 335,341 ----
          aggref->target = lfirst(fargs);
          aggref->aggstar = agg_star;
          aggref->aggdistinct = agg_distinct;
+         aggref->args = fargs;

          retval = (Node *) aggref;

Index: src/backend/parser/parse_oper.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_oper.c,v
retrieving revision 1.64
diff -c -r1.64 parse_oper.c
*** src/backend/parser/parse_oper.c    26 May 2003 00:11:27 -0000    1.64
--- src/backend/parser/parse_oper.c    2 Jun 2003 05:28:16 -0000
***************
*** 137,142 ****
--- 137,169 ----
  equality_oper(Oid argtype, bool noError)
  {
      Operator    optup;
+     Oid            elem_type = get_element_type(argtype);
+
+     if (OidIsValid(elem_type))
+     {
+         bool    found = false;
+         /*
+          * If the datatype is an array, look for an "=" operator for the
+          * element datatype.  We require it to be an exact or binary-compatible
+          * match, since most callers are not prepared to cope with adding any
+          * run-time type coercion steps.
+          */
+         optup = equality_oper(elem_type, true);
+         if (optup != NULL)
+         {
+             found = true;
+             ReleaseSysCache(optup);
+         }
+
+         if (!found)
+         {
+             if (!noError)
+                 elog(ERROR, "Unable to identify an equality operator for " \
+                             "array type's element type %s",
+                              format_type_be(elem_type));
+             return NULL;
+         }
+     }

      /*
       * Look for an "=" operator for the datatype.  We require it to be
***************
*** 175,180 ****
--- 202,234 ----
  ordering_oper(Oid argtype, bool noError)
  {
      Operator    optup;
+     Oid            elem_type = get_element_type(argtype);
+
+     if (OidIsValid(elem_type))
+     {
+         bool    found = false;
+         /*
+          * If the datatype is an array, find the array element type's equality
+          * operator, and use its lsortop (it *must* be mergejoinable).  We use
+          * this definition because for sorting and grouping purposes, it's
+          * important that the equality and ordering operators are consistent.
+          */
+         optup = ordering_oper(elem_type, true);
+         if (optup != NULL)
+         {
+             found = true;
+             ReleaseSysCache(optup);
+         }
+
+         if (!found)
+         {
+             if (!noError)
+                 elog(ERROR, "Unable to identify an ordering operator for " \
+                             "array type's element type %s",
+                              format_type_be(elem_type));
+             return NULL;
+         }
+     }

      /*
       * Find the type's equality operator, and use its lsortop (it *must*
***************
*** 215,220 ****
--- 269,289 ----
      Oid            result;

      optup = equality_oper(argtype, false);
+     result = oprfuncid(optup);
+     ReleaseSysCache(optup);
+     return result;
+ }
+
+ /*
+  * ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper())
+  */
+ Oid
+ ordering_oper_funcid(Oid argtype)
+ {
+     Operator    optup;
+     Oid            result;
+
+     optup = ordering_oper(argtype, false);
      result = oprfuncid(optup);
      ReleaseSysCache(optup);
      return result;
Index: src/backend/utils/adt/acl.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/acl.c,v
retrieving revision 1.86
diff -c -r1.86 acl.c
*** src/backend/utils/adt/acl.c    24 Jan 2003 21:53:29 -0000    1.86
--- src/backend/utils/adt/acl.c    1 Jun 2003 19:07:00 -0000
***************
*** 720,732 ****
      aidat = ACL_DAT(acl);
      for (i = 0; i < num; ++i)
      {
!         if (aip->ai_grantee == aidat[i].ai_grantee &&
!             aip->ai_privs == aidat[i].ai_privs)
              PG_RETURN_BOOL(true);
      }
      PG_RETURN_BOOL(false);
  }


  /*
   * has_table_privilege variants
--- 720,740 ----
      aidat = ACL_DAT(acl);
      for (i = 0; i < num; ++i)
      {
!         if (aclitemeq(aip, &aidat[i]))
              PG_RETURN_BOOL(true);
      }
      PG_RETURN_BOOL(false);
  }

+ /*
+  * user-facing version of aclitemeq() for use as the
+  * aclitem equality operator
+  */
+ Datum
+ aclitem_eq(PG_FUNCTION_ARGS)
+ {
+     PG_RETURN_BOOL(aclitemeq(PG_GETARG_ACLITEM_P(0), PG_GETARG_ACLITEM_P(1)));
+ }

  /*
   * has_table_privilege variants
Index: src/backend/utils/adt/array_userfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/array_userfuncs.c,v
retrieving revision 1.1
diff -c -r1.1 array_userfuncs.c
*** src/backend/utils/adt/array_userfuncs.c    8 Apr 2003 23:20:02 -0000    1.1
--- src/backend/utils/adt/array_userfuncs.c    1 Jun 2003 15:41:15 -0000
***************
*** 42,48 ****
      else
          ndims = 1;

!     PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
                                                   PG_GETARG_DATUM(0),
                                                   ndims));
  }
--- 42,48 ----
      else
          ndims = 1;

!     PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, elem_type,
                                                   PG_GETARG_DATUM(0),
                                                   ndims));
  }
***************
*** 70,75 ****
--- 70,76 ----
      Oid            arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
      Oid            arg0_elemid;
      Oid            arg1_elemid;
+     ArrayMetaState *my_extra;

      if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
          elog(ERROR, "array_push: cannot determine input data types");
***************
*** 95,122 ****
          PG_RETURN_NULL();        /* keep compiler quiet */
      }

!     /* Sanity check: do we have a one-dimensional array */
!     if (ARR_NDIM(v) != 1)
!         elog(ERROR, "Arrays greater than one-dimension are not supported");
!
!     lb = ARR_LBOUND(v);
!     dimv = ARR_DIMS(v);
!     if (arg0_elemid != InvalidOid)
      {
!         /* append newelem */
!         int    ub = dimv[0] + lb[0] - 1;
!         indx = ub + 1;
      }
      else
      {
!         /* prepend newelem */
!         indx = lb[0] - 1;
      }

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

!     result = array_set(v, 1, &indx, newelem, -1,
!                        typlen, typbyval, typalign, &isNull);

      PG_RETURN_ARRAYTYPE_P(result);
  }
--- 96,156 ----
          PG_RETURN_NULL();        /* keep compiler quiet */
      }

!     if (ARR_NDIM(v) == 1)
      {
!         lb = ARR_LBOUND(v);
!         dimv = ARR_DIMS(v);
!
!         if (arg0_elemid != InvalidOid)
!         {
!             /* append newelem */
!             int    ub = dimv[0] + lb[0] - 1;
!             indx = ub + 1;
!         }
!         else
!         {
!             /* prepend newelem */
!             indx = lb[0] - 1;
!         }
      }
+     else if (ARR_NDIM(v) == 0)
+         indx = 1;
      else
+         elog(ERROR, "only empty and one-dimensional arrays are supported");
+
+
+     /*
+      * We arrange to look up info about element type only once per series
+      * of calls, assuming the element type doesn't change underneath us.
+      */
+     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+     if (my_extra == NULL)
      {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
      }

!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }
!
!     result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval,
!                                                  typalign, &isNull);

      PG_RETURN_ARRAYTYPE_P(result);
  }
***************
*** 145,157 ****

      /*
       * We must have one of the following combinations of inputs:
!      * 1) two arrays with ndims1 == ndims2
!      * 2) ndims1 == ndims2 - 1
!      * 3) ndims1 == ndims2 + 1
       */
      ndims1 = ARR_NDIM(v1);
      ndims2 = ARR_NDIM(v2);

      if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
          elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
                      "%d dimensions", ndims1, ndims2);
--- 179,206 ----

      /*
       * We must have one of the following combinations of inputs:
!      * 1) one empty array, and one non-empty array
!      * 2) both arrays empty
!      * 3) two arrays with ndims1 == ndims2
!      * 4) ndims1 == ndims2 - 1
!      * 5) ndims1 == ndims2 + 1
       */
      ndims1 = ARR_NDIM(v1);
      ndims2 = ARR_NDIM(v2);

+     /*
+      * short circuit - if one input array is empty, and the other is not,
+      * we return the non-empty one as the result
+      *
+      * if both are empty, return the first one
+      */
+     if (ndims1 == 0 && ndims2 > 0)
+         PG_RETURN_ARRAYTYPE_P(v2);
+
+     if (ndims2 == 0)
+         PG_RETURN_ARRAYTYPE_P(v1);
+
+     /* the rest fall into combo 2, 3, or 4 */
      if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
          elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
                      "%d dimensions", ndims1, ndims2);
***************
*** 266,314 ****
      PG_RETURN_ARRAYTYPE_P(result);
  }

- /*----------------------------------------------------------------------------
-  * array_accum :
-  *        accumulator to build a 1-D array from input values -- this can be used
-  *        to create custom aggregates.
-  *
-  * This function is not marked strict, so we have to be careful about nulls.
-  *----------------------------------------------------------------------------
-  */
- Datum
- array_accum(PG_FUNCTION_ARGS)
- {
-     /* return NULL if both arguments are NULL */
-     if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
-         PG_RETURN_NULL();
-
-     /* create a new 1-D array from the new element if the array is NULL */
-     if (PG_ARGISNULL(0))
-     {
-         Oid            tgt_type = get_fn_expr_rettype(fcinfo);
-         Oid            tgt_elem_type;
-
-         if (tgt_type == InvalidOid)
-             elog(ERROR, "Cannot determine target array type");
-         tgt_elem_type = get_element_type(tgt_type);
-         if (tgt_elem_type == InvalidOid)
-             elog(ERROR, "Target type is not an array");
-
-         PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
-                                                      PG_GETARG_DATUM(1),
-                                                      1));
-     }
-
-     /* return the array if the new element is NULL */
-     if (PG_ARGISNULL(1))
-         PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0));
-
-     /*
-      * Otherwise this is equivalent to array_push.  We hack the call a little
-      * so that array_push can see the fn_expr information.
-      */
-     return array_push(fcinfo);
- }
-
  /*-----------------------------------------------------------------------------
   * array_assign :
   *        assign an element of an array to a new value and return the
--- 315,320 ----
***************
*** 329,334 ****
--- 335,341 ----
      int16        typlen;
      bool        typbyval;
      char        typalign;
+     ArrayMetaState *my_extra;

      v = PG_GETARG_ARRAYTYPE_P(0);
      idx_to_chg = PG_GETARG_INT32(1);
***************
*** 349,355 ****
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      result = array_set(v, 1, &idx_to_chg, newelem, -1,
                         typlen, typbyval, typalign, &isNull);
--- 356,390 ----
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      result = array_set(v, 1, &idx_to_chg, newelem, -1,
                         typlen, typbyval, typalign, &isNull);
***************
*** 375,380 ****
--- 410,416 ----
      int16        typlen;
      bool        typbyval;
      char        typalign;
+     ArrayMetaState *my_extra;

      v = PG_GETARG_ARRAYTYPE_P(0);
      idx = PG_GETARG_INT32(1);
***************
*** 394,400 ****
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);

--- 430,464 ----
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);

***************
*** 402,412 ****
  }

  /*
!  * actually does the work for singleton_array(), and array_accum() if it is
!  * given a null input array.
   */
  ArrayType *
! create_singleton_array(Oid element_type, Datum element, int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
--- 466,478 ----
  }

  /*
!  * actually does the work for singleton_array()
   */
  ArrayType *
! create_singleton_array(FunctionCallInfo fcinfo,
!                        Oid element_type,
!                        Datum element,
!                        int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
***************
*** 415,420 ****
--- 481,487 ----
      int        dims[MAXDIM];
      int        lbs[MAXDIM];
      int        i;
+     ArrayMetaState *my_extra;

      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);
***************
*** 429,435 ****
          lbs[i] = 1;
      }

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      return construct_md_array(dvalues, ndims, dims, lbs, element_type,
                                typlen, typbyval, typalign);
--- 496,530 ----
          lbs[i] = 1;
      }

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      return construct_md_array(dvalues, ndims, dims, lbs, element_type,
                                typlen, typbyval, typalign);
Index: src/backend/utils/adt/arrayfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v
retrieving revision 1.89
diff -c -r1.89 arrayfuncs.c
*** src/backend/utils/adt/arrayfuncs.c    9 May 2003 23:01:45 -0000    1.89
--- src/backend/utils/adt/arrayfuncs.c    2 Jun 2003 02:24:39 -0000
***************
*** 21,28 ****
--- 21,30 ----
  #include "catalog/pg_type.h"
  #include "libpq/pqformat.h"
  #include "parser/parse_coerce.h"
+ #include "parser/parse_oper.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
+ #include "utils/datum.h"
  #include "utils/memutils.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
***************
*** 70,85 ****

  #define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)

- /* I/O function selector for system_cache_lookup */
- typedef enum IOFuncSelector
- {
-     IOFunc_input,
-     IOFunc_output,
-     IOFunc_receive,
-     IOFunc_send
- } IOFuncSelector;
-
-
  static int    ArrayCount(char *str, int *dim, char typdelim);
  static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
               FmgrInfo *inputproc, Oid typelem, int32 typmod,
--- 72,77 ----
***************
*** 93,102 ****
  static void CopyArrayEls(char *p, Datum *values, int nitems,
               int typlen, bool typbyval, char typalign,
               bool freedata);
- static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
-                                 int *typlen, bool *typbyval,
-                                 char *typdelim, Oid *typelem,
-                                 Oid *proc, char *typalign);
  static Datum ArrayCast(char *value, bool byval, int len);
  static int ArrayCastAndSet(Datum src,
                  int typlen, bool typbyval, char typalign,
--- 85,90 ----
***************
*** 119,125 ****
                     char *destPtr,
                     int *st, int *endp, char *srcPtr,
                     int typlen, bool typbyval, char typalign);
!

  /*---------------------------------------------------------------------
   * array_in :
--- 107,113 ----
                     char *destPtr,
                     int *st, int *endp, char *srcPtr,
                     int typlen, bool typbyval, char typalign);
! static int array_cmp(FunctionCallInfo fcinfo);

  /*---------------------------------------------------------------------
   * array_in :
***************
*** 154,165 ****
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;

!     /* Get info about element type, including its input conversion proc */
!     system_cache_lookup(element_type, IOFunc_input,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typinput, &typalign);
!     fmgr_info(typinput, &inputproc);

      /* Make a modifiable copy of the input */
      /* XXX why are we allocating an extra 2 bytes here? */
--- 142,190 ----
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;
+     ArrayMetaState *my_extra;

!     /*
!      * We arrange to look up info about element type, including its input
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its input conversion proc */
!         get_type_metadata(element_type, IOFunc_input,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typinput, &typalign);
!         fmgr_info(typinput, &inputproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typinput;
!         my_extra->typalign = typalign;
!         my_extra->proc = inputproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typinput = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         inputproc = my_extra->proc;
!     }

      /* Make a modifiable copy of the input */
      /* XXX why are we allocating an extra 2 bytes here? */
***************
*** 636,647 ****
                  indx[MAXDIM];
      int            ndim,
                 *dim;

      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_output,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typoutput, &typalign);
!     fmgr_info(typoutput, &outputproc);

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
--- 661,711 ----
                  indx[MAXDIM];
      int            ndim,
                 *dim;
+     ArrayMetaState *my_extra;

      element_type = ARR_ELEMTYPE(v);
!
!     /*
!      * We arrange to look up info about element type, including its input
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its output conversion proc */
!         get_type_metadata(element_type, IOFunc_output,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typoutput, &typalign);
!         fmgr_info(typoutput, &outputproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typoutput;
!         my_extra->typalign = typalign;
!         my_extra->proc = outputproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typoutput = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         outputproc = my_extra->proc;
!     }

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
***************
*** 800,805 ****
--- 864,870 ----
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;
+     ArrayMetaState *my_extra;

      /* Get the array header information */
      ndim = pq_getmsgint(buf, 4);
***************
*** 831,844 ****
          PG_RETURN_ARRAYTYPE_P(retval);
      }

!     /* Get info about element type, including its receive conversion proc */
!     system_cache_lookup(element_type, IOFunc_receive,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typreceive, &typalign);
!     if (!OidIsValid(typreceive))
!         elog(ERROR, "No binary input function available for type %s",
!              format_type_be(element_type));
!     fmgr_info(typreceive, &receiveproc);

      dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
                                typlen, typbyval, typalign,
--- 896,945 ----
          PG_RETURN_ARRAYTYPE_P(retval);
      }

!     /*
!      * We arrange to look up info about element type, including its receive
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its receive conversion proc */
!         get_type_metadata(element_type, IOFunc_receive,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typreceive, &typalign);
!         if (!OidIsValid(typreceive))
!             elog(ERROR, "No binary input function available for type %s",
!                  format_type_be(element_type));
!         fmgr_info(typreceive, &receiveproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typreceive;
!         my_extra->typalign = typalign;
!         my_extra->proc = receiveproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typreceive = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         receiveproc = my_extra->proc;
!     }

      dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
                                typlen, typbyval, typalign,
***************
*** 976,990 ****
      int            ndim,
                 *dim;
      StringInfoData buf;

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
!                         &typdelim, &typelem, &typsend, &typalign);
!     if (!OidIsValid(typsend))
!         elog(ERROR, "No binary output function available for type %s",
!              format_type_be(element_type));
!     fmgr_info(typsend, &sendproc);

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
--- 1077,1130 ----
      int            ndim,
                 *dim;
      StringInfoData buf;
+     ArrayMetaState *my_extra;

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!
!     /*
!      * We arrange to look up info about element type, including its send
!      * proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its send proc */
!         get_type_metadata(element_type, IOFunc_send, &typlen, &typbyval,
!                             &typdelim, &typelem, &typsend, &typalign);
!         if (!OidIsValid(typsend))
!             elog(ERROR, "No binary output function available for type %s",
!                  format_type_be(element_type));
!         fmgr_info(typsend, &sendproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typsend;
!         my_extra->typalign = typalign;
!         my_extra->proc = sendproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typsend = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         sendproc = my_extra->proc;
!     }

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
***************
*** 1476,1481 ****
--- 1616,1641 ----
      array = DatumGetArrayTypeP(PointerGetDatum(array));

      ndim = ARR_NDIM(array);
+
+     /*
+      * if number of dims is zero, i.e. an empty array, create an array
+      * with nSubscripts dimensions, and set the lower bounds to the supplied
+      * subscripts
+      */
+     if (ndim == 0)
+     {
+         Oid        elmtype = ARR_ELEMTYPE(array);
+
+         for (i = 0; i < nSubscripts; i++)
+         {
+             dim[i] = 1;
+             lb[i] = indx[i];
+         }
+
+         return construct_md_array(&dataValue, nSubscripts, dim, lb, elmtype,
+                                                 elmlen, elmbyval, elmalign);
+     }
+
      if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
          elog(ERROR, "Invalid array subscripts");

***************
*** 1632,1637 ****
--- 1792,1822 ----
      /* note: we assume srcArray contains no toasted elements */

      ndim = ARR_NDIM(array);
+
+     /*
+      * if number of dims is zero, i.e. an empty array, create an array
+      * with nSubscripts dimensions, and set the upper and lower bounds
+      * to the supplied subscripts
+      */
+     if (ndim == 0)
+     {
+         Datum  *dvalues;
+         int        nelems;
+         Oid        elmtype = ARR_ELEMTYPE(array);
+
+         deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
+                                                         &dvalues, &nelems);
+
+         for (i = 0; i < nSubscripts; i++)
+         {
+             dim[i] = 1 + upperIndx[i] - lowerIndx[i];
+             lb[i] = lowerIndx[i];
+         }
+
+         return construct_md_array(dvalues, nSubscripts, dim, lb, elmtype,
+                                                  elmlen, elmbyval, elmalign);
+     }
+
      if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
          elog(ERROR, "Invalid array subscripts");

***************
*** 1811,1816 ****
--- 1996,2008 ----
      Oid            typelem;
      Oid            proc;
      char       *s;
+     typedef struct {
+         ArrayMetaState *inp_extra;
+         ArrayMetaState *ret_extra;
+     } am_extra;
+     am_extra  *my_extra;
+     ArrayMetaState *inp_extra;
+     ArrayMetaState *ret_extra;

      /* Get input array */
      if (fcinfo->nargs < 1)
***************
*** 1829,1839 ****
      if (nitems <= 0)
          PG_RETURN_ARRAYTYPE_P(v);

!     /* Lookup source and result types. Unneeded variables are reused. */
!     system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
!                         &typdelim, &typelem, &proc, &inp_typalign);
!     system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
!                         &typdelim, &typelem, &proc, &typalign);

      /* Allocate temporary array for new values */
      values = (Datum *) palloc(nitems * sizeof(Datum));
--- 2021,2101 ----
      if (nitems <= 0)
          PG_RETURN_ARRAYTYPE_P(v);

!     /*
!      * We arrange to look up info about input and return element types only
!      * once per series of calls, assuming the element type doesn't change
!      * underneath us.
!      */
!     my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(am_extra));
!         my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
!
!         my_extra->inp_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         inp_extra = my_extra->inp_extra;
!         inp_extra->element_type = InvalidOid;
!
!         my_extra->ret_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         ret_extra = my_extra->ret_extra;
!         ret_extra->element_type = InvalidOid;
!     }
!     else
!     {
!         inp_extra = my_extra->inp_extra;
!         ret_extra = my_extra->ret_extra;
!     }
!
!     if (inp_extra->element_type != inpType)
!     {
!         /* Lookup source and result types. Unneeded variables are reused. */
!         get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
!                             &typdelim, &typelem, &proc, &inp_typalign);
!
!         inp_extra->element_type = inpType;
!         inp_extra->typlen = inp_typlen;
!         inp_extra->typbyval = inp_typbyval;
!         inp_extra->typdelim = typdelim;
!         inp_extra->typelem = typelem;
!         inp_extra->typiofunc = proc;
!         inp_extra->typalign = inp_typalign;
!     }
!     else
!     {
!         inp_typlen = inp_extra->typlen;
!         inp_typbyval = inp_extra->typbyval;
!         typdelim = inp_extra->typdelim;
!         typelem = inp_extra->typelem;
!         proc = inp_extra->typiofunc;
!         inp_typalign = inp_extra->typalign;
!     }
!
!     if (ret_extra->element_type != retType)
!     {
!         /* Lookup source and result types. Unneeded variables are reused. */
!         get_type_metadata(retType, IOFunc_input, &typlen, &typbyval,
!                             &typdelim, &typelem, &proc, &typalign);
!
!         ret_extra->element_type = retType;
!         ret_extra->typlen = typlen;
!         ret_extra->typbyval = typbyval;
!         ret_extra->typdelim = typdelim;
!         ret_extra->typelem = typelem;
!         ret_extra->typiofunc = proc;
!         ret_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = ret_extra->typlen;
!         typbyval = ret_extra->typbyval;
!         typdelim = ret_extra->typdelim;
!         typelem = ret_extra->typelem;
!         proc = ret_extra->typiofunc;
!         typalign = ret_extra->typalign;
!     }

      /* Allocate temporary array for new values */
      values = (Datum *) palloc(nitems * sizeof(Datum));
***************
*** 2049,2056 ****
   *          compares two arrays for equality
   * result :
   *          returns true if the arrays are equal, false otherwise.
-  *
-  * XXX bitwise equality is pretty bogus ...
   *-----------------------------------------------------------------------------
   */
  Datum
--- 2311,2316 ----
***************
*** 2058,2069 ****
  {
      ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
      ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
      bool        result = true;

!     if (ARR_SIZE(array1) != ARR_SIZE(array2))
!         result = false;
!     else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
          result = false;

      /* Avoid leaking memory when handed toasted input. */
      PG_FREE_IF_COPY(array1, 0);
--- 2318,2435 ----
  {
      ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
      ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
+     char       *p1 = (char *) ARR_DATA_PTR(array1);
+     char       *p2 = (char *) ARR_DATA_PTR(array2);
+     int            ndims1 = ARR_NDIM(array1);
+     int            ndims2 = ARR_NDIM(array2);
+     int           *dims1 = ARR_DIMS(array1);
+     int           *dims2 = ARR_DIMS(array2);
+     int            nitems1 = ArrayGetNItems(ndims1, dims1);
+     int            nitems2 = ArrayGetNItems(ndims2, dims2);
+     Oid            element_type = ARR_ELEMTYPE(array1);
+     FmgrInfo   *ae_fmgr_info = fcinfo->flinfo;
      bool        result = true;
+     int            typlen;
+     bool        typbyval;
+     char        typdelim;
+     Oid            typelem;
+     char        typalign;
+     Oid            typiofunc;
+     int            i;
+     ArrayMetaState *my_extra;
+     FunctionCallInfoData locfcinfo;

!     /* fast path if the arrays do not have the same number of elements */
!     if (nitems1 != nitems2)
          result = false;
+     else
+     {
+         /*
+          * We arrange to look up the equality function only once per series of
+          * calls, assuming the element type doesn't change underneath us.
+          */
+         my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
+         if (my_extra == NULL)
+         {
+             ae_fmgr_info->fn_extra = MemoryContextAlloc(ae_fmgr_info->fn_mcxt,
+                                                      sizeof(ArrayMetaState));
+             my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
+             my_extra->element_type = InvalidOid;
+         }
+
+         if (my_extra->element_type != element_type)
+         {
+             Oid        opfuncid = equality_oper_funcid(element_type);
+
+             if (OidIsValid(opfuncid))
+                 fmgr_info_cxt(opfuncid, &my_extra->proc, ae_fmgr_info->fn_mcxt);
+             else
+                 elog(ERROR,
+                      "array_eq: cannot find equality operator for type: %u",
+                      element_type);
+
+             get_type_metadata(element_type, IOFunc_output,
+                               &typlen, &typbyval, &typdelim,
+                               &typelem, &typiofunc, &typalign);
+
+             my_extra->element_type = element_type;
+             my_extra->typlen = typlen;
+             my_extra->typbyval = typbyval;
+             my_extra->typdelim = typdelim;
+             my_extra->typelem = typelem;
+             my_extra->typiofunc = typiofunc;
+             my_extra->typalign = typalign;
+         }
+         else
+         {
+             typlen = my_extra->typlen;
+             typbyval = my_extra->typbyval;
+             typdelim = my_extra->typdelim;
+             typelem = my_extra->typelem;
+             typiofunc = my_extra->typiofunc;
+             typalign = my_extra->typalign;
+         }
+
+         /*
+          * apply the operator to each pair of array elements.
+          */
+         MemSet(&locfcinfo, 0, sizeof(locfcinfo));
+         locfcinfo.flinfo = &my_extra->proc;
+         locfcinfo.nargs = 2;
+
+         /* Loop over source data */
+         for (i = 0; i < nitems1; i++)
+         {
+             Datum    elt1;
+             Datum    elt2;
+             bool    oprresult;
+
+             /* Get element pair */
+             elt1 = fetch_att(p1, typbyval, typlen);
+             elt2 = fetch_att(p2, typbyval, typlen);
+
+             p1 = att_addlength(p1, typlen, PointerGetDatum(p1));
+             p1 = (char *) att_align(p1, typalign);
+
+             p2 = att_addlength(p2, typlen, PointerGetDatum(p2));
+             p2 = (char *) att_align(p2, typalign);
+
+             /*
+              * Apply the operator to the element pair
+              */
+             locfcinfo.arg[0] = elt1;
+             locfcinfo.arg[1] = elt2;
+             locfcinfo.argnull[0] = false;
+             locfcinfo.argnull[1] = false;
+             locfcinfo.isnull = false;
+             oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+             if (!oprresult)
+             {
+                 result = false;
+                 break;
+             }
+         }
+     }

      /* Avoid leaking memory when handed toasted input. */
      PG_FREE_IF_COPY(array1, 0);
***************
*** 2073,2125 ****
  }


! /***************************************************************************/
! /******************|          Support  Routines              |*****************/
! /***************************************************************************/

! static void
! system_cache_lookup(Oid element_type,
!                     IOFuncSelector which_func,
!                     int *typlen,
!                     bool *typbyval,
!                     char *typdelim,
!                     Oid *typelem,
!                     Oid *proc,
!                     char *typalign)
! {
!     HeapTuple    typeTuple;
!     Form_pg_type typeStruct;
!
!     typeTuple = SearchSysCache(TYPEOID,
!                                ObjectIdGetDatum(element_type),
!                                0, 0, 0);
!     if (!HeapTupleIsValid(typeTuple))
!         elog(ERROR, "cache lookup failed for type %u", element_type);
!     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
!
!     *typlen = typeStruct->typlen;
!     *typbyval = typeStruct->typbyval;
!     *typdelim = typeStruct->typdelim;
!     *typelem = typeStruct->typelem;
!     *typalign = typeStruct->typalign;
!     switch (which_func)
!     {
!         case IOFunc_input:
!             *proc = typeStruct->typinput;
!             break;
!         case IOFunc_output:
!             *proc = typeStruct->typoutput;
!             break;
!         case IOFunc_receive:
!             *proc = typeStruct->typreceive;
!             break;
!         case IOFunc_send:
!             *proc = typeStruct->typsend;
!             break;
      }
!     ReleaseSysCache(typeTuple);
  }

  /*
   * Fetch array element at pointer, converted correctly to a Datum
   */
--- 2439,2628 ----
  }


! /*-----------------------------------------------------------------------------
!  * array-array bool operators:
!  *        Given two arrays, iterate comparison operators
!  *        over the array. Uses logic similar to text comparison
!  *        functions, except element-by-element instead of
!  *        character-by-character.
!  *----------------------------------------------------------------------------
!  */
! Datum
! array_ne(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
! }

! Datum
! array_lt(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
! }
!
! Datum
! array_gt(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
! }
!
! Datum
! array_le(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
! }
!
! Datum
! array_ge(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
! }
!
! Datum
! btarraycmp(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_INT32(array_cmp(fcinfo));
! }
!
! /*
!  * array_cmp()
!  * Internal comparison function for arrays.
!  *
!  * Returns -1, 0 or 1
!  */
! static int
! array_cmp(FunctionCallInfo fcinfo)
! {
!     ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
!     ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
!     FmgrInfo   *ac_fmgr_info = fcinfo->flinfo;
!     Datum        opresult;
!     int            result = 0;
!     Oid            element_type = InvalidOid;
!     int            typlen;
!     bool        typbyval;
!     char        typdelim;
!     Oid            typelem;
!     char        typalign;
!     Oid            typiofunc;
!     Datum       *dvalues1;
!     int            nelems1;
!     Datum       *dvalues2;
!     int            nelems2;
!     int            min_nelems;
!     int            i;
!     typedef struct
!     {
!         Oid                element_type;
!         int                typlen;
!         bool            typbyval;
!         char            typdelim;
!         Oid                typelem;
!         Oid                typiofunc;
!         char            typalign;
!         FmgrInfo        eqproc;
!         FmgrInfo        ordproc;
!     } ac_extra;
!     ac_extra *my_extra;
!
!     element_type = ARR_ELEMTYPE(array1);
!
!     /*
!      * We arrange to look up the element type operator function only once
!      * per series of calls, assuming the element type and opname don't
!      * change underneath us.
!      */
!     my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
!     if (my_extra == NULL)
!     {
!         ac_fmgr_info->fn_extra = MemoryContextAlloc(ac_fmgr_info->fn_mcxt,
!                                                          sizeof(ac_extra));
!         my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         Oid        eqfuncid = equality_oper_funcid(element_type);
!         Oid        ordfuncid = ordering_oper_funcid(element_type);
!
!         fmgr_info_cxt(eqfuncid, &my_extra->eqproc, ac_fmgr_info->fn_mcxt);
!         fmgr_info_cxt(ordfuncid, &my_extra->ordproc, ac_fmgr_info->fn_mcxt);
!
!         if (my_extra->eqproc.fn_nargs != 2)
!             elog(ERROR, "Equality operator does not take 2 arguments: %u",
!                                                                  eqfuncid);
!         if (my_extra->ordproc.fn_nargs != 2)
!             elog(ERROR, "Ordering operator does not take 2 arguments: %u",
!                                                                  ordfuncid);
!
!         get_type_metadata(element_type, IOFunc_output,
!                           &typlen, &typbyval, &typdelim,
!                           &typelem, &typiofunc, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = InvalidOid;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }
!
!     /* extract a C array of arg array datums */
!     deconstruct_array(array1, element_type, typlen, typbyval, typalign,
!                                                     &dvalues1, &nelems1);
!
!     deconstruct_array(array2, element_type, typlen, typbyval, typalign,
!                                                     &dvalues2, &nelems2);
!
!     min_nelems = Min(nelems1, nelems2);
!     for (i = 0; i < min_nelems; i++)
!     {
!         /* are they equal */
!         opresult = FunctionCall2(&my_extra->eqproc,
!                                  dvalues1[i], dvalues2[i]);
!
!         if (!DatumGetBool(opresult))
!         {
!             /* nope, see if arg1 is less than arg2 */
!             opresult = FunctionCall2(&my_extra->ordproc,
!                                      dvalues1[i], dvalues2[i]);
!             if (DatumGetBool(opresult))
!             {
!                 /* arg1 is less than arg2 */
!                 result = -1;
!                 break;
!             }
!             else
!             {
!                 /* arg1 is greater than arg2 */
!                 result = 1;
!                 break;
!             }
!         }
      }
!
!     if ((result == 0) && (nelems1 != nelems2))
!         result = (nelems1 < nelems2) ? -1 : 1;
!
!     /* Avoid leaking memory when handed toasted input. */
!     PG_FREE_IF_COPY(array1, 0);
!     PG_FREE_IF_COPY(array2, 1);
!
!     return result;
  }

+
+ /***************************************************************************/
+ /******************|          Support  Routines              |*****************/
+ /***************************************************************************/
+
  /*
   * Fetch array element at pointer, converted correctly to a Datum
   */
***************
*** 2423,2428 ****
--- 2926,2943 ----
          if (tgt_elem_type == InvalidOid)
              elog(ERROR, "Target type is not an array");

+         /*
+          * We don't deal with domain constraints yet, so bail out.
+          * This isn't currently a problem, because we also don't
+          * support arrays of domain type elements either. But in the
+          * future we might. At that point consideration should be given
+          * to removing the check below and adding a domain constraints
+          * check to the coercion.
+          */
+         if (getBaseType(tgt_elem_type) != tgt_elem_type)
+             elog(ERROR, "array coercion to domain type elements not " \
+                         "currently supported");
+
          if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
                                     COERCION_EXPLICIT, &funcId))
          {
***************
*** 2439,2448 ****
      }

      /*
!      * If it's binary-compatible, return the array unmodified.
       */
      if (my_extra->coerce_finfo.fn_oid == InvalidOid)
!         PG_RETURN_ARRAYTYPE_P(src);

      /*
       * Use array_map to apply the function to each array element.
--- 2954,2969 ----
      }

      /*
!      * If it's binary-compatible, modify the element type in the array header,
!      * but otherwise leave the array as we received it.
       */
      if (my_extra->coerce_finfo.fn_oid == InvalidOid)
!     {
!         ArrayType  *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
!
!         ARR_ELEMTYPE(result) = my_extra->desttype;
!         PG_RETURN_ARRAYTYPE_P(result);
!     }

      /*
       * Use array_map to apply the function to each array element.
***************
*** 2453,2456 ****
--- 2974,3092 ----
      locfcinfo.arg[0] = PointerGetDatum(src);

      return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
+ }
+
+ /*
+  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
+  *
+  *    astate is working state (NULL on first call)
+  *    rcontext is where to keep working state
+  */
+ ArrayBuildState *
+ accumArrayResult(ArrayBuildState *astate,
+                  Datum dvalue, bool disnull,
+                  Oid element_type,
+                  MemoryContext rcontext)
+ {
+     MemoryContext arr_context,
+                   oldcontext;
+
+     if (astate == NULL)
+     {
+         /* First time through --- initialize */
+
+         /* Make a temporary context to hold all the junk */
+         arr_context = AllocSetContextCreate(rcontext,
+                                             "accumArrayResult",
+                                             ALLOCSET_DEFAULT_MINSIZE,
+                                             ALLOCSET_DEFAULT_INITSIZE,
+                                             ALLOCSET_DEFAULT_MAXSIZE);
+         oldcontext = MemoryContextSwitchTo(arr_context);
+         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
+         astate->mcontext = arr_context;
+         astate->dvalues = (Datum *)
+             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
+         astate->nelems = 0;
+         astate->element_type = element_type;
+         get_typlenbyvalalign(element_type,
+                              &astate->typlen,
+                              &astate->typbyval,
+                              &astate->typalign);
+     }
+     else
+     {
+         oldcontext = MemoryContextSwitchTo(astate->mcontext);
+         Assert(astate->element_type == element_type);
+         /* enlarge dvalues[] if needed */
+         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
+             astate->dvalues = (Datum *)
+                 repalloc(astate->dvalues,
+                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
+     }
+
+     if (disnull)
+         elog(ERROR, "NULL elements not allowed in Arrays");
+
+     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
+     astate->dvalues[astate->nelems++] =
+         datumCopy(dvalue, astate->typbyval, astate->typlen);
+
+     MemoryContextSwitchTo(oldcontext);
+
+     return astate;
+ }
+
+ /*
+  * makeArrayResult - produce final result of accumArrayResult
+  *
+  *    astate is working state (not NULL)
+  *    rcontext is where to construct result
+  */
+ Datum
+ makeArrayResult(ArrayBuildState *astate,
+                 MemoryContext rcontext)
+ {
+     int            dims[1];
+     int            lbs[1];
+
+     dims[0] = astate->nelems;
+     lbs[0] = 1;
+
+     return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
+ }
+
+ /*
+  * makeMdArrayResult - produce md final result of accumArrayResult
+  *
+  *    astate is working state (not NULL)
+  *    rcontext is where to construct result
+  */
+ Datum
+ makeMdArrayResult(ArrayBuildState *astate,
+                 int ndims,
+                 int *dims,
+                 int *lbs,
+                 MemoryContext rcontext)
+ {
+     ArrayType  *result;
+     MemoryContext oldcontext;
+
+     /* Build the final array result in rcontext */
+     oldcontext = MemoryContextSwitchTo(rcontext);
+
+     result = construct_md_array(astate->dvalues,
+                                 ndims,
+                                 dims,
+                                 lbs,
+                                 astate->element_type,
+                                 astate->typlen,
+                                 astate->typbyval,
+                                 astate->typalign);
+
+     MemoryContextSwitchTo(oldcontext);
+
+     /* Clean up all the junk */
+     MemoryContextDelete(astate->mcontext);
+
+     return PointerGetDatum(result);
  }
Index: src/backend/utils/adt/varlena.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/varlena.c,v
retrieving revision 1.98
diff -c -r1.98 varlena.c
*** src/backend/utils/adt/varlena.c    15 May 2003 15:50:19 -0000    1.98
--- src/backend/utils/adt/varlena.c    1 Jun 2003 15:41:15 -0000
***************
*** 19,29 ****
--- 19,32 ----
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
  #include "access/tuptoaster.h"
+ #include "catalog/pg_type.h"
  #include "lib/stringinfo.h"
  #include "libpq/crypt.h"
  #include "libpq/pqformat.h"
+ #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/pg_locale.h"
+ #include "utils/lsyscache.h"


  typedef struct varlena unknown;
***************
*** 1983,1990 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

--- 1986,1992 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

***************
*** 2004,2011 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
--- 2006,2012 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
***************
*** 2026,2031 ****
--- 2027,2217 ----
          result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn -
fldsep_len,false); 
          PG_RETURN_TEXT_P(result_text);
      }
+ }
+
+ /*
+  * text_to_array
+  * parse input string
+  * return text array of elements
+  * based on provided field separator
+  */
+ Datum
+ text_to_array(PG_FUNCTION_ARGS)
+ {
+     text       *inputstring = PG_GETARG_TEXT_P(0);
+     int            inputstring_len = TEXTLEN(inputstring);
+     text       *fldsep = PG_GETARG_TEXT_P(1);
+     int            fldsep_len = TEXTLEN(fldsep);
+     int            fldnum;
+     int            start_posn = 0;
+     int            end_posn = 0;
+     text       *result_text = NULL;
+     ArrayBuildState *astate = NULL;
+     MemoryContext oldcontext = CurrentMemoryContext;
+
+     /* return NULL for empty input string */
+     if (inputstring_len < 1)
+         PG_RETURN_NULL();
+
+     /* empty field separator
+      * return one element, 1D, array using the input string */
+     if (fldsep_len < 1)
+         PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+                               CStringGetDatum(inputstring), 1));
+
+     /* start with end position holding the initial start position */
+     end_posn = 0;
+     for (fldnum=1;;fldnum++)    /* field number is 1 based */
+     {
+         Datum    dvalue;
+         bool    disnull = false;
+
+         start_posn = end_posn;
+         end_posn = text_position(PointerGetDatum(inputstring),
+                                  PointerGetDatum(fldsep),
+                                  fldnum);
+
+         if ((start_posn == 0) && (end_posn == 0))    /* fldsep not found */
+         {
+             if (fldnum == 1)
+             {
+                 /* first element
+                  * return one element, 1D, array using the input string */
+                 PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+                                       CStringGetDatum(inputstring), 1));
+             }
+             else
+             {
+                 /* otherwise create array and exit */
+                 PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
+             }
+         }
+         else if ((start_posn != 0) && (end_posn == 0))
+         {
+             /* last field requested */
+             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
+         }
+         else if ((start_posn == 0) && (end_posn != 0))
+         {
+             /* first field requested */
+             result_text = LEFT(inputstring, fldsep);
+         }
+         else
+         {
+             /* prior to last field requested */
+             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn
-fldsep_len, false); 
+         }
+
+         /* stash away current value */
+         dvalue = PointerGetDatum(result_text);
+         astate = accumArrayResult(astate, dvalue,
+                                   disnull, TEXTOID, oldcontext);
+
+     }
+
+     /* never reached -- keep compiler quiet */
+     PG_RETURN_NULL();
+ }
+
+ /*
+  * array_to_text
+  * concatenate Cstring representation of input array elements
+  * using provided field separator
+  */
+ Datum
+ array_to_text(PG_FUNCTION_ARGS)
+ {
+     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
+     char       *fldsep = PG_TEXTARG_GET_STR(1);
+     int            nitems, *dims, ndims;
+     char       *p;
+     Oid            element_type;
+     int            typlen;
+     bool        typbyval;
+     char        typdelim;
+     Oid            typoutput,
+                 typelem;
+     FmgrInfo    outputproc;
+     char        typalign;
+     StringInfo    result_str = makeStringInfo();
+     int            i;
+     ArrayMetaState *my_extra;
+
+     p = ARR_DATA_PTR(v);
+     ndims = ARR_NDIM(v);
+     dims = ARR_DIMS(v);
+     nitems = ArrayGetNItems(ndims, dims);
+
+     /* if there are no elements, return an empty string */
+     if (nitems == 0)
+         PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
+
+     element_type = ARR_ELEMTYPE(v);
+
+     /*
+      * We arrange to look up info about element type, including its output
+      * conversion proc only once per series of calls, assuming the element
+      * type doesn't change underneath us.
+      */
+     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+     if (my_extra == NULL)
+     {
+         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+                                                      sizeof(ArrayMetaState));
+         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+         my_extra->element_type = InvalidOid;
+     }
+
+     if (my_extra->element_type != element_type)
+     {
+         /* Get info about element type, including its output conversion proc */
+         get_type_metadata(element_type, IOFunc_output,
+                             &typlen, &typbyval, &typdelim,
+                             &typelem, &typoutput, &typalign);
+         fmgr_info(typoutput, &outputproc);
+
+         my_extra->element_type = element_type;
+         my_extra->typlen = typlen;
+         my_extra->typbyval = typbyval;
+         my_extra->typdelim = typdelim;
+         my_extra->typelem = typelem;
+         my_extra->typiofunc = typoutput;
+         my_extra->typalign = typalign;
+         my_extra->proc = outputproc;
+     }
+     else
+     {
+         typlen = my_extra->typlen;
+         typbyval = my_extra->typbyval;
+         typdelim = my_extra->typdelim;
+         typelem = my_extra->typelem;
+         typoutput = my_extra->typiofunc;
+         typalign = my_extra->typalign;
+         outputproc = my_extra->proc;
+     }
+
+     for (i = 0; i < nitems; i++)
+     {
+         Datum        itemvalue;
+         char       *value;
+
+         itemvalue = fetch_att(p, typbyval, typlen);
+
+         value = DatumGetCString(FunctionCall3(&outputproc,
+                                               itemvalue,
+                                               ObjectIdGetDatum(typelem),
+                                               Int32GetDatum(-1)));
+
+         if (i > 0)
+             appendStringInfo(result_str, "%s%s", fldsep, value);
+         else
+             appendStringInfo(result_str, "%s", value);
+
+         p = att_addlength(p, typlen, PointerGetDatum(p));
+         p = (char *) att_align(p, typalign);
+     }
+
+     PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
  }

  #define HEXBASE 16
Index: src/backend/utils/cache/lsyscache.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v
retrieving revision 1.95
diff -c -r1.95 lsyscache.c
*** src/backend/utils/cache/lsyscache.c    26 May 2003 00:11:27 -0000    1.95
--- src/backend/utils/cache/lsyscache.c    1 Jun 2003 15:41:15 -0000
***************
*** 625,630 ****
--- 625,664 ----
  }

  /*
+  * get_func_argtypes
+  *        Given procedure id, return the function's argument types.
+  *        Also pass back the number of arguments.
+  */
+ Oid *
+ get_func_argtypes(Oid funcid, int *nargs)
+ {
+     HeapTuple        tp;
+     Form_pg_proc    procstruct;
+     Oid               *result = NULL;
+     int                i;
+
+     tp = SearchSysCache(PROCOID,
+                         ObjectIdGetDatum(funcid),
+                         0, 0, 0);
+     if (!HeapTupleIsValid(tp))
+         elog(ERROR, "Function OID %u does not exist", funcid);
+
+     procstruct = (Form_pg_proc) GETSTRUCT(tp);
+     *nargs = (int) procstruct->pronargs;
+
+     if (*nargs > 0)
+     {
+         result = (Oid *) palloc(*nargs * sizeof(Oid));
+
+         for (i = 0; i < *nargs; i++)
+             result[i] = procstruct->proargtypes[i];
+     }
+
+     ReleaseSysCache(tp);
+     return result;
+ }
+
+ /*
   * get_func_retset
   *        Given procedure id, return the function's proretset flag.
   */
***************
*** 994,999 ****
--- 1028,1083 ----
      *typbyval = typtup->typbyval;
      *typalign = typtup->typalign;
      ReleaseSysCache(tp);
+ }
+
+ /*
+  * get_type_metadata
+  *
+  *        A six-fer:    given the type OID, return typlen, typbyval, typalign,
+  *                    typdelim, typelem, IO function Oid. The IO function
+  *                    returned is controlled by IOFuncSelector
+  */
+ void
+ get_type_metadata(Oid element_type,
+                     IOFuncSelector which_func,
+                     int *typlen,
+                     bool *typbyval,
+                     char *typdelim,
+                     Oid *typelem,
+                     Oid *proc,
+                     char *typalign)
+ {
+     HeapTuple    typeTuple;
+     Form_pg_type typeStruct;
+
+     typeTuple = SearchSysCache(TYPEOID,
+                                ObjectIdGetDatum(element_type),
+                                0, 0, 0);
+     if (!HeapTupleIsValid(typeTuple))
+         elog(ERROR, "cache lookup failed for type %u", element_type);
+     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+
+     *typlen = typeStruct->typlen;
+     *typbyval = typeStruct->typbyval;
+     *typdelim = typeStruct->typdelim;
+     *typelem = typeStruct->typelem;
+     *typalign = typeStruct->typalign;
+     switch (which_func)
+     {
+         case IOFunc_input:
+             *proc = typeStruct->typinput;
+             break;
+         case IOFunc_output:
+             *proc = typeStruct->typoutput;
+             break;
+         case IOFunc_receive:
+             *proc = typeStruct->typreceive;
+             break;
+         case IOFunc_send:
+             *proc = typeStruct->typsend;
+             break;
+     }
+     ReleaseSysCache(typeTuple);
  }

  #ifdef NOT_USED
Index: src/backend/utils/fmgr/fmgr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/fmgr/fmgr.c,v
retrieving revision 1.68
diff -c -r1.68 fmgr.c
*** src/backend/utils/fmgr/fmgr.c    8 Apr 2003 23:20:02 -0000    1.68
--- src/backend/utils/fmgr/fmgr.c    1 Jun 2003 15:41:15 -0000
***************
*** 1673,1675 ****
--- 1673,1701 ----

      return exprType((Node *) nth(argnum, args));
  }
+
+ /*
+  * Get the OID of the function or operator
+  *
+  * Returns InvalidOid if information is not available
+  */
+ Oid
+ get_fn_expr_functype(FunctionCallInfo fcinfo)
+ {
+     Node   *expr;
+
+     /*
+      * can't return anything useful if we have no FmgrInfo or if
+      * its fn_expr node has not been initialized
+      */
+     if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
+         return InvalidOid;
+
+     expr = fcinfo->flinfo->fn_expr;
+     if (IsA(expr, FuncExpr))
+         return ((FuncExpr *) expr)->funcid;
+     else if (IsA(expr, OpExpr))
+         return ((OpExpr *) expr)->opno;
+     else
+         return InvalidOid;
+ }
Index: src/include/fmgr.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/fmgr.h,v
retrieving revision 1.27
diff -c -r1.27 fmgr.h
*** src/include/fmgr.h    8 Apr 2003 23:20:04 -0000    1.27
--- src/include/fmgr.h    1 Jun 2003 15:41:15 -0000
***************
*** 18,23 ****
--- 18,24 ----
  #ifndef FMGR_H
  #define FMGR_H

+ #include "nodes/nodes.h"

  /*
   * All functions that can be called directly by fmgr must have this signature.
***************
*** 372,385 ****
                   Datum arg6, Datum arg7, Datum arg8,
                   Datum arg9);

-
  /*
   * Routines in fmgr.c
   */
  extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
! extern Oid    fmgr_internal_function(const char *proname);
! extern Oid    get_fn_expr_rettype(FunctionCallInfo fcinfo);
! extern Oid    get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);

  /*
   * Routines in dfmgr.c
--- 373,386 ----
                   Datum arg6, Datum arg7, Datum arg8,
                   Datum arg9);

  /*
   * Routines in fmgr.c
   */
  extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
! extern Oid fmgr_internal_function(const char *proname);
! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo);
! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
! extern Oid get_fn_expr_functype(FunctionCallInfo fcinfo);

  /*
   * Routines in dfmgr.c
Index: src/include/catalog/pg_amop.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amop.h,v
retrieving revision 1.49
diff -c -r1.49 pg_amop.h
*** src/include/catalog/pg_amop.h    26 May 2003 00:11:27 -0000    1.49
--- src/include/catalog/pg_amop.h    2 Jun 2003 02:00:05 -0000
***************
*** 418,423 ****
--- 418,432 ----
  DATA(insert (    2098 4 f 2335 ));
  DATA(insert (    2098 5 f 2336 ));

+ /*
+  *    btree array_ops
+  */
+
+ DATA(insert (     397 1 f 1072 ));
+ DATA(insert (     397 2 f 1074 ));
+ DATA(insert (     397 3 f 1070 ));
+ DATA(insert (     397 4 f 1075 ));
+ DATA(insert (     397 5 f 1073 ));

  /*
   *    hash index _ops
Index: src/include/catalog/pg_amproc.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amproc.h,v
retrieving revision 1.37
diff -c -r1.37 pg_amproc.h
*** src/include/catalog/pg_amproc.h    26 May 2003 00:11:27 -0000    1.37
--- src/include/catalog/pg_amproc.h    2 Jun 2003 02:25:34 -0000
***************
*** 78,83 ****
--- 78,84 ----


  /* btree */
+ DATA(insert (     397 1  398 ));
  DATA(insert (     421 1    357 ));
  DATA(insert (     423 1 1596 ));
  DATA(insert (     424 1 1693 ));
Index: src/include/catalog/pg_opclass.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_opclass.h,v
retrieving revision 1.50
diff -c -r1.50 pg_opclass.h
*** src/include/catalog/pg_opclass.h    28 May 2003 16:04:00 -0000    1.50
--- src/include/catalog/pg_opclass.h    2 Jun 2003 01:47:07 -0000
***************
*** 87,92 ****
--- 87,93 ----
   */

  DATA(insert OID =  421 (    403        abstime_ops        PGNSP PGUID  702 t 0 ));
+ DATA(insert OID =  397 (    403        array_ops        PGNSP PGUID 2277 t 0 ));
  DATA(insert OID =  422 (    402        bigbox_ops        PGNSP PGUID  603 f 0 ));
  DATA(insert OID =  423 (    403        bit_ops            PGNSP PGUID 1560 t 0 ));
  DATA(insert OID =  424 (    403        bool_ops        PGNSP PGUID   16 t 0 ));
Index: src/include/catalog/pg_operator.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_operator.h,v
retrieving revision 1.114
diff -c -r1.114 pg_operator.h
*** src/include/catalog/pg_operator.h    26 May 2003 00:11:27 -0000    1.114
--- src/include/catalog/pg_operator.h    1 Jun 2003 23:10:35 -0000
***************
*** 116,125 ****
  DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
  DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 

! DATA(insert OID = 329 (  "="       PGNSP PGUID b f 2277 2277 16   329 0 0 0    0 0 array_eq     eqsel eqjoinsel ));
! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277   0 0 0 0    0 0 array_append   -       -     ));
! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277   0 0 0 0    0 0 array_prepend  -       -     ));
! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277   0 0 0 0    0 0 array_cat      -       -     ));

  DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
  DATA(insert OID = 353 (  "="       PGNSP PGUID b t    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
--- 116,130 ----
  DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
  DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 

! DATA(insert OID = 1070 (  "="       PGNSP PGUID b f 2277 2277 16 1070 1071  1072 1072 1072 1073 array_eq eqsel
eqjoinsel)); 
! DATA(insert OID = 1071 (  "<>"       PGNSP PGUID b f 2277 2277 16 1071 1070  0 0 0 0 array_ne neqsel neqjoinsel ));
! DATA(insert OID = 1072 (  "<"       PGNSP PGUID b f 2277 2277 16 1073 1075  0 0 0 0 array_lt scalarltsel
scalarltjoinsel)); 
! DATA(insert OID = 1073 (  ">"       PGNSP PGUID b f 2277 2277 16 1072 1074  0 0 0 0 array_gt scalargtsel
scalargtjoinsel)); 
! DATA(insert OID = 1074 (  "<="       PGNSP PGUID b f 2277 2277 16 1075 1073  0 0 0 0 array_le scalarltsel
scalarltjoinsel)); 
! DATA(insert OID = 1075 (  ">="       PGNSP PGUID b f 2277 2277 16 1074 1072  0 0 0 0 array_ge scalargtsel
scalargtjoinsel)); 
! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277 0 0  0 0 0 0 array_append   -       -     ));
! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277 0 0  0 0 0 0 array_prepend  -       -     ));
! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277 0 0  0 0 0 0 array_cat      -       -     ));

  DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
  DATA(insert OID = 353 (  "="       PGNSP PGUID b t    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
***************
*** 425,430 ****
--- 430,436 ----
  DATA(insert OID = 966 (  "+"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - ));
  DATA(insert OID = 967 (  "-"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclremove - - ));
  DATA(insert OID = 968 (  "~"       PGNSP PGUID b f 1034 1033     16 0 0 0 0 0 0 aclcontains - - ));
+ DATA(insert OID = 974 (  "="       PGNSP PGUID b f 1033 1033     16 0 0 0 0 0 0 aclitemeq eqsel eqjoinsel ));

  /* additional geometric operators - thomas 1997-07-09 */
  DATA(insert OID =  969 (  "@@"       PGNSP PGUID l f    0  601    600    0  0 0 0 0 0 lseg_center - - ));
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v
retrieving revision 1.302
diff -c -r1.302 pg_proc.h
*** src/include/catalog/pg_proc.h    26 May 2003 00:11:27 -0000    1.302
--- src/include/catalog/pg_proc.h    2 Jun 2003 02:21:05 -0000
***************
*** 758,763 ****
--- 758,765 ----
  DESCR("btree less-equal-greater");
  DATA(insert OID = 360 (  bttextcmp           PGNSP PGUID 12 f f t f i 2 23 "25 25"    bttextcmp - _null_ ));
  DESCR("btree less-equal-greater");
+ DATA(insert OID = 398 (  btarraycmp           PGNSP PGUID 12 f f t f i 2 23 "2277 2277"    btarraycmp - _null_ ));
+ DESCR("btree less-equal-greater");

  DATA(insert OID = 361 (  lseg_distance       PGNSP PGUID 12 f f t f i 2 701 "601 601"  lseg_distance - _null_ ));
  DESCR("distance between");
***************
*** 984,997 ****
  DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
  DESCR("greater-than-or-equal");

- DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
- DESCR("array equal");
-
  DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
  DESCR("current user name");
  DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
  DESCR("session user name");

  DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
  DESCR("array dimensions");
  DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
--- 986,1008 ----
  DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
  DESCR("greater-than-or-equal");

  DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
  DESCR("current user name");
  DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
  DESCR("session user name");

+ DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
+ DESCR("array equal");
+ DATA(insert OID = 390 (  array_ne           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ne - _null_ ));
+ DESCR("array not equal");
+ DATA(insert OID = 391 (  array_lt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_lt - _null_ ));
+ DESCR("array less than");
+ DATA(insert OID = 392 (  array_gt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_gt - _null_ ));
+ DESCR("array greater than");
+ DATA(insert OID = 393 (  array_le           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_le - _null_ ));
+ DESCR("array less than or equal");
+ DATA(insert OID = 396 (  array_ge           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ge - _null_ ));
+ DESCR("array greater than or equal");
  DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
  DESCR("array dimensions");
  DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
***************
*** 1008,1015 ****
  DESCR("append element onto end of array");
  DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
  DESCR("prepend element onto front of array");
- DATA(insert OID = 380 (  array_accum       PGNSP PGUID 12 f f f f i 2 2277 "2277 2283" array_accum - _null_ ));
- DESCR("push element onto end of array, creating array if needed");
  DATA(insert OID = 381 (  array_assign       PGNSP PGUID 12 f f t f i 3 2277 "2277 23 2283" array_assign - _null_ ));
  DESCR("assign specific array element");
  DATA(insert OID = 382 (  array_subscript  PGNSP PGUID 12 f f t f i 2 2283 "2277 23" array_subscript - _null_ ));
--- 1019,1024 ----
***************
*** 1018,1023 ****
--- 1027,1036 ----
  DESCR("concatenate two arrays");
  DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
  DESCR("coerce array type to another array type");
+ DATA(insert OID = 394 (  str_to_array       PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ ));
+ DESCR("split delimited text into text[]");
+ DATA(insert OID = 395 (  array_to_str       PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ ));
+ DESCR("concatenate array elements, using delimiter, into text");

  DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
  DESCR("I/O");
***************
*** 1318,1323 ****
--- 1331,1338 ----
  DESCR("remove ACL item");
  DATA(insert OID = 1037 (  aclcontains       PGNSP PGUID 12 f f t f s 2 16 "1034 1033"    aclcontains - _null_ ));
  DESCR("does ACL contain item?");
+ DATA(insert OID = 1062 (  aclitemeq           PGNSP PGUID 12 f f t f s 2 16 "1033 1033"    aclitem_eq - _null_ ));
+ DESCR("equality operator for ACL items");
  DATA(insert OID = 1038 (  seteval           PGNSP PGUID 12 f f t t v 1 23 "26"  seteval - _null_ ));
  DESCR("internal function supporting PostQuel-style sets");
  DATA(insert OID = 1044 (  bpcharin           PGNSP PGUID 12 f f t f i 3 1042 "2275 26 23" bpcharin - _null_ ));
Index: src/include/nodes/primnodes.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v
retrieving revision 1.82
diff -c -r1.82 primnodes.h
*** src/include/nodes/primnodes.h    6 May 2003 00:20:33 -0000    1.82
--- src/include/nodes/primnodes.h    1 Jun 2003 15:41:47 -0000
***************
*** 225,230 ****
--- 225,231 ----
      Expr       *target;            /* expression we are aggregating on */
      bool        aggstar;        /* TRUE if argument was really '*' */
      bool        aggdistinct;    /* TRUE if it's agg(DISTINCT ...) */
+     List       *args;            /* arguments to the aggregate */
  } Aggref;

  /* ----------------
***************
*** 357,371 ****
  /* ----------------
   * SubLink
   *
!  * A SubLink represents a subselect appearing in an expression, and in some
!  * cases also the combining operator(s) just above it.    The subLinkType
!  * indicates the form of the expression represented:
   *    EXISTS_SUBLINK        EXISTS(SELECT ...)
   *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
   *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
   *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
   *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
   *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
   * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
   * same length as the subselect's targetlist.  MULTIEXPR will *always* have
   * a list with more than one entry; if the subselect has just one target
--- 358,376 ----
  /* ----------------
   * SubLink
   *
!  * A SubLink represents a subselect, or an expression, appearing in an
!  * expression, and in some cases also the combining operator(s) just above
!  * it.    The subLinkType indicates the form of the expression represented:
   *    EXISTS_SUBLINK        EXISTS(SELECT ...)
   *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
   *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
   *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
   *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
   *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
+  * If an expression is used in place of the subselect, it is transformed
+  * into a simple "(SELECT expr)" in gram.y. This is to allow arrays to be
+  * used as if they were the result of a single column subselect. If the
+  * expression is scalar, it is treated as a one element array.
   * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
   * same length as the subselect's targetlist.  MULTIEXPR will *always* have
   * a list with more than one entry; if the subselect has just one target
***************
*** 414,419 ****
--- 419,426 ----
      SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
      bool        useOr;            /* TRUE to combine column results with
                                   * "OR" not "AND" */
+     bool        isExpr;            /* TRUE if the subselect is really derived
+                                  * from a single expression */
      List       *lefthand;        /* list of outer-query expressions on the
                                   * left */
      List       *operName;        /* originally specified operator name */
***************
*** 455,460 ****
--- 462,476 ----
      SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
      bool        useOr;            /* TRUE to combine column results with
                                   * "OR" not "AND" */
+     bool        isExpr;            /* TRUE if the subselect is really derived
+                                  * from a single expression */
+     /* runtime cache for single array expressions */
+     Oid            exprtype;        /* array and element type, and other info
+                                  * needed deconstruct the array */
+     Oid            elemtype;
+     int16        elmlen;
+     bool        elmbyval;
+     char        elmalign;
      /* The combining operators, transformed to executable expressions: */
      List       *exprs;            /* list of OpExpr expression trees */
      List       *paramIds;        /* IDs of Params embedded in the above */
Index: src/include/optimizer/clauses.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/optimizer/clauses.h,v
retrieving revision 1.63
diff -c -r1.63 clauses.h
*** src/include/optimizer/clauses.h    28 May 2003 16:04:02 -0000    1.63
--- src/include/optimizer/clauses.h    1 Jun 2003 15:41:15 -0000
***************
*** 28,33 ****
--- 28,36 ----
  extern Node *get_leftop(Expr *clause);
  extern Node *get_rightop(Expr *clause);

+ extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
+                                     CoercionForm funcformat, List *funcargs);
+
  extern bool not_clause(Node *clause);
  extern Expr *make_notclause(Expr *notclause);
  extern Expr *get_notclausearg(Expr *notclause);
Index: src/include/parser/parse_oper.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_oper.h,v
retrieving revision 1.25
diff -c -r1.25 parse_oper.h
*** src/include/parser/parse_oper.h    29 Apr 2003 22:13:11 -0000    1.25
--- src/include/parser/parse_oper.h    1 Jun 2003 23:21:48 -0000
***************
*** 44,49 ****
--- 44,50 ----
  /* Convenience routines for common calls on the above */
  extern Oid    compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError);
  extern Oid    equality_oper_funcid(Oid argtype);
+ extern Oid  ordering_oper_funcid(Oid argtype);
  extern Oid    ordering_oper_opid(Oid argtype);

  /* Extract operator OID or underlying-function OID from an Operator tuple */
Index: src/include/utils/acl.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/acl.h,v
retrieving revision 1.51
diff -c -r1.51 acl.h
*** src/include/utils/acl.h    23 Jan 2003 23:39:07 -0000    1.51
--- src/include/utils/acl.h    1 Jun 2003 19:03:25 -0000
***************
*** 188,193 ****
--- 188,194 ----
  extern Datum aclinsert(PG_FUNCTION_ARGS);
  extern Datum aclremove(PG_FUNCTION_ARGS);
  extern Datum aclcontains(PG_FUNCTION_ARGS);
+ extern Datum aclitem_eq(PG_FUNCTION_ARGS);

  /*
   * prototypes for functions in aclchk.c
Index: src/include/utils/array.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v
retrieving revision 1.38
diff -c -r1.38 array.h
*** src/include/utils/array.h    8 May 2003 22:19:57 -0000    1.38
--- src/include/utils/array.h    2 Jun 2003 02:25:03 -0000
***************
*** 32,37 ****
--- 32,68 ----
      Oid            elemtype;        /* element type OID */
  } ArrayType;

+ typedef struct ArrayBuildState
+ {
+     MemoryContext mcontext;        /* where all the temp stuff is kept */
+     Datum       *dvalues;        /* array of accumulated Datums */
+     /*
+      * The allocated size of dvalues[] is always a multiple of
+      * ARRAY_ELEMS_CHUNKSIZE
+      */
+ #define ARRAY_ELEMS_CHUNKSIZE    64
+     int            nelems;            /* number of valid Datums in dvalues[] */
+     Oid            element_type;    /* data type of the Datums */
+     int16        typlen;            /* needed info about datatype */
+     bool        typbyval;
+     char        typalign;
+ } ArrayBuildState;
+
+ /*
+  * structure to cache type metadata needed for array manipulation
+  */
+ typedef struct ArrayMetaState
+ {
+     Oid                element_type;
+     int                typlen;
+     bool            typbyval;
+     char            typdelim;
+     Oid                typelem;
+     Oid                typiofunc;
+     char            typalign;
+     FmgrInfo        proc;
+ } ArrayMetaState;
+
  /*
   * fmgr macros for array objects
   */
***************
*** 86,91 ****
--- 117,128 ----
  extern Datum array_send(PG_FUNCTION_ARGS);
  extern Datum array_length_coerce(PG_FUNCTION_ARGS);
  extern Datum array_eq(PG_FUNCTION_ARGS);
+ extern Datum array_ne(PG_FUNCTION_ARGS);
+ extern Datum array_lt(PG_FUNCTION_ARGS);
+ extern Datum array_gt(PG_FUNCTION_ARGS);
+ extern Datum array_le(PG_FUNCTION_ARGS);
+ extern Datum array_ge(PG_FUNCTION_ARGS);
+ extern Datum btarraycmp(PG_FUNCTION_ARGS);
  extern Datum array_dims(PG_FUNCTION_ARGS);
  extern Datum array_lower(PG_FUNCTION_ARGS);
  extern Datum array_upper(PG_FUNCTION_ARGS);
***************
*** 124,130 ****
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
!

  /*
   * prototypes for functions defined in arrayutils.c
--- 161,174 ----
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
! extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
!                                          Datum dvalue, bool disnull,
!                                          Oid element_type,
!                                          MemoryContext rcontext);
! extern Datum makeArrayResult(ArrayBuildState *astate,
!                              MemoryContext rcontext);
! extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
!                                int *dims, int *lbs, MemoryContext rcontext);

  /*
   * prototypes for functions defined in arrayutils.c
***************
*** 143,152 ****
   */
  extern Datum singleton_array(PG_FUNCTION_ARGS);
  extern Datum array_push(PG_FUNCTION_ARGS);
- extern Datum array_accum(PG_FUNCTION_ARGS);
  extern Datum array_cat(PG_FUNCTION_ARGS);

! extern ArrayType *create_singleton_array(Oid element_type,
                                           Datum element,
                                           int ndims);

--- 187,196 ----
   */
  extern Datum singleton_array(PG_FUNCTION_ARGS);
  extern Datum array_push(PG_FUNCTION_ARGS);
  extern Datum array_cat(PG_FUNCTION_ARGS);

! extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
!                                          Oid element_type,
                                           Datum element,
                                           int ndims);

Index: src/include/utils/builtins.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/builtins.h,v
retrieving revision 1.219
diff -c -r1.219 builtins.h
*** src/include/utils/builtins.h    26 May 2003 00:11:28 -0000    1.219
--- src/include/utils/builtins.h    1 Jun 2003 15:41:15 -0000
***************
*** 530,535 ****
--- 530,537 ----
                        List **namelist);
  extern Datum replace_text(PG_FUNCTION_ARGS);
  extern Datum split_text(PG_FUNCTION_ARGS);
+ extern Datum text_to_array(PG_FUNCTION_ARGS);
+ extern Datum array_to_text(PG_FUNCTION_ARGS);
  extern Datum to_hex32(PG_FUNCTION_ARGS);
  extern Datum to_hex64(PG_FUNCTION_ARGS);
  extern Datum md5_text(PG_FUNCTION_ARGS);
Index: src/include/utils/lsyscache.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v
retrieving revision 1.70
diff -c -r1.70 lsyscache.h
*** src/include/utils/lsyscache.h    26 May 2003 00:11:28 -0000    1.70
--- src/include/utils/lsyscache.h    1 Jun 2003 15:41:15 -0000
***************
*** 15,20 ****
--- 15,29 ----

  #include "access/htup.h"

+ /* I/O function selector for system_cache_lookup */
+ typedef enum IOFuncSelector
+ {
+     IOFunc_input,
+     IOFunc_output,
+     IOFunc_receive,
+     IOFunc_send
+ } IOFuncSelector;
+
  extern bool op_in_opclass(Oid opno, Oid opclass);
  extern bool op_requires_recheck(Oid opno, Oid opclass);
  extern Oid    get_opclass_member(Oid opclass, int16 strategy);
***************
*** 39,44 ****
--- 48,54 ----
  extern RegProcedure get_oprjoin(Oid opno);
  extern char *get_func_name(Oid funcid);
  extern Oid    get_func_rettype(Oid funcid);
+ extern Oid *get_func_argtypes(Oid funcid, int *nargs);
  extern bool get_func_retset(Oid funcid);
  extern bool func_strict(Oid funcid);
  extern char func_volatile(Oid funcid);
***************
*** 54,59 ****
--- 64,77 ----
  extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
  extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
                       char *typalign);
+ extern void get_type_metadata(Oid element_type,
+                                 IOFuncSelector which_func,
+                                 int *typlen,
+                                 bool *typbyval,
+                                 char *typdelim,
+                                 Oid *typelem,
+                                 Oid *proc,
+                                 char *typalign);
  extern char get_typstorage(Oid typid);
  extern int32 get_typtypmod(Oid typid);
  extern Node *get_typdefault(Oid typid);

Re: array support patch phase 1 patch

From
Joe Conway
Date:
Tom Lane wrote:
> Joe Conway <mail@joeconway.com> writes:
>>What if we modify equality_oper() and friends to do the check for us
>>when argtype is a varlena array?
>
>>If an array type is checked for an ordering operator, its element type
>>now *must* have an ordering operator in order for the result to be
>>meaningful. Same goes for ordering_oper().
>
> That sounds more like a solution.  Is there anyplace else we need to
> push knowledge of this into?  (Offhand I can't think of any, but ...)
>

Poking around:

1) I found that addTargetToSortList() uses compatible_oper_opid() if it
is given a specific opname, but ordering_oper_opid (which in turn uses
ordering_oper()) if not. Do you know in what cases will
addTargetToSortList() get called with a specific opname? If it's only
for "non-standard" operators, then we should be fine here I'd think.

2) AlterTableAddForeignKeyConstraint() uses oper() to check directly for
an "=" operator. This should probably be changed anyway to
equality_oper(), shouldn't it?

3) process_implied_equality() uses compatible_oper() to directly obtain
    "=" operator. This should also be changed anyway to equality_oper(),
    shouldn't it?

4) SelectSortFunction() might need work -- I'll have to study that one
    more.

That's all I could find that looked suspect.

Joe




Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> 1) I found that addTargetToSortList() uses compatible_oper_opid() if it
> is given a specific opname, but ordering_oper_opid (which in turn uses
> ordering_oper()) if not. Do you know in what cases will
> addTargetToSortList() get called with a specific opname?

I think only for "ORDER BY foo ASC/DESC/USING opname".  As long as the
"<" and ">" (ASC, DESC) cases work, it should be okay to punt on other
operator names for arrays.

> 2) AlterTableAddForeignKeyConstraint() uses oper() to check directly for
> an "=" operator. This should probably be changed anyway to
> equality_oper(), shouldn't it?

Can't use it as-is, since the datatypes may differ.

> 3) process_implied_equality() uses compatible_oper() to directly obtain
>     "=" operator. This should also be changed anyway to equality_oper(),
>     shouldn't it?

As above.

I'm not sure though that any of these cases really are of concern.  Do
we care in any of them whether failure to find the operator is postponed
till runtime?  ANALYZE needs to know because it has a fallback path if
there's no "<" operator, but ORDER BY does not.

            regards, tom lane

Re: array support patch phase 1 patch

From
Joe Conway
Date:
Tom Lane wrote:
> That would mask the problem for ANALYZE, but not for anything else.
>
> Whether there is anything else I'm not sure, but I can't say I feel
> comfortable about that answer.
>

What if we modify equality_oper() and friends to do the check for us
when argtype is a varlena array?

If an array type is checked for an ordering operator, its element type
now *must* have an ordering operator in order for the result to be
meaningful. Same goes for ordering_oper().

Joe



Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> Tom Lane wrote:
>> That would mask the problem for ANALYZE, but not for anything else.

> What if we modify equality_oper() and friends to do the check for us
> when argtype is a varlena array?

> If an array type is checked for an ordering operator, its element type
> now *must* have an ordering operator in order for the result to be
> meaningful. Same goes for ordering_oper().

That sounds more like a solution.  Is there anyplace else we need to
push knowledge of this into?  (Offhand I can't think of any, but ...)

            regards, tom lane

Re: array support patch phase 1 patch

From
Joe Conway
Date:
Kris Jurka wrote:
> The problem arises when trying to join arrays as if they were tables.
>

How 'bout something like this:

create or replace function array_map_attr(smallint[], oid)
returns text[] as '
declare
   v_arr alias for $1;
   v_toid alias for $2;
   v_lb int;
   v_ub int;
   v_attname text;
   v_result text[] := ''{}'';
begin
   v_lb := array_lower(v_arr, 1);
   v_ub := array_upper(v_arr, 1);
   if v_lb is not null and v_ub is not null then
     for i in v_lb..v_ub loop
       select into v_attname attname::text
         from pg_attribute
         where attrelid = v_toid and attnum = v_arr[i];
       v_result := v_result || v_attname;
     end loop;
     return v_result;
   end if;
   return NULL;
end;
' language 'plpgsql' strict;

SELECT
         pkn.nspname AS PKTABLE_SCHEM,
         pkt.relname AS PKTABLE_NAME,
         array_map_attr(c.confkey, pkt.oid) AS PKCOLUMN_NAME,
         fkn.nspname AS FKTABLE_SCHEM,
         fkt.relname AS FKTABLE_NAME,
         array_map_attr(c.conkey, fkt.oid) AS FKCOLUMN_NAME,
         c.conname AS FK_NAME,
         pkc.conname AS PK_NAME
FROM pg_namespace pkn, pg_class pkt,
         pg_namespace fkn, pg_class fkt,
         pg_constraint c, pg_constraint pkc
WHERE
         pkn.oid = pkt.relnamespace
         AND fkn.oid = fkt.relnamespace
         AND c.conrelid = fkt.oid
         AND c.confrelid = pkt.oid
         AND c.confrelid = pkc.conrelid
;

-[ RECORD 1 ]-+-----------------
pktable_schem | public
pktable_name  | clstr_tst_s
pkcolumn_name | {rf_a}
fktable_schem | public
fktable_name  | clstr_tst
fkcolumn_name | {b}
fk_name       | clstr_tst_con
pk_name       | clstr_tst_s_pkey
-[ RECORD 2 ]-+-----------------
pktable_schem | public
pktable_name  | rfi1
pkcolumn_name | {f1,f2}
fktable_schem | public
fktable_name  | rfi2
fkcolumn_name | {f1,f2}
fk_name       | $1
pk_name       | rfi1_pkey

It wouldn't be hard to turn array_map_attr() (or whatever name) into a C
function.

Joe


Re: array support patch phase 1 patch

From
Tom Lane
Date:
Peter Eisentraut <peter_e@gmx.net> writes:
> That is fine, but what if someone defines a new data type and different
> ordering operators, say << and <<<.  Then I'd think he should be able to
> use those automatically on the array type whose elements are the new type.

I'm unconvinced that this can or should be "automatic".  How are we
supposed to know what the appropriate combination logic is to extend
a random comparison operator to arrays?  For operators that follow
the ordinary <, =, > logic we know what to do, but if someone invents
a new operator that doesn't fit into that structure then I don't think
we can be expected to handle it.

If you're complaining that equality_oper() and ordering_oper() are
bogus, I'd tend to agree, but that hardly affects the validity of this
patch.  The present definition of ordering_oper() isn't too horrible
(since it looks for mergejoinability rather than depending on any
assumptions about names) but equality_oper() is hokey.  It'd be nice
to have some more catalog-driven way of identifying the equality operator
for a datatype.

BTW, it might be better for array_cmp to insist that the array element
type have a default btree opclass, and use the comparison proc from that
opclass in place of equality_oper() and ordering_oper().  This'd be
faster (only one call per comparison) as well as more semantically pure.

I've got some other problems with this patch (I still don't understand
what the hacking on aggregates is supposed to accomplish, for example)
but the array comparison stuff seems relatively straightforward.

            regards, tom lane

Re: array support patch phase 1 patch

From
Joe Conway
Date:
Tom Lane wrote:
> BTW, it might be better for array_cmp to insist that the array element
> type have a default btree opclass, and use the comparison proc from that
> opclass in place of equality_oper() and ordering_oper().  This'd be
> faster (only one call per comparison) as well as more semantically pure.

Sounds like a good idea. Should I work this now, or wait for more comments?

> I've got some other problems with this patch (I still don't understand
> what the hacking on aggregates is supposed to accomplish, for example)

The aggregates hacking was a bit convoluted by the desire to support
polymorphic aggregates using polymorphic functions. You end up with many
possible states, some of which are invalid because they don't provide
enough context to resolve the polymorphic types. Any suggestions?

Joe




Re: array support patch phase 1 patch

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> The aggregates hacking was a bit convoluted by the desire to support
> polymorphic aggregates using polymorphic functions. You end up with many
> possible states, some of which are invalid because they don't provide
> enough context to resolve the polymorphic types. Any suggestions?

I think we'd be better off leaving that out till we understand it a
little better, then ...

            regards, tom lane

Re: array support patch phase 1 patch

From
Joe Conway
Date:
Tom Lane wrote:
> Joe Conway <mail@joeconway.com> writes:
>
>>The aggregates hacking was a bit convoluted by the desire to support
>>polymorphic aggregates using polymorphic functions. You end up with many
>>possible states, some of which are invalid because they don't provide
>>enough context to resolve the polymorphic types. Any suggestions?
>
> I think we'd be better off leaving that out till we understand it a
> little better, then ...

I'm reasonably sure I covered the bases on it. Did you find behavior you
didn't like?

In any case, I think we ought to allow polymorphic functions to be used
in aggregates. Life is simpler if the aggregate uses well defined data
types.

Joe


Re: array support patch phase 1 patch

From
Peter Eisentraut
Date:
Joe Conway writes:

> And if you use "multidimensional", does that mean you also use
> "onedimensional" and "twodimensional", etc.?

No, the analogues would be "unidimensional", "bidimensional", and
"many-dimensional".  There difference is that you close up prefixes (which
are not words by themselves), but you hyphenate compounds of independent
words.

> > The function array_subscript() should be removed from code and
> > The function array_assign() should be removed from code and documentation.
> > The function singleton_array() should be removed from code and
>
> I still have an uneasy feeling that there are situations where your only
> option to get this functionality is via a function call.

Once that feeling materializes into a real concern, we can still consider
action.  Maybe there will at some point be a need for these functions, but
maybe we will need a similar function with slightly different
functionality, or whatever.  Hard to tell as long as we're fantasizing.

> I've heard no one else vote to remove them.

Was there ever a vote to add them?

> > The functions array_prepend() and array_cat() should be removed from the
> > documentation and considered internal implementations of the operator ||.
>
> I disagree with this one. Both array_prepend() and array_cat() have
> valid use in aggregates.

> > The function array_append() should be documented as being equivalent to
> > 'array || element' and specifically intended for user-defined aggregate
> > functions.
>
> That's fine. I would add similar descriptions for array_prepend() and
> array_cat().

Sounds good.  My main concern is that people will have a clear view of
what's standard and recommended for which situation, so that support will
be easier and users won't be confronted with a long list of equivalent,
undifferentiated options.

--
Peter Eisentraut   peter_e@gmx.net


Re: array support patch phase 1 patch

From
Joe Conway
Date:
Peter Eisentraut wrote:
[...much snipping follows...]
> No, the analogues would be "unidimensional", "bidimensional", and
> "many-dimensional".  There difference is that you close up prefixes (which
> are not words by themselves), but you hyphenate compounds of independent
> words.
>
>>>The function array_subscript() should be removed from code and
>>>The function array_assign() should be removed from code and documentation.
>>>The function singleton_array() should be removed from code and
>>
>>>The function array_append() should be documented as being equivalent to
>>>'array || element' and specifically intended for user-defined aggregate
>>>functions.
>>
>>That's fine. I would add similar descriptions for array_prepend() and
>>array_cat().
>
> Sounds good.  My main concern is that people will have a clear view of
> what's standard and recommended for which situation, so that support will
> be easier and users won't be confronted with a long list of equivalent,
> undifferentiated options.
>

The attached patch addresses Peter's concerns, subject to our agreements
above. I.e, the changes are:

1) All instances of "multi-dimensional array" and "multiple dimension
array" throughout the source tree have been changed to "multidimensional
array". Even if everyone doesn't agree this is correct, it is at least
consistent throughout, so if we later decide to change it back, it will
be easier ;-)

2) The functions singleton_array(), array_assign(), and
array_subscript() have been removed along with all references thereof.

3) The documentation emphasizes use of the concatenation operator '||'
as preferred over the functions array_append(), array_prepend(), and
array_cat(). A mention of direct use of the functions in user-defined
aggregates is also included.

[...more snipage from a different part of the thread...]
Tom Lane wrote:
 > BTW, it might be better for array_cmp to insist that the array element
 > type have a default btree opclass, and use the comparison proc from
 > that opclass in place of equality_oper() and ordering_oper().  This'd
 > be faster (only one call per comparison) as well as more semantically
 > pure.

I stopped short of this change because there doesn't seem to be a way to
get the btree opclass tuple given just the opcintype without scanning
pg_opclass. Am I missing something? Or should I just not worry about it
and scan pg_opclass?

 > I've got some other problems with this patch (I still don't understand
 > what the hacking on aggregates is supposed to accomplish, for example)
 > but the array comparison stuff seems relatively straightforward.

As I said in an earlier message, I believe that I have implemented this
correctly. Is there a specific corner case not covered? I'd really like
to see some form of polymorphic function support for aggregates --
please let me know what I can do to make that happen.

Joe

p.s. I added Bruce to the thread because he asked me about the status of
the patch, and because the lists have been so slow the last few days.
Index: doc/src/sgml/array.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
retrieving revision 1.25
diff -c -r1.25 array.sgml
*** doc/src/sgml/array.sgml    13 Mar 2003 01:30:26 -0000    1.25
--- doc/src/sgml/array.sgml    9 Jun 2003 02:13:32 -0000
***************
*** 60,73 ****
  </programlisting>
   </para>

   <note>
    <para>
!    A limitation of the present array implementation is that individual
!    elements of an array cannot be SQL null values.  The entire array can be set
!    to null, but you can't have an array with some elements null and some
!    not.  Fixing this is on the to-do list.
    </para>
   </note>
   </sect2>

   <sect2>
--- 60,133 ----
  </programlisting>
   </para>

+  <para>
+   A limitation of the present array implementation is that individual
+   elements of an array cannot be SQL null values.  The entire array can be set
+   to null, but you can't have an array with some elements null and some
+   not.
+  </para>
+  <para>
+   This can lead to surprising results. For example, the result of the
+   previous two inserts looks like this:
+ <programlisting>
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |      schedule
+ -------+---------------------------+--------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting},{""}}
+  Carol | {20000,25000,25000,25000} | {{talk},{meeting}}
+ (2 rows)
+ </programlisting>
+   Because the <literal>[2][2]</literal> element of
+   <structfield>schedule</structfield> is missing in each of the
+   <command>INSERT</command> statements, the <literal>[1][2]</literal>
+   element is discarded.
+  </para>
+
+  <note>
+   <para>
+    Fixing this is on the to-do list.
+   </para>
+  </note>
+
+  <para>
+   The <command>ARRAY</command> expression syntax may also be used:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Bill',
+     ARRAY[10000, 10000, 10000, 10000],
+     ARRAY[['meeting', 'lunch'], ['','']]);
+
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting', '']]);
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |           schedule
+ -------+---------------------------+-------------------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}}
+  Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}}
+ (2 rows)
+ </programlisting>
+   Note that with this syntax, multidimensional arrays must have matching
+   extents for each dimension. This eliminates the missing-array-elements
+   problem above. For example:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting']]);
+ ERROR:  Multidimensional arrays must have array expressions with matching dimensions
+ </programlisting>
+   Also notice that string literals are single quoted instead of double quoted.
+  </para>
+
   <note>
    <para>
!    The examples in the rest of this section are based on the
!    <command>ARRAY</command> expression syntax <command>INSERT</command>s.
    </para>
   </note>
+
   </sect2>

   <sect2>
***************
*** 132,142 ****
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the
!   form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified.
   </para>

   <para>
--- 192,221 ----
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified; another example follows:
! <programlisting>
! SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
!          schedule
! ---------------------------
!  {{meeting,lunch},{"",""}}
! (1 row)
! </programlisting>
!  </para>
!
!  <para>
!   Additionally, we can also access a single arbitrary array element of
!   a one-dimensional array with the <function>array_subscript</function>
!   function:
! <programlisting>
! SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
!  array_subscript
! -----------------
!            10000
! (1 row)
! </programlisting>
   </para>

   <para>
***************
*** 147,153 ****
      WHERE name = 'Carol';
  </programlisting>

!   or updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
--- 226,248 ----
      WHERE name = 'Carol';
  </programlisting>

!   or using the <command>ARRAY</command> expression syntax:
!
! <programlisting>
! UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
!     WHERE name = 'Carol';
! </programlisting>
!
!   <note>
!    <para>
!     Anywhere you can use the <quote>curly braces</quote> array syntax,
!     you can also use the <command>ARRAY</command> expression syntax. The
!     remainder of this section will illustrate only one or the other, but
!     not both.
!    </para>
!   </note>
!
!   An array may also be updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
***************
*** 160,165 ****
--- 255,268 ----
  UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
      WHERE name = 'Carol';
  </programlisting>
+
+   A one-dimensional array may also be updated with the
+   <function>array_assign</function> function:
+
+ <programlisting>
+ UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000)
+     WHERE name = 'Bill';
+ </programListing>
   </para>

   <para>
***************
*** 179,184 ****
--- 282,369 ----
   </para>

   <para>
+   An array can also be enlarged by using the concatenation operator,
+   <command>||</command>.
+ <programlisting>
+ SELECT ARRAY[1,2] || ARRAY[3,4];
+    ?column?
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
+       ?column?
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ (1 row)
+ </programlisting>
+
+   The concatenation operator allows a single element to be pushed on to the
+   beginning or end of a one-dimensional array. It also allows two
+   <replaceable>N</>-dimensional arrays, or an <replaceable>N</>-dimensional
+   and an <replaceable>N+1</>-dimensional array. In the former case, the two
+   <replaceable>N</>-dimension arrays become outer elements of an
+   <replaceable>N+1</>-dimensional array. In the latter, the
+   <replaceable>N</>-dimensional array is added as either the first or last
+   outer element of the <replaceable>N+1</>-dimensional array.
+
+   The array is extended in the direction of the push. Hence, by pushing
+   onto the beginning of an array with a one-based subscript, a zero-based
+   subscript array is created:
+
+ <programlisting>
+ SELECT array_dims(t.f) FROM (SELECT 1 || ARRAY[2,3] AS f) AS t;
+  array_dims
+ ------------
+  [0:2]
+ (1 row)
+ </programlisting>
+  </para>
+
+  <para>
+   An array can also be enlarged by using the functions
+   <function>array_prepend</function>, <function>array_append</function>,
+   or <function>array_cat</function>. The first two only support one-dimensional
+   arrays, but <function>array_cat</function> supports multidimensional arrays.
+
+   Note that the concatenation operator discussed above is preferred over
+   direct use of these functions. In fact, the functions are primarily for use
+   in implementing the concatenation operator. However, they may be directly
+   useful in the creation of user-defined aggregates. Some examples:
+
+ <programlisting>
+ SELECT array_prepend(1, ARRAY[2,3]);
+  array_prepend
+ ---------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_append(ARRAY[1,2], 3);
+  array_append
+ --------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
+    array_cat
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
+       array_cat
+ ---------------------
+  {{1,2},{3,4},{5,6}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
+       array_cat
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ </programlisting>
+  </para>
+
+  <para>
    The syntax for <command>CREATE TABLE</command> allows fixed-length
    arrays to be defined:

***************
*** 194,199 ****
--- 379,394 ----
   </para>

   <para>
+   An alternative syntax for one-dimensional arrays may be used.
+   <structfield>pay_by_quarter</structfield> could have been defined as:
+ <programlisting>
+     pay_by_quarter  integer ARRAY[4],
+ </programlisting>
+   This syntax may <emphasis>only</emphasis> be used with the integer
+   constant to denote the array size.
+  </para>
+
+  <para>
    Actually, the current implementation does not enforce the declared
    number of dimensions either.  Arrays of a particular element type are
    all considered to be of the same type, regardless of size or number
***************
*** 300,305 ****
--- 495,566 ----
     is not ignored, however: after skipping leading whitespace, everything
     up to the next right brace or delimiter is taken as the item value.
    </para>
+
+   <para>
+    As illustrated earlier in this chapter, arrays may also be represented
+    using the <command>ARRAY</command> expression syntax. This representation
+    of an array value consists of items that are interpreted according to the
+    I/O conversion rules for the array's element type, plus decoration that
+    indicates the array structure. The decoration consists of the keyword
+    <command>ARRAY</command> and square brackets (<literal>[</> and
+    <literal>]</>) around the array values, plus delimiter characters between
+    adjacent items. The delimiter character is always a comma (<literal>,</>).
+    When representing multidimensional arrays, the keyword
+    <command>ARRAY</command> is only necessary for the outer level. For example,
+    <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as:
+ <programlisting>
+ SELECT ARRAY[['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   or it also could be written as:
+ <programlisting>
+ SELECT ARRAY[ARRAY['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   </para>
+
+   <para>
+    A final method to represent an array, is through an
+    <command>ARRAY</command> sub-select expression. For example:
+ <programlisting>
+ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
+                           ?column?
+ -------------------------------------------------------------
+  {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
+ (1 row)
+ </programlisting>
+   The sub-select may <emphasis>only</emphasis> return a single column. The
+   resulting one-dimensional array will have an element for each row in the
+   sub-select result, with an element type matching that of the sub-select's
+   target column.
+   </para>
+
+   <para>
+    Arrays may be cast from one type to another in similar fashion to other
+    data types:
+
+ <programlisting>
+ SELECT ARRAY[1,2,3]::oid[];
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+
+ SELECT CAST(ARRAY[1,2,3] AS float8[]);
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+ </programlisting>
+
+   </para>
+
   </sect2>

   <sect2>
***************
*** 316,321 ****
--- 577,590 ----
     Alternatively, you can use backslash-escaping to protect all data characters
     that would otherwise be taken as array syntax or ignorable white space.
    </para>
+
+  <note>
+   <para>
+    The discussion in the preceding paragraph with respect to double quoting does
+    not pertain to the <command>ARRAY</command> expression syntax. In that case,
+    each element is quoted exactly as any other literal value of the element type.
+   </para>
+  </note>

    <para>
     The array output routine will put double quotes around element values
Index: doc/src/sgml/func.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v
retrieving revision 1.154
diff -c -r1.154 func.sgml
*** doc/src/sgml/func.sgml    5 May 2003 15:08:49 -0000    1.154
--- doc/src/sgml/func.sgml    9 Jun 2003 01:53:35 -0000
***************
*** 6962,6967 ****
--- 6962,7164 ----

    </sect1>

+  <sect1 id="functions-array">
+   <title>Array Functions</title>
+
+   <para>
+    <xref linkend="array-operators-table"> shows the operators
+    available for the <type>array</type> types.
+   </para>
+
+     <table id="array-operators-table">
+      <title><type>array</type> Operators</title>
+      <tgroup cols="4">
+       <thead>
+        <row>
+     <entry>Operator</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry> <literal>=</literal> </entry>
+     <entry>equals</entry>
+     <entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry>
+     <entry><literal>t</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>element-to-array concatenation</entry>
+     <entry><literal>3 || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{3,4,5,6}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-element concatenation</entry>
+     <entry><literal>ARRAY[4,5,6] || 7</literal></entry>
+     <entry><literal>{4,5,6,7}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+
+   <para>
+    <xref linkend="array-functions-table"> shows the functions
+    available for use with array types. See <xref linkend="arrays">
+    for more discussion and examples for the use of these functions.
+   </para>
+
+     <table id="array-functions-table">
+      <title><type>array</type> Functions</title>
+      <tgroup cols="5">
+       <thead>
+        <row>
+     <entry>Function</entry>
+     <entry>Return Type</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry>
+      <literal>
+       <function>array_append</function>
+       (<type>anyarray</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the end of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_append(ARRAY[1,2], 3)</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_cat</function>
+       (<type>anyarray</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      concatenate two arrays, returning <literal>NULL</literal>
+      for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_dims</function>
+       (<type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      returns a text representation of array dimension lower and upper bounds,
+      generating an ERROR for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry>
+     <entry><literal>[1:2][1:3]</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_lower</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns lower bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry>
+     <entry><literal>0</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_prepend</function>
+       (<type>anyelement</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the beginning of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_to_string</function>
+       (<type>anyarray</type>, <type>text</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      concatenates array elements using provided delimiter, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_to_string(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry>
+     <entry><literal>1.10~^~2.20~^~3.30</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_upper</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns upper bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry>
+     <entry><literal>4</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>string_to_array</function>
+       (<type>text</type>, <type>text</type>)
+      </literal>
+     </entry>
+     <entry><type>text[]</type></entry>
+     <entry>
+      splits string into array elements using provided delimiter, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>string_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry>
+     <entry><literal>{1.1,2.2,3.3}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+   </sect1>

   <sect1 id="functions-aggregate">
    <title>Aggregate Functions</title>
Index: src/backend/catalog/pg_aggregate.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_aggregate.c,v
retrieving revision 1.56
diff -c -r1.56 pg_aggregate.c
*** src/backend/catalog/pg_aggregate.c    18 Sep 2002 21:35:20 -0000    1.56
--- src/backend/catalog/pg_aggregate.c    9 Jun 2003 01:31:10 -0000
***************
*** 50,59 ****
      Oid            finalfn = InvalidOid;    /* can be omitted */
      Oid            finaltype;
      Oid            fnArgs[FUNC_MAX_ARGS];
!     int            nargs;
      Oid            procOid;
      TupleDesc    tupDesc;
      int            i;
      ObjectAddress myself,
                  referenced;

--- 50,65 ----
      Oid            finalfn = InvalidOid;    /* can be omitted */
      Oid            finaltype;
      Oid            fnArgs[FUNC_MAX_ARGS];
!     int            nargs_transfn;
!     int            nargs_finalfn;
      Oid            procOid;
      TupleDesc    tupDesc;
      int            i;
+     Oid            rettype;
+     Oid           *true_oid_array_transfn;
+     Oid           *true_oid_array_finalfn;
+     bool        retset;
+     FuncDetailCode fdresult;
      ObjectAddress myself,
                  referenced;

***************
*** 68,91 ****
      MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
      fnArgs[0] = aggTransType;
      if (aggBaseType == ANYOID)
!         nargs = 1;
      else
      {
          fnArgs[1] = aggBaseType;
!         nargs = 2;
      }
!     transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
      if (!OidIsValid(transfn))
!         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
      tup = SearchSysCache(PROCOID,
                           ObjectIdGetDatum(transfn),
                           0, 0, 0);
      if (!HeapTupleIsValid(tup))
!         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
      proc = (Form_pg_proc) GETSTRUCT(tup);
-     if (proc->prorettype != aggTransType)
-         elog(ERROR, "return type of transition function %s is not %s",
-          NameListToString(aggtransfnName), format_type_be(aggTransType));

      /*
       * If the transfn is strict and the initval is NULL, make sure input
--- 74,122 ----
      MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
      fnArgs[0] = aggTransType;
      if (aggBaseType == ANYOID)
!         nargs_transfn = 1;
      else
      {
          fnArgs[1] = aggBaseType;
!         nargs_transfn = 2;
      }
!
!     /*
!      * func_get_detail looks up the function in the catalogs, does
!      * disambiguation for polymorphic functions, handles inheritance, and
!      * returns the funcid and type and set or singleton status of the
!      * function's return value.  it also returns the true argument types
!      * to the function.
!      */
!     fdresult = func_get_detail(aggtransfnName, NIL, nargs_transfn, fnArgs,
!                                &transfn, &rettype, &retset,
!                                &true_oid_array_transfn);
!
!     /* only valid case is a normal function */
!     if (fdresult != FUNCDETAIL_NORMAL)
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
      if (!OidIsValid(transfn))
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
!     /*
!      * enforce consistency with ANYARRAY and ANYELEMENT argument
!      * and return types, possibly modifying return type along the way
!      */
!     rettype = enforce_generic_type_consistency(fnArgs, true_oid_array_transfn,
!                                                        nargs_transfn, rettype);
!
!     if (rettype != aggTransType)
!         elog(ERROR, "return type of transition function %s is not %s",
!          NameListToString(aggtransfnName), format_type_be(aggTransType));
!
      tup = SearchSysCache(PROCOID,
                           ObjectIdGetDatum(transfn),
                           0, 0, 0);
      if (!HeapTupleIsValid(tup))
!         func_error("AggregateCreate", aggtransfnName,
!                         nargs_transfn, fnArgs, NULL);
      proc = (Form_pg_proc) GETSTRUCT(tup);

      /*
       * If the transfn is strict and the initval is NULL, make sure input
***************
*** 105,121 ****
      {
          MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
          fnArgs[0] = aggTransType;
!         finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
!         if (!OidIsValid(finalfn))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!         tup = SearchSysCache(PROCOID,
!                              ObjectIdGetDatum(finalfn),
!                              0, 0, 0);
!         if (!HeapTupleIsValid(tup))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!         proc = (Form_pg_proc) GETSTRUCT(tup);
!         finaltype = proc->prorettype;
!         ReleaseSysCache(tup);
      }
      else
      {
--- 136,161 ----
      {
          MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
          fnArgs[0] = aggTransType;
!         nargs_finalfn = 1;
!
!         fdresult = func_get_detail(aggfinalfnName, NIL, 1, fnArgs,
!                                    &finalfn, &rettype, &retset,
!                                    &true_oid_array_finalfn);
!
!         /* only valid case is a normal function */
!         if (fdresult != FUNCDETAIL_NORMAL)
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!
!         if (!OidIsValid(finalfn))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!
!         /*
!          * enforce consistency with ANYARRAY and ANYELEMENT argument
!          * and return types, possibly modifying return type along the way
!          */
!         finaltype = enforce_generic_type_consistency(fnArgs,
!                                                      true_oid_array_finalfn,
!                                                      nargs_finalfn, rettype);
      }
      else
      {
***************
*** 125,130 ****
--- 165,191 ----
          finaltype = aggTransType;
      }
      Assert(OidIsValid(finaltype));
+
+     /*
+      * special disallowed cases:
+      * 1)    if finaltype is polymorphic, basetype cannot be ANY
+      * 2)    if finaltype is polymorphic, both args to transfn must be
+      *        polymorphic
+      */
+     if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
+     {
+         if (aggBaseType == ANYOID)
+             elog(ERROR, "aggregate with base type ANY must have a " \
+                         "non-polymorphic return type");
+
+         if (nargs_transfn > 1 && (
+             (true_oid_array_transfn[0] != ANYARRAYOID &&
+              true_oid_array_transfn[0] != ANYELEMENTOID) ||
+             (true_oid_array_transfn[1] != ANYARRAYOID &&
+              true_oid_array_transfn[1] != ANYELEMENTOID)))
+             elog(ERROR, "aggregate with polymorphic return type requires " \
+                         "state function with both arguments polymorphic");
+     }

      /*
       * Everything looks okay.  Try to create the pg_proc entry for the
Index: src/backend/commands/aggregatecmds.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/aggregatecmds.c,v
retrieving revision 1.5
diff -c -r1.5 aggregatecmds.c
*** src/backend/commands/aggregatecmds.c    4 Sep 2002 20:31:14 -0000    1.5
--- src/backend/commands/aggregatecmds.c    9 Jun 2003 01:31:10 -0000
***************
*** 119,125 ****
          baseTypeId = typenameTypeId(baseType);

      transTypeId = typenameTypeId(transType);
!     if (get_typtype(transTypeId) == 'p')
          elog(ERROR, "Aggregate transition datatype cannot be %s",
               format_type_be(transTypeId));

--- 119,127 ----
          baseTypeId = typenameTypeId(baseType);

      transTypeId = typenameTypeId(transType);
!     if (get_typtype(transTypeId) == 'p' &&
!         transTypeId != ANYARRAYOID &&
!         transTypeId != ANYELEMENTOID)
          elog(ERROR, "Aggregate transition datatype cannot be %s",
               format_type_be(transTypeId));

Index: src/backend/executor/execQual.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/execQual.c,v
retrieving revision 1.130
diff -c -r1.130 execQual.c
*** src/backend/executor/execQual.c    28 May 2003 22:32:49 -0000    1.130
--- src/backend/executor/execQual.c    9 Jun 2003 01:42:58 -0000
***************
*** 1528,1544 ****
              {
                  /* Check other sub-arrays are compatible */
                  if (elem_ndims != ARR_NDIM(array))
!                     elog(ERROR, "Multiple dimension arrays must have array "
                           "expressions with matching number of dimensions");

                  if (memcmp(elem_dims, ARR_DIMS(array),
                             elem_ndims * sizeof(int)) != 0)
!                     elog(ERROR, "Multiple dimension arrays must have array "
                           "expressions with matching dimensions");

                  if (memcmp(elem_lbs, ARR_LBOUND(array),
                             elem_ndims * sizeof(int)) != 0)
!                     elog(ERROR, "Multiple dimension arrays must have array "
                           "expressions with matching dimensions");
              }

--- 1528,1544 ----
              {
                  /* Check other sub-arrays are compatible */
                  if (elem_ndims != ARR_NDIM(array))
!                     elog(ERROR, "Multidimensional arrays must have array "
                           "expressions with matching number of dimensions");

                  if (memcmp(elem_dims, ARR_DIMS(array),
                             elem_ndims * sizeof(int)) != 0)
!                     elog(ERROR, "Multidimensional arrays must have array "
                           "expressions with matching dimensions");

                  if (memcmp(elem_lbs, ARR_LBOUND(array),
                             elem_ndims * sizeof(int)) != 0)
!                     elog(ERROR, "Multidimensional arrays must have array "
                           "expressions with matching dimensions");
              }

Index: src/backend/executor/nodeAgg.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeAgg.c,v
retrieving revision 1.105
diff -c -r1.105 nodeAgg.c
*** src/backend/executor/nodeAgg.c    30 May 2003 20:23:10 -0000    1.105
--- src/backend/executor/nodeAgg.c    9 Jun 2003 01:31:10 -0000
***************
*** 58,63 ****
--- 58,64 ----
  #include "executor/executor.h"
  #include "executor/nodeAgg.h"
  #include "miscadmin.h"
+ #include "nodes/makefuncs.h"
  #include "optimizer/clauses.h"
  #include "parser/parse_coerce.h"
  #include "parser/parse_expr.h"
***************
*** 212,218 ****
  static void agg_fill_hash_table(AggState *aggstate);
  static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
  static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
!

  /*
   * Initialize all aggregates for a new group of input values.
--- 213,219 ----
  static void agg_fill_hash_table(AggState *aggstate);
  static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
  static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
! static Oid resolve_type(Oid type_to_resolve, Oid context_type);

  /*
   * Initialize all aggregates for a new group of input values.
***************
*** 351,364 ****
      fcinfo.context = NULL;
      fcinfo.resultinfo = NULL;
      fcinfo.isnull = false;
-
      fcinfo.flinfo = &peraggstate->transfn;
      fcinfo.nargs = 2;
      fcinfo.arg[0] = pergroupstate->transValue;
      fcinfo.argnull[0] = pergroupstate->transValueIsNull;
      fcinfo.arg[1] = newVal;
      fcinfo.argnull[1] = isNull;
-
      newVal = FunctionCallInvoke(&fcinfo);

      /*
--- 352,363 ----
***************
*** 1176,1182 ****
--- 1175,1195 ----
          AclResult    aclresult;
          Oid            transfn_oid,
                      finalfn_oid;
+         FuncExpr   *transfnexpr,
+                    *finalfnexpr;
          Datum        textInitVal;
+         List       *fargs;
+         Oid            agg_rt_type;
+         Oid           *transfn_arg_types;
+         List       *transfn_args = NIL;
+         int            transfn_nargs;
+         Oid            transfn_ret_type;
+         Oid           *finalfn_arg_types = NULL;
+         List       *finalfn_args = NIL;
+         Oid            finalfn_ret_type = InvalidOid;
+         int            finalfn_nargs = 0;
+         Node       *arg0;
+         Node       *arg1;
          int            i;

          /* Look for a previous duplicate aggregate */
***************
*** 1224,1229 ****
--- 1237,1402 ----
                          &peraggstate->transtypeLen,
                          &peraggstate->transtypeByVal);

+         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
+         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
+
+         /* get the runtime aggregate argument type */
+         fargs = aggref->args;
+         agg_rt_type = exprType((Node *) nth(0, fargs));
+
+         /* get the transition function argument and return types */
+         transfn_ret_type = get_func_rettype(transfn_oid);
+         transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs);
+
+         /* resolve any polymorphic types */
+         if (transfn_nargs == 2)
+         /* base type was not ANY */
+         {
+             if (transfn_arg_types[1] == ANYARRAYOID ||
+                 transfn_arg_types[1] == ANYELEMENTOID)
+                 transfn_arg_types[1] = agg_rt_type;
+
+             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+                                                         agg_rt_type);
+
+             /*
+              * Build arg list to use on the transfn FuncExpr node. We really
+              * only care that the node type is correct so that the transfn
+              * can discover the actual argument types at runtime using
+              * get_fn_expr_argtype()
+              */
+             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
+                                                             -1, COERCE_DONTCARE);
+             arg1 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[1],
+                                                             -1, COERCE_DONTCARE);
+             transfn_args = makeList2(arg0, arg1);
+
+             /*
+              * the state transition function always returns the same type
+              * as its first argument
+              */
+             if (transfn_ret_type == ANYARRAYOID ||
+                 transfn_ret_type == ANYELEMENTOID)
+                 transfn_ret_type = transfn_arg_types[0];
+         }
+         else if (transfn_nargs == 1)
+         /*
+          * base type was ANY, therefore the aggregate return type should
+          * be non-polymorphic
+          */
+         {
+             Oid    finaltype = get_func_rettype(aggref->aggfnoid);
+
+             /*
+              * this should have been prevented in AggregateCreate,
+              * but check anyway
+              */
+             if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
+                 elog(ERROR, "aggregate with base type ANY must have a " \
+                             "non-polymorphic return type");
+
+             /* see if we have a final function */
+             if (OidIsValid(finalfn_oid))
+             {
+                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
+                 if (finalfn_nargs != 1)
+                     elog(ERROR, "final function takes unexpected number " \
+                                 "of arguments: %d", finalfn_nargs);
+
+                 /*
+                  * final function argument is always the same as the state
+                  * function return type
+                  */
+                 if (finalfn_arg_types[0] != ANYARRAYOID &&
+                     finalfn_arg_types[0] != ANYELEMENTOID)
+                 {
+                     /* if it is not ambiguous, use it */
+                     transfn_ret_type = finalfn_arg_types[0];
+                 }
+                 else
+                 {
+                     /* if it is ambiguous, try to derive it */
+                     finalfn_ret_type = finaltype;
+                     finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0],
+                                                             finalfn_ret_type);
+                     transfn_ret_type = finalfn_arg_types[0];
+                 }
+             }
+             else
+                 transfn_ret_type = finaltype;
+
+             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+                                                         transfn_ret_type);
+
+             /*
+              * Build arg list to use on the transfn FuncExpr node. We really
+              * only care that the node type is correct so that the transfn
+              * can discover the actual argument types at runtime using
+              * get_fn_expr_argtype()
+              */
+             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
+                                                             -1, COERCE_DONTCARE);
+             transfn_args = makeList1(arg0);
+         }
+         else
+             elog(ERROR, "state transition function takes unexpected number " \
+                         "of arguments: %d", transfn_nargs);
+
+         if (OidIsValid(finalfn_oid))
+         {
+             /* get the final function argument and return types */
+             if (finalfn_ret_type == InvalidOid)
+                 finalfn_ret_type = get_func_rettype(finalfn_oid);
+
+             if (!finalfn_arg_types)
+             {
+                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
+                 if (finalfn_nargs != 1)
+                     elog(ERROR, "final function takes unexpected number " \
+                                 "of arguments: %d", finalfn_nargs);
+             }
+
+             /*
+              * final function argument is always the same as the state
+              * function return type, which by now should have been resolved
+              */
+             if (finalfn_arg_types[0] == ANYARRAYOID ||
+                 finalfn_arg_types[0] == ANYELEMENTOID)
+                 finalfn_arg_types[0] = transfn_ret_type;
+
+             /*
+              * Build arg list to use on the finalfn FuncExpr node. We really
+              * only care that the node type is correct so that the finalfn
+              * can discover the actual argument type at runtime using
+              * get_fn_expr_argtype()
+              */
+             arg0 = (Node *) makeRelabelType((Expr *) NULL, finalfn_arg_types[0],
+                                                             -1, COERCE_DONTCARE);
+             finalfn_args = makeList1(arg0);
+
+             finalfn_ret_type = resolve_type(finalfn_ret_type,
+                                                 finalfn_arg_types[0]);
+         }
+
+         fmgr_info(transfn_oid, &peraggstate->transfn);
+         transfnexpr = (FuncExpr *) make_funcclause(transfn_oid,
+                           transfn_ret_type,
+                           false,            /* cannot be a set */
+                           COERCE_DONTCARE,    /* to match any user expr */
+                           transfn_args);
+         peraggstate->transfn.fn_expr = (Node *) transfnexpr;
+
+         if (OidIsValid(finalfn_oid))
+         {
+             fmgr_info(finalfn_oid, &peraggstate->finalfn);
+             finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid,
+                           finalfn_ret_type,
+                           false,            /* cannot be a set */
+                           COERCE_DONTCARE,    /* to match any user expr */
+                           finalfn_args);
+             peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
+         }
+
          /*
           * initval is potentially null, so don't try to access it as a
           * struct field. Must do it the hard way with SysCacheGetAttr.
***************
*** 1236,1249 ****
              peraggstate->initValue = (Datum) 0;
          else
              peraggstate->initValue = GetAggInitVal(textInitVal,
!                                                    aggform->aggtranstype);
!
!         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
!         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
!
!         fmgr_info(transfn_oid, &peraggstate->transfn);
!         if (OidIsValid(finalfn_oid))
!             fmgr_info(finalfn_oid, &peraggstate->finalfn);

          /*
           * If the transfn is strict and the initval is NULL, make sure
--- 1409,1415 ----
              peraggstate->initValue = (Datum) 0;
          else
              peraggstate->initValue = GetAggInitVal(textInitVal,
!                                                    transfn_arg_types[0]);

          /*
           * If the transfn is strict and the initval is NULL, make sure
***************
*** 1454,1457 ****
--- 1620,1656 ----
      elog(ERROR, "Aggregate function %u called as normal function",
           fcinfo->flinfo->fn_oid);
      return (Datum) 0;            /* keep compiler quiet */
+ }
+
+ static Oid
+ resolve_type(Oid type_to_resolve, Oid context_type)
+ {
+     Oid        resolved_type;
+
+     if (context_type == ANYARRAYOID || context_type == ANYELEMENTOID)
+         resolved_type = type_to_resolve;
+     else if (type_to_resolve == ANYARRAYOID)
+     /* any array */
+     {
+         Oid        context_type_arraytype = get_array_type(context_type);
+
+         if (context_type_arraytype != InvalidOid)
+             resolved_type = context_type_arraytype;
+         else
+             resolved_type = context_type;
+     }
+     else if (type_to_resolve == ANYELEMENTOID)
+     /* any element */
+     {
+         Oid        context_type_elemtype = get_element_type(context_type);
+
+         if (context_type_elemtype != InvalidOid)
+             resolved_type = context_type_elemtype;
+         else
+             resolved_type = context_type;
+     }
+     else
+         resolved_type = type_to_resolve;
+
+     return resolved_type;
  }
Index: src/backend/executor/nodeSubplan.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v
retrieving revision 1.45
diff -c -r1.45 nodeSubplan.c
*** src/backend/executor/nodeSubplan.c    8 Apr 2003 23:20:01 -0000    1.45
--- src/backend/executor/nodeSubplan.c    9 Jun 2003 01:31:10 -0000
***************
*** 28,50 ****
  #include "utils/datum.h"
  #include "utils/lsyscache.h"

-
- typedef struct ArrayBuildState
- {
-     MemoryContext mcontext;        /* where all the temp stuff is kept */
-     Datum       *dvalues;        /* array of accumulated Datums */
-     /*
-      * The allocated size of dvalues[] is always a multiple of
-      * ARRAY_ELEMS_CHUNKSIZE
-      */
- #define ARRAY_ELEMS_CHUNKSIZE    64
-     int            nelems;            /* number of valid Datums in dvalues[] */
-     Oid            element_type;    /* data type of the Datums */
-     int16        typlen;            /* needed info about datatype */
-     bool        typbyval;
-     char        typalign;
- } ArrayBuildState;
-
  static Datum ExecHashSubPlan(SubPlanState *node,
                               ExprContext *econtext,
                               bool *isNull);
--- 28,33 ----
***************
*** 54,66 ****
  static void buildSubPlanHash(SubPlanState *node);
  static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
  static bool tupleAllNulls(HeapTuple tuple);
- static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
-                                          Datum dvalue, bool disnull,
-                                          Oid element_type,
-                                          MemoryContext rcontext);
- static Datum makeArrayResult(ArrayBuildState *astate,
-                              MemoryContext rcontext);
-

  /* ----------------------------------------------------------------
   *        ExecSubPlan
--- 37,42 ----
***************
*** 224,229 ****
--- 200,206 ----
      PlanState  *planstate = node->planstate;
      SubLinkType subLinkType = subplan->subLinkType;
      bool        useOr = subplan->useOr;
+     bool        isExpr = subplan->isExpr;
      MemoryContext oldcontext;
      TupleTableSlot *slot;
      Datum        result;
***************
*** 292,297 ****
--- 269,279 ----
          bool        rownull = false;
          int            col = 1;
          List       *plst;
+         int            numelems;
+         int            elemnum;
+         Datum        dvalue;
+         Datum       *dvalues = NULL;
+         bool        disnull;

          if (subLinkType == EXISTS_SUBLINK)
          {
***************
*** 329,337 ****

          if (subLinkType == ARRAY_SUBLINK)
          {
-             Datum    dvalue;
-             bool    disnull;
-
              found = true;
              /* stash away current value */
              dvalue = heap_getattr(tup, 1, tdesc, &disnull);
--- 311,316 ----
***************
*** 349,446 ****
          found = true;

          /*
!          * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
!          * operators for columns of tuple.
           */
!         plst = subplan->paramIds;
!         foreach(lst, node->exprs)
          {
!             ExprState  *exprstate = (ExprState *) lfirst(lst);
!             int            paramid = lfirsti(plst);
!             ParamExecData *prmdata;
!             Datum        expresult;
!             bool        expnull;
!
!             /*
!              * Load up the Param representing this column of the sub-select.
!              */
!             prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
!             Assert(prmdata->execPlan == NULL);
!             prmdata->value = heap_getattr(tup, col, tdesc,
!                                           &(prmdata->isnull));

!             /*
!              * Now we can eval the combining operator for this column.
!              */
!             expresult = ExecEvalExprSwitchContext(exprstate, econtext,
!                                                   &expnull, NULL);
!
!             /*
!              * Combine the result into the row result as appropriate.
!              */
!             if (col == 1)
              {
!                 rowresult = expresult;
!                 rownull = expnull;
              }
!             else if (useOr)
              {
!                 /* combine within row per OR semantics */
!                 if (expnull)
!                     rownull = true;
!                 else if (DatumGetBool(expresult))
                  {
!                     rowresult = BoolGetDatum(true);
!                     rownull = false;
!                     break;        /* needn't look at any more columns */
                  }
              }
              else
              {
!                 /* combine within row per AND semantics */
!                 if (expnull)
!                     rownull = true;
!                 else if (!DatumGetBool(expresult))
!                 {
!                     rowresult = BoolGetDatum(false);
!                     rownull = false;
!                     break;        /* needn't look at any more columns */
!                 }
              }

-             plst = lnext(plst);
-             col++;
          }

!         if (subLinkType == ANY_SUBLINK)
          {
!             /* combine across rows per OR semantics */
!             if (rownull)
!                 *isNull = true;
!             else if (DatumGetBool(rowresult))
              {
!                 result = BoolGetDatum(true);
!                 *isNull = false;
!                 break;            /* needn't look at any more rows */
              }
!         }
!         else if (subLinkType == ALL_SUBLINK)
!         {
!             /* combine across rows per AND semantics */
!             if (rownull)
!                 *isNull = true;
!             else if (!DatumGetBool(rowresult))
              {
!                 result = BoolGetDatum(false);
!                 *isNull = false;
!                 break;            /* needn't look at any more rows */
              }
-         }
-         else
-         {
-             /* must be MULTIEXPR_SUBLINK */
-             result = rowresult;
-             *isNull = rownull;
          }
      }

--- 328,490 ----
          found = true;

          /*
!          * When isExpr is true, we have either a scalar expression or an
!          * array. In the former case, this is no different than the !isExpr
!          * case. In the latter case, iterate over the elements as if they
!          * were from multiple input tuples.
           */
!         if (!isExpr)
!             numelems = 1;
!         else
          {
!             Oid        expr_typeid = tdesc->attrs[0]->atttypid;

!             if (expr_typeid != subplan->exprtype)
              {
!                 subplan->exprtype = expr_typeid;
!                 subplan->elemtype = get_element_type(expr_typeid);
!
!                 if (subplan->elemtype != InvalidOid)
!                     get_typlenbyvalalign(subplan->elemtype,
!                                          &subplan->elmlen,
!                                          &subplan->elmbyval,
!                                          &subplan->elmalign);
              }
!
!             /* get current value */
!             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
!
!             /* XXX this will need work if/when arrays support NULL elements */
!             if (!disnull)
              {
!                 if (subplan->elemtype != InvalidOid)
!                 {
!                     ArrayType   *v = DatumGetArrayTypeP(dvalue);
!
!                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
!                                       subplan->elmbyval, subplan->elmalign,
!                                         &dvalues, &numelems);
!                 }
!                 else
                  {
!                     numelems = 1;
!                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                     dvalues[0] = dvalue;
                  }
              }
              else
              {
!                 numelems = 1;
!                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                 dvalues[0] = (Datum) 0;
              }

          }

!         for (elemnum = 0; elemnum < numelems; elemnum++)
          {
!             /*
!              * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
!              * operators for columns of tuple.
!              */
!             col = 1;
!             plst = subplan->paramIds;
!             foreach(lst, node->exprs)
              {
!                 ExprState  *exprstate = (ExprState *) lfirst(lst);
!                 int            paramid = lfirsti(plst);
!                 ParamExecData *prmdata;
!                 Datum        expresult;
!                 bool        expnull;
!
!                 /*
!                  * Load up the Param representing this column of the sub-select.
!                  */
!                 prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
!                 Assert(prmdata->execPlan == NULL);
!
!                 if (!isExpr)
!                     prmdata->value = heap_getattr(tup, col, tdesc,
!                                                   &(prmdata->isnull));
!                 else
!                 {
!                     prmdata->value = dvalues[elemnum];
!                     prmdata->isnull = disnull;
!                 }
!
!                 /*
!                  * Now we can eval the combining operator for this column.
!                  */
!                 expresult = ExecEvalExprSwitchContext(exprstate, econtext,
!                                                       &expnull, NULL);
!
!                 /*
!                  * Combine the result into the row result as appropriate.
!                  */
!                 if (col == 1)
!                 {
!                     rowresult = expresult;
!                     rownull = expnull;
!                 }
!                 else if (useOr)
!                 {
!                     /* combine within row per OR semantics */
!                     if (expnull)
!                         rownull = true;
!                     else if (DatumGetBool(expresult))
!                     {
!                         rowresult = BoolGetDatum(true);
!                         rownull = false;
!                         break;        /* needn't look at any more columns */
!                     }
!                 }
!                 else
!                 {
!                     /* combine within row per AND semantics */
!                     if (expnull)
!                         rownull = true;
!                     else if (!DatumGetBool(expresult))
!                     {
!                         rowresult = BoolGetDatum(false);
!                         rownull = false;
!                         break;        /* needn't look at any more columns */
!                     }
!                 }
!
!                 plst = lnext(plst);
!                 col++;
              }
!
!             if (subLinkType == ANY_SUBLINK)
              {
!                 /* combine across rows per OR semantics */
!                 if (rownull)
!                     *isNull = true;
!                 else if (DatumGetBool(rowresult))
!                 {
!                     result = BoolGetDatum(true);
!                     *isNull = false;
!                     break;            /* needn't look at any more rows */
!                 }
!             }
!             else if (subLinkType == ALL_SUBLINK)
!             {
!                 /* combine across rows per AND semantics */
!                 if (rownull)
!                     *isNull = true;
!                 else if (!DatumGetBool(rowresult))
!                 {
!                     result = BoolGetDatum(false);
!                     *isNull = false;
!                     break;            /* needn't look at any more rows */
!                 }
!             }
!             else
!             {
!                 /* must be MULTIEXPR_SUBLINK */
!                 result = rowresult;
!                 *isNull = rownull;
              }
          }
      }

***************
*** 478,483 ****
--- 522,528 ----
  buildSubPlanHash(SubPlanState *node)
  {
      SubPlan       *subplan = (SubPlan *) node->xprstate.expr;
+     bool        isExpr = subplan->isExpr;
      PlanState  *planstate = node->planstate;
      int            ncols = length(node->exprs);
      ExprContext *innerecontext = node->innerecontext;
***************
*** 485,490 ****
--- 530,536 ----
      MemoryContext oldcontext;
      int            nbuckets;
      TupleTableSlot *slot;
+     TupleTableSlot *arrslot = NULL;

      Assert(subplan->subLinkType == ANY_SUBLINK);
      Assert(!subplan->useOr);
***************
*** 562,604 ****
      {
          HeapTuple    tup = slot->val;
          TupleDesc    tdesc = slot->ttc_tupleDescriptor;
!         int            col = 1;
          List       *plst;
          bool        isnew;

          /*
!          * Load up the Params representing the raw sub-select outputs,
!          * then form the projection tuple to store in the hashtable.
           */
!         foreach(plst, subplan->paramIds)
          {
!             int            paramid = lfirsti(plst);
!             ParamExecData *prmdata;

!             prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
!             Assert(prmdata->execPlan == NULL);
!             prmdata->value = heap_getattr(tup, col, tdesc,
!                                           &(prmdata->isnull));
!             col++;
!         }
!         slot = ExecProject(node->projRight, NULL);
!         tup = slot->val;

-         /*
-          * If result contains any nulls, store separately or not at all.
-          * (Since we know the projection tuple has no junk columns, we
-          * can just look at the overall hasnull info bit, instead of
-          * groveling through the columns.)
-          */
-         if (HeapTupleNoNulls(tup))
-         {
-             (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
-             node->havehashrows = true;
          }
!         else if (node->hashnulls)
          {
!             (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
!             node->havenullrows = true;
          }

          /*
--- 608,746 ----
      {
          HeapTuple    tup = slot->val;
          TupleDesc    tdesc = slot->ttc_tupleDescriptor;
!         TupleDesc    arrtdesc = NULL;
          List       *plst;
          bool        isnew;
+         int            numelems;
+         int            elemnum;
+         Datum        dvalue;
+         Datum       *dvalues = NULL;
+         bool        disnull;

          /*
!          * When isExpr is true, we have either a scalar expression or an
!          * array. In the former case, this is no different than the !isExpr
!          * case. In the latter case, iterate over the elements as if they
!          * were from multiple input tuples.
           */
!         if (!isExpr)
!             numelems = 1;
!         else
          {
!             Oid        expr_typeid = tdesc->attrs[0]->atttypid;

!             if (expr_typeid != subplan->exprtype)
!             {
!                 subplan->exprtype = expr_typeid;
!                 subplan->elemtype = get_element_type(expr_typeid);
!
!                 if (subplan->elemtype != InvalidOid)
!                     get_typlenbyvalalign(subplan->elemtype,
!                                          &subplan->elmlen,
!                                          &subplan->elmbyval,
!                                          &subplan->elmalign);
!             }
!
!             /* get current value */
!             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
!
!             if (subplan->elemtype != InvalidOid)
!             {
!                 TupleTable    tupleTable;
!                 ArrayType   *v = DatumGetArrayTypeP(dvalue);
!
!                 arrtdesc = CreateTemplateTupleDesc(1, false);
!                 TupleDescInitEntry(arrtdesc, 1, "elem", subplan->elemtype,
!                                                             -1, 0, false);
!
!                 tupleTable = ExecCreateTupleTable(1);
!                 arrslot = ExecAllocTableSlot(tupleTable);
!                 ExecSetSlotDescriptor(arrslot, arrtdesc, true);
!
!                 /* XXX this will need work if/when arrays support NULL elements */
!                 if (!disnull)
!                 {
!                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
!                                       subplan->elmbyval, subplan->elmalign,
!                                         &dvalues, &numelems);
!                 }
!                 else
!                 {
!                     numelems = 1;
!                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                     dvalues[0] = (Datum) 0;
!                 }
!             }
!             else
!             {
!                 numelems = 1;
!                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                 dvalues[0] = dvalue;
!             }

          }
!
!         for (elemnum = 0; elemnum < numelems; elemnum++)
          {
!             int    col = 1;
!
!             if (!isExpr || subplan->elemtype == InvalidOid)
!             {
!                 /*
!                  * Load up the Params representing the raw sub-select outputs,
!                  * then form the projection tuple to store in the hashtable.
!                  */
!                 foreach(plst, subplan->paramIds)
!                 {
!                     int            paramid = lfirsti(plst);
!                     ParamExecData *prmdata;
!
!                     prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
!                     Assert(prmdata->execPlan == NULL);
!
!                     prmdata->value = heap_getattr(tup, col, tdesc,
!                                                   &(prmdata->isnull));
!
!                     col++;
!                 }
!                 slot = ExecProject(node->projRight, NULL);
!                 tup = slot->val;
!             }
!             else
!             {
!                 /*
!                  * For array type expressions, we need to build up our own
!                  * tuple and slot
!                  */
!                 char        nullflag;
!
!                 nullflag = disnull ? 'n' : ' ';
!                 tup = heap_formtuple(arrtdesc, &dvalues[elemnum], &nullflag);
!                 arrslot = ExecStoreTuple(tup, arrslot, InvalidBuffer, true);
!             }
!
!             /*
!              * If result contains any nulls, store separately or not at all.
!              * (Since we know the projection tuple has no junk columns, we
!              * can just look at the overall hasnull info bit, instead of
!              * groveling through the columns.)
!              */
!             if (HeapTupleNoNulls(tup))
!             {
!                 if (!isExpr)
!                     (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
!                 else
!                     (void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew);
!                 node->havehashrows = true;
!             }
!             else if (node->hashnulls)
!             {
!                 if (!isExpr)
!                     (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
!                 else
!                     (void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew);
!                 node->havenullrows = true;
!             }
          }

          /*
***************
*** 615,620 ****
--- 757,764 ----
       * have the potential for a double free attempt.
       */
      ExecClearTuple(node->projRight->pi_slot);
+     if (arrslot)
+         ExecClearTuple(arrslot);

      MemoryContextSwitchTo(oldcontext);
  }
***************
*** 1084,1185 ****
          prm->execPlan = node;
          parent->chgParam = bms_add_member(parent->chgParam, paramid);
      }
- }
-
- /*
-  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
-  *
-  *    astate is working state (NULL on first call)
-  *    rcontext is where to keep working state
-  */
- static ArrayBuildState *
- accumArrayResult(ArrayBuildState *astate,
-                  Datum dvalue, bool disnull,
-                  Oid element_type,
-                  MemoryContext rcontext)
- {
-     MemoryContext arr_context,
-                   oldcontext;
-
-     if (astate == NULL)
-     {
-         /* First time through --- initialize */
-
-         /* Make a temporary context to hold all the junk */
-         arr_context = AllocSetContextCreate(rcontext,
-                                             "ARRAY_SUBLINK Result",
-                                             ALLOCSET_DEFAULT_MINSIZE,
-                                             ALLOCSET_DEFAULT_INITSIZE,
-                                             ALLOCSET_DEFAULT_MAXSIZE);
-         oldcontext = MemoryContextSwitchTo(arr_context);
-         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
-         astate->mcontext = arr_context;
-         astate->dvalues = (Datum *)
-             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
-         astate->nelems = 0;
-         astate->element_type = element_type;
-         get_typlenbyvalalign(element_type,
-                              &astate->typlen,
-                              &astate->typbyval,
-                              &astate->typalign);
-     }
-     else
-     {
-         oldcontext = MemoryContextSwitchTo(astate->mcontext);
-         Assert(astate->element_type == element_type);
-         /* enlarge dvalues[] if needed */
-         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
-             astate->dvalues = (Datum *)
-                 repalloc(astate->dvalues,
-                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
-     }
-
-     if (disnull)
-         elog(ERROR, "NULL elements not allowed in Arrays");
-
-     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
-     astate->dvalues[astate->nelems++] =
-         datumCopy(dvalue, astate->typbyval, astate->typlen);
-
-     MemoryContextSwitchTo(oldcontext);
-
-     return astate;
- }
-
- /*
-  * makeArrayResult - produce final result of ARRAY_SUBLINK
-  *
-  *    astate is working state (not NULL)
-  *    rcontext is where to construct result
-  */
- static Datum
- makeArrayResult(ArrayBuildState *astate,
-                 MemoryContext rcontext)
- {
-     ArrayType  *result;
-     int            dims[1];
-     int            lbs[1];
-     MemoryContext oldcontext;
-
-     /* Build the final array result in rcontext */
-     oldcontext = MemoryContextSwitchTo(rcontext);
-
-     dims[0] = astate->nelems;
-     lbs[0] = 1;
-
-     result = construct_md_array(astate->dvalues,
-                                 1,
-                                 dims,
-                                 lbs,
-                                 astate->element_type,
-                                 astate->typlen,
-                                 astate->typbyval,
-                                 astate->typalign);
-
-     MemoryContextSwitchTo(oldcontext);
-
-     /* Clean up all the junk */
-     MemoryContextDelete(astate->mcontext);
-
-     return PointerGetDatum(result);
  }
--- 1228,1231 ----
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.251
diff -c -r1.251 copyfuncs.c
*** src/backend/nodes/copyfuncs.c    28 May 2003 16:03:56 -0000    1.251
--- src/backend/nodes/copyfuncs.c    9 Jun 2003 01:31:10 -0000
***************
*** 727,732 ****
--- 727,733 ----
      COPY_NODE_FIELD(target);
      COPY_SCALAR_FIELD(aggstar);
      COPY_SCALAR_FIELD(aggdistinct);
+     COPY_NODE_FIELD(args);

      return newnode;
  }
***************
*** 825,830 ****
--- 826,832 ----

      COPY_SCALAR_FIELD(subLinkType);
      COPY_SCALAR_FIELD(useOr);
+     COPY_SCALAR_FIELD(isExpr);
      COPY_NODE_FIELD(lefthand);
      COPY_NODE_FIELD(operName);
      COPY_OIDLIST_FIELD(operOids);
***************
*** 843,848 ****
--- 845,856 ----

      COPY_SCALAR_FIELD(subLinkType);
      COPY_SCALAR_FIELD(useOr);
+     COPY_SCALAR_FIELD(isExpr);
+     COPY_SCALAR_FIELD(exprtype);
+     COPY_SCALAR_FIELD(elemtype);
+     COPY_SCALAR_FIELD(elmlen);
+     COPY_SCALAR_FIELD(elmbyval);
+     COPY_SCALAR_FIELD(elmalign);
      COPY_NODE_FIELD(exprs);
      COPY_INTLIST_FIELD(paramIds);
      COPY_NODE_FIELD(plan);
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.194
diff -c -r1.194 equalfuncs.c
*** src/backend/nodes/equalfuncs.c    28 May 2003 16:03:56 -0000    1.194
--- src/backend/nodes/equalfuncs.c    9 Jun 2003 01:31:10 -0000
***************
*** 204,209 ****
--- 204,210 ----
      COMPARE_NODE_FIELD(target);
      COMPARE_SCALAR_FIELD(aggstar);
      COMPARE_SCALAR_FIELD(aggdistinct);
+     COMPARE_NODE_FIELD(args);

      return true;
  }
***************
*** 300,305 ****
--- 301,307 ----
  {
      COMPARE_SCALAR_FIELD(subLinkType);
      COMPARE_SCALAR_FIELD(useOr);
+     COMPARE_SCALAR_FIELD(isExpr);
      COMPARE_NODE_FIELD(lefthand);
      COMPARE_NODE_FIELD(operName);
      COMPARE_OIDLIST_FIELD(operOids);
***************
*** 313,318 ****
--- 315,326 ----
  {
      COMPARE_SCALAR_FIELD(subLinkType);
      COMPARE_SCALAR_FIELD(useOr);
+     COMPARE_SCALAR_FIELD(isExpr);
+     COMPARE_SCALAR_FIELD(exprtype);
+     COMPARE_SCALAR_FIELD(elemtype);
+     COMPARE_SCALAR_FIELD(elmlen);
+     COMPARE_SCALAR_FIELD(elmbyval);
+     COMPARE_SCALAR_FIELD(elmalign);
      COMPARE_NODE_FIELD(exprs);
      COMPARE_INTLIST_FIELD(paramIds);
      /* should compare plans, but have to settle for comparing plan IDs */
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v
retrieving revision 1.206
diff -c -r1.206 outfuncs.c
*** src/backend/nodes/outfuncs.c    28 May 2003 16:03:56 -0000    1.206
--- src/backend/nodes/outfuncs.c    9 Jun 2003 01:31:10 -0000
***************
*** 615,620 ****
--- 615,621 ----
      WRITE_NODE_FIELD(target);
      WRITE_BOOL_FIELD(aggstar);
      WRITE_BOOL_FIELD(aggdistinct);
+     WRITE_NODE_FIELD(args);
  }

  static void
***************
*** 700,705 ****
--- 701,707 ----

      WRITE_ENUM_FIELD(subLinkType, SubLinkType);
      WRITE_BOOL_FIELD(useOr);
+     WRITE_BOOL_FIELD(isExpr);
      WRITE_NODE_FIELD(lefthand);
      WRITE_NODE_FIELD(operName);
      WRITE_OIDLIST_FIELD(operOids);
***************
*** 713,718 ****
--- 715,726 ----

      WRITE_ENUM_FIELD(subLinkType, SubLinkType);
      WRITE_BOOL_FIELD(useOr);
+     WRITE_BOOL_FIELD(isExpr);
+     WRITE_OID_FIELD(exprtype);
+     WRITE_OID_FIELD(elemtype);
+     WRITE_INT_FIELD(elmlen);
+     WRITE_BOOL_FIELD(elmbyval);
+     WRITE_CHAR_FIELD(elmalign);
      WRITE_NODE_FIELD(exprs);
      WRITE_INTLIST_FIELD(paramIds);
      WRITE_NODE_FIELD(plan);
Index: src/backend/nodes/readfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v
retrieving revision 1.153
diff -c -r1.153 readfuncs.c
*** src/backend/nodes/readfuncs.c    6 May 2003 00:20:32 -0000    1.153
--- src/backend/nodes/readfuncs.c    9 Jun 2003 01:31:10 -0000
***************
*** 415,420 ****
--- 415,421 ----
      READ_NODE_FIELD(target);
      READ_BOOL_FIELD(aggstar);
      READ_BOOL_FIELD(aggdistinct);
+     READ_NODE_FIELD(args);

      READ_DONE();
  }
***************
*** 544,549 ****
--- 545,551 ----

      READ_ENUM_FIELD(subLinkType, SubLinkType);
      READ_BOOL_FIELD(useOr);
+     READ_BOOL_FIELD(isExpr);
      READ_NODE_FIELD(lefthand);
      READ_NODE_FIELD(operName);
      READ_OIDLIST_FIELD(operOids);
Index: src/backend/optimizer/plan/subselect.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/plan/subselect.c,v
retrieving revision 1.75
diff -c -r1.75 subselect.c
*** src/backend/optimizer/plan/subselect.c    29 Apr 2003 22:13:09 -0000    1.75
--- src/backend/optimizer/plan/subselect.c    9 Jun 2003 01:31:10 -0000
***************
*** 67,73 ****

  static List *convert_sublink_opers(List *lefthand, List *operOids,
                                     List *targetlist, int rtindex,
!                                    List **righthandIds);
  static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
  static Node *replace_correlation_vars_mutator(Node *node, void *context);
  static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
--- 67,73 ----

  static List *convert_sublink_opers(List *lefthand, List *operOids,
                                     List *targetlist, int rtindex,
!                                    bool isExpr, List **righthandIds);
  static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
  static Node *replace_correlation_vars_mutator(Node *node, void *context);
  static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
***************
*** 240,245 ****
--- 240,251 ----
       */
      node->subLinkType = slink->subLinkType;
      node->useOr = slink->useOr;
+     node->isExpr = slink->isExpr;
+     node->exprtype = InvalidOid;
+     node->elemtype = InvalidOid;
+     node->elmlen = 0;
+     node->elmbyval = false;
+     node->elmalign = '\0';
      node->exprs = NIL;
      node->paramIds = NIL;
      node->useHashTable = false;
***************
*** 316,322 ****
          exprs = convert_sublink_opers(lefthand,
                                        slink->operOids,
                                        plan->targetlist,
!                                       0,
                                        &node->paramIds);
          node->setParam = listCopy(node->paramIds);
          PlannerInitPlan = lappend(PlannerInitPlan, node);
--- 322,328 ----
          exprs = convert_sublink_opers(lefthand,
                                        slink->operOids,
                                        plan->targetlist,
!                                       0, node->isExpr,
                                        &node->paramIds);
          node->setParam = listCopy(node->paramIds);
          PlannerInitPlan = lappend(PlannerInitPlan, node);
***************
*** 399,405 ****
          node->exprs = convert_sublink_opers(lefthand,
                                              slink->operOids,
                                              plan->targetlist,
!                                             0,
                                              &node->paramIds);

          /*
--- 405,411 ----
          node->exprs = convert_sublink_opers(lefthand,
                                              slink->operOids,
                                              plan->targetlist,
!                                             0, node->isExpr,
                                              &node->paramIds);

          /*
***************
*** 444,450 ****
  static List *
  convert_sublink_opers(List *lefthand, List *operOids,
                        List *targetlist, int rtindex,
!                       List **righthandIds)
  {
      List       *result = NIL;
      List       *lst;
--- 450,456 ----
  static List *
  convert_sublink_opers(List *lefthand, List *operOids,
                        List *targetlist, int rtindex,
!                       bool isExpr, List **righthandIds)
  {
      List       *result = NIL;
      List       *lst;
***************
*** 499,511 ****
           * are not expecting to have to resolve unknown Params, so
           * it's okay to pass a null pstate.)
           */
!         result = lappend(result,
!                          make_op_expr(NULL,
!                                       tup,
!                                       leftop,
!                                       rightop,
!                                       exprType(leftop),
!                                       te->resdom->restype));

          ReleaseSysCache(tup);

--- 505,542 ----
           * are not expecting to have to resolve unknown Params, so
           * it's okay to pass a null pstate.)
           */
!         if (!isExpr)
!         {
!             result = lappend(result,
!                              make_op_expr(NULL,
!                                           tup,
!                                           leftop,
!                                           rightop,
!                                           exprType(leftop),
!                                           te->resdom->restype));
!         }
!         else
!         {
!             Oid        exprtype = te->resdom->restype;
!             Oid        elemtype = get_element_type(exprtype);
!
!             if (elemtype != InvalidOid)
!                 result = lappend(result,
!                                  make_op_expr(NULL,
!                                               tup,
!                                               leftop,
!                                               rightop,
!                                               exprType(leftop),
!                                               elemtype));
!             else
!                 result = lappend(result,
!                                  make_op_expr(NULL,
!                                               tup,
!                                               leftop,
!                                               rightop,
!                                               exprType(leftop),
!                                               exprtype));
!         }

          ReleaseSysCache(tup);

***************
*** 616,628 ****
      /*
       * The sublink type must be "= ANY" --- that is, an IN operator.
       * (We require the operator name to be unqualified, which may be
!      * overly paranoid, or may not be.)
       */
      if (sublink->subLinkType != ANY_SUBLINK)
          return NULL;
      if (length(sublink->operName) != 1 ||
          strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
          return NULL;
      /*
       * The sub-select must not refer to any Vars of the parent query.
       * (Vars of higher levels should be okay, though.)
--- 647,663 ----
      /*
       * The sublink type must be "= ANY" --- that is, an IN operator.
       * (We require the operator name to be unqualified, which may be
!      * overly paranoid, or may not be.) It must not be an Expression
!      * sublink.
       */
      if (sublink->subLinkType != ANY_SUBLINK)
          return NULL;
      if (length(sublink->operName) != 1 ||
          strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
          return NULL;
+     if (sublink->isExpr)
+         return NULL;
+
      /*
       * The sub-select must not refer to any Vars of the parent query.
       * (Vars of higher levels should be okay, though.)
***************
*** 675,681 ****
      exprs = convert_sublink_opers(sublink->lefthand,
                                    sublink->operOids,
                                    subselect->targetList,
!                                   rtindex,
                                    &ininfo->sub_targetlist);
      return (Node *) make_ands_explicit(exprs);
  }
--- 710,716 ----
      exprs = convert_sublink_opers(sublink->lefthand,
                                    sublink->operOids,
                                    subselect->targetList,
!                                   rtindex, sublink->isExpr,
                                    &ininfo->sub_targetlist);
      return (Node *) make_ands_explicit(exprs);
  }
Index: src/backend/optimizer/util/clauses.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/util/clauses.c,v
retrieving revision 1.138
diff -c -r1.138 clauses.c
*** src/backend/optimizer/util/clauses.c    28 May 2003 22:32:49 -0000    1.138
--- src/backend/optimizer/util/clauses.c    9 Jun 2003 01:31:10 -0000
***************
*** 133,138 ****
--- 133,160 ----
  }

  /*****************************************************************************
+  *              FUNCTION clause functions
+  *****************************************************************************/
+
+ /*
+  * make_funcclause
+  *        Creates a function clause given its function info and argument list.
+  */
+ Expr *
+ make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
+                             CoercionForm funcformat, List *funcargs)
+ {
+     FuncExpr   *expr = makeNode(FuncExpr);
+
+     expr->funcid = funcid;
+     expr->funcresulttype = funcresulttype;
+     expr->funcretset = funcretset;
+     expr->funcformat = funcformat;
+     expr->args = funcargs;
+     return (Expr *) expr;
+ }
+
+ /*****************************************************************************
   *        NOT clause functions
   *****************************************************************************/

Index: src/backend/parser/gram.y
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/gram.y,v
retrieving revision 2.416
diff -c -r2.416 gram.y
*** src/backend/parser/gram.y    29 May 2003 20:40:36 -0000    2.416
--- src/backend/parser/gram.y    9 Jun 2003 01:31:10 -0000
***************
*** 5490,5495 ****
--- 5490,5496 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ANY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = makeList1(makeString("="));
                      n->subselect = $3;
***************
*** 5500,5505 ****
--- 5501,5507 ----
                      /* Make an IN node */
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ANY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = makeList1(makeString("="));
                      n->subselect = $4;
***************
*** 5511,5516 ****
--- 5513,5519 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = $3;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = $2;
                      n->subselect = $4;
***************
*** 5521,5526 ****
--- 5524,5530 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = MULTIEXPR_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = $2;
                      n->subselect = $3;
***************
*** 5904,5909 ****
--- 5908,5914 ----
                      {
                              SubLink *n = (SubLink *)$3;
                              n->subLinkType = ANY_SUBLINK;
+                             n->isExpr = false;
                              n->lefthand = makeList1($1);
                              n->operName = makeList1(makeString("="));
                              $$ = (Node *)n;
***************
*** 5931,5936 ****
--- 5936,5942 ----
                      {
                          /* Make an IN node */
                          SubLink *n = (SubLink *)$4;
+                         n->isExpr = false;
                          n->subLinkType = ANY_SUBLINK;
                          n->lefthand = makeList1($1);
                          n->operName = makeList1(makeString("="));
***************
*** 5957,5967 ****
--- 5963,6000 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = $3;
+                     n->isExpr = false;
                      n->lefthand = makeList1($1);
                      n->operName = $2;
                      n->subselect = $4;
                      $$ = (Node *)n;
                  }
+             | a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op
+                 {
+                     SubLink *n = makeNode(SubLink);
+                     SelectStmt *s = makeNode(SelectStmt);
+                     ResTarget *r = makeNode(ResTarget);
+
+                     r->name = NULL;
+                     r->indirection = NIL;
+                     r->val = (Node *)$5;
+
+                     s->distinctClause = NIL;
+                     s->targetList = makeList1(r);
+                     s->into = NULL;
+                     s->intoColNames = NIL;
+                     s->fromClause = NIL;
+                     s->whereClause = NULL;
+                     s->groupClause = NIL;
+                     s->havingClause = NULL;
+
+                     n->subLinkType = $3;
+                     n->isExpr = true;
+                     n->lefthand = makeList1($1);
+                     n->operName = $2;
+                     n->subselect = (Node *) s;
+                     $$ = (Node *)n;
+                 }
              | UNIQUE select_with_parens %prec Op
                  {
                      /* Not sure how to get rid of the parentheses
***************
*** 6538,6543 ****
--- 6571,6577 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = EXPR_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $1;
***************
*** 6547,6552 ****
--- 6581,6587 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = EXISTS_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $2;
***************
*** 6556,6561 ****
--- 6591,6597 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ARRAY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $2;
***************
*** 6730,6735 ****
--- 6766,6772 ----
  in_expr:    select_with_parens
                  {
                      SubLink *n = makeNode(SubLink);
+                     n->isExpr = false;
                      n->subselect = $1;
                      /* other fields will be filled later */
                      $$ = (Node *)n;
Index: src/backend/parser/parse_coerce.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v
retrieving revision 2.97
diff -c -r2.97 parse_coerce.c
*** src/backend/parser/parse_coerce.c    26 May 2003 00:11:27 -0000    2.97
--- src/backend/parser/parse_coerce.c    9 Jun 2003 01:31:10 -0000
***************
*** 859,865 ****
      /* Get the element type based on the array type, if we have one */
      if (OidIsValid(array_typeid))
      {
!         array_typelem = get_element_type(array_typeid);
          if (!OidIsValid(array_typelem))
              elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
                   format_type_be(array_typeid));
--- 859,869 ----
      /* Get the element type based on the array type, if we have one */
      if (OidIsValid(array_typeid))
      {
!         if (array_typeid != ANYARRAYOID)
!             array_typelem = get_element_type(array_typeid);
!         else
!             array_typelem = ANYELEMENTOID;
!
          if (!OidIsValid(array_typelem))
              elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
                   format_type_be(array_typeid));
***************
*** 919,925 ****
      {
          if (!OidIsValid(array_typeid))
          {
!             array_typeid = get_array_type(elem_typeid);
              if (!OidIsValid(array_typeid))
                  elog(ERROR, "Cannot find array type for datatype %s",
                       format_type_be(elem_typeid));
--- 923,933 ----
      {
          if (!OidIsValid(array_typeid))
          {
!             if (elem_typeid != ANYELEMENTOID)
!                 array_typeid = get_array_type(elem_typeid);
!             else
!                 array_typeid = ANYARRAYOID;
!
              if (!OidIsValid(array_typeid))
                  elog(ERROR, "Cannot find array type for datatype %s",
                       format_type_be(elem_typeid));
***************
*** 1169,1174 ****
--- 1177,1187 ----
      /* Somewhat-fast path for domain -> base type case */
      if (srctype == targettype)
          return true;
+
+     /* Last of the fast-paths: check for matching polymorphic arrays */
+     if (targettype == ANYARRAYOID)
+         if (get_element_type(srctype) != InvalidOid)
+             return true;

      /* Else look in pg_cast */
      tuple = SearchSysCache(CASTSOURCETARGET,
Index: src/backend/parser/parse_expr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_expr.c,v
retrieving revision 1.148
diff -c -r1.148 parse_expr.c
*** src/backend/parser/parse_expr.c    29 Apr 2003 22:13:10 -0000    1.148
--- src/backend/parser/parse_expr.c    9 Jun 2003 01:42:58 -0000
***************
*** 436,441 ****
--- 436,442 ----
                      sublink->operName = NIL;
                      sublink->operOids = NIL;
                      sublink->useOr = FALSE;
+                     sublink->isExpr = FALSE;
                  }
                  else if (sublink->subLinkType == EXPR_SUBLINK ||
                           sublink->subLinkType == ARRAY_SUBLINK)
***************
*** 463,468 ****
--- 464,470 ----
                      sublink->operName = NIL;
                      sublink->operOids = NIL;
                      sublink->useOr = FALSE;
+                     sublink->isExpr = FALSE;
                  }
                  else
                  {
***************
*** 538,547 ****
                           * here, because make_subplan() will insert type
                           * coercion calls if needed.
                           */
!                         optup = oper(op,
!                                      exprType(lexpr),
!                                      exprType((Node *) tent->expr),
!                                      false);
                          opform = (Form_pg_operator) GETSTRUCT(optup);

                          if (opform->oprresult != BOOLOID)
--- 540,569 ----
                           * here, because make_subplan() will insert type
                           * coercion calls if needed.
                           */
!                         if (!sublink->isExpr)
!                         {
!                             optup = oper(op,
!                                          exprType(lexpr),
!                                          exprType((Node *) tent->expr),
!                                          false);
!                         }
!                         else
!                         {
!                             Oid        exprtype = exprType((Node *) tent->expr);
!                             Oid        elemtype = get_element_type(exprtype);
!
!                             if (elemtype != InvalidOid)
!                                 optup = oper(op,
!                                              exprType(lexpr),
!                                              elemtype,
!                                              false);
!                             else
!                                 optup = oper(op,
!                                              exprType(lexpr),
!                                              exprtype,
!                                              false);
!                         }
!
                          opform = (Form_pg_operator) GETSTRUCT(optup);

                          if (opform->oprresult != BOOLOID)
***************
*** 743,749 ****
                          ArrayExpr  *e = (ArrayExpr *) lfirst(element);

                          if (!IsA(e, ArrayExpr))
!                             elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions");
                          if (ndims == 0)
                              ndims = e->ndims;
                          else if (e->ndims != ndims)
--- 765,771 ----
                          ArrayExpr  *e = (ArrayExpr *) lfirst(element);

                          if (!IsA(e, ArrayExpr))
!                             elog(ERROR, "Multidimensional ARRAY[] must be built from nested array expressions");
                          if (ndims == 0)
                              ndims = e->ndims;
                          else if (e->ndims != ndims)
Index: src/backend/parser/parse_func.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_func.c,v
retrieving revision 1.148
diff -c -r1.148 parse_func.c
*** src/backend/parser/parse_func.c    26 May 2003 00:11:27 -0000    1.148
--- src/backend/parser/parse_func.c    9 Jun 2003 01:31:10 -0000
***************
*** 335,340 ****
--- 335,341 ----
          aggref->target = lfirst(fargs);
          aggref->aggstar = agg_star;
          aggref->aggdistinct = agg_distinct;
+         aggref->args = fargs;

          retval = (Node *) aggref;

Index: src/backend/parser/parse_oper.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_oper.c,v
retrieving revision 1.64
diff -c -r1.64 parse_oper.c
*** src/backend/parser/parse_oper.c    26 May 2003 00:11:27 -0000    1.64
--- src/backend/parser/parse_oper.c    9 Jun 2003 01:31:10 -0000
***************
*** 137,142 ****
--- 137,169 ----
  equality_oper(Oid argtype, bool noError)
  {
      Operator    optup;
+     Oid            elem_type = get_element_type(argtype);
+
+     if (OidIsValid(elem_type))
+     {
+         bool    found = false;
+         /*
+          * If the datatype is an array, look for an "=" operator for the
+          * element datatype.  We require it to be an exact or binary-compatible
+          * match, since most callers are not prepared to cope with adding any
+          * run-time type coercion steps.
+          */
+         optup = equality_oper(elem_type, true);
+         if (optup != NULL)
+         {
+             found = true;
+             ReleaseSysCache(optup);
+         }
+
+         if (!found)
+         {
+             if (!noError)
+                 elog(ERROR, "Unable to identify an equality operator for " \
+                             "array type's element type %s",
+                              format_type_be(elem_type));
+             return NULL;
+         }
+     }

      /*
       * Look for an "=" operator for the datatype.  We require it to be
***************
*** 175,180 ****
--- 202,234 ----
  ordering_oper(Oid argtype, bool noError)
  {
      Operator    optup;
+     Oid            elem_type = get_element_type(argtype);
+
+     if (OidIsValid(elem_type))
+     {
+         bool    found = false;
+         /*
+          * If the datatype is an array, find the array element type's equality
+          * operator, and use its lsortop (it *must* be mergejoinable).  We use
+          * this definition because for sorting and grouping purposes, it's
+          * important that the equality and ordering operators are consistent.
+          */
+         optup = ordering_oper(elem_type, true);
+         if (optup != NULL)
+         {
+             found = true;
+             ReleaseSysCache(optup);
+         }
+
+         if (!found)
+         {
+             if (!noError)
+                 elog(ERROR, "Unable to identify an ordering operator for " \
+                             "array type's element type %s",
+                              format_type_be(elem_type));
+             return NULL;
+         }
+     }

      /*
       * Find the type's equality operator, and use its lsortop (it *must*
***************
*** 215,220 ****
--- 269,289 ----
      Oid            result;

      optup = equality_oper(argtype, false);
+     result = oprfuncid(optup);
+     ReleaseSysCache(optup);
+     return result;
+ }
+
+ /*
+  * ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper())
+  */
+ Oid
+ ordering_oper_funcid(Oid argtype)
+ {
+     Operator    optup;
+     Oid            result;
+
+     optup = ordering_oper(argtype, false);
      result = oprfuncid(optup);
      ReleaseSysCache(optup);
      return result;
Index: src/backend/utils/adt/acl.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/acl.c,v
retrieving revision 1.86
diff -c -r1.86 acl.c
*** src/backend/utils/adt/acl.c    24 Jan 2003 21:53:29 -0000    1.86
--- src/backend/utils/adt/acl.c    9 Jun 2003 01:31:10 -0000
***************
*** 720,732 ****
      aidat = ACL_DAT(acl);
      for (i = 0; i < num; ++i)
      {
!         if (aip->ai_grantee == aidat[i].ai_grantee &&
!             aip->ai_privs == aidat[i].ai_privs)
              PG_RETURN_BOOL(true);
      }
      PG_RETURN_BOOL(false);
  }


  /*
   * has_table_privilege variants
--- 720,740 ----
      aidat = ACL_DAT(acl);
      for (i = 0; i < num; ++i)
      {
!         if (aclitemeq(aip, &aidat[i]))
              PG_RETURN_BOOL(true);
      }
      PG_RETURN_BOOL(false);
  }

+ /*
+  * user-facing version of aclitemeq() for use as the
+  * aclitem equality operator
+  */
+ Datum
+ aclitem_eq(PG_FUNCTION_ARGS)
+ {
+     PG_RETURN_BOOL(aclitemeq(PG_GETARG_ACLITEM_P(0), PG_GETARG_ACLITEM_P(1)));
+ }

  /*
   * has_table_privilege variants
Index: src/backend/utils/adt/array_userfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/array_userfuncs.c,v
retrieving revision 1.1
diff -c -r1.1 array_userfuncs.c
*** src/backend/utils/adt/array_userfuncs.c    8 Apr 2003 23:20:02 -0000    1.1
--- src/backend/utils/adt/array_userfuncs.c    9 Jun 2003 01:46:03 -0000
***************
*** 18,52 ****
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"

-
- /*-----------------------------------------------------------------------------
-  * singleton_array :
-  *        Form a multi-dimensional array given one starting element.
-  *
-  * - first argument is the datum with which to build the array
-  * - second argument is the number of dimensions the array should have;
-  *     defaults to 1 if no second argument is provided
-  *----------------------------------------------------------------------------
-  */
- Datum
- singleton_array(PG_FUNCTION_ARGS)
- {
-     Oid            elem_type = get_fn_expr_argtype(fcinfo, 0);
-     int            ndims;
-
-     if (elem_type == InvalidOid)
-         elog(ERROR, "Cannot determine input datatype");
-
-     if (PG_NARGS() == 2)
-         ndims = PG_GETARG_INT32(1);
-     else
-         ndims = 1;
-
-     PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
-                                                  PG_GETARG_DATUM(0),
-                                                  ndims));
- }
-
  /*-----------------------------------------------------------------------------
   * array_push :
   *        push an element onto either end of a one-dimensional array
--- 18,23 ----
***************
*** 70,75 ****
--- 41,47 ----
      Oid            arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
      Oid            arg0_elemid;
      Oid            arg1_elemid;
+     ArrayMetaState *my_extra;

      if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
          elog(ERROR, "array_push: cannot determine input data types");
***************
*** 95,122 ****
          PG_RETURN_NULL();        /* keep compiler quiet */
      }

!     /* Sanity check: do we have a one-dimensional array */
!     if (ARR_NDIM(v) != 1)
!         elog(ERROR, "Arrays greater than one-dimension are not supported");
!
!     lb = ARR_LBOUND(v);
!     dimv = ARR_DIMS(v);
!     if (arg0_elemid != InvalidOid)
      {
!         /* append newelem */
!         int    ub = dimv[0] + lb[0] - 1;
!         indx = ub + 1;
      }
      else
      {
!         /* prepend newelem */
!         indx = lb[0] - 1;
      }

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

!     result = array_set(v, 1, &indx, newelem, -1,
!                        typlen, typbyval, typalign, &isNull);

      PG_RETURN_ARRAYTYPE_P(result);
  }
--- 67,127 ----
          PG_RETURN_NULL();        /* keep compiler quiet */
      }

!     if (ARR_NDIM(v) == 1)
      {
!         lb = ARR_LBOUND(v);
!         dimv = ARR_DIMS(v);
!
!         if (arg0_elemid != InvalidOid)
!         {
!             /* append newelem */
!             int    ub = dimv[0] + lb[0] - 1;
!             indx = ub + 1;
!         }
!         else
!         {
!             /* prepend newelem */
!             indx = lb[0] - 1;
!         }
      }
+     else if (ARR_NDIM(v) == 0)
+         indx = 1;
      else
+         elog(ERROR, "only empty and one-dimensional arrays are supported");
+
+
+     /*
+      * We arrange to look up info about element type only once per series
+      * of calls, assuming the element type doesn't change underneath us.
+      */
+     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+     if (my_extra == NULL)
      {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
      }

!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }
!
!     result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval,
!                                                  typalign, &isNull);

      PG_RETURN_ARRAYTYPE_P(result);
  }
***************
*** 145,157 ****

      /*
       * We must have one of the following combinations of inputs:
!      * 1) two arrays with ndims1 == ndims2
!      * 2) ndims1 == ndims2 - 1
!      * 3) ndims1 == ndims2 + 1
       */
      ndims1 = ARR_NDIM(v1);
      ndims2 = ARR_NDIM(v2);

      if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
          elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
                      "%d dimensions", ndims1, ndims2);
--- 150,177 ----

      /*
       * We must have one of the following combinations of inputs:
!      * 1) one empty array, and one non-empty array
!      * 2) both arrays empty
!      * 3) two arrays with ndims1 == ndims2
!      * 4) ndims1 == ndims2 - 1
!      * 5) ndims1 == ndims2 + 1
       */
      ndims1 = ARR_NDIM(v1);
      ndims2 = ARR_NDIM(v2);

+     /*
+      * short circuit - if one input array is empty, and the other is not,
+      * we return the non-empty one as the result
+      *
+      * if both are empty, return the first one
+      */
+     if (ndims1 == 0 && ndims2 > 0)
+         PG_RETURN_ARRAYTYPE_P(v2);
+
+     if (ndims2 == 0)
+         PG_RETURN_ARRAYTYPE_P(v1);
+
+     /* the rest fall into combo 2, 3, or 4 */
      if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
          elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
                      "%d dimensions", ndims1, ndims2);
***************
*** 266,412 ****
      PG_RETURN_ARRAYTYPE_P(result);
  }

- /*----------------------------------------------------------------------------
-  * array_accum :
-  *        accumulator to build a 1-D array from input values -- this can be used
-  *        to create custom aggregates.
-  *
-  * This function is not marked strict, so we have to be careful about nulls.
-  *----------------------------------------------------------------------------
-  */
- Datum
- array_accum(PG_FUNCTION_ARGS)
- {
-     /* return NULL if both arguments are NULL */
-     if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
-         PG_RETURN_NULL();
-
-     /* create a new 1-D array from the new element if the array is NULL */
-     if (PG_ARGISNULL(0))
-     {
-         Oid            tgt_type = get_fn_expr_rettype(fcinfo);
-         Oid            tgt_elem_type;
-
-         if (tgt_type == InvalidOid)
-             elog(ERROR, "Cannot determine target array type");
-         tgt_elem_type = get_element_type(tgt_type);
-         if (tgt_elem_type == InvalidOid)
-             elog(ERROR, "Target type is not an array");
-
-         PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
-                                                      PG_GETARG_DATUM(1),
-                                                      1));
-     }
-
-     /* return the array if the new element is NULL */
-     if (PG_ARGISNULL(1))
-         PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0));
-
-     /*
-      * Otherwise this is equivalent to array_push.  We hack the call a little
-      * so that array_push can see the fn_expr information.
-      */
-     return array_push(fcinfo);
- }
-
- /*-----------------------------------------------------------------------------
-  * array_assign :
-  *        assign an element of an array to a new value and return the
-  *        redefined array
-  *----------------------------------------------------------------------------
-  */
- Datum
- array_assign(PG_FUNCTION_ARGS)
- {
-     ArrayType  *v;
-     int            idx_to_chg;
-     Datum        newelem;
-     int           *dimv,
-                *lb, ub;
-     ArrayType  *result;
-     bool        isNull;
-     Oid            element_type;
-     int16        typlen;
-     bool        typbyval;
-     char        typalign;
-
-     v = PG_GETARG_ARRAYTYPE_P(0);
-     idx_to_chg = PG_GETARG_INT32(1);
-     newelem = PG_GETARG_DATUM(2);
-
-     /* Sanity check: do we have a one-dimensional array */
-     if (ARR_NDIM(v) != 1)
-         elog(ERROR, "Arrays greater than one-dimension are not supported");
-
-     lb = ARR_LBOUND(v);
-     dimv = ARR_DIMS(v);
-     ub = dimv[0] + lb[0] - 1;
-     if (idx_to_chg < lb[0] || idx_to_chg > ub)
-         elog(ERROR, "Cannot alter nonexistent array element: %d", idx_to_chg);
-
-     element_type = ARR_ELEMTYPE(v);
-     /* Sanity check: do we have a non-zero element type */
-     if (element_type == 0)
-         elog(ERROR, "Invalid array element type: %u", element_type);
-
-     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
-
-     result = array_set(v, 1, &idx_to_chg, newelem, -1,
-                        typlen, typbyval, typalign, &isNull);
-
-     PG_RETURN_ARRAYTYPE_P(result);
- }
-
- /*-----------------------------------------------------------------------------
-  * array_subscript :
-  *        return specific element of an array
-  *----------------------------------------------------------------------------
-  */
- Datum
- array_subscript(PG_FUNCTION_ARGS)
- {
-     ArrayType  *v;
-     int            idx;
-     int           *dimv,
-                *lb, ub;
-     Datum        result;
-     bool        isNull;
-     Oid            element_type;
-     int16        typlen;
-     bool        typbyval;
-     char        typalign;
-
-     v = PG_GETARG_ARRAYTYPE_P(0);
-     idx = PG_GETARG_INT32(1);
-
-     /* Sanity check: do we have a one-dimensional array */
-     if (ARR_NDIM(v) != 1)
-         elog(ERROR, "Arrays greater than one-dimension are not supported");
-
-     lb = ARR_LBOUND(v);
-     dimv = ARR_DIMS(v);
-     ub = dimv[0] + lb[0] - 1;
-     if (idx < lb[0] || idx > ub)
-         elog(ERROR, "Cannot return nonexistent array element: %d", idx);
-
-     element_type = ARR_ELEMTYPE(v);
-     /* Sanity check: do we have a non-zero element type */
-     if (element_type == 0)
-         elog(ERROR, "Invalid array element type: %u", element_type);
-
-     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
-
-     result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);
-
-     PG_RETURN_DATUM(result);
- }

  /*
!  * actually does the work for singleton_array(), and array_accum() if it is
!  * given a null input array.
   */
  ArrayType *
! create_singleton_array(Oid element_type, Datum element, int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
--- 286,300 ----
      PG_RETURN_ARRAYTYPE_P(result);
  }


  /*
!  * used by text_to_array() in varlena.c
   */
  ArrayType *
! create_singleton_array(FunctionCallInfo fcinfo,
!                        Oid element_type,
!                        Datum element,
!                        int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
***************
*** 415,420 ****
--- 303,309 ----
      int        dims[MAXDIM];
      int        lbs[MAXDIM];
      int        i;
+     ArrayMetaState *my_extra;

      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);
***************
*** 429,435 ****
          lbs[i] = 1;
      }

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      return construct_md_array(dvalues, ndims, dims, lbs, element_type,
                                typlen, typbyval, typalign);
--- 318,352 ----
          lbs[i] = 1;
      }

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      return construct_md_array(dvalues, ndims, dims, lbs, element_type,
                                typlen, typbyval, typalign);
Index: src/backend/utils/adt/arrayfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v
retrieving revision 1.89
diff -c -r1.89 arrayfuncs.c
*** src/backend/utils/adt/arrayfuncs.c    9 May 2003 23:01:45 -0000    1.89
--- src/backend/utils/adt/arrayfuncs.c    9 Jun 2003 01:31:10 -0000
***************
*** 21,28 ****
--- 21,30 ----
  #include "catalog/pg_type.h"
  #include "libpq/pqformat.h"
  #include "parser/parse_coerce.h"
+ #include "parser/parse_oper.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
+ #include "utils/datum.h"
  #include "utils/memutils.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
***************
*** 70,85 ****

  #define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)

- /* I/O function selector for system_cache_lookup */
- typedef enum IOFuncSelector
- {
-     IOFunc_input,
-     IOFunc_output,
-     IOFunc_receive,
-     IOFunc_send
- } IOFuncSelector;
-
-
  static int    ArrayCount(char *str, int *dim, char typdelim);
  static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
               FmgrInfo *inputproc, Oid typelem, int32 typmod,
--- 72,77 ----
***************
*** 93,102 ****
  static void CopyArrayEls(char *p, Datum *values, int nitems,
               int typlen, bool typbyval, char typalign,
               bool freedata);
- static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
-                                 int *typlen, bool *typbyval,
-                                 char *typdelim, Oid *typelem,
-                                 Oid *proc, char *typalign);
  static Datum ArrayCast(char *value, bool byval, int len);
  static int ArrayCastAndSet(Datum src,
                  int typlen, bool typbyval, char typalign,
--- 85,90 ----
***************
*** 119,125 ****
                     char *destPtr,
                     int *st, int *endp, char *srcPtr,
                     int typlen, bool typbyval, char typalign);
!

  /*---------------------------------------------------------------------
   * array_in :
--- 107,113 ----
                     char *destPtr,
                     int *st, int *endp, char *srcPtr,
                     int typlen, bool typbyval, char typalign);
! static int array_cmp(FunctionCallInfo fcinfo);

  /*---------------------------------------------------------------------
   * array_in :
***************
*** 154,165 ****
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;

!     /* Get info about element type, including its input conversion proc */
!     system_cache_lookup(element_type, IOFunc_input,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typinput, &typalign);
!     fmgr_info(typinput, &inputproc);

      /* Make a modifiable copy of the input */
      /* XXX why are we allocating an extra 2 bytes here? */
--- 142,190 ----
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;
+     ArrayMetaState *my_extra;

!     /*
!      * We arrange to look up info about element type, including its input
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its input conversion proc */
!         get_type_metadata(element_type, IOFunc_input,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typinput, &typalign);
!         fmgr_info(typinput, &inputproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typinput;
!         my_extra->typalign = typalign;
!         my_extra->proc = inputproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typinput = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         inputproc = my_extra->proc;
!     }

      /* Make a modifiable copy of the input */
      /* XXX why are we allocating an extra 2 bytes here? */
***************
*** 636,647 ****
                  indx[MAXDIM];
      int            ndim,
                 *dim;

      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_output,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typoutput, &typalign);
!     fmgr_info(typoutput, &outputproc);

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
--- 661,711 ----
                  indx[MAXDIM];
      int            ndim,
                 *dim;
+     ArrayMetaState *my_extra;

      element_type = ARR_ELEMTYPE(v);
!
!     /*
!      * We arrange to look up info about element type, including its input
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its output conversion proc */
!         get_type_metadata(element_type, IOFunc_output,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typoutput, &typalign);
!         fmgr_info(typoutput, &outputproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typoutput;
!         my_extra->typalign = typalign;
!         my_extra->proc = outputproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typoutput = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         outputproc = my_extra->proc;
!     }

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
***************
*** 800,805 ****
--- 864,870 ----
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;
+     ArrayMetaState *my_extra;

      /* Get the array header information */
      ndim = pq_getmsgint(buf, 4);
***************
*** 831,844 ****
          PG_RETURN_ARRAYTYPE_P(retval);
      }

!     /* Get info about element type, including its receive conversion proc */
!     system_cache_lookup(element_type, IOFunc_receive,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typreceive, &typalign);
!     if (!OidIsValid(typreceive))
!         elog(ERROR, "No binary input function available for type %s",
!              format_type_be(element_type));
!     fmgr_info(typreceive, &receiveproc);

      dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
                                typlen, typbyval, typalign,
--- 896,945 ----
          PG_RETURN_ARRAYTYPE_P(retval);
      }

!     /*
!      * We arrange to look up info about element type, including its receive
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its receive conversion proc */
!         get_type_metadata(element_type, IOFunc_receive,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typreceive, &typalign);
!         if (!OidIsValid(typreceive))
!             elog(ERROR, "No binary input function available for type %s",
!                  format_type_be(element_type));
!         fmgr_info(typreceive, &receiveproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typreceive;
!         my_extra->typalign = typalign;
!         my_extra->proc = receiveproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typreceive = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         receiveproc = my_extra->proc;
!     }

      dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
                                typlen, typbyval, typalign,
***************
*** 976,990 ****
      int            ndim,
                 *dim;
      StringInfoData buf;

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
!                         &typdelim, &typelem, &typsend, &typalign);
!     if (!OidIsValid(typsend))
!         elog(ERROR, "No binary output function available for type %s",
!              format_type_be(element_type));
!     fmgr_info(typsend, &sendproc);

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
--- 1077,1130 ----
      int            ndim,
                 *dim;
      StringInfoData buf;
+     ArrayMetaState *my_extra;

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!
!     /*
!      * We arrange to look up info about element type, including its send
!      * proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its send proc */
!         get_type_metadata(element_type, IOFunc_send, &typlen, &typbyval,
!                             &typdelim, &typelem, &typsend, &typalign);
!         if (!OidIsValid(typsend))
!             elog(ERROR, "No binary output function available for type %s",
!                  format_type_be(element_type));
!         fmgr_info(typsend, &sendproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typsend;
!         my_extra->typalign = typalign;
!         my_extra->proc = sendproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typsend = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         sendproc = my_extra->proc;
!     }

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
***************
*** 1476,1481 ****
--- 1616,1641 ----
      array = DatumGetArrayTypeP(PointerGetDatum(array));

      ndim = ARR_NDIM(array);
+
+     /*
+      * if number of dims is zero, i.e. an empty array, create an array
+      * with nSubscripts dimensions, and set the lower bounds to the supplied
+      * subscripts
+      */
+     if (ndim == 0)
+     {
+         Oid        elmtype = ARR_ELEMTYPE(array);
+
+         for (i = 0; i < nSubscripts; i++)
+         {
+             dim[i] = 1;
+             lb[i] = indx[i];
+         }
+
+         return construct_md_array(&dataValue, nSubscripts, dim, lb, elmtype,
+                                                 elmlen, elmbyval, elmalign);
+     }
+
      if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
          elog(ERROR, "Invalid array subscripts");

***************
*** 1632,1637 ****
--- 1792,1822 ----
      /* note: we assume srcArray contains no toasted elements */

      ndim = ARR_NDIM(array);
+
+     /*
+      * if number of dims is zero, i.e. an empty array, create an array
+      * with nSubscripts dimensions, and set the upper and lower bounds
+      * to the supplied subscripts
+      */
+     if (ndim == 0)
+     {
+         Datum  *dvalues;
+         int        nelems;
+         Oid        elmtype = ARR_ELEMTYPE(array);
+
+         deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
+                                                         &dvalues, &nelems);
+
+         for (i = 0; i < nSubscripts; i++)
+         {
+             dim[i] = 1 + upperIndx[i] - lowerIndx[i];
+             lb[i] = lowerIndx[i];
+         }
+
+         return construct_md_array(dvalues, nSubscripts, dim, lb, elmtype,
+                                                  elmlen, elmbyval, elmalign);
+     }
+
      if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
          elog(ERROR, "Invalid array subscripts");

***************
*** 1811,1816 ****
--- 1996,2008 ----
      Oid            typelem;
      Oid            proc;
      char       *s;
+     typedef struct {
+         ArrayMetaState *inp_extra;
+         ArrayMetaState *ret_extra;
+     } am_extra;
+     am_extra  *my_extra;
+     ArrayMetaState *inp_extra;
+     ArrayMetaState *ret_extra;

      /* Get input array */
      if (fcinfo->nargs < 1)
***************
*** 1829,1839 ****
      if (nitems <= 0)
          PG_RETURN_ARRAYTYPE_P(v);

!     /* Lookup source and result types. Unneeded variables are reused. */
!     system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
!                         &typdelim, &typelem, &proc, &inp_typalign);
!     system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
!                         &typdelim, &typelem, &proc, &typalign);

      /* Allocate temporary array for new values */
      values = (Datum *) palloc(nitems * sizeof(Datum));
--- 2021,2101 ----
      if (nitems <= 0)
          PG_RETURN_ARRAYTYPE_P(v);

!     /*
!      * We arrange to look up info about input and return element types only
!      * once per series of calls, assuming the element type doesn't change
!      * underneath us.
!      */
!     my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(am_extra));
!         my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
!
!         my_extra->inp_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         inp_extra = my_extra->inp_extra;
!         inp_extra->element_type = InvalidOid;
!
!         my_extra->ret_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         ret_extra = my_extra->ret_extra;
!         ret_extra->element_type = InvalidOid;
!     }
!     else
!     {
!         inp_extra = my_extra->inp_extra;
!         ret_extra = my_extra->ret_extra;
!     }
!
!     if (inp_extra->element_type != inpType)
!     {
!         /* Lookup source and result types. Unneeded variables are reused. */
!         get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
!                             &typdelim, &typelem, &proc, &inp_typalign);
!
!         inp_extra->element_type = inpType;
!         inp_extra->typlen = inp_typlen;
!         inp_extra->typbyval = inp_typbyval;
!         inp_extra->typdelim = typdelim;
!         inp_extra->typelem = typelem;
!         inp_extra->typiofunc = proc;
!         inp_extra->typalign = inp_typalign;
!     }
!     else
!     {
!         inp_typlen = inp_extra->typlen;
!         inp_typbyval = inp_extra->typbyval;
!         typdelim = inp_extra->typdelim;
!         typelem = inp_extra->typelem;
!         proc = inp_extra->typiofunc;
!         inp_typalign = inp_extra->typalign;
!     }
!
!     if (ret_extra->element_type != retType)
!     {
!         /* Lookup source and result types. Unneeded variables are reused. */
!         get_type_metadata(retType, IOFunc_input, &typlen, &typbyval,
!                             &typdelim, &typelem, &proc, &typalign);
!
!         ret_extra->element_type = retType;
!         ret_extra->typlen = typlen;
!         ret_extra->typbyval = typbyval;
!         ret_extra->typdelim = typdelim;
!         ret_extra->typelem = typelem;
!         ret_extra->typiofunc = proc;
!         ret_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = ret_extra->typlen;
!         typbyval = ret_extra->typbyval;
!         typdelim = ret_extra->typdelim;
!         typelem = ret_extra->typelem;
!         proc = ret_extra->typiofunc;
!         typalign = ret_extra->typalign;
!     }

      /* Allocate temporary array for new values */
      values = (Datum *) palloc(nitems * sizeof(Datum));
***************
*** 2049,2056 ****
   *          compares two arrays for equality
   * result :
   *          returns true if the arrays are equal, false otherwise.
-  *
-  * XXX bitwise equality is pretty bogus ...
   *-----------------------------------------------------------------------------
   */
  Datum
--- 2311,2316 ----
***************
*** 2058,2069 ****
  {
      ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
      ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
      bool        result = true;

!     if (ARR_SIZE(array1) != ARR_SIZE(array2))
!         result = false;
!     else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
          result = false;

      /* Avoid leaking memory when handed toasted input. */
      PG_FREE_IF_COPY(array1, 0);
--- 2318,2435 ----
  {
      ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
      ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
+     char       *p1 = (char *) ARR_DATA_PTR(array1);
+     char       *p2 = (char *) ARR_DATA_PTR(array2);
+     int            ndims1 = ARR_NDIM(array1);
+     int            ndims2 = ARR_NDIM(array2);
+     int           *dims1 = ARR_DIMS(array1);
+     int           *dims2 = ARR_DIMS(array2);
+     int            nitems1 = ArrayGetNItems(ndims1, dims1);
+     int            nitems2 = ArrayGetNItems(ndims2, dims2);
+     Oid            element_type = ARR_ELEMTYPE(array1);
+     FmgrInfo   *ae_fmgr_info = fcinfo->flinfo;
      bool        result = true;
+     int            typlen;
+     bool        typbyval;
+     char        typdelim;
+     Oid            typelem;
+     char        typalign;
+     Oid            typiofunc;
+     int            i;
+     ArrayMetaState *my_extra;
+     FunctionCallInfoData locfcinfo;

!     /* fast path if the arrays do not have the same number of elements */
!     if (nitems1 != nitems2)
          result = false;
+     else
+     {
+         /*
+          * We arrange to look up the equality function only once per series of
+          * calls, assuming the element type doesn't change underneath us.
+          */
+         my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
+         if (my_extra == NULL)
+         {
+             ae_fmgr_info->fn_extra = MemoryContextAlloc(ae_fmgr_info->fn_mcxt,
+                                                      sizeof(ArrayMetaState));
+             my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
+             my_extra->element_type = InvalidOid;
+         }
+
+         if (my_extra->element_type != element_type)
+         {
+             Oid        opfuncid = equality_oper_funcid(element_type);
+
+             if (OidIsValid(opfuncid))
+                 fmgr_info_cxt(opfuncid, &my_extra->proc, ae_fmgr_info->fn_mcxt);
+             else
+                 elog(ERROR,
+                      "array_eq: cannot find equality operator for type: %u",
+                      element_type);
+
+             get_type_metadata(element_type, IOFunc_output,
+                               &typlen, &typbyval, &typdelim,
+                               &typelem, &typiofunc, &typalign);
+
+             my_extra->element_type = element_type;
+             my_extra->typlen = typlen;
+             my_extra->typbyval = typbyval;
+             my_extra->typdelim = typdelim;
+             my_extra->typelem = typelem;
+             my_extra->typiofunc = typiofunc;
+             my_extra->typalign = typalign;
+         }
+         else
+         {
+             typlen = my_extra->typlen;
+             typbyval = my_extra->typbyval;
+             typdelim = my_extra->typdelim;
+             typelem = my_extra->typelem;
+             typiofunc = my_extra->typiofunc;
+             typalign = my_extra->typalign;
+         }
+
+         /*
+          * apply the operator to each pair of array elements.
+          */
+         MemSet(&locfcinfo, 0, sizeof(locfcinfo));
+         locfcinfo.flinfo = &my_extra->proc;
+         locfcinfo.nargs = 2;
+
+         /* Loop over source data */
+         for (i = 0; i < nitems1; i++)
+         {
+             Datum    elt1;
+             Datum    elt2;
+             bool    oprresult;
+
+             /* Get element pair */
+             elt1 = fetch_att(p1, typbyval, typlen);
+             elt2 = fetch_att(p2, typbyval, typlen);
+
+             p1 = att_addlength(p1, typlen, PointerGetDatum(p1));
+             p1 = (char *) att_align(p1, typalign);
+
+             p2 = att_addlength(p2, typlen, PointerGetDatum(p2));
+             p2 = (char *) att_align(p2, typalign);
+
+             /*
+              * Apply the operator to the element pair
+              */
+             locfcinfo.arg[0] = elt1;
+             locfcinfo.arg[1] = elt2;
+             locfcinfo.argnull[0] = false;
+             locfcinfo.argnull[1] = false;
+             locfcinfo.isnull = false;
+             oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+             if (!oprresult)
+             {
+                 result = false;
+                 break;
+             }
+         }
+     }

      /* Avoid leaking memory when handed toasted input. */
      PG_FREE_IF_COPY(array1, 0);
***************
*** 2073,2125 ****
  }


! /***************************************************************************/
! /******************|          Support  Routines              |*****************/
! /***************************************************************************/

! static void
! system_cache_lookup(Oid element_type,
!                     IOFuncSelector which_func,
!                     int *typlen,
!                     bool *typbyval,
!                     char *typdelim,
!                     Oid *typelem,
!                     Oid *proc,
!                     char *typalign)
! {
!     HeapTuple    typeTuple;
!     Form_pg_type typeStruct;
!
!     typeTuple = SearchSysCache(TYPEOID,
!                                ObjectIdGetDatum(element_type),
!                                0, 0, 0);
!     if (!HeapTupleIsValid(typeTuple))
!         elog(ERROR, "cache lookup failed for type %u", element_type);
!     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
!
!     *typlen = typeStruct->typlen;
!     *typbyval = typeStruct->typbyval;
!     *typdelim = typeStruct->typdelim;
!     *typelem = typeStruct->typelem;
!     *typalign = typeStruct->typalign;
!     switch (which_func)
!     {
!         case IOFunc_input:
!             *proc = typeStruct->typinput;
!             break;
!         case IOFunc_output:
!             *proc = typeStruct->typoutput;
!             break;
!         case IOFunc_receive:
!             *proc = typeStruct->typreceive;
!             break;
!         case IOFunc_send:
!             *proc = typeStruct->typsend;
!             break;
      }
!     ReleaseSysCache(typeTuple);
  }

  /*
   * Fetch array element at pointer, converted correctly to a Datum
   */
--- 2439,2628 ----
  }


! /*-----------------------------------------------------------------------------
!  * array-array bool operators:
!  *        Given two arrays, iterate comparison operators
!  *        over the array. Uses logic similar to text comparison
!  *        functions, except element-by-element instead of
!  *        character-by-character.
!  *----------------------------------------------------------------------------
!  */
! Datum
! array_ne(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
! }

! Datum
! array_lt(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
! }
!
! Datum
! array_gt(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
! }
!
! Datum
! array_le(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
! }
!
! Datum
! array_ge(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
! }
!
! Datum
! btarraycmp(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_INT32(array_cmp(fcinfo));
! }
!
! /*
!  * array_cmp()
!  * Internal comparison function for arrays.
!  *
!  * Returns -1, 0 or 1
!  */
! static int
! array_cmp(FunctionCallInfo fcinfo)
! {
!     ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
!     ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
!     FmgrInfo   *ac_fmgr_info = fcinfo->flinfo;
!     Datum        opresult;
!     int            result = 0;
!     Oid            element_type = InvalidOid;
!     int            typlen;
!     bool        typbyval;
!     char        typdelim;
!     Oid            typelem;
!     char        typalign;
!     Oid            typiofunc;
!     Datum       *dvalues1;
!     int            nelems1;
!     Datum       *dvalues2;
!     int            nelems2;
!     int            min_nelems;
!     int            i;
!     typedef struct
!     {
!         Oid                element_type;
!         int                typlen;
!         bool            typbyval;
!         char            typdelim;
!         Oid                typelem;
!         Oid                typiofunc;
!         char            typalign;
!         FmgrInfo        eqproc;
!         FmgrInfo        ordproc;
!     } ac_extra;
!     ac_extra *my_extra;
!
!     element_type = ARR_ELEMTYPE(array1);
!
!     /*
!      * We arrange to look up the element type operator function only once
!      * per series of calls, assuming the element type and opname don't
!      * change underneath us.
!      */
!     my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
!     if (my_extra == NULL)
!     {
!         ac_fmgr_info->fn_extra = MemoryContextAlloc(ac_fmgr_info->fn_mcxt,
!                                                          sizeof(ac_extra));
!         my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         Oid        eqfuncid = equality_oper_funcid(element_type);
!         Oid        ordfuncid = ordering_oper_funcid(element_type);
!
!         fmgr_info_cxt(eqfuncid, &my_extra->eqproc, ac_fmgr_info->fn_mcxt);
!         fmgr_info_cxt(ordfuncid, &my_extra->ordproc, ac_fmgr_info->fn_mcxt);
!
!         if (my_extra->eqproc.fn_nargs != 2)
!             elog(ERROR, "Equality operator does not take 2 arguments: %u",
!                                                                  eqfuncid);
!         if (my_extra->ordproc.fn_nargs != 2)
!             elog(ERROR, "Ordering operator does not take 2 arguments: %u",
!                                                                  ordfuncid);
!
!         get_type_metadata(element_type, IOFunc_output,
!                           &typlen, &typbyval, &typdelim,
!                           &typelem, &typiofunc, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = InvalidOid;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }
!
!     /* extract a C array of arg array datums */
!     deconstruct_array(array1, element_type, typlen, typbyval, typalign,
!                                                     &dvalues1, &nelems1);
!
!     deconstruct_array(array2, element_type, typlen, typbyval, typalign,
!                                                     &dvalues2, &nelems2);
!
!     min_nelems = Min(nelems1, nelems2);
!     for (i = 0; i < min_nelems; i++)
!     {
!         /* are they equal */
!         opresult = FunctionCall2(&my_extra->eqproc,
!                                  dvalues1[i], dvalues2[i]);
!
!         if (!DatumGetBool(opresult))
!         {
!             /* nope, see if arg1 is less than arg2 */
!             opresult = FunctionCall2(&my_extra->ordproc,
!                                      dvalues1[i], dvalues2[i]);
!             if (DatumGetBool(opresult))
!             {
!                 /* arg1 is less than arg2 */
!                 result = -1;
!                 break;
!             }
!             else
!             {
!                 /* arg1 is greater than arg2 */
!                 result = 1;
!                 break;
!             }
!         }
      }
!
!     if ((result == 0) && (nelems1 != nelems2))
!         result = (nelems1 < nelems2) ? -1 : 1;
!
!     /* Avoid leaking memory when handed toasted input. */
!     PG_FREE_IF_COPY(array1, 0);
!     PG_FREE_IF_COPY(array2, 1);
!
!     return result;
  }

+
+ /***************************************************************************/
+ /******************|          Support  Routines              |*****************/
+ /***************************************************************************/
+
  /*
   * Fetch array element at pointer, converted correctly to a Datum
   */
***************
*** 2423,2428 ****
--- 2926,2943 ----
          if (tgt_elem_type == InvalidOid)
              elog(ERROR, "Target type is not an array");

+         /*
+          * We don't deal with domain constraints yet, so bail out.
+          * This isn't currently a problem, because we also don't
+          * support arrays of domain type elements either. But in the
+          * future we might. At that point consideration should be given
+          * to removing the check below and adding a domain constraints
+          * check to the coercion.
+          */
+         if (getBaseType(tgt_elem_type) != tgt_elem_type)
+             elog(ERROR, "array coercion to domain type elements not " \
+                         "currently supported");
+
          if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
                                     COERCION_EXPLICIT, &funcId))
          {
***************
*** 2439,2448 ****
      }

      /*
!      * If it's binary-compatible, return the array unmodified.
       */
      if (my_extra->coerce_finfo.fn_oid == InvalidOid)
!         PG_RETURN_ARRAYTYPE_P(src);

      /*
       * Use array_map to apply the function to each array element.
--- 2954,2969 ----
      }

      /*
!      * If it's binary-compatible, modify the element type in the array header,
!      * but otherwise leave the array as we received it.
       */
      if (my_extra->coerce_finfo.fn_oid == InvalidOid)
!     {
!         ArrayType  *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
!
!         ARR_ELEMTYPE(result) = my_extra->desttype;
!         PG_RETURN_ARRAYTYPE_P(result);
!     }

      /*
       * Use array_map to apply the function to each array element.
***************
*** 2453,2456 ****
--- 2974,3092 ----
      locfcinfo.arg[0] = PointerGetDatum(src);

      return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
+ }
+
+ /*
+  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
+  *
+  *    astate is working state (NULL on first call)
+  *    rcontext is where to keep working state
+  */
+ ArrayBuildState *
+ accumArrayResult(ArrayBuildState *astate,
+                  Datum dvalue, bool disnull,
+                  Oid element_type,
+                  MemoryContext rcontext)
+ {
+     MemoryContext arr_context,
+                   oldcontext;
+
+     if (astate == NULL)
+     {
+         /* First time through --- initialize */
+
+         /* Make a temporary context to hold all the junk */
+         arr_context = AllocSetContextCreate(rcontext,
+                                             "accumArrayResult",
+                                             ALLOCSET_DEFAULT_MINSIZE,
+                                             ALLOCSET_DEFAULT_INITSIZE,
+                                             ALLOCSET_DEFAULT_MAXSIZE);
+         oldcontext = MemoryContextSwitchTo(arr_context);
+         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
+         astate->mcontext = arr_context;
+         astate->dvalues = (Datum *)
+             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
+         astate->nelems = 0;
+         astate->element_type = element_type;
+         get_typlenbyvalalign(element_type,
+                              &astate->typlen,
+                              &astate->typbyval,
+                              &astate->typalign);
+     }
+     else
+     {
+         oldcontext = MemoryContextSwitchTo(astate->mcontext);
+         Assert(astate->element_type == element_type);
+         /* enlarge dvalues[] if needed */
+         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
+             astate->dvalues = (Datum *)
+                 repalloc(astate->dvalues,
+                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
+     }
+
+     if (disnull)
+         elog(ERROR, "NULL elements not allowed in Arrays");
+
+     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
+     astate->dvalues[astate->nelems++] =
+         datumCopy(dvalue, astate->typbyval, astate->typlen);
+
+     MemoryContextSwitchTo(oldcontext);
+
+     return astate;
+ }
+
+ /*
+  * makeArrayResult - produce final result of accumArrayResult
+  *
+  *    astate is working state (not NULL)
+  *    rcontext is where to construct result
+  */
+ Datum
+ makeArrayResult(ArrayBuildState *astate,
+                 MemoryContext rcontext)
+ {
+     int            dims[1];
+     int            lbs[1];
+
+     dims[0] = astate->nelems;
+     lbs[0] = 1;
+
+     return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
+ }
+
+ /*
+  * makeMdArrayResult - produce md final result of accumArrayResult
+  *
+  *    astate is working state (not NULL)
+  *    rcontext is where to construct result
+  */
+ Datum
+ makeMdArrayResult(ArrayBuildState *astate,
+                 int ndims,
+                 int *dims,
+                 int *lbs,
+                 MemoryContext rcontext)
+ {
+     ArrayType  *result;
+     MemoryContext oldcontext;
+
+     /* Build the final array result in rcontext */
+     oldcontext = MemoryContextSwitchTo(rcontext);
+
+     result = construct_md_array(astate->dvalues,
+                                 ndims,
+                                 dims,
+                                 lbs,
+                                 astate->element_type,
+                                 astate->typlen,
+                                 astate->typbyval,
+                                 astate->typalign);
+
+     MemoryContextSwitchTo(oldcontext);
+
+     /* Clean up all the junk */
+     MemoryContextDelete(astate->mcontext);
+
+     return PointerGetDatum(result);
  }
Index: src/backend/utils/adt/varlena.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/varlena.c,v
retrieving revision 1.98
diff -c -r1.98 varlena.c
*** src/backend/utils/adt/varlena.c    15 May 2003 15:50:19 -0000    1.98
--- src/backend/utils/adt/varlena.c    9 Jun 2003 01:31:10 -0000
***************
*** 19,29 ****
--- 19,32 ----
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
  #include "access/tuptoaster.h"
+ #include "catalog/pg_type.h"
  #include "lib/stringinfo.h"
  #include "libpq/crypt.h"
  #include "libpq/pqformat.h"
+ #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/pg_locale.h"
+ #include "utils/lsyscache.h"


  typedef struct varlena unknown;
***************
*** 1983,1990 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

--- 1986,1992 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

***************
*** 2004,2011 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
--- 2006,2012 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
***************
*** 2026,2031 ****
--- 2027,2217 ----
          result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn -
fldsep_len,false); 
          PG_RETURN_TEXT_P(result_text);
      }
+ }
+
+ /*
+  * text_to_array
+  * parse input string
+  * return text array of elements
+  * based on provided field separator
+  */
+ Datum
+ text_to_array(PG_FUNCTION_ARGS)
+ {
+     text       *inputstring = PG_GETARG_TEXT_P(0);
+     int            inputstring_len = TEXTLEN(inputstring);
+     text       *fldsep = PG_GETARG_TEXT_P(1);
+     int            fldsep_len = TEXTLEN(fldsep);
+     int            fldnum;
+     int            start_posn = 0;
+     int            end_posn = 0;
+     text       *result_text = NULL;
+     ArrayBuildState *astate = NULL;
+     MemoryContext oldcontext = CurrentMemoryContext;
+
+     /* return NULL for empty input string */
+     if (inputstring_len < 1)
+         PG_RETURN_NULL();
+
+     /* empty field separator
+      * return one element, 1D, array using the input string */
+     if (fldsep_len < 1)
+         PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+                               CStringGetDatum(inputstring), 1));
+
+     /* start with end position holding the initial start position */
+     end_posn = 0;
+     for (fldnum=1;;fldnum++)    /* field number is 1 based */
+     {
+         Datum    dvalue;
+         bool    disnull = false;
+
+         start_posn = end_posn;
+         end_posn = text_position(PointerGetDatum(inputstring),
+                                  PointerGetDatum(fldsep),
+                                  fldnum);
+
+         if ((start_posn == 0) && (end_posn == 0))    /* fldsep not found */
+         {
+             if (fldnum == 1)
+             {
+                 /* first element
+                  * return one element, 1D, array using the input string */
+                 PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+                                       CStringGetDatum(inputstring), 1));
+             }
+             else
+             {
+                 /* otherwise create array and exit */
+                 PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
+             }
+         }
+         else if ((start_posn != 0) && (end_posn == 0))
+         {
+             /* last field requested */
+             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
+         }
+         else if ((start_posn == 0) && (end_posn != 0))
+         {
+             /* first field requested */
+             result_text = LEFT(inputstring, fldsep);
+         }
+         else
+         {
+             /* prior to last field requested */
+             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn
-fldsep_len, false); 
+         }
+
+         /* stash away current value */
+         dvalue = PointerGetDatum(result_text);
+         astate = accumArrayResult(astate, dvalue,
+                                   disnull, TEXTOID, oldcontext);
+
+     }
+
+     /* never reached -- keep compiler quiet */
+     PG_RETURN_NULL();
+ }
+
+ /*
+  * array_to_text
+  * concatenate Cstring representation of input array elements
+  * using provided field separator
+  */
+ Datum
+ array_to_text(PG_FUNCTION_ARGS)
+ {
+     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
+     char       *fldsep = PG_TEXTARG_GET_STR(1);
+     int            nitems, *dims, ndims;
+     char       *p;
+     Oid            element_type;
+     int            typlen;
+     bool        typbyval;
+     char        typdelim;
+     Oid            typoutput,
+                 typelem;
+     FmgrInfo    outputproc;
+     char        typalign;
+     StringInfo    result_str = makeStringInfo();
+     int            i;
+     ArrayMetaState *my_extra;
+
+     p = ARR_DATA_PTR(v);
+     ndims = ARR_NDIM(v);
+     dims = ARR_DIMS(v);
+     nitems = ArrayGetNItems(ndims, dims);
+
+     /* if there are no elements, return an empty string */
+     if (nitems == 0)
+         PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
+
+     element_type = ARR_ELEMTYPE(v);
+
+     /*
+      * We arrange to look up info about element type, including its output
+      * conversion proc only once per series of calls, assuming the element
+      * type doesn't change underneath us.
+      */
+     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+     if (my_extra == NULL)
+     {
+         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+                                                      sizeof(ArrayMetaState));
+         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+         my_extra->element_type = InvalidOid;
+     }
+
+     if (my_extra->element_type != element_type)
+     {
+         /* Get info about element type, including its output conversion proc */
+         get_type_metadata(element_type, IOFunc_output,
+                             &typlen, &typbyval, &typdelim,
+                             &typelem, &typoutput, &typalign);
+         fmgr_info(typoutput, &outputproc);
+
+         my_extra->element_type = element_type;
+         my_extra->typlen = typlen;
+         my_extra->typbyval = typbyval;
+         my_extra->typdelim = typdelim;
+         my_extra->typelem = typelem;
+         my_extra->typiofunc = typoutput;
+         my_extra->typalign = typalign;
+         my_extra->proc = outputproc;
+     }
+     else
+     {
+         typlen = my_extra->typlen;
+         typbyval = my_extra->typbyval;
+         typdelim = my_extra->typdelim;
+         typelem = my_extra->typelem;
+         typoutput = my_extra->typiofunc;
+         typalign = my_extra->typalign;
+         outputproc = my_extra->proc;
+     }
+
+     for (i = 0; i < nitems; i++)
+     {
+         Datum        itemvalue;
+         char       *value;
+
+         itemvalue = fetch_att(p, typbyval, typlen);
+
+         value = DatumGetCString(FunctionCall3(&outputproc,
+                                               itemvalue,
+                                               ObjectIdGetDatum(typelem),
+                                               Int32GetDatum(-1)));
+
+         if (i > 0)
+             appendStringInfo(result_str, "%s%s", fldsep, value);
+         else
+             appendStringInfo(result_str, "%s", value);
+
+         p = att_addlength(p, typlen, PointerGetDatum(p));
+         p = (char *) att_align(p, typalign);
+     }
+
+     PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
  }

  #define HEXBASE 16
Index: src/backend/utils/cache/lsyscache.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v
retrieving revision 1.95
diff -c -r1.95 lsyscache.c
*** src/backend/utils/cache/lsyscache.c    26 May 2003 00:11:27 -0000    1.95
--- src/backend/utils/cache/lsyscache.c    9 Jun 2003 01:31:10 -0000
***************
*** 625,630 ****
--- 625,664 ----
  }

  /*
+  * get_func_argtypes
+  *        Given procedure id, return the function's argument types.
+  *        Also pass back the number of arguments.
+  */
+ Oid *
+ get_func_argtypes(Oid funcid, int *nargs)
+ {
+     HeapTuple        tp;
+     Form_pg_proc    procstruct;
+     Oid               *result = NULL;
+     int                i;
+
+     tp = SearchSysCache(PROCOID,
+                         ObjectIdGetDatum(funcid),
+                         0, 0, 0);
+     if (!HeapTupleIsValid(tp))
+         elog(ERROR, "Function OID %u does not exist", funcid);
+
+     procstruct = (Form_pg_proc) GETSTRUCT(tp);
+     *nargs = (int) procstruct->pronargs;
+
+     if (*nargs > 0)
+     {
+         result = (Oid *) palloc(*nargs * sizeof(Oid));
+
+         for (i = 0; i < *nargs; i++)
+             result[i] = procstruct->proargtypes[i];
+     }
+
+     ReleaseSysCache(tp);
+     return result;
+ }
+
+ /*
   * get_func_retset
   *        Given procedure id, return the function's proretset flag.
   */
***************
*** 994,999 ****
--- 1028,1083 ----
      *typbyval = typtup->typbyval;
      *typalign = typtup->typalign;
      ReleaseSysCache(tp);
+ }
+
+ /*
+  * get_type_metadata
+  *
+  *        A six-fer:    given the type OID, return typlen, typbyval, typalign,
+  *                    typdelim, typelem, IO function Oid. The IO function
+  *                    returned is controlled by IOFuncSelector
+  */
+ void
+ get_type_metadata(Oid element_type,
+                     IOFuncSelector which_func,
+                     int *typlen,
+                     bool *typbyval,
+                     char *typdelim,
+                     Oid *typelem,
+                     Oid *proc,
+                     char *typalign)
+ {
+     HeapTuple    typeTuple;
+     Form_pg_type typeStruct;
+
+     typeTuple = SearchSysCache(TYPEOID,
+                                ObjectIdGetDatum(element_type),
+                                0, 0, 0);
+     if (!HeapTupleIsValid(typeTuple))
+         elog(ERROR, "cache lookup failed for type %u", element_type);
+     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+
+     *typlen = typeStruct->typlen;
+     *typbyval = typeStruct->typbyval;
+     *typdelim = typeStruct->typdelim;
+     *typelem = typeStruct->typelem;
+     *typalign = typeStruct->typalign;
+     switch (which_func)
+     {
+         case IOFunc_input:
+             *proc = typeStruct->typinput;
+             break;
+         case IOFunc_output:
+             *proc = typeStruct->typoutput;
+             break;
+         case IOFunc_receive:
+             *proc = typeStruct->typreceive;
+             break;
+         case IOFunc_send:
+             *proc = typeStruct->typsend;
+             break;
+     }
+     ReleaseSysCache(typeTuple);
  }

  #ifdef NOT_USED
Index: src/backend/utils/fmgr/fmgr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/fmgr/fmgr.c,v
retrieving revision 1.68
diff -c -r1.68 fmgr.c
*** src/backend/utils/fmgr/fmgr.c    8 Apr 2003 23:20:02 -0000    1.68
--- src/backend/utils/fmgr/fmgr.c    9 Jun 2003 01:31:10 -0000
***************
*** 1673,1675 ****
--- 1673,1701 ----

      return exprType((Node *) nth(argnum, args));
  }
+
+ /*
+  * Get the OID of the function or operator
+  *
+  * Returns InvalidOid if information is not available
+  */
+ Oid
+ get_fn_expr_functype(FunctionCallInfo fcinfo)
+ {
+     Node   *expr;
+
+     /*
+      * can't return anything useful if we have no FmgrInfo or if
+      * its fn_expr node has not been initialized
+      */
+     if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
+         return InvalidOid;
+
+     expr = fcinfo->flinfo->fn_expr;
+     if (IsA(expr, FuncExpr))
+         return ((FuncExpr *) expr)->funcid;
+     else if (IsA(expr, OpExpr))
+         return ((OpExpr *) expr)->opno;
+     else
+         return InvalidOid;
+ }
Index: src/include/fmgr.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/fmgr.h,v
retrieving revision 1.27
diff -c -r1.27 fmgr.h
*** src/include/fmgr.h    8 Apr 2003 23:20:04 -0000    1.27
--- src/include/fmgr.h    9 Jun 2003 01:31:10 -0000
***************
*** 18,23 ****
--- 18,24 ----
  #ifndef FMGR_H
  #define FMGR_H

+ #include "nodes/nodes.h"

  /*
   * All functions that can be called directly by fmgr must have this signature.
***************
*** 372,385 ****
                   Datum arg6, Datum arg7, Datum arg8,
                   Datum arg9);

-
  /*
   * Routines in fmgr.c
   */
  extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
! extern Oid    fmgr_internal_function(const char *proname);
! extern Oid    get_fn_expr_rettype(FunctionCallInfo fcinfo);
! extern Oid    get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);

  /*
   * Routines in dfmgr.c
--- 373,386 ----
                   Datum arg6, Datum arg7, Datum arg8,
                   Datum arg9);

  /*
   * Routines in fmgr.c
   */
  extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
! extern Oid fmgr_internal_function(const char *proname);
! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo);
! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
! extern Oid get_fn_expr_functype(FunctionCallInfo fcinfo);

  /*
   * Routines in dfmgr.c
Index: src/include/catalog/pg_amop.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amop.h,v
retrieving revision 1.49
diff -c -r1.49 pg_amop.h
*** src/include/catalog/pg_amop.h    26 May 2003 00:11:27 -0000    1.49
--- src/include/catalog/pg_amop.h    9 Jun 2003 01:31:10 -0000
***************
*** 418,423 ****
--- 418,432 ----
  DATA(insert (    2098 4 f 2335 ));
  DATA(insert (    2098 5 f 2336 ));

+ /*
+  *    btree array_ops
+  */
+
+ DATA(insert (     397 1 f 1072 ));
+ DATA(insert (     397 2 f 1074 ));
+ DATA(insert (     397 3 f 1070 ));
+ DATA(insert (     397 4 f 1075 ));
+ DATA(insert (     397 5 f 1073 ));

  /*
   *    hash index _ops
Index: src/include/catalog/pg_amproc.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amproc.h,v
retrieving revision 1.37
diff -c -r1.37 pg_amproc.h
*** src/include/catalog/pg_amproc.h    26 May 2003 00:11:27 -0000    1.37
--- src/include/catalog/pg_amproc.h    9 Jun 2003 01:31:10 -0000
***************
*** 78,83 ****
--- 78,84 ----


  /* btree */
+ DATA(insert (     397 1  398 ));
  DATA(insert (     421 1    357 ));
  DATA(insert (     423 1 1596 ));
  DATA(insert (     424 1 1693 ));
Index: src/include/catalog/pg_opclass.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_opclass.h,v
retrieving revision 1.50
diff -c -r1.50 pg_opclass.h
*** src/include/catalog/pg_opclass.h    28 May 2003 16:04:00 -0000    1.50
--- src/include/catalog/pg_opclass.h    9 Jun 2003 02:48:16 -0000
***************
*** 87,92 ****
--- 87,94 ----
   */

  DATA(insert OID =  421 (    403        abstime_ops        PGNSP PGUID  702 t 0 ));
+ DATA(insert OID =  397 (    403        array_ops        PGNSP PGUID 2277 t 0 ));
+ #define ARRAY_BTREE_OPS_OID 397
  DATA(insert OID =  422 (    402        bigbox_ops        PGNSP PGUID  603 f 0 ));
  DATA(insert OID =  423 (    403        bit_ops            PGNSP PGUID 1560 t 0 ));
  DATA(insert OID =  424 (    403        bool_ops        PGNSP PGUID   16 t 0 ));
Index: src/include/catalog/pg_operator.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_operator.h,v
retrieving revision 1.114
diff -c -r1.114 pg_operator.h
*** src/include/catalog/pg_operator.h    26 May 2003 00:11:27 -0000    1.114
--- src/include/catalog/pg_operator.h    9 Jun 2003 01:31:10 -0000
***************
*** 116,125 ****
  DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
  DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 

! DATA(insert OID = 329 (  "="       PGNSP PGUID b f 2277 2277 16   329 0 0 0    0 0 array_eq     eqsel eqjoinsel ));
! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277   0 0 0 0    0 0 array_append   -       -     ));
! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277   0 0 0 0    0 0 array_prepend  -       -     ));
! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277   0 0 0 0    0 0 array_cat      -       -     ));

  DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
  DATA(insert OID = 353 (  "="       PGNSP PGUID b t    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
--- 116,130 ----
  DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
  DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 

! DATA(insert OID = 1070 (  "="       PGNSP PGUID b f 2277 2277 16 1070 1071  1072 1072 1072 1073 array_eq eqsel
eqjoinsel)); 
! DATA(insert OID = 1071 (  "<>"       PGNSP PGUID b f 2277 2277 16 1071 1070  0 0 0 0 array_ne neqsel neqjoinsel ));
! DATA(insert OID = 1072 (  "<"       PGNSP PGUID b f 2277 2277 16 1073 1075  0 0 0 0 array_lt scalarltsel
scalarltjoinsel)); 
! DATA(insert OID = 1073 (  ">"       PGNSP PGUID b f 2277 2277 16 1072 1074  0 0 0 0 array_gt scalargtsel
scalargtjoinsel)); 
! DATA(insert OID = 1074 (  "<="       PGNSP PGUID b f 2277 2277 16 1075 1073  0 0 0 0 array_le scalarltsel
scalarltjoinsel)); 
! DATA(insert OID = 1075 (  ">="       PGNSP PGUID b f 2277 2277 16 1074 1072  0 0 0 0 array_ge scalargtsel
scalargtjoinsel)); 
! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277 0 0  0 0 0 0 array_append   -       -     ));
! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277 0 0  0 0 0 0 array_prepend  -       -     ));
! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277 0 0  0 0 0 0 array_cat      -       -     ));

  DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
  DATA(insert OID = 353 (  "="       PGNSP PGUID b t    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
***************
*** 425,430 ****
--- 430,436 ----
  DATA(insert OID = 966 (  "+"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - ));
  DATA(insert OID = 967 (  "-"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclremove - - ));
  DATA(insert OID = 968 (  "~"       PGNSP PGUID b f 1034 1033     16 0 0 0 0 0 0 aclcontains - - ));
+ DATA(insert OID = 974 (  "="       PGNSP PGUID b f 1033 1033     16 0 0 0 0 0 0 aclitemeq eqsel eqjoinsel ));

  /* additional geometric operators - thomas 1997-07-09 */
  DATA(insert OID =  969 (  "@@"       PGNSP PGUID l f    0  601    600    0  0 0 0 0 0 lseg_center - - ));
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v
retrieving revision 1.302
diff -c -r1.302 pg_proc.h
*** src/include/catalog/pg_proc.h    26 May 2003 00:11:27 -0000    1.302
--- src/include/catalog/pg_proc.h    9 Jun 2003 01:52:28 -0000
***************
*** 758,763 ****
--- 758,765 ----
  DESCR("btree less-equal-greater");
  DATA(insert OID = 360 (  bttextcmp           PGNSP PGUID 12 f f t f i 2 23 "25 25"    bttextcmp - _null_ ));
  DESCR("btree less-equal-greater");
+ DATA(insert OID = 398 (  btarraycmp           PGNSP PGUID 12 f f t f i 2 23 "2277 2277"    btarraycmp - _null_ ));
+ DESCR("btree less-equal-greater");

  DATA(insert OID = 361 (  lseg_distance       PGNSP PGUID 12 f f t f i 2 701 "601 601"  lseg_distance - _null_ ));
  DESCR("distance between");
***************
*** 984,997 ****
  DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
  DESCR("greater-than-or-equal");

- DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
- DESCR("array equal");
-
  DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
  DESCR("current user name");
  DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
  DESCR("session user name");

  DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
  DESCR("array dimensions");
  DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
--- 986,1008 ----
  DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
  DESCR("greater-than-or-equal");

  DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
  DESCR("current user name");
  DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
  DESCR("session user name");

+ DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
+ DESCR("array equal");
+ DATA(insert OID = 390 (  array_ne           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ne - _null_ ));
+ DESCR("array not equal");
+ DATA(insert OID = 391 (  array_lt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_lt - _null_ ));
+ DESCR("array less than");
+ DATA(insert OID = 392 (  array_gt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_gt - _null_ ));
+ DESCR("array greater than");
+ DATA(insert OID = 393 (  array_le           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_le - _null_ ));
+ DESCR("array less than or equal");
+ DATA(insert OID = 396 (  array_ge           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ge - _null_ ));
+ DESCR("array greater than or equal");
  DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
  DESCR("array dimensions");
  DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
***************
*** 1002,1023 ****
  DESCR("array lower dimension");
  DATA(insert OID = 2092 (  array_upper       PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ ));
  DESCR("array upper dimension");
- DATA(insert OID = 377 (  singleton_array  PGNSP PGUID 12 f f t f i 1 2277 "2283" singleton_array - _null_ ));
- DESCR("create array from single element");
  DATA(insert OID = 378 (  array_append       PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ ));
  DESCR("append element onto end of array");
  DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
  DESCR("prepend element onto front of array");
- DATA(insert OID = 380 (  array_accum       PGNSP PGUID 12 f f f f i 2 2277 "2277 2283" array_accum - _null_ ));
- DESCR("push element onto end of array, creating array if needed");
- DATA(insert OID = 381 (  array_assign       PGNSP PGUID 12 f f t f i 3 2277 "2277 23 2283" array_assign - _null_ ));
- DESCR("assign specific array element");
- DATA(insert OID = 382 (  array_subscript  PGNSP PGUID 12 f f t f i 2 2283 "2277 23" array_subscript - _null_ ));
- DESCR("return specific array element");
  DATA(insert OID = 383 (  array_cat           PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ ));
  DESCR("concatenate two arrays");
  DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
  DESCR("coerce array type to another array type");

  DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
  DESCR("I/O");
--- 1013,1030 ----
  DESCR("array lower dimension");
  DATA(insert OID = 2092 (  array_upper       PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ ));
  DESCR("array upper dimension");
  DATA(insert OID = 378 (  array_append       PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ ));
  DESCR("append element onto end of array");
  DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
  DESCR("prepend element onto front of array");
  DATA(insert OID = 383 (  array_cat           PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ ));
  DESCR("concatenate two arrays");
  DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
  DESCR("coerce array type to another array type");
+ DATA(insert OID = 394 (  string_to_array   PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ ));
+ DESCR("split delimited text into text[]");
+ DATA(insert OID = 395 (  array_to_string   PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ ));
+ DESCR("concatenate array elements, using delimiter, into text");

  DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
  DESCR("I/O");
***************
*** 1318,1323 ****
--- 1325,1332 ----
  DESCR("remove ACL item");
  DATA(insert OID = 1037 (  aclcontains       PGNSP PGUID 12 f f t f s 2 16 "1034 1033"    aclcontains - _null_ ));
  DESCR("does ACL contain item?");
+ DATA(insert OID = 1062 (  aclitemeq           PGNSP PGUID 12 f f t f s 2 16 "1033 1033"    aclitem_eq - _null_ ));
+ DESCR("equality operator for ACL items");
  DATA(insert OID = 1038 (  seteval           PGNSP PGUID 12 f f t t v 1 23 "26"  seteval - _null_ ));
  DESCR("internal function supporting PostQuel-style sets");
  DATA(insert OID = 1044 (  bpcharin           PGNSP PGUID 12 f f t f i 3 1042 "2275 26 23" bpcharin - _null_ ));
Index: src/include/nodes/primnodes.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v
retrieving revision 1.82
diff -c -r1.82 primnodes.h
*** src/include/nodes/primnodes.h    6 May 2003 00:20:33 -0000    1.82
--- src/include/nodes/primnodes.h    9 Jun 2003 01:31:10 -0000
***************
*** 225,230 ****
--- 225,231 ----
      Expr       *target;            /* expression we are aggregating on */
      bool        aggstar;        /* TRUE if argument was really '*' */
      bool        aggdistinct;    /* TRUE if it's agg(DISTINCT ...) */
+     List       *args;            /* arguments to the aggregate */
  } Aggref;

  /* ----------------
***************
*** 357,371 ****
  /* ----------------
   * SubLink
   *
!  * A SubLink represents a subselect appearing in an expression, and in some
!  * cases also the combining operator(s) just above it.    The subLinkType
!  * indicates the form of the expression represented:
   *    EXISTS_SUBLINK        EXISTS(SELECT ...)
   *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
   *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
   *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
   *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
   *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
   * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
   * same length as the subselect's targetlist.  MULTIEXPR will *always* have
   * a list with more than one entry; if the subselect has just one target
--- 358,376 ----
  /* ----------------
   * SubLink
   *
!  * A SubLink represents a subselect, or an expression, appearing in an
!  * expression, and in some cases also the combining operator(s) just above
!  * it.    The subLinkType indicates the form of the expression represented:
   *    EXISTS_SUBLINK        EXISTS(SELECT ...)
   *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
   *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
   *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
   *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
   *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
+  * If an expression is used in place of the subselect, it is transformed
+  * into a simple "(SELECT expr)" in gram.y. This is to allow arrays to be
+  * used as if they were the result of a single column subselect. If the
+  * expression is scalar, it is treated as a one element array.
   * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
   * same length as the subselect's targetlist.  MULTIEXPR will *always* have
   * a list with more than one entry; if the subselect has just one target
***************
*** 414,419 ****
--- 419,426 ----
      SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
      bool        useOr;            /* TRUE to combine column results with
                                   * "OR" not "AND" */
+     bool        isExpr;            /* TRUE if the subselect is really derived
+                                  * from a single expression */
      List       *lefthand;        /* list of outer-query expressions on the
                                   * left */
      List       *operName;        /* originally specified operator name */
***************
*** 455,460 ****
--- 462,476 ----
      SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
      bool        useOr;            /* TRUE to combine column results with
                                   * "OR" not "AND" */
+     bool        isExpr;            /* TRUE if the subselect is really derived
+                                  * from a single expression */
+     /* runtime cache for single array expressions */
+     Oid            exprtype;        /* array and element type, and other info
+                                  * needed deconstruct the array */
+     Oid            elemtype;
+     int16        elmlen;
+     bool        elmbyval;
+     char        elmalign;
      /* The combining operators, transformed to executable expressions: */
      List       *exprs;            /* list of OpExpr expression trees */
      List       *paramIds;        /* IDs of Params embedded in the above */
Index: src/include/optimizer/clauses.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/optimizer/clauses.h,v
retrieving revision 1.63
diff -c -r1.63 clauses.h
*** src/include/optimizer/clauses.h    28 May 2003 16:04:02 -0000    1.63
--- src/include/optimizer/clauses.h    9 Jun 2003 01:31:10 -0000
***************
*** 28,33 ****
--- 28,36 ----
  extern Node *get_leftop(Expr *clause);
  extern Node *get_rightop(Expr *clause);

+ extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
+                                     CoercionForm funcformat, List *funcargs);
+
  extern bool not_clause(Node *clause);
  extern Expr *make_notclause(Expr *notclause);
  extern Expr *get_notclausearg(Expr *notclause);
Index: src/include/parser/parse_oper.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_oper.h,v
retrieving revision 1.25
diff -c -r1.25 parse_oper.h
*** src/include/parser/parse_oper.h    29 Apr 2003 22:13:11 -0000    1.25
--- src/include/parser/parse_oper.h    9 Jun 2003 01:31:10 -0000
***************
*** 44,49 ****
--- 44,50 ----
  /* Convenience routines for common calls on the above */
  extern Oid    compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError);
  extern Oid    equality_oper_funcid(Oid argtype);
+ extern Oid  ordering_oper_funcid(Oid argtype);
  extern Oid    ordering_oper_opid(Oid argtype);

  /* Extract operator OID or underlying-function OID from an Operator tuple */
Index: src/include/utils/acl.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/acl.h,v
retrieving revision 1.51
diff -c -r1.51 acl.h
*** src/include/utils/acl.h    23 Jan 2003 23:39:07 -0000    1.51
--- src/include/utils/acl.h    9 Jun 2003 01:31:10 -0000
***************
*** 188,193 ****
--- 188,194 ----
  extern Datum aclinsert(PG_FUNCTION_ARGS);
  extern Datum aclremove(PG_FUNCTION_ARGS);
  extern Datum aclcontains(PG_FUNCTION_ARGS);
+ extern Datum aclitem_eq(PG_FUNCTION_ARGS);

  /*
   * prototypes for functions in aclchk.c
Index: src/include/utils/array.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v
retrieving revision 1.38
diff -c -r1.38 array.h
*** src/include/utils/array.h    8 May 2003 22:19:57 -0000    1.38
--- src/include/utils/array.h    9 Jun 2003 01:47:03 -0000
***************
*** 32,37 ****
--- 32,68 ----
      Oid            elemtype;        /* element type OID */
  } ArrayType;

+ typedef struct ArrayBuildState
+ {
+     MemoryContext mcontext;        /* where all the temp stuff is kept */
+     Datum       *dvalues;        /* array of accumulated Datums */
+     /*
+      * The allocated size of dvalues[] is always a multiple of
+      * ARRAY_ELEMS_CHUNKSIZE
+      */
+ #define ARRAY_ELEMS_CHUNKSIZE    64
+     int            nelems;            /* number of valid Datums in dvalues[] */
+     Oid            element_type;    /* data type of the Datums */
+     int16        typlen;            /* needed info about datatype */
+     bool        typbyval;
+     char        typalign;
+ } ArrayBuildState;
+
+ /*
+  * structure to cache type metadata needed for array manipulation
+  */
+ typedef struct ArrayMetaState
+ {
+     Oid                element_type;
+     int                typlen;
+     bool            typbyval;
+     char            typdelim;
+     Oid                typelem;
+     Oid                typiofunc;
+     char            typalign;
+     FmgrInfo        proc;
+ } ArrayMetaState;
+
  /*
   * fmgr macros for array objects
   */
***************
*** 86,96 ****
  extern Datum array_send(PG_FUNCTION_ARGS);
  extern Datum array_length_coerce(PG_FUNCTION_ARGS);
  extern Datum array_eq(PG_FUNCTION_ARGS);
  extern Datum array_dims(PG_FUNCTION_ARGS);
  extern Datum array_lower(PG_FUNCTION_ARGS);
  extern Datum array_upper(PG_FUNCTION_ARGS);
- extern Datum array_assign(PG_FUNCTION_ARGS);
- extern Datum array_subscript(PG_FUNCTION_ARGS);
  extern Datum array_type_coerce(PG_FUNCTION_ARGS);

  extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
--- 117,131 ----
  extern Datum array_send(PG_FUNCTION_ARGS);
  extern Datum array_length_coerce(PG_FUNCTION_ARGS);
  extern Datum array_eq(PG_FUNCTION_ARGS);
+ extern Datum array_ne(PG_FUNCTION_ARGS);
+ extern Datum array_lt(PG_FUNCTION_ARGS);
+ extern Datum array_gt(PG_FUNCTION_ARGS);
+ extern Datum array_le(PG_FUNCTION_ARGS);
+ extern Datum array_ge(PG_FUNCTION_ARGS);
+ extern Datum btarraycmp(PG_FUNCTION_ARGS);
  extern Datum array_dims(PG_FUNCTION_ARGS);
  extern Datum array_lower(PG_FUNCTION_ARGS);
  extern Datum array_upper(PG_FUNCTION_ARGS);
  extern Datum array_type_coerce(PG_FUNCTION_ARGS);

  extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
***************
*** 124,130 ****
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
!

  /*
   * prototypes for functions defined in arrayutils.c
--- 159,172 ----
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
! extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
!                                          Datum dvalue, bool disnull,
!                                          Oid element_type,
!                                          MemoryContext rcontext);
! extern Datum makeArrayResult(ArrayBuildState *astate,
!                              MemoryContext rcontext);
! extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
!                                int *dims, int *lbs, MemoryContext rcontext);

  /*
   * prototypes for functions defined in arrayutils.c
***************
*** 141,152 ****
  /*
   * prototypes for functions defined in array_userfuncs.c
   */
- extern Datum singleton_array(PG_FUNCTION_ARGS);
  extern Datum array_push(PG_FUNCTION_ARGS);
- extern Datum array_accum(PG_FUNCTION_ARGS);
  extern Datum array_cat(PG_FUNCTION_ARGS);

! extern ArrayType *create_singleton_array(Oid element_type,
                                           Datum element,
                                           int ndims);

--- 183,193 ----
  /*
   * prototypes for functions defined in array_userfuncs.c
   */
  extern Datum array_push(PG_FUNCTION_ARGS);
  extern Datum array_cat(PG_FUNCTION_ARGS);

! extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
!                                          Oid element_type,
                                           Datum element,
                                           int ndims);

Index: src/include/utils/builtins.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/builtins.h,v
retrieving revision 1.219
diff -c -r1.219 builtins.h
*** src/include/utils/builtins.h    26 May 2003 00:11:28 -0000    1.219
--- src/include/utils/builtins.h    9 Jun 2003 01:31:10 -0000
***************
*** 530,535 ****
--- 530,537 ----
                        List **namelist);
  extern Datum replace_text(PG_FUNCTION_ARGS);
  extern Datum split_text(PG_FUNCTION_ARGS);
+ extern Datum text_to_array(PG_FUNCTION_ARGS);
+ extern Datum array_to_text(PG_FUNCTION_ARGS);
  extern Datum to_hex32(PG_FUNCTION_ARGS);
  extern Datum to_hex64(PG_FUNCTION_ARGS);
  extern Datum md5_text(PG_FUNCTION_ARGS);
Index: src/include/utils/lsyscache.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v
retrieving revision 1.70
diff -c -r1.70 lsyscache.h
*** src/include/utils/lsyscache.h    26 May 2003 00:11:28 -0000    1.70
--- src/include/utils/lsyscache.h    9 Jun 2003 01:31:10 -0000
***************
*** 15,20 ****
--- 15,29 ----

  #include "access/htup.h"

+ /* I/O function selector for system_cache_lookup */
+ typedef enum IOFuncSelector
+ {
+     IOFunc_input,
+     IOFunc_output,
+     IOFunc_receive,
+     IOFunc_send
+ } IOFuncSelector;
+
  extern bool op_in_opclass(Oid opno, Oid opclass);
  extern bool op_requires_recheck(Oid opno, Oid opclass);
  extern Oid    get_opclass_member(Oid opclass, int16 strategy);
***************
*** 39,44 ****
--- 48,54 ----
  extern RegProcedure get_oprjoin(Oid opno);
  extern char *get_func_name(Oid funcid);
  extern Oid    get_func_rettype(Oid funcid);
+ extern Oid *get_func_argtypes(Oid funcid, int *nargs);
  extern bool get_func_retset(Oid funcid);
  extern bool func_strict(Oid funcid);
  extern char func_volatile(Oid funcid);
***************
*** 54,59 ****
--- 64,77 ----
  extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
  extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
                       char *typalign);
+ extern void get_type_metadata(Oid element_type,
+                                 IOFuncSelector which_func,
+                                 int *typlen,
+                                 bool *typbyval,
+                                 char *typdelim,
+                                 Oid *typelem,
+                                 Oid *proc,
+                                 char *typalign);
  extern char get_typstorage(Oid typid);
  extern int32 get_typtypmod(Oid typid);
  extern Node *get_typdefault(Oid typid);
Index: src/interfaces/ecpg/preproc/preproc.y
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/preproc.y,v
retrieving revision 1.227
diff -c -r1.227 preproc.y
*** src/interfaces/ecpg/preproc/preproc.y    30 May 2003 13:22:02 -0000    1.227
--- src/interfaces/ecpg/preproc/preproc.y    9 Jun 2003 01:42:58 -0000
***************
*** 4549,4555 ****
                  $3.type_enum != ECPGt_char &&
                      $3.type_enum != ECPGt_unsigned_char &&
                  atoi(this->type->type_index) >= 0)
!                 mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");

              types = this;
          }
--- 4549,4555 ----
                  $3.type_enum != ECPGt_char &&
                      $3.type_enum != ECPGt_unsigned_char &&
                  atoi(this->type->type_index) >= 0)
!                 mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");

              types = this;
          }
***************
*** 5372,5378 ****
                      $5.type_enum != ECPGt_char &&
                      $5.type_enum != ECPGt_unsigned_char &&
                      atoi(this->type->type_index) >= 0)
!                     mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");

                  types = this;
              }
--- 5372,5378 ----
                      $5.type_enum != ECPGt_char &&
                      $5.type_enum != ECPGt_unsigned_char &&
                      atoi(this->type->type_index) >= 0)
!                     mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");

                  types = this;
              }
***************
*** 5439,5445 ****

                      default:
                          if (atoi(length) >= 0)
!                             mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data
types");

                          if (atoi(dimension) < 0)
                              type = ECPGmake_simple_type($5.type_enum, make_str("1"));
--- 5439,5445 ----

                      default:
                          if (atoi(length) >= 0)
!                             mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data
types");

                          if (atoi(dimension) < 0)
                              type = ECPGmake_simple_type($5.type_enum, make_str("1"));
Index: src/interfaces/ecpg/preproc/type.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/type.c,v
retrieving revision 1.51
diff -c -r1.51 type.c
*** src/interfaces/ecpg/preproc/type.c    29 May 2003 13:59:26 -0000    1.51
--- src/interfaces/ecpg/preproc/type.c    9 Jun 2003 01:42:58 -0000
***************
*** 493,499 ****
                  switch (type->u.element->type)
                  {
                      case ECPGt_array:
!                         yyerror("internal error, found multi-dimensional array\n");
                          break;
                      case ECPGt_struct:
                      case ECPGt_union:
--- 493,499 ----
                  switch (type->u.element->type)
                  {
                      case ECPGt_array:
!                         yyerror("internal error, found multidimensional array\n");
                          break;
                      case ECPGt_struct:
                      case ECPGt_union:
Index: src/interfaces/ecpg/preproc/variable.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/variable.c,v
retrieving revision 1.20
diff -c -r1.20 variable.c
*** src/interfaces/ecpg/preproc/variable.c    29 May 2003 13:59:26 -0000    1.20
--- src/interfaces/ecpg/preproc/variable.c    9 Jun 2003 01:42:58 -0000
***************
*** 405,411 ****
      if (atoi(type_index) >= 0)
      {
          if (atoi(*length) >= 0)
!             mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");

          *length = type_index;
      }
--- 405,411 ----
      if (atoi(type_index) >= 0)
      {
          if (atoi(*length) >= 0)
!             mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");

          *length = type_index;
      }
***************
*** 413,419 ****
      if (atoi(type_dimension) >= 0)
      {
          if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
!             mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");

          if (atoi(*dimension) >= 0)
              *length = *dimension;
--- 413,419 ----
      if (atoi(type_dimension) >= 0)
      {
          if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
!             mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");

          if (atoi(*dimension) >= 0)
              *length = *dimension;
***************
*** 432,441 ****
          mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");

      if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
!         mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");

      if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
!         mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");

      switch (type_enum)
      {
--- 432,441 ----
          mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");

      if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
!         mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");

      if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
!         mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");

      switch (type_enum)
      {
***************
*** 449,455 ****
              }

              if (atoi(*length) >= 0)
!                 mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for structures");

              break;
          case ECPGt_varchar:
--- 449,455 ----
              }

              if (atoi(*length) >= 0)
!                 mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for structures");

              break;
          case ECPGt_varchar:
***************
*** 494,500 ****
              }

              if (atoi(*length) >= 0)
!                 mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for simple data types");

              break;
      }
--- 494,500 ----
              }

              if (atoi(*length) >= 0)
!                 mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for simple data types");

              break;
      }
Index: src/test/regress/expected/arrays.out
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/test/regress/expected/arrays.out,v
retrieving revision 1.11
diff -c -r1.11 arrays.out
*** src/test/regress/expected/arrays.out    8 Apr 2003 23:20:04 -0000    1.11
--- src/test/regress/expected/arrays.out    9 Jun 2003 04:43:05 -0000
***************
*** 178,196 ****
  (1 row)

  -- functions
! SELECT singleton_array(42) AS "{42}";
!  {42}
! ------
!  {42}
! (1 row)
!
! SELECT array_append(singleton_array(42), 6) AS "{42,6}";
   {42,6}
  --------
   {42,6}
  (1 row)

! SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
   {6,42}
  --------
   {6,42}
--- 178,190 ----
  (1 row)

  -- functions
! SELECT array_append(array[42], 6) AS "{42,6}";
   {42,6}
  --------
   {42,6}
  (1 row)

! SELECT array_prepend(6, array[42]) AS "{6,42}";
   {6,42}
  --------
   {6,42}
***************
*** 212,235 ****
   {{3,4},{5,6},{1,2}}
  ---------------------
   {{3,4},{5,6},{1,2}}
- (1 row)
-
- SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
-  1.2
- -----
-  1.2
- (1 row)
-
- SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
-  {1.1,9.99,1.3}
- ----------------
-  {1.1,9.99,1.3}
- (1 row)
-
- SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
-  9.99
- ------
-  9.99
  (1 row)

  -- operators
--- 206,211 ----
Index: src/test/regress/sql/arrays.sql
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/test/regress/sql/arrays.sql,v
retrieving revision 1.10
diff -c -r1.10 arrays.sql
*** src/test/regress/sql/arrays.sql    8 Apr 2003 23:20:04 -0000    1.10
--- src/test/regress/sql/arrays.sql    9 Jun 2003 04:42:20 -0000
***************
*** 130,144 ****
  SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";

  -- functions
! SELECT singleton_array(42) AS "{42}";
! SELECT array_append(singleton_array(42), 6) AS "{42,6}";
! SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
  SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}";
  SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
  SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
- SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
- SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
- SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;

  -- operators
  SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
--- 130,140 ----
  SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";

  -- functions
! SELECT array_append(array[42], 6) AS "{42,6}";
! SELECT array_prepend(6, array[42]) AS "{6,42}";
  SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}";
  SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
  SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";

  -- operators
  SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];

Re: array support patch phase 1 patch

From
Bruce Momjian
Date:
Your patch has been added to the PostgreSQL unapplied patches list at:

    http://momjian.postgresql.org/cgi-bin/pgpatches

I will try to apply it within the next 48 hours.

---------------------------------------------------------------------------


Joe Conway wrote:
> Peter Eisentraut wrote:
> [...much snipping follows...]
> > No, the analogues would be "unidimensional", "bidimensional", and
> > "many-dimensional".  There difference is that you close up prefixes (which
> > are not words by themselves), but you hyphenate compounds of independent
> > words.
> >
> >>>The function array_subscript() should be removed from code and
> >>>The function array_assign() should be removed from code and documentation.
> >>>The function singleton_array() should be removed from code and
> >>
> >>>The function array_append() should be documented as being equivalent to
> >>>'array || element' and specifically intended for user-defined aggregate
> >>>functions.
> >>
> >>That's fine. I would add similar descriptions for array_prepend() and
> >>array_cat().
> >
> > Sounds good.  My main concern is that people will have a clear view of
> > what's standard and recommended for which situation, so that support will
> > be easier and users won't be confronted with a long list of equivalent,
> > undifferentiated options.
> >
>
> The attached patch addresses Peter's concerns, subject to our agreements
> above. I.e, the changes are:
>
> 1) All instances of "multi-dimensional array" and "multiple dimension
> array" throughout the source tree have been changed to "multidimensional
> array". Even if everyone doesn't agree this is correct, it is at least
> consistent throughout, so if we later decide to change it back, it will
> be easier ;-)
>
> 2) The functions singleton_array(), array_assign(), and
> array_subscript() have been removed along with all references thereof.
>
> 3) The documentation emphasizes use of the concatenation operator '||'
> as preferred over the functions array_append(), array_prepend(), and
> array_cat(). A mention of direct use of the functions in user-defined
> aggregates is also included.
>
> [...more snipage from a different part of the thread...]
> Tom Lane wrote:
>  > BTW, it might be better for array_cmp to insist that the array element
>  > type have a default btree opclass, and use the comparison proc from
>  > that opclass in place of equality_oper() and ordering_oper().  This'd
>  > be faster (only one call per comparison) as well as more semantically
>  > pure.
>
> I stopped short of this change because there doesn't seem to be a way to
> get the btree opclass tuple given just the opcintype without scanning
> pg_opclass. Am I missing something? Or should I just not worry about it
> and scan pg_opclass?
>
>  > I've got some other problems with this patch (I still don't understand
>  > what the hacking on aggregates is supposed to accomplish, for example)
>  > but the array comparison stuff seems relatively straightforward.
>
> As I said in an earlier message, I believe that I have implemented this
> correctly. Is there a specific corner case not covered? I'd really like
> to see some form of polymorphic function support for aggregates --
> please let me know what I can do to make that happen.
>
> Joe
>
> p.s. I added Bruce to the thread because he asked me about the status of
> the patch, and because the lists have been so slow the last few days.

> Index: doc/src/sgml/array.sgml
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
> retrieving revision 1.25
> diff -c -r1.25 array.sgml
> *** doc/src/sgml/array.sgml    13 Mar 2003 01:30:26 -0000    1.25
> --- doc/src/sgml/array.sgml    9 Jun 2003 02:13:32 -0000
> ***************
> *** 60,73 ****
>   </programlisting>
>    </para>
>
>    <note>
>     <para>
> !    A limitation of the present array implementation is that individual
> !    elements of an array cannot be SQL null values.  The entire array can be set
> !    to null, but you can't have an array with some elements null and some
> !    not.  Fixing this is on the to-do list.
>     </para>
>    </note>
>    </sect2>
>
>    <sect2>
> --- 60,133 ----
>   </programlisting>
>    </para>
>
> +  <para>
> +   A limitation of the present array implementation is that individual
> +   elements of an array cannot be SQL null values.  The entire array can be set
> +   to null, but you can't have an array with some elements null and some
> +   not.
> +  </para>
> +  <para>
> +   This can lead to surprising results. For example, the result of the
> +   previous two inserts looks like this:
> + <programlisting>
> + SELECT * FROM sal_emp;
> +  name  |      pay_by_quarter       |      schedule
> + -------+---------------------------+--------------------
> +  Bill  | {10000,10000,10000,10000} | {{meeting},{""}}
> +  Carol | {20000,25000,25000,25000} | {{talk},{meeting}}
> + (2 rows)
> + </programlisting>
> +   Because the <literal>[2][2]</literal> element of
> +   <structfield>schedule</structfield> is missing in each of the
> +   <command>INSERT</command> statements, the <literal>[1][2]</literal>
> +   element is discarded.
> +  </para>
> +
> +  <note>
> +   <para>
> +    Fixing this is on the to-do list.
> +   </para>
> +  </note>
> +
> +  <para>
> +   The <command>ARRAY</command> expression syntax may also be used:
> + <programlisting>
> + INSERT INTO sal_emp
> +     VALUES ('Bill',
> +     ARRAY[10000, 10000, 10000, 10000],
> +     ARRAY[['meeting', 'lunch'], ['','']]);
> +
> + INSERT INTO sal_emp
> +     VALUES ('Carol',
> +     ARRAY[20000, 25000, 25000, 25000],
> +     ARRAY[['talk', 'consult'], ['meeting', '']]);
> + SELECT * FROM sal_emp;
> +  name  |      pay_by_quarter       |           schedule
> + -------+---------------------------+-------------------------------
> +  Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}}
> +  Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}}
> + (2 rows)
> + </programlisting>
> +   Note that with this syntax, multidimensional arrays must have matching
> +   extents for each dimension. This eliminates the missing-array-elements
> +   problem above. For example:
> + <programlisting>
> + INSERT INTO sal_emp
> +     VALUES ('Carol',
> +     ARRAY[20000, 25000, 25000, 25000],
> +     ARRAY[['talk', 'consult'], ['meeting']]);
> + ERROR:  Multidimensional arrays must have array expressions with matching dimensions
> + </programlisting>
> +   Also notice that string literals are single quoted instead of double quoted.
> +  </para>
> +
>    <note>
>     <para>
> !    The examples in the rest of this section are based on the
> !    <command>ARRAY</command> expression syntax <command>INSERT</command>s.
>     </para>
>    </note>
> +
>    </sect2>
>
>    <sect2>
> ***************
> *** 132,142 ****
>   </programlisting>
>
>     with the same result.  An array subscripting operation is always taken to
> !   represent an array slice if any of the subscripts are written in the
> !   form
>     <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
>     A lower bound of 1 is assumed for any subscript where only one value
> !   is specified.
>    </para>
>
>    <para>
> --- 192,221 ----
>   </programlisting>
>
>     with the same result.  An array subscripting operation is always taken to
> !   represent an array slice if any of the subscripts are written in the form
>     <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
>     A lower bound of 1 is assumed for any subscript where only one value
> !   is specified; another example follows:
> ! <programlisting>
> ! SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
> !          schedule
> ! ---------------------------
> !  {{meeting,lunch},{"",""}}
> ! (1 row)
> ! </programlisting>
> !  </para>
> !
> !  <para>
> !   Additionally, we can also access a single arbitrary array element of
> !   a one-dimensional array with the <function>array_subscript</function>
> !   function:
> ! <programlisting>
> ! SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
> !  array_subscript
> ! -----------------
> !            10000
> ! (1 row)
> ! </programlisting>
>    </para>
>
>    <para>
> ***************
> *** 147,153 ****
>       WHERE name = 'Carol';
>   </programlisting>
>
> !   or updated at a single element:
>
>   <programlisting>
>   UPDATE sal_emp SET pay_by_quarter[4] = 15000
> --- 226,248 ----
>       WHERE name = 'Carol';
>   </programlisting>
>
> !   or using the <command>ARRAY</command> expression syntax:
> !
> ! <programlisting>
> ! UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
> !     WHERE name = 'Carol';
> ! </programlisting>
> !
> !   <note>
> !    <para>
> !     Anywhere you can use the <quote>curly braces</quote> array syntax,
> !     you can also use the <command>ARRAY</command> expression syntax. The
> !     remainder of this section will illustrate only one or the other, but
> !     not both.
> !    </para>
> !   </note>
> !
> !   An array may also be updated at a single element:
>
>   <programlisting>
>   UPDATE sal_emp SET pay_by_quarter[4] = 15000
> ***************
> *** 160,165 ****
> --- 255,268 ----
>   UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
>       WHERE name = 'Carol';
>   </programlisting>
> +
> +   A one-dimensional array may also be updated with the
> +   <function>array_assign</function> function:
> +
> + <programlisting>
> + UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000)
> +     WHERE name = 'Bill';
> + </programListing>
>    </para>
>
>    <para>
> ***************
> *** 179,184 ****
> --- 282,369 ----
>    </para>
>
>    <para>
> +   An array can also be enlarged by using the concatenation operator,
> +   <command>||</command>.
> + <programlisting>
> + SELECT ARRAY[1,2] || ARRAY[3,4];
> +    ?column?
> + ---------------
> +  {{1,2},{3,4}}
> + (1 row)
> +
> + SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
> +       ?column?
> + ---------------------
> +  {{5,6},{1,2},{3,4}}
> + (1 row)
> + </programlisting>
> +
> +   The concatenation operator allows a single element to be pushed on to the
> +   beginning or end of a one-dimensional array. It also allows two
> +   <replaceable>N</>-dimensional arrays, or an <replaceable>N</>-dimensional
> +   and an <replaceable>N+1</>-dimensional array. In the former case, the two
> +   <replaceable>N</>-dimension arrays become outer elements of an
> +   <replaceable>N+1</>-dimensional array. In the latter, the
> +   <replaceable>N</>-dimensional array is added as either the first or last
> +   outer element of the <replaceable>N+1</>-dimensional array.
> +
> +   The array is extended in the direction of the push. Hence, by pushing
> +   onto the beginning of an array with a one-based subscript, a zero-based
> +   subscript array is created:
> +
> + <programlisting>
> + SELECT array_dims(t.f) FROM (SELECT 1 || ARRAY[2,3] AS f) AS t;
> +  array_dims
> + ------------
> +  [0:2]
> + (1 row)
> + </programlisting>
> +  </para>
> +
> +  <para>
> +   An array can also be enlarged by using the functions
> +   <function>array_prepend</function>, <function>array_append</function>,
> +   or <function>array_cat</function>. The first two only support one-dimensional
> +   arrays, but <function>array_cat</function> supports multidimensional arrays.
> +
> +   Note that the concatenation operator discussed above is preferred over
> +   direct use of these functions. In fact, the functions are primarily for use
> +   in implementing the concatenation operator. However, they may be directly
> +   useful in the creation of user-defined aggregates. Some examples:
> +
> + <programlisting>
> + SELECT array_prepend(1, ARRAY[2,3]);
> +  array_prepend
> + ---------------
> +  {1,2,3}
> + (1 row)
> +
> + SELECT array_append(ARRAY[1,2], 3);
> +  array_append
> + --------------
> +  {1,2,3}
> + (1 row)
> +
> + SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
> +    array_cat
> + ---------------
> +  {{1,2},{3,4}}
> + (1 row)
> +
> + SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
> +       array_cat
> + ---------------------
> +  {{1,2},{3,4},{5,6}}
> + (1 row)
> +
> + SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
> +       array_cat
> + ---------------------
> +  {{5,6},{1,2},{3,4}}
> + </programlisting>
> +  </para>
> +
> +  <para>
>     The syntax for <command>CREATE TABLE</command> allows fixed-length
>     arrays to be defined:
>
> ***************
> *** 194,199 ****
> --- 379,394 ----
>    </para>
>
>    <para>
> +   An alternative syntax for one-dimensional arrays may be used.
> +   <structfield>pay_by_quarter</structfield> could have been defined as:
> + <programlisting>
> +     pay_by_quarter  integer ARRAY[4],
> + </programlisting>
> +   This syntax may <emphasis>only</emphasis> be used with the integer
> +   constant to denote the array size.
> +  </para>
> +
> +  <para>
>     Actually, the current implementation does not enforce the declared
>     number of dimensions either.  Arrays of a particular element type are
>     all considered to be of the same type, regardless of size or number
> ***************
> *** 300,305 ****
> --- 495,566 ----
>      is not ignored, however: after skipping leading whitespace, everything
>      up to the next right brace or delimiter is taken as the item value.
>     </para>
> +
> +   <para>
> +    As illustrated earlier in this chapter, arrays may also be represented
> +    using the <command>ARRAY</command> expression syntax. This representation
> +    of an array value consists of items that are interpreted according to the
> +    I/O conversion rules for the array's element type, plus decoration that
> +    indicates the array structure. The decoration consists of the keyword
> +    <command>ARRAY</command> and square brackets (<literal>[</> and
> +    <literal>]</>) around the array values, plus delimiter characters between
> +    adjacent items. The delimiter character is always a comma (<literal>,</>).
> +    When representing multidimensional arrays, the keyword
> +    <command>ARRAY</command> is only necessary for the outer level. For example,
> +    <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as:
> + <programlisting>
> + SELECT ARRAY[['hello world', 'happy birthday']];
> +                array
> + ------------------------------------
> +  {{"hello world","happy birthday"}}
> + (1 row)
> + </programlisting>
> +   or it also could be written as:
> + <programlisting>
> + SELECT ARRAY[ARRAY['hello world', 'happy birthday']];
> +                array
> + ------------------------------------
> +  {{"hello world","happy birthday"}}
> + (1 row)
> + </programlisting>
> +   </para>
> +
> +   <para>
> +    A final method to represent an array, is through an
> +    <command>ARRAY</command> sub-select expression. For example:
> + <programlisting>
> + SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
> +                           ?column?
> + -------------------------------------------------------------
> +  {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
> + (1 row)
> + </programlisting>
> +   The sub-select may <emphasis>only</emphasis> return a single column. The
> +   resulting one-dimensional array will have an element for each row in the
> +   sub-select result, with an element type matching that of the sub-select's
> +   target column.
> +   </para>
> +
> +   <para>
> +    Arrays may be cast from one type to another in similar fashion to other
> +    data types:
> +
> + <programlisting>
> + SELECT ARRAY[1,2,3]::oid[];
> +   array
> + ---------
> +  {1,2,3}
> + (1 row)
> +
> + SELECT CAST(ARRAY[1,2,3] AS float8[]);
> +   array
> + ---------
> +  {1,2,3}
> + (1 row)
> + </programlisting>
> +
> +   </para>
> +
>    </sect2>
>
>    <sect2>
> ***************
> *** 316,321 ****
> --- 577,590 ----
>      Alternatively, you can use backslash-escaping to protect all data characters
>      that would otherwise be taken as array syntax or ignorable white space.
>     </para>
> +
> +  <note>
> +   <para>
> +    The discussion in the preceding paragraph with respect to double quoting does
> +    not pertain to the <command>ARRAY</command> expression syntax. In that case,
> +    each element is quoted exactly as any other literal value of the element type.
> +   </para>
> +  </note>
>
>     <para>
>      The array output routine will put double quotes around element values
> Index: doc/src/sgml/func.sgml
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v
> retrieving revision 1.154
> diff -c -r1.154 func.sgml
> *** doc/src/sgml/func.sgml    5 May 2003 15:08:49 -0000    1.154
> --- doc/src/sgml/func.sgml    9 Jun 2003 01:53:35 -0000
> ***************
> *** 6962,6967 ****
> --- 6962,7164 ----
>
>     </sect1>
>
> +  <sect1 id="functions-array">
> +   <title>Array Functions</title>
> +
> +   <para>
> +    <xref linkend="array-operators-table"> shows the operators
> +    available for the <type>array</type> types.
> +   </para>
> +
> +     <table id="array-operators-table">
> +      <title><type>array</type> Operators</title>
> +      <tgroup cols="4">
> +       <thead>
> +        <row>
> +     <entry>Operator</entry>
> +     <entry>Description</entry>
> +     <entry>Example</entry>
> +     <entry>Result</entry>
> +        </row>
> +       </thead>
> +       <tbody>
> +        <row>
> +     <entry> <literal>=</literal> </entry>
> +     <entry>equals</entry>
> +     <entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry>
> +     <entry><literal>t</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>array-to-array concatenation</entry>
> +     <entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry>
> +     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>array-to-array concatenation</entry>
> +     <entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry>
> +     <entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>element-to-array concatenation</entry>
> +     <entry><literal>3 || ARRAY[4,5,6]</literal></entry>
> +     <entry><literal>{3,4,5,6}</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>array-to-element concatenation</entry>
> +     <entry><literal>ARRAY[4,5,6] || 7</literal></entry>
> +     <entry><literal>{4,5,6,7}</literal></entry>
> +        </row>
> +       </tbody>
> +      </tgroup>
> +     </table>
> +
> +   <para>
> +    <xref linkend="array-functions-table"> shows the functions
> +    available for use with array types. See <xref linkend="arrays">
> +    for more discussion and examples for the use of these functions.
> +   </para>
> +
> +     <table id="array-functions-table">
> +      <title><type>array</type> Functions</title>
> +      <tgroup cols="5">
> +       <thead>
> +        <row>
> +     <entry>Function</entry>
> +     <entry>Return Type</entry>
> +     <entry>Description</entry>
> +     <entry>Example</entry>
> +     <entry>Result</entry>
> +        </row>
> +       </thead>
> +       <tbody>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_append</function>
> +       (<type>anyarray</type>, <type>anyelement</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>anyarray</type></entry>
> +     <entry>
> +      append an element to the end of an array, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_append(ARRAY[1,2], 3)</literal></entry>
> +     <entry><literal>{1,2,3}</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_cat</function>
> +       (<type>anyarray</type>, <type>anyarray</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>anyarray</type></entry>
> +     <entry>
> +      concatenate two arrays, returning <literal>NULL</literal>
> +      for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry>
> +     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_dims</function>
> +       (<type>anyarray</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>text</type></entry>
> +     <entry>
> +      returns a text representation of array dimension lower and upper bounds,
> +      generating an ERROR for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry>
> +     <entry><literal>[1:2][1:3]</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_lower</function>
> +       (<type>anyarray</type>, <type>integer</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>integer</type></entry>
> +     <entry>
> +      returns lower bound of the requested array dimension, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry>
> +     <entry><literal>0</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_prepend</function>
> +       (<type>anyelement</type>, <type>anyarray</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>anyarray</type></entry>
> +     <entry>
> +      append an element to the beginning of an array, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry>
> +     <entry><literal>{1,2,3}</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_to_string</function>
> +       (<type>anyarray</type>, <type>text</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>text</type></entry>
> +     <entry>
> +      concatenates array elements using provided delimiter, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_to_string(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry>
> +     <entry><literal>1.10~^~2.20~^~3.30</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_upper</function>
> +       (<type>anyarray</type>, <type>integer</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>integer</type></entry>
> +     <entry>
> +      returns upper bound of the requested array dimension, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry>
> +     <entry><literal>4</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>string_to_array</function>
> +       (<type>text</type>, <type>text</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>text[]</type></entry>
> +     <entry>
> +      splits string into array elements using provided delimiter, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>string_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry>
> +     <entry><literal>{1.1,2.2,3.3}</literal></entry>
> +        </row>
> +       </tbody>
> +      </tgroup>
> +     </table>
> +   </sect1>
>
>    <sect1 id="functions-aggregate">
>     <title>Aggregate Functions</title>
> Index: src/backend/catalog/pg_aggregate.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_aggregate.c,v
> retrieving revision 1.56
> diff -c -r1.56 pg_aggregate.c
> *** src/backend/catalog/pg_aggregate.c    18 Sep 2002 21:35:20 -0000    1.56
> --- src/backend/catalog/pg_aggregate.c    9 Jun 2003 01:31:10 -0000
> ***************
> *** 50,59 ****
>       Oid            finalfn = InvalidOid;    /* can be omitted */
>       Oid            finaltype;
>       Oid            fnArgs[FUNC_MAX_ARGS];
> !     int            nargs;
>       Oid            procOid;
>       TupleDesc    tupDesc;
>       int            i;
>       ObjectAddress myself,
>                   referenced;
>
> --- 50,65 ----
>       Oid            finalfn = InvalidOid;    /* can be omitted */
>       Oid            finaltype;
>       Oid            fnArgs[FUNC_MAX_ARGS];
> !     int            nargs_transfn;
> !     int            nargs_finalfn;
>       Oid            procOid;
>       TupleDesc    tupDesc;
>       int            i;
> +     Oid            rettype;
> +     Oid           *true_oid_array_transfn;
> +     Oid           *true_oid_array_finalfn;
> +     bool        retset;
> +     FuncDetailCode fdresult;
>       ObjectAddress myself,
>                   referenced;
>
> ***************
> *** 68,91 ****
>       MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>       fnArgs[0] = aggTransType;
>       if (aggBaseType == ANYOID)
> !         nargs = 1;
>       else
>       {
>           fnArgs[1] = aggBaseType;
> !         nargs = 2;
>       }
> !     transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
>       if (!OidIsValid(transfn))
> !         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
>       tup = SearchSysCache(PROCOID,
>                            ObjectIdGetDatum(transfn),
>                            0, 0, 0);
>       if (!HeapTupleIsValid(tup))
> !         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
>       proc = (Form_pg_proc) GETSTRUCT(tup);
> -     if (proc->prorettype != aggTransType)
> -         elog(ERROR, "return type of transition function %s is not %s",
> -          NameListToString(aggtransfnName), format_type_be(aggTransType));
>
>       /*
>        * If the transfn is strict and the initval is NULL, make sure input
> --- 74,122 ----
>       MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>       fnArgs[0] = aggTransType;
>       if (aggBaseType == ANYOID)
> !         nargs_transfn = 1;
>       else
>       {
>           fnArgs[1] = aggBaseType;
> !         nargs_transfn = 2;
>       }
> !
> !     /*
> !      * func_get_detail looks up the function in the catalogs, does
> !      * disambiguation for polymorphic functions, handles inheritance, and
> !      * returns the funcid and type and set or singleton status of the
> !      * function's return value.  it also returns the true argument types
> !      * to the function.
> !      */
> !     fdresult = func_get_detail(aggtransfnName, NIL, nargs_transfn, fnArgs,
> !                                &transfn, &rettype, &retset,
> !                                &true_oid_array_transfn);
> !
> !     /* only valid case is a normal function */
> !     if (fdresult != FUNCDETAIL_NORMAL)
> !         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
> !
>       if (!OidIsValid(transfn))
> !         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
> !
> !     /*
> !      * enforce consistency with ANYARRAY and ANYELEMENT argument
> !      * and return types, possibly modifying return type along the way
> !      */
> !     rettype = enforce_generic_type_consistency(fnArgs, true_oid_array_transfn,
> !                                                        nargs_transfn, rettype);
> !
> !     if (rettype != aggTransType)
> !         elog(ERROR, "return type of transition function %s is not %s",
> !          NameListToString(aggtransfnName), format_type_be(aggTransType));
> !
>       tup = SearchSysCache(PROCOID,
>                            ObjectIdGetDatum(transfn),
>                            0, 0, 0);
>       if (!HeapTupleIsValid(tup))
> !         func_error("AggregateCreate", aggtransfnName,
> !                         nargs_transfn, fnArgs, NULL);
>       proc = (Form_pg_proc) GETSTRUCT(tup);
>
>       /*
>        * If the transfn is strict and the initval is NULL, make sure input
> ***************
> *** 105,121 ****
>       {
>           MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>           fnArgs[0] = aggTransType;
> !         finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
> !         if (!OidIsValid(finalfn))
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !         tup = SearchSysCache(PROCOID,
> !                              ObjectIdGetDatum(finalfn),
> !                              0, 0, 0);
> !         if (!HeapTupleIsValid(tup))
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !         proc = (Form_pg_proc) GETSTRUCT(tup);
> !         finaltype = proc->prorettype;
> !         ReleaseSysCache(tup);
>       }
>       else
>       {
> --- 136,161 ----
>       {
>           MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>           fnArgs[0] = aggTransType;
> !         nargs_finalfn = 1;
> !
> !         fdresult = func_get_detail(aggfinalfnName, NIL, 1, fnArgs,
> !                                    &finalfn, &rettype, &retset,
> !                                    &true_oid_array_finalfn);
> !
> !         /* only valid case is a normal function */
> !         if (fdresult != FUNCDETAIL_NORMAL)
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !
> !         if (!OidIsValid(finalfn))
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !
> !         /*
> !          * enforce consistency with ANYARRAY and ANYELEMENT argument
> !          * and return types, possibly modifying return type along the way
> !          */
> !         finaltype = enforce_generic_type_consistency(fnArgs,
> !                                                      true_oid_array_finalfn,
> !                                                      nargs_finalfn, rettype);
>       }
>       else
>       {
> ***************
> *** 125,130 ****
> --- 165,191 ----
>           finaltype = aggTransType;
>       }
>       Assert(OidIsValid(finaltype));
> +
> +     /*
> +      * special disallowed cases:
> +      * 1)    if finaltype is polymorphic, basetype cannot be ANY
> +      * 2)    if finaltype is polymorphic, both args to transfn must be
> +      *        polymorphic
> +      */
> +     if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
> +     {
> +         if (aggBaseType == ANYOID)
> +             elog(ERROR, "aggregate with base type ANY must have a " \
> +                         "non-polymorphic return type");
> +
> +         if (nargs_transfn > 1 && (
> +             (true_oid_array_transfn[0] != ANYARRAYOID &&
> +              true_oid_array_transfn[0] != ANYELEMENTOID) ||
> +             (true_oid_array_transfn[1] != ANYARRAYOID &&
> +              true_oid_array_transfn[1] != ANYELEMENTOID)))
> +             elog(ERROR, "aggregate with polymorphic return type requires " \
> +                         "state function with both arguments polymorphic");
> +     }
>
>       /*
>        * Everything looks okay.  Try to create the pg_proc entry for the
> Index: src/backend/commands/aggregatecmds.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/aggregatecmds.c,v
> retrieving revision 1.5
> diff -c -r1.5 aggregatecmds.c
> *** src/backend/commands/aggregatecmds.c    4 Sep 2002 20:31:14 -0000    1.5
> --- src/backend/commands/aggregatecmds.c    9 Jun 2003 01:31:10 -0000
> ***************
> *** 119,125 ****
>           baseTypeId = typenameTypeId(baseType);
>
>       transTypeId = typenameTypeId(transType);
> !     if (get_typtype(transTypeId) == 'p')
>           elog(ERROR, "Aggregate transition datatype cannot be %s",
>                format_type_be(transTypeId));
>
> --- 119,127 ----
>           baseTypeId = typenameTypeId(baseType);
>
>       transTypeId = typenameTypeId(transType);
> !     if (get_typtype(transTypeId) == 'p' &&
> !         transTypeId != ANYARRAYOID &&
> !         transTypeId != ANYELEMENTOID)
>           elog(ERROR, "Aggregate transition datatype cannot be %s",
>                format_type_be(transTypeId));
>
> Index: src/backend/executor/execQual.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/execQual.c,v
> retrieving revision 1.130
> diff -c -r1.130 execQual.c
> *** src/backend/executor/execQual.c    28 May 2003 22:32:49 -0000    1.130
> --- src/backend/executor/execQual.c    9 Jun 2003 01:42:58 -0000
> ***************
> *** 1528,1544 ****
>               {
>                   /* Check other sub-arrays are compatible */
>                   if (elem_ndims != ARR_NDIM(array))
> !                     elog(ERROR, "Multiple dimension arrays must have array "
>                            "expressions with matching number of dimensions");
>
>                   if (memcmp(elem_dims, ARR_DIMS(array),
>                              elem_ndims * sizeof(int)) != 0)
> !                     elog(ERROR, "Multiple dimension arrays must have array "
>                            "expressions with matching dimensions");
>
>                   if (memcmp(elem_lbs, ARR_LBOUND(array),
>                              elem_ndims * sizeof(int)) != 0)
> !                     elog(ERROR, "Multiple dimension arrays must have array "
>                            "expressions with matching dimensions");
>               }
>
> --- 1528,1544 ----
>               {
>                   /* Check other sub-arrays are compatible */
>                   if (elem_ndims != ARR_NDIM(array))
> !                     elog(ERROR, "Multidimensional arrays must have array "
>                            "expressions with matching number of dimensions");
>
>                   if (memcmp(elem_dims, ARR_DIMS(array),
>                              elem_ndims * sizeof(int)) != 0)
> !                     elog(ERROR, "Multidimensional arrays must have array "
>                            "expressions with matching dimensions");
>
>                   if (memcmp(elem_lbs, ARR_LBOUND(array),
>                              elem_ndims * sizeof(int)) != 0)
> !                     elog(ERROR, "Multidimensional arrays must have array "
>                            "expressions with matching dimensions");
>               }
>
> Index: src/backend/executor/nodeAgg.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeAgg.c,v
> retrieving revision 1.105
> diff -c -r1.105 nodeAgg.c
> *** src/backend/executor/nodeAgg.c    30 May 2003 20:23:10 -0000    1.105
> --- src/backend/executor/nodeAgg.c    9 Jun 2003 01:31:10 -0000
> ***************
> *** 58,63 ****
> --- 58,64 ----
>   #include "executor/executor.h"
>   #include "executor/nodeAgg.h"
>   #include "miscadmin.h"
> + #include "nodes/makefuncs.h"
>   #include "optimizer/clauses.h"
>   #include "parser/parse_coerce.h"
>   #include "parser/parse_expr.h"
> ***************
> *** 212,218 ****
>   static void agg_fill_hash_table(AggState *aggstate);
>   static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
>   static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
> !
>
>   /*
>    * Initialize all aggregates for a new group of input values.
> --- 213,219 ----
>   static void agg_fill_hash_table(AggState *aggstate);
>   static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
>   static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
> ! static Oid resolve_type(Oid type_to_resolve, Oid context_type);
>
>   /*
>    * Initialize all aggregates for a new group of input values.
> ***************
> *** 351,364 ****
>       fcinfo.context = NULL;
>       fcinfo.resultinfo = NULL;
>       fcinfo.isnull = false;
> -
>       fcinfo.flinfo = &peraggstate->transfn;
>       fcinfo.nargs = 2;
>       fcinfo.arg[0] = pergroupstate->transValue;
>       fcinfo.argnull[0] = pergroupstate->transValueIsNull;
>       fcinfo.arg[1] = newVal;
>       fcinfo.argnull[1] = isNull;
> -
>       newVal = FunctionCallInvoke(&fcinfo);
>
>       /*
> --- 352,363 ----
> ***************
> *** 1176,1182 ****
> --- 1175,1195 ----
>           AclResult    aclresult;
>           Oid            transfn_oid,
>                       finalfn_oid;
> +         FuncExpr   *transfnexpr,
> +                    *finalfnexpr;
>           Datum        textInitVal;
> +         List       *fargs;
> +         Oid            agg_rt_type;
> +         Oid           *transfn_arg_types;
> +         List       *transfn_args = NIL;
> +         int            transfn_nargs;
> +         Oid            transfn_ret_type;
> +         Oid           *finalfn_arg_types = NULL;
> +         List       *finalfn_args = NIL;
> +         Oid            finalfn_ret_type = InvalidOid;
> +         int            finalfn_nargs = 0;
> +         Node       *arg0;
> +         Node       *arg1;
>           int            i;
>
>           /* Look for a previous duplicate aggregate */
> ***************
> *** 1224,1229 ****
> --- 1237,1402 ----
>                           &peraggstate->transtypeLen,
>                           &peraggstate->transtypeByVal);
>
> +         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
> +         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
> +
> +         /* get the runtime aggregate argument type */
> +         fargs = aggref->args;
> +         agg_rt_type = exprType((Node *) nth(0, fargs));
> +
> +         /* get the transition function argument and return types */
> +         transfn_ret_type = get_func_rettype(transfn_oid);
> +         transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs);
> +
> +         /* resolve any polymorphic types */
> +         if (transfn_nargs == 2)
> +         /* base type was not ANY */
> +         {
> +             if (transfn_arg_types[1] == ANYARRAYOID ||
> +                 transfn_arg_types[1] == ANYELEMENTOID)
> +                 transfn_arg_types[1] = agg_rt_type;
> +
> +             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
> +                                                         agg_rt_type);
> +
> +             /*
> +              * Build arg list to use on the transfn FuncExpr node. We really
> +              * only care that the node type is correct so that the transfn
> +              * can discover the actual argument types at runtime using
> +              * get_fn_expr_argtype()
> +              */
> +             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
> +                                                             -1, COERCE_DONTCARE);
> +             arg1 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[1],
> +                                                             -1, COERCE_DONTCARE);
> +             transfn_args = makeList2(arg0, arg1);
> +
> +             /*
> +              * the state transition function always returns the same type
> +              * as its first argument
> +              */
> +             if (transfn_ret_type == ANYARRAYOID ||
> +                 transfn_ret_type == ANYELEMENTOID)
> +                 transfn_ret_type = transfn_arg_types[0];
> +         }
> +         else if (transfn_nargs == 1)
> +         /*
> +          * base type was ANY, therefore the aggregate return type should
> +          * be non-polymorphic
> +          */
> +         {
> +             Oid    finaltype = get_func_rettype(aggref->aggfnoid);
> +
> +             /*
> +              * this should have been prevented in AggregateCreate,
> +              * but check anyway
> +              */
> +             if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
> +                 elog(ERROR, "aggregate with base type ANY must have a " \
> +                             "non-polymorphic return type");
> +
> +             /* see if we have a final function */
> +             if (OidIsValid(finalfn_oid))
> +             {
> +                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
> +                 if (finalfn_nargs != 1)
> +                     elog(ERROR, "final function takes unexpected number " \
> +                                 "of arguments: %d", finalfn_nargs);
> +
> +                 /*
> +                  * final function argument is always the same as the state
> +                  * function return type
> +                  */
> +                 if (finalfn_arg_types[0] != ANYARRAYOID &&
> +                     finalfn_arg_types[0] != ANYELEMENTOID)
> +                 {
> +                     /* if it is not ambiguous, use it */
> +                     transfn_ret_type = finalfn_arg_types[0];
> +                 }
> +                 else
> +                 {
> +                     /* if it is ambiguous, try to derive it */
> +                     finalfn_ret_type = finaltype;
> +                     finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0],
> +                                                             finalfn_ret_type);
> +                     transfn_ret_type = finalfn_arg_types[0];
> +                 }
> +             }
> +             else
> +                 transfn_ret_type = finaltype;
> +
> +             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
> +                                                         transfn_ret_type);
> +
> +             /*
> +              * Build arg list to use on the transfn FuncExpr node. We really
> +              * only care that the node type is correct so that the transfn
> +              * can discover the actual argument types at runtime using
> +              * get_fn_expr_argtype()
> +              */
> +             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
> +                                                             -1, COERCE_DONTCARE);
> +             transfn_args = makeList1(arg0);
> +         }
> +         else
> +             elog(ERROR, "state transition function takes unexpected number " \
> +                         "of arguments: %d", transfn_nargs);
> +
> +         if (OidIsValid(finalfn_oid))
> +         {
> +             /* get the final function argument and return types */
> +             if (finalfn_ret_type == InvalidOid)
> +                 finalfn_ret_type = get_func_rettype(finalfn_oid);
> +
> +             if (!finalfn_arg_types)
> +             {
> +                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
> +                 if (finalfn_nargs != 1)
> +                     elog(ERROR, "final function takes unexpected number " \
> +                                 "of arguments: %d", finalfn_nargs);
> +             }
> +
> +             /*
> +              * final function argument is always the same as the state
> +              * function return type, which by now should have been resolved
> +              */
> +             if (finalfn_arg_types[0] == ANYARRAYOID ||
> +                 finalfn_arg_types[0] == ANYELEMENTOID)
> +                 finalfn_arg_types[0] = transfn_ret_type;
> +
> +             /*
> +              * Build arg list to use on the finalfn FuncExpr node. We really
> +              * only care that the node type is correct so that the finalfn
> +              * can discover the actual argument type at runtime using
> +              * get_fn_expr_argtype()
> +              */
> +             arg0 = (Node *) makeRelabelType((Expr *) NULL, finalfn_arg_types[0],
> +                                                             -1, COERCE_DONTCARE);
> +             finalfn_args = makeList1(arg0);
> +
> +             finalfn_ret_type = resolve_type(finalfn_ret_type,
> +                                                 finalfn_arg_types[0]);
> +         }
> +
> +         fmgr_info(transfn_oid, &peraggstate->transfn);
> +         transfnexpr = (FuncExpr *) make_funcclause(transfn_oid,
> +                           transfn_ret_type,
> +                           false,            /* cannot be a set */
> +                           COERCE_DONTCARE,    /* to match any user expr */
> +                           transfn_args);
> +         peraggstate->transfn.fn_expr = (Node *) transfnexpr;
> +
> +         if (OidIsValid(finalfn_oid))
> +         {
> +             fmgr_info(finalfn_oid, &peraggstate->finalfn);
> +             finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid,
> +                           finalfn_ret_type,
> +                           false,            /* cannot be a set */
> +                           COERCE_DONTCARE,    /* to match any user expr */
> +                           finalfn_args);
> +             peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
> +         }
> +
>           /*
>            * initval is potentially null, so don't try to access it as a
>            * struct field. Must do it the hard way with SysCacheGetAttr.
> ***************
> *** 1236,1249 ****
>               peraggstate->initValue = (Datum) 0;
>           else
>               peraggstate->initValue = GetAggInitVal(textInitVal,
> !                                                    aggform->aggtranstype);
> !
> !         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
> !         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
> !
> !         fmgr_info(transfn_oid, &peraggstate->transfn);
> !         if (OidIsValid(finalfn_oid))
> !             fmgr_info(finalfn_oid, &peraggstate->finalfn);
>
>           /*
>            * If the transfn is strict and the initval is NULL, make sure
> --- 1409,1415 ----
>               peraggstate->initValue = (Datum) 0;
>           else
>               peraggstate->initValue = GetAggInitVal(textInitVal,
> !                                                    transfn_arg_types[0]);
>
>           /*
>            * If the transfn is strict and the initval is NULL, make sure
> ***************
> *** 1454,1457 ****
> --- 1620,1656 ----
>       elog(ERROR, "Aggregate function %u called as normal function",
>            fcinfo->flinfo->fn_oid);
>       return (Datum) 0;            /* keep compiler quiet */
> + }
> +
> + static Oid
> + resolve_type(Oid type_to_resolve, Oid context_type)
> + {
> +     Oid        resolved_type;
> +
> +     if (context_type == ANYARRAYOID || context_type == ANYELEMENTOID)
> +         resolved_type = type_to_resolve;
> +     else if (type_to_resolve == ANYARRAYOID)
> +     /* any array */
> +     {
> +         Oid        context_type_arraytype = get_array_type(context_type);
> +
> +         if (context_type_arraytype != InvalidOid)
> +             resolved_type = context_type_arraytype;
> +         else
> +             resolved_type = context_type;
> +     }
> +     else if (type_to_resolve == ANYELEMENTOID)
> +     /* any element */
> +     {
> +         Oid        context_type_elemtype = get_element_type(context_type);
> +
> +         if (context_type_elemtype != InvalidOid)
> +             resolved_type = context_type_elemtype;
> +         else
> +             resolved_type = context_type;
> +     }
> +     else
> +         resolved_type = type_to_resolve;
> +
> +     return resolved_type;
>   }
> Index: src/backend/executor/nodeSubplan.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v
> retrieving revision 1.45
> diff -c -r1.45 nodeSubplan.c
> *** src/backend/executor/nodeSubplan.c    8 Apr 2003 23:20:01 -0000    1.45
> --- src/backend/executor/nodeSubplan.c    9 Jun 2003 01:31:10 -0000
> ***************
> *** 28,50 ****
>   #include "utils/datum.h"
>   #include "utils/lsyscache.h"
>
> -
> - typedef struct ArrayBuildState
> - {
> -     MemoryContext mcontext;        /* where all the temp stuff is kept */
> -     Datum       *dvalues;        /* array of accumulated Datums */
> -     /*
> -      * The allocated size of dvalues[] is always a multiple of
> -      * ARRAY_ELEMS_CHUNKSIZE
> -      */
> - #define ARRAY_ELEMS_CHUNKSIZE    64
> -     int            nelems;            /* number of valid Datums in dvalues[] */
> -     Oid            element_type;    /* data type of the Datums */
> -     int16        typlen;            /* needed info about datatype */
> -     bool        typbyval;
> -     char        typalign;
> - } ArrayBuildState;
> -
>   static Datum ExecHashSubPlan(SubPlanState *node,
>                                ExprContext *econtext,
>                                bool *isNull);
> --- 28,33 ----
> ***************
> *** 54,66 ****
>   static void buildSubPlanHash(SubPlanState *node);
>   static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
>   static bool tupleAllNulls(HeapTuple tuple);
> - static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
> -                                          Datum dvalue, bool disnull,
> -                                          Oid element_type,
> -                                          MemoryContext rcontext);
> - static Datum makeArrayResult(ArrayBuildState *astate,
> -                              MemoryContext rcontext);
> -
>
>   /* ----------------------------------------------------------------
>    *        ExecSubPlan
> --- 37,42 ----
> ***************
> *** 224,229 ****
> --- 200,206 ----
>       PlanState  *planstate = node->planstate;
>       SubLinkType subLinkType = subplan->subLinkType;
>       bool        useOr = subplan->useOr;
> +     bool        isExpr = subplan->isExpr;
>       MemoryContext oldcontext;
>       TupleTableSlot *slot;
>       Datum        result;
> ***************
> *** 292,297 ****
> --- 269,279 ----
>           bool        rownull = false;
>           int            col = 1;
>           List       *plst;
> +         int            numelems;
> +         int            elemnum;
> +         Datum        dvalue;
> +         Datum       *dvalues = NULL;
> +         bool        disnull;
>
>           if (subLinkType == EXISTS_SUBLINK)
>           {
> ***************
> *** 329,337 ****
>
>           if (subLinkType == ARRAY_SUBLINK)
>           {
> -             Datum    dvalue;
> -             bool    disnull;
> -
>               found = true;
>               /* stash away current value */
>               dvalue = heap_getattr(tup, 1, tdesc, &disnull);
> --- 311,316 ----
> ***************
> *** 349,446 ****
>           found = true;
>
>           /*
> !          * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
> !          * operators for columns of tuple.
>            */
> !         plst = subplan->paramIds;
> !         foreach(lst, node->exprs)
>           {
> !             ExprState  *exprstate = (ExprState *) lfirst(lst);
> !             int            paramid = lfirsti(plst);
> !             ParamExecData *prmdata;
> !             Datum        expresult;
> !             bool        expnull;
> !
> !             /*
> !              * Load up the Param representing this column of the sub-select.
> !              */
> !             prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
> !             Assert(prmdata->execPlan == NULL);
> !             prmdata->value = heap_getattr(tup, col, tdesc,
> !                                           &(prmdata->isnull));
>
> !             /*
> !              * Now we can eval the combining operator for this column.
> !              */
> !             expresult = ExecEvalExprSwitchContext(exprstate, econtext,
> !                                                   &expnull, NULL);
> !
> !             /*
> !              * Combine the result into the row result as appropriate.
> !              */
> !             if (col == 1)
>               {
> !                 rowresult = expresult;
> !                 rownull = expnull;
>               }
> !             else if (useOr)
>               {
> !                 /* combine within row per OR semantics */
> !                 if (expnull)
> !                     rownull = true;
> !                 else if (DatumGetBool(expresult))
>                   {
> !                     rowresult = BoolGetDatum(true);
> !                     rownull = false;
> !                     break;        /* needn't look at any more columns */
>                   }
>               }
>               else
>               {
> !                 /* combine within row per AND semantics */
> !                 if (expnull)
> !                     rownull = true;
> !                 else if (!DatumGetBool(expresult))
> !                 {
> !                     rowresult = BoolGetDatum(false);
> !                     rownull = false;
> !                     break;        /* needn't look at any more columns */
> !                 }
>               }
>
> -             plst = lnext(plst);
> -             col++;
>           }
>
> !         if (subLinkType == ANY_SUBLINK)
>           {
> !             /* combine across rows per OR semantics */
> !             if (rownull)
> !                 *isNull = true;
> !             else if (DatumGetBool(rowresult))
>               {
> !                 result = BoolGetDatum(true);
> !                 *isNull = false;
> !                 break;            /* needn't look at any more rows */
>               }
> !         }
> !         else if (subLinkType == ALL_SUBLINK)
> !         {
> !             /* combine across rows per AND semantics */
> !             if (rownull)
> !                 *isNull = true;
> !             else if (!DatumGetBool(rowresult))
>               {
> !                 result = BoolGetDatum(false);
> !                 *isNull = false;
> !                 break;            /* needn't look at any more rows */
>               }
> -         }
> -         else
> -         {
> -             /* must be MULTIEXPR_SUBLINK */
> -             result = rowresult;
> -             *isNull = rownull;
>           }
>       }
>
> --- 328,490 ----
>           found = true;
>
>           /*
> !          * When isExpr is true, we have either a scalar expression or an
> !          * array. In the former case, this is no different than the !isExpr
> !          * case. In the latter case, iterate over the elements as if they
> !          * were from multiple input tuples.
>            */
> !         if (!isExpr)
> !             numelems = 1;
> !         else
>           {
> !             Oid        expr_typeid = tdesc->attrs[0]->atttypid;
>
> !             if (expr_typeid != subplan->exprtype)
>               {
> !                 subplan->exprtype = expr_typeid;
> !                 subplan->elemtype = get_element_type(expr_typeid);
> !
> !                 if (subplan->elemtype != InvalidOid)
> !                     get_typlenbyvalalign(subplan->elemtype,
> !                                          &subplan->elmlen,
> !                                          &subplan->elmbyval,
> !                                          &subplan->elmalign);
>               }
> !
> !             /* get current value */
> !             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
> !
> !             /* XXX this will need work if/when arrays support NULL elements */
> !             if (!disnull)
>               {
> !                 if (subplan->elemtype != InvalidOid)
> !                 {
> !                     ArrayType   *v = DatumGetArrayTypeP(dvalue);
> !
> !                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
> !                                       subplan->elmbyval, subplan->elmalign,
> !                                         &dvalues, &numelems);
> !                 }
> !                 else
>                   {
> !                     numelems = 1;
> !                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                     dvalues[0] = dvalue;
>                   }
>               }
>               else
>               {
> !                 numelems = 1;
> !                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                 dvalues[0] = (Datum) 0;
>               }
>
>           }
>
> !         for (elemnum = 0; elemnum < numelems; elemnum++)
>           {
> !             /*
> !              * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
> !              * operators for columns of tuple.
> !              */
> !             col = 1;
> !             plst = subplan->paramIds;
> !             foreach(lst, node->exprs)
>               {
> !                 ExprState  *exprstate = (ExprState *) lfirst(lst);
> !                 int            paramid = lfirsti(plst);
> !                 ParamExecData *prmdata;
> !                 Datum        expresult;
> !                 bool        expnull;
> !
> !                 /*
> !                  * Load up the Param representing this column of the sub-select.
> !                  */
> !                 prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
> !                 Assert(prmdata->execPlan == NULL);
> !
> !                 if (!isExpr)
> !                     prmdata->value = heap_getattr(tup, col, tdesc,
> !                                                   &(prmdata->isnull));
> !                 else
> !                 {
> !                     prmdata->value = dvalues[elemnum];
> !                     prmdata->isnull = disnull;
> !                 }
> !
> !                 /*
> !                  * Now we can eval the combining operator for this column.
> !                  */
> !                 expresult = ExecEvalExprSwitchContext(exprstate, econtext,
> !                                                       &expnull, NULL);
> !
> !                 /*
> !                  * Combine the result into the row result as appropriate.
> !                  */
> !                 if (col == 1)
> !                 {
> !                     rowresult = expresult;
> !                     rownull = expnull;
> !                 }
> !                 else if (useOr)
> !                 {
> !                     /* combine within row per OR semantics */
> !                     if (expnull)
> !                         rownull = true;
> !                     else if (DatumGetBool(expresult))
> !                     {
> !                         rowresult = BoolGetDatum(true);
> !                         rownull = false;
> !                         break;        /* needn't look at any more columns */
> !                     }
> !                 }
> !                 else
> !                 {
> !                     /* combine within row per AND semantics */
> !                     if (expnull)
> !                         rownull = true;
> !                     else if (!DatumGetBool(expresult))
> !                     {
> !                         rowresult = BoolGetDatum(false);
> !                         rownull = false;
> !                         break;        /* needn't look at any more columns */
> !                     }
> !                 }
> !
> !                 plst = lnext(plst);
> !                 col++;
>               }
> !
> !             if (subLinkType == ANY_SUBLINK)
>               {
> !                 /* combine across rows per OR semantics */
> !                 if (rownull)
> !                     *isNull = true;
> !                 else if (DatumGetBool(rowresult))
> !                 {
> !                     result = BoolGetDatum(true);
> !                     *isNull = false;
> !                     break;            /* needn't look at any more rows */
> !                 }
> !             }
> !             else if (subLinkType == ALL_SUBLINK)
> !             {
> !                 /* combine across rows per AND semantics */
> !                 if (rownull)
> !                     *isNull = true;
> !                 else if (!DatumGetBool(rowresult))
> !                 {
> !                     result = BoolGetDatum(false);
> !                     *isNull = false;
> !                     break;            /* needn't look at any more rows */
> !                 }
> !             }
> !             else
> !             {
> !                 /* must be MULTIEXPR_SUBLINK */
> !                 result = rowresult;
> !                 *isNull = rownull;
>               }
>           }
>       }
>
> ***************
> *** 478,483 ****
> --- 522,528 ----
>   buildSubPlanHash(SubPlanState *node)
>   {
>       SubPlan       *subplan = (SubPlan *) node->xprstate.expr;
> +     bool        isExpr = subplan->isExpr;
>       PlanState  *planstate = node->planstate;
>       int            ncols = length(node->exprs);
>       ExprContext *innerecontext = node->innerecontext;
> ***************
> *** 485,490 ****
> --- 530,536 ----
>       MemoryContext oldcontext;
>       int            nbuckets;
>       TupleTableSlot *slot;
> +     TupleTableSlot *arrslot = NULL;
>
>       Assert(subplan->subLinkType == ANY_SUBLINK);
>       Assert(!subplan->useOr);
> ***************
> *** 562,604 ****
>       {
>           HeapTuple    tup = slot->val;
>           TupleDesc    tdesc = slot->ttc_tupleDescriptor;
> !         int            col = 1;
>           List       *plst;
>           bool        isnew;
>
>           /*
> !          * Load up the Params representing the raw sub-select outputs,
> !          * then form the projection tuple to store in the hashtable.
>            */
> !         foreach(plst, subplan->paramIds)
>           {
> !             int            paramid = lfirsti(plst);
> !             ParamExecData *prmdata;
>
> !             prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
> !             Assert(prmdata->execPlan == NULL);
> !             prmdata->value = heap_getattr(tup, col, tdesc,
> !                                           &(prmdata->isnull));
> !             col++;
> !         }
> !         slot = ExecProject(node->projRight, NULL);
> !         tup = slot->val;
>
> -         /*
> -          * If result contains any nulls, store separately or not at all.
> -          * (Since we know the projection tuple has no junk columns, we
> -          * can just look at the overall hasnull info bit, instead of
> -          * groveling through the columns.)
> -          */
> -         if (HeapTupleNoNulls(tup))
> -         {
> -             (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
> -             node->havehashrows = true;
>           }
> !         else if (node->hashnulls)
>           {
> !             (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
> !             node->havenullrows = true;
>           }
>
>           /*
> --- 608,746 ----
>       {
>           HeapTuple    tup = slot->val;
>           TupleDesc    tdesc = slot->ttc_tupleDescriptor;
> !         TupleDesc    arrtdesc = NULL;
>           List       *plst;
>           bool        isnew;
> +         int            numelems;
> +         int            elemnum;
> +         Datum        dvalue;
> +         Datum       *dvalues = NULL;
> +         bool        disnull;
>
>           /*
> !          * When isExpr is true, we have either a scalar expression or an
> !          * array. In the former case, this is no different than the !isExpr
> !          * case. In the latter case, iterate over the elements as if they
> !          * were from multiple input tuples.
>            */
> !         if (!isExpr)
> !             numelems = 1;
> !         else
>           {
> !             Oid        expr_typeid = tdesc->attrs[0]->atttypid;
>
> !             if (expr_typeid != subplan->exprtype)
> !             {
> !                 subplan->exprtype = expr_typeid;
> !                 subplan->elemtype = get_element_type(expr_typeid);
> !
> !                 if (subplan->elemtype != InvalidOid)
> !                     get_typlenbyvalalign(subplan->elemtype,
> !                                          &subplan->elmlen,
> !                                          &subplan->elmbyval,
> !                                          &subplan->elmalign);
> !             }
> !
> !             /* get current value */
> !             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
> !
> !             if (subplan->elemtype != InvalidOid)
> !             {
> !                 TupleTable    tupleTable;
> !                 ArrayType   *v = DatumGetArrayTypeP(dvalue);
> !
> !                 arrtdesc = CreateTemplateTupleDesc(1, false);
> !                 TupleDescInitEntry(arrtdesc, 1, "elem", subplan->elemtype,
> !                                                             -1, 0, false);
> !
> !                 tupleTable = ExecCreateTupleTable(1);
> !                 arrslot = ExecAllocTableSlot(tupleTable);
> !                 ExecSetSlotDescriptor(arrslot, arrtdesc, true);
> !
> !                 /* XXX this will need work if/when arrays support NULL elements */
> !                 if (!disnull)
> !                 {
> !                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
> !                                       subplan->elmbyval, subplan->elmalign,
> !                                         &dvalues, &numelems);
> !                 }
> !                 else
> !                 {
> !                     numelems = 1;
> !                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                     dvalues[0] = (Datum) 0;
> !                 }
> !             }
> !             else
> !             {
> !                 numelems = 1;
> !                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                 dvalues[0] = dvalue;
> !             }
>
>           }
> !
> !         for (elemnum = 0; elemnum < numelems; elemnum++)
>           {
> !             int    col = 1;
> !
> !             if (!isExpr || subplan->elemtype == InvalidOid)
> !             {
> !                 /*
> !                  * Load up the Params representing the raw sub-select outputs,
> !                  * then form the projection tuple to store in the hashtable.
> !                  */
> !                 foreach(plst, subplan->paramIds)
> !                 {
> !                     int            paramid = lfirsti(plst);
> !                     ParamExecData *prmdata;
> !
> !                     prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
> !                     Assert(prmdata->execPlan == NULL);
> !
> !                     prmdata->value = heap_getattr(tup, col, tdesc,
> !                                                   &(prmdata->isnull));
> !
> !                     col++;
> !                 }
> !                 slot = ExecProject(node->projRight, NULL);
> !                 tup = slot->val;
> !             }
> !             else
> !             {
> !                 /*
> !                  * For array type expressions, we need to build up our own
> !                  * tuple and slot
> !                  */
> !                 char        nullflag;
> !
> !                 nullflag = disnull ? 'n' : ' ';
> !                 tup = heap_formtuple(arrtdesc, &dvalues[elemnum], &nullflag);
> !                 arrslot = ExecStoreTuple(tup, arrslot, InvalidBuffer, true);
> !             }
> !
> !             /*
> !              * If result contains any nulls, store separately or not at all.
> !              * (Since we know the projection tuple has no junk columns, we
> !              * can just look at the overall hasnull info bit, instead of
> !              * groveling through the columns.)
> !              */
> !             if (HeapTupleNoNulls(tup))
> !             {
> !                 if (!isExpr)
> !                     (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
> !                 else
> !                     (void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew);
> !                 node->havehashrows = true;
> !             }
> !             else if (node->hashnulls)
> !             {
> !                 if (!isExpr)
> !                     (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
> !                 else
> !                     (void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew);
> !                 node->havenullrows = true;
> !             }
>           }
>
>           /*
> ***************
> *** 615,620 ****
> --- 757,764 ----
>        * have the potential for a double free attempt.
>        */
>       ExecClearTuple(node->projRight->pi_slot);
> +     if (arrslot)
> +         ExecClearTuple(arrslot);
>
>       MemoryContextSwitchTo(oldcontext);
>   }
> ***************
> *** 1084,1185 ****
>           prm->execPlan = node;
>           parent->chgParam = bms_add_member(parent->chgParam, paramid);
>       }
> - }
> -
> - /*
> -  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
> -  *
> -  *    astate is working state (NULL on first call)
> -  *    rcontext is where to keep working state
> -  */
> - static ArrayBuildState *
> - accumArrayResult(ArrayBuildState *astate,
> -                  Datum dvalue, bool disnull,
> -                  Oid element_type,
> -                  MemoryContext rcontext)
> - {
> -     MemoryContext arr_context,
> -                   oldcontext;
> -
> -     if (astate == NULL)
> -     {
> -         /* First time through --- initialize */
> -
> -         /* Make a temporary context to hold all the junk */
> -         arr_context = AllocSetContextCreate(rcontext,
> -                                             "ARRAY_SUBLINK Result",
> -                                             ALLOCSET_DEFAULT_MINSIZE,
> -                                             ALLOCSET_DEFAULT_INITSIZE,
> -                                             ALLOCSET_DEFAULT_MAXSIZE);
> -         oldcontext = MemoryContextSwitchTo(arr_context);
> -         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
> -         astate->mcontext = arr_context;
> -         astate->dvalues = (Datum *)
> -             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
> -         astate->nelems = 0;
> -         astate->element_type = element_type;
> -         get_typlenbyvalalign(element_type,
> -                              &astate->typlen,
> -                              &astate->typbyval,
> -                              &astate->typalign);
> -     }
> -     else
> -     {
> -         oldcontext = MemoryContextSwitchTo(astate->mcontext);
> -         Assert(astate->element_type == element_type);
> -         /* enlarge dvalues[] if needed */
> -         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
> -             astate->dvalues = (Datum *)
> -                 repalloc(astate->dvalues,
> -                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
> -     }
> -
> -     if (disnull)
> -         elog(ERROR, "NULL elements not allowed in Arrays");
> -
> -     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
> -     astate->dvalues[astate->nelems++] =
> -         datumCopy(dvalue, astate->typbyval, astate->typlen);
> -
> -     MemoryContextSwitchTo(oldcontext);
> -
> -     return astate;
> - }
> -
> - /*
> -  * makeArrayResult - produce final result of ARRAY_SUBLINK
> -  *
> -  *    astate is working state (not NULL)
> -  *    rcontext is where to construct result
> -  */
> - static Datum
> - makeArrayResult(ArrayBuildState *astate,
> -                 MemoryContext rcontext)
> - {
> -     ArrayType  *result;
> -     int            dims[1];
> -     int            lbs[1];
> -     MemoryContext oldcontext;
> -
> -     /* Build the final array result in rcontext */
> -     oldcontext = MemoryContextSwitchTo(rcontext);
> -
> -     dims[0] = astate->nelems;
> -     lbs[0] = 1;
> -
> -     result = construct_md_array(astate->dvalues,
> -                                 1,
> -                                 dims,
> -                                 lbs,
> -                                 astate->element_type,
> -                                 astate->typlen,
> -                                 astate->typbyval,
> -                                 astate->typalign);
> -
> -     MemoryContextSwitchTo(oldcontext);
> -
> -     /* Clean up all the junk */
> -     MemoryContextDelete(astate->mcontext);
> -
> -     return PointerGetDatum(result);
>   }
> --- 1228,1231 ----
> Index: src/backend/nodes/copyfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
> retrieving revision 1.251
> diff -c -r1.251 copyfuncs.c
> *** src/backend/nodes/copyfuncs.c    28 May 2003 16:03:56 -0000    1.251
> --- src/backend/nodes/copyfuncs.c    9 Jun 2003 01:31:10 -0000
> ***************
> *** 727,732 ****
> --- 727,733 ----
>       COPY_NODE_FIELD(target);
>       COPY_SCALAR_FIELD(aggstar);
>       COPY_SCALAR_FIELD(aggdistinct);
> +     COPY_NODE_FIELD(args);
>
>       return newnode;
>   }
> ***************
> *** 825,830 ****
> --- 826,832 ----
>
>       COPY_SCALAR_FIELD(subLinkType);
>       COPY_SCALAR_FIELD(useOr);
> +     COPY_SCALAR_FIELD(isExpr);
>       COPY_NODE_FIELD(lefthand);
>       COPY_NODE_FIELD(operName);
>       COPY_OIDLIST_FIELD(operOids);
> ***************
> *** 843,848 ****
> --- 845,856 ----
>
>       COPY_SCALAR_FIELD(subLinkType);
>       COPY_SCALAR_FIELD(useOr);
> +     COPY_SCALAR_FIELD(isExpr);
> +     COPY_SCALAR_FIELD(exprtype);
> +     COPY_SCALAR_FIELD(elemtype);
> +     COPY_SCALAR_FIELD(elmlen);
> +     COPY_SCALAR_FIELD(elmbyval);
> +     COPY_SCALAR_FIELD(elmalign);
>       COPY_NODE_FIELD(exprs);
>       COPY_INTLIST_FIELD(paramIds);
>       COPY_NODE_FIELD(plan);
> Index: src/backend/nodes/equalfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v
> retrieving revision 1.194
> diff -c -r1.194 equalfuncs.c
> *** src/backend/nodes/equalfuncs.c    28 May 2003 16:03:56 -0000    1.194
> --- src/backend/nodes/equalfuncs.c    9 Jun 2003 01:31:10 -0000
> ***************
> *** 204,209 ****
> --- 204,210 ----
>       COMPARE_NODE_FIELD(target);
>       COMPARE_SCALAR_FIELD(aggstar);
>       COMPARE_SCALAR_FIELD(aggdistinct);
> +     COMPARE_NODE_FIELD(args);
>
>       return true;
>   }
> ***************
> *** 300,305 ****
> --- 301,307 ----
>   {
>       COMPARE_SCALAR_FIELD(subLinkType);
>       COMPARE_SCALAR_FIELD(useOr);
> +     COMPARE_SCALAR_FIELD(isExpr);
>       COMPARE_NODE_FIELD(lefthand);
>       COMPARE_NODE_FIELD(operName);
>       COMPARE_OIDLIST_FIELD(operOids);
> ***************
> *** 313,318 ****
> --- 315,326 ----
>   {
>       COMPARE_SCALAR_FIELD(subLinkType);
>       COMPARE_SCALAR_FIELD(useOr);
> +     COMPARE_SCALAR_FIELD(isExpr);
> +     COMPARE_SCALAR_FIELD(exprtype);
> +     COMPARE_SCALAR_FIELD(elemtype);
> +     COMPARE_SCALAR_FIELD(elmlen);
> +     COMPARE_SCALAR_FIELD(elmbyval);
> +     COMPARE_SCALAR_FIELD(elmalign);
>       COMPARE_NODE_FIELD(exprs);
>       COMPARE_INTLIST_FIELD(paramIds);
>       /* should compare plans, but have to settle for comparing plan IDs */
> Index: src/backend/nodes/outfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v
> retrieving revision 1.206
> diff -c -r1.206 outfuncs.c
> *** src/backend/nodes/outfuncs.c    28 May 2003 16:03:56 -0000    1.206
> --- src/backend/nodes/outfuncs.c    9 Jun 2003 01:31:10 -0000
> ***************
> *** 615,620 ****
> --- 615,621 ----
>       WRITE_NODE_FIELD(target);
>       WRITE_BOOL_FIELD(aggstar);
>       WRITE_BOOL_FIELD(aggdistinct);
> +     WRITE_NODE_FIELD(args);
>   }
>
>   static void
> ***************
> *** 700,705 ****
> --- 701,707 ----
>
>       WRITE_ENUM_FIELD(subLinkType, SubLinkType);
>       WRITE_BOOL_FIELD(useOr);
> +     WRITE_BOOL_FIELD(isExpr);
>       WRITE_NODE_FIELD(lefthand);
>       WRITE_NODE_FIELD(operName);
>       WRITE_OIDLIST_FIELD(operOids);
> ***************
> *** 713,718 ****
> --- 715,726 ----
>
>       WRITE_ENUM_FIELD(subLinkType, SubLinkType);
>       WRITE_BOOL_FIELD(useOr);
> +     WRITE_BOOL_FIELD(isExpr);
> +     WRITE_OID_FIELD(exprtype);
> +     WRITE_OID_FIELD(elemtype);
> +     WRITE_INT_FIELD(elmlen);
> +     WRITE_BOOL_FIELD(elmbyval);
> +     WRITE_CHAR_FIELD(elmalign);
>       WRITE_NODE_FIELD(exprs);
>       WRITE_INTLIST_FIELD(paramIds);
>       WRITE_NODE_FIELD(plan);
> Index: src/backend/nodes/readfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v
> retrieving revision 1.153
> diff -c -r1.153 readfuncs.c
> *** src/backend/nodes/readfuncs.c    6 May 2003 00:20:32 -0000    1.153
> --- src/backend/nodes/readfuncs.c    9 Jun 2003 01:31:10 -0000
> ***************
> *** 415,420 ****
> --- 415,421 ----
>       READ_NODE_FIELD(target);
>       READ_BOOL_FIELD(aggstar);
>       READ_BOOL_FIELD(aggdistinct);
> +     READ_NODE_FIELD(args);
>
>       READ_DONE();
>   }
> ***************
> *** 544,549 ****
> --- 545,551 ----
>
>       READ_ENUM_FIELD(subLinkType, SubLinkType);
>       READ_BOOL_FIELD(useOr);
> +     READ_BOOL_FIELD(isExpr);
>       READ_NODE_FIELD(lefthand);
>       READ_NODE_FIELD(operName);
>       READ_OIDLIST_FIELD(operOids);
> Index: src/backend/optimizer/plan/subselect.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/plan/subselect.c,v
> retrieving revision 1.75
> diff -c -r1.75 subselect.c
> *** src/backend/optimizer/plan/subselect.c    29 Apr 2003 22:13:09 -0000    1.75
> --- src/backend/optimizer/plan/subselect.c    9 Jun 2003 01:31:10 -0000
> ***************
> *** 67,73 ****
>
>   static List *convert_sublink_opers(List *lefthand, List *operOids,
>                                      List *targetlist, int rtindex,
> !                                    List **righthandIds);
>   static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
>   static Node *replace_correlation_vars_mutator(Node *node, void *context);
>   static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
> --- 67,73 ----
>
>   static List *convert_sublink_opers(List *lefthand, List *operOids,
>                                      List *targetlist, int rtindex,
> !                                    bool isExpr, List **righthandIds);
>   static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
>   static Node *replace_correlation_vars_mutator(Node *node, void *context);
>   static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
> ***************
> *** 240,245 ****
> --- 240,251 ----
>        */
>       node->subLinkType = slink->subLinkType;
>       node->useOr = slink->useOr;
> +     node->isExpr = slink->isExpr;
> +     node->exprtype = InvalidOid;
> +     node->elemtype = InvalidOid;
> +     node->elmlen = 0;
> +     node->elmbyval = false;
> +     node->elmalign = '\0';
>       node->exprs = NIL;
>       node->paramIds = NIL;
>       node->useHashTable = false;
> ***************
> *** 316,322 ****
>           exprs = convert_sublink_opers(lefthand,
>                                         slink->operOids,
>                                         plan->targetlist,
> !                                       0,
>                                         &node->paramIds);
>           node->setParam = listCopy(node->paramIds);
>           PlannerInitPlan = lappend(PlannerInitPlan, node);
> --- 322,328 ----
>           exprs = convert_sublink_opers(lefthand,
>                                         slink->operOids,
>                                         plan->targetlist,
> !                                       0, node->isExpr,
>                                         &node->paramIds);
>           node->setParam = listCopy(node->paramIds);
>           PlannerInitPlan = lappend(PlannerInitPlan, node);
> ***************
> *** 399,405 ****
>           node->exprs = convert_sublink_opers(lefthand,
>                                               slink->operOids,
>                                               plan->targetlist,
> !                                             0,
>                                               &node->paramIds);
>
>           /*
> --- 405,411 ----
>           node->exprs = convert_sublink_opers(lefthand,
>                                               slink->operOids,
>                                               plan->targetlist,
> !                                             0, node->isExpr,
>                                               &node->paramIds);
>
>           /*
> ***************
> *** 444,450 ****
>   static List *
>   convert_sublink_opers(List *lefthand, List *operOids,
>                         List *targetlist, int rtindex,
> !                       List **righthandIds)
>   {
>       List       *result = NIL;
>       List       *lst;
> --- 450,456 ----
>   static List *
>   convert_sublink_opers(List *lefthand, List *operOids,
>                         List *targetlist, int rtindex,
> !                       bool isExpr, List **righthandIds)
>   {
>       List       *result = NIL;
>       List       *lst;
> ***************
> *** 499,511 ****
>            * are not expecting to have to resolve unknown Params, so
>            * it's okay to pass a null pstate.)
>            */
> !         result = lappend(result,
> !                          make_op_expr(NULL,
> !                                       tup,
> !                                       leftop,
> !                                       rightop,
> !                                       exprType(leftop),
> !                                       te->resdom->restype));
>
>           ReleaseSysCache(tup);
>
> --- 505,542 ----
>            * are not expecting to have to resolve unknown Params, so
>            * it's okay to pass a null pstate.)
>            */
> !         if (!isExpr)
> !         {
> !             result = lappend(result,
> !                              make_op_expr(NULL,
> !                                           tup,
> !                                           leftop,
> !                                           rightop,
> !                                           exprType(leftop),
> !                                           te->resdom->restype));
> !         }
> !         else
> !         {
> !             Oid        exprtype = te->resdom->restype;
> !             Oid        elemtype = get_element_type(exprtype);
> !
> !             if (elemtype != InvalidOid)
> !                 result = lappend(result,
> !                                  make_op_expr(NULL,
> !                                               tup,
> !                                               leftop,
> !                                               rightop,
> !                                               exprType(leftop),
> !                                               elemtype));
> !             else
> !                 result = lappend(result,
> !                                  make_op_expr(NULL,
> !                                               tup,
> !                                               leftop,
> !                                               rightop,
> !                                               exprType(leftop),
> !                                               exprtype));
> !         }
>
>           ReleaseSysCache(tup);
>
> ***************
> *** 616,628 ****
>       /*
>        * The sublink type must be "= ANY" --- that is, an IN operator.
>        * (We require the operator name to be unqualified, which may be
> !      * overly paranoid, or may not be.)
>        */
>       if (sublink->subLinkType != ANY_SUBLINK)
>           return NULL;
>       if (length(sublink->operName) != 1 ||
>           strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
>           return NULL;
>       /*
>        * The sub-select must not refer to any Vars of the parent query.
>        * (Vars of higher levels should be okay, though.)
> --- 647,663 ----
>       /*
>        * The sublink type must be "= ANY" --- that is, an IN operator.
>        * (We require the operator name to be unqualified, which may be
> !      * overly paranoid, or may not be.) It must not be an Expression
> !      * sublink.
>        */
>       if (sublink->subLinkType != ANY_SUBLINK)
>           return NULL;
>       if (length(sublink->operName) != 1 ||
>           strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
>           return NULL;
> +     if (sublink->isExpr)
> +         return NULL;
> +
>       /*
>        * The sub-select must not refer to any Vars of the parent query.
>        * (Vars of higher levels should be okay, though.)
> ***************
> *** 675,681 ****
>       exprs = convert_sublink_opers(sublink->lefthand,
>                                     sublink->operOids,
>                                     subselect->targetList,
> !                                   rtindex,
>                                     &ininfo->sub_targetlist);
>       return (Node *) make_ands_explicit(exprs);
>   }
> --- 710,716 ----
>       exprs = convert_sublink_opers(sublink->lefthand,
>                                     sublink->operOids,
>                                     subselect->targetList,
> !                                   rtindex, sublink->isExpr,
>                                     &ininfo->sub_targetlist);
>       return (Node *) make_ands_explicit(exprs);
>   }
> Index: src/backend/optimizer/util/clauses.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/util/clauses.c,v
> retrieving revision 1.138
> diff -c -r1.138 clauses.c
> *** src/backend/optimizer/util/clauses.c    28 May 2003 22:32:49 -0000    1.138
> --- src/backend/optimizer/util/clauses.c    9 Jun 2003 01:31:10 -0000
> ***************
> *** 133,138 ****
> --- 133,160 ----
>   }
>
>   /*****************************************************************************
> +  *              FUNCTION clause functions
> +  *****************************************************************************/
> +
> + /*
> +  * make_funcclause
> +  *        Creates a function clause given its function info and argument list.
> +  */
> + Expr *
> + make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
> +                             CoercionForm funcformat, List *funcargs)
> + {
> +     FuncExpr   *expr = makeNode(FuncExpr);
> +
> +     expr->funcid = funcid;
> +     expr->funcresulttype = funcresulttype;
> +     expr->funcretset = funcretset;
> +     expr->funcformat = funcformat;
> +     expr->args = funcargs;
> +     return (Expr *) expr;
> + }
> +
> + /*****************************************************************************
>    *        NOT clause functions
>    *****************************************************************************/
>
> Index: src/backend/parser/gram.y
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/gram.y,v
> retrieving revision 2.416
> diff -c -r2.416 gram.y
> *** src/backend/parser/gram.y    29 May 2003 20:40:36 -0000    2.416
> --- src/backend/parser/gram.y    9 Jun 2003 01:31:10 -0000
> ***************
> *** 5490,5495 ****
> --- 5490,5496 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = ANY_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = makeList1(makeString("="));
>                       n->subselect = $3;
> ***************
> *** 5500,5505 ****
> --- 5501,5507 ----
>                       /* Make an IN node */
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = ANY_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = makeList1(makeString("="));
>                       n->subselect = $4;
> ***************
> *** 5511,5516 ****
> --- 5513,5519 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = $3;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = $2;
>                       n->subselect = $4;
> ***************
> *** 5521,5526 ****
> --- 5524,5530 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = MULTIEXPR_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = $2;
>                       n->subselect = $3;
> ***************
> *** 5904,5909 ****
> --- 5908,5914 ----
>                       {
>                               SubLink *n = (SubLink *)$3;
>                               n->subLinkType = ANY_SUBLINK;
> +                             n->isExpr = false;
>                               n->lefthand = makeList1($1);
>                               n->operName = makeList1(makeString("="));
>                               $$ = (Node *)n;
> ***************
> *** 5931,5936 ****
> --- 5936,5942 ----
>                       {
>                           /* Make an IN node */
>                           SubLink *n = (SubLink *)$4;
> +                         n->isExpr = false;
>                           n->subLinkType = ANY_SUBLINK;
>                           n->lefthand = makeList1($1);
>                           n->operName = makeList1(makeString("="));
> ***************
> *** 5957,5967 ****
> --- 5963,6000 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = $3;
> +                     n->isExpr = false;
>                       n->lefthand = makeList1($1);
>                       n->operName = $2;
>                       n->subselect = $4;
>                       $$ = (Node *)n;
>                   }
> +             | a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op
> +                 {
> +                     SubLink *n = makeNode(SubLink);
> +                     SelectStmt *s = makeNode(SelectStmt);
> +                     ResTarget *r = makeNode(ResTarget);
> +
> +                     r->name = NULL;
> +                     r->indirection = NIL;
> +                     r->val = (Node *)$5;
> +
> +                     s->distinctClause = NIL;
> +                     s->targetList = makeList1(r);
> +                     s->into = NULL;
> +                     s->intoColNames = NIL;
> +                     s->fromClause = NIL;
> +                     s->whereClause = NULL;
> +                     s->groupClause = NIL;
> +                     s->havingClause = NULL;
> +
> +                     n->subLinkType = $3;
> +                     n->isExpr = true;
> +                     n->lefthand = makeList1($1);
> +                     n->operName = $2;
> +                     n->subselect = (Node *) s;
> +                     $$ = (Node *)n;
> +                 }
>               | UNIQUE select_with_parens %prec Op
>                   {
>                       /* Not sure how to get rid of the parentheses
> ***************
> *** 6538,6543 ****
> --- 6571,6577 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = EXPR_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = NIL;
>                       n->operName = NIL;
>                       n->subselect = $1;
> ***************
> *** 6547,6552 ****
> --- 6581,6587 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = EXISTS_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = NIL;
>                       n->operName = NIL;
>                       n->subselect = $2;
> ***************
> *** 6556,6561 ****
> --- 6591,6597 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = ARRAY_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = NIL;
>                       n->operName = NIL;
>                       n->subselect = $2;
> ***************
> *** 6730,6735 ****
> --- 6766,6772 ----
>   in_expr:    select_with_parens
>                   {
>                       SubLink *n = makeNode(SubLink);
> +                     n->isExpr = false;
>                       n->subselect = $1;
>                       /* other fields will be filled later */
>                       $$ = (Node *)n;
> Index: src/backend/parser/parse_coerce.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v
> retrieving revision 2.97
> diff -c -r2.97 parse_coerce.c
> *** src/backend/parser/parse_coerce.c    26 May 2003 00:11:27 -0000    2.97
> --- src/backend/parser/parse_coerce.c    9 Jun 2003 01:31:10 -0000
> ***************
> *** 859,865 ****
>       /* Get the element type based on the array type, if we have one */
>       if (OidIsValid(array_typeid))
>       {
> !         array_typelem = get_element_type(array_typeid);
>           if (!OidIsValid(array_typelem))
>               elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
>                    format_type_be(array_typeid));
> --- 859,869 ----
>       /* Get the element type based on the array type, if we have one */
>       if (OidIsValid(array_typeid))
>       {
> !         if (array_typeid != ANYARRAYOID)
> !             array_typelem = get_element_type(array_typeid);
> !         else
> !             array_typelem = ANYELEMENTOID;
> !
>           if (!OidIsValid(array_typelem))
>               elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
>                    format_type_be(array_typeid));
> ***************
> *** 919,925 ****
>       {
>           if (!OidIsValid(array_typeid))
>           {
> !             array_typeid = get_array_type(elem_typeid);
>               if (!OidIsValid(array_typeid))
>                   elog(ERROR, "Cannot find array type for datatype %s",
>                        format_type_be(elem_typeid));
> --- 923,933 ----
>       {
>           if (!OidIsValid(array_typeid))
>           {
> !             if (elem_typeid != ANYELEMENTOID)
> !                 array_typeid = get_array_type(elem_typeid);
> !             else
> !                 array_typeid = ANYARRAYOID;
> !
>               if (!OidIsValid(array_typeid))
>                   elog(ERROR, "Cannot find array type for datatype %s",
>                        format_type_be(elem_typeid));
> ***************
> *** 1169,1174 ****
> --- 1177,1187 ----
>       /* Somewhat-fast path for domain -> base type case */
>       if (srctype == targettype)
>           return true;
> +
> +     /* Last of the fast-paths: check for matching polymorphic arrays */
> +     if (targettype == ANYARRAYOID)
> +         if (get_element_type(srctype) != InvalidOid)
> +             return true;
>
>       /* Else look in pg_cast */
>       tuple = SearchSysCache(CASTSOURCETARGET,
> Index: src/backend/parser/parse_expr.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_expr.c,v
> retrieving revision 1.148
> diff -c -r1.148 parse_expr.c
> *** src/backend/parser/parse_expr.c    29 Apr 2003 22:13:10 -0000    1.148
> --- src/backend/parser/parse_expr.c    9 Jun 2003 01:42:58 -0000
> ***************
> *** 436,441 ****
> --- 436,442 ----
>                       sublink->operName = NIL;
>                       sublink->operOids = NIL;
>                       sublink->useOr = FALSE;
> +                     sublink->isExpr = FALSE;
>                   }
>                   else if (sublink->subLinkType == EXPR_SUBLINK ||
>                            sublink->subLinkType == ARRAY_SUBLINK)
> ***************
> *** 463,468 ****
> --- 464,470 ----
>                       sublink->operName = NIL;
>                       sublink->operOids = NIL;
>                       sublink->useOr = FALSE;
> +                     sublink->isExpr = FALSE;
>                   }
>                   else
>                   {
> ***************
> *** 538,547 ****
>                            * here, because make_subplan() will insert type
>                            * coercion calls if needed.
>                            */
> !                         optup = oper(op,
> !                                      exprType(lexpr),
> !                                      exprType((Node *) tent->expr),
> !                                      false);
>                           opform = (Form_pg_operator) GETSTRUCT(optup);
>
>                           if (opform->oprresult != BOOLOID)
> --- 540,569 ----
>                            * here, because make_subplan() will insert type
>                            * coercion calls if needed.
>                            */
> !                         if (!sublink->isExpr)
> !                         {
> !                             optup = oper(op,
> !                                          exprType(lexpr),
> !                                          exprType((Node *) tent->expr),
> !                                          false);
> !                         }
> !                         else
> !                         {
> !                             Oid        exprtype = exprType((Node *) tent->expr);
> !                             Oid        elemtype = get_element_type(exprtype);
> !
> !                             if (elemtype != InvalidOid)
> !                                 optup = oper(op,
> !                                              exprType(lexpr),
> !                                              elemtype,
> !                                              false);
> !                             else
> !                                 optup = oper(op,
> !                                              exprType(lexpr),
> !                                              exprtype,
> !                                              false);
> !                         }
> !
>                           opform = (Form_pg_operator) GETSTRUCT(optup);
>
>                           if (opform->oprresult != BOOLOID)
> ***************
> *** 743,749 ****
>                           ArrayExpr  *e = (ArrayExpr *) lfirst(element);
>
>                           if (!IsA(e, ArrayExpr))
> !                             elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions");
>                           if (ndims == 0)
>                               ndims = e->ndims;
>                           else if (e->ndims != ndims)
> --- 765,771 ----
>                           ArrayExpr  *e = (ArrayExpr *) lfirst(element);
>
>                           if (!IsA(e, ArrayExpr))
> !                             elog(ERROR, "Multidimensional ARRAY[] must be built from nested array expressions");
>                           if (ndims == 0)
>                               ndims = e->ndims;
>                           else if (e->ndims != ndims)
> Index: src/backend/parser/parse_func.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_func.c,v
> retrieving revision 1.148
> diff -c -r1.148 parse_func.c
> *** src/backend/parser/parse_func.c    26 May 2003 00:11:27 -0000    1.148
> --- src/backend/parser/parse_func.c    9 Jun 2003 01:31:10 -0000
> ***************
> *** 335,340 ****
> --- 335,341 ----
>           aggref->target = lfirst(fargs);
>           aggref->aggstar = agg_star;
>           aggref->aggdistinct = agg_distinct;
> +         aggref->args = fargs;
>
>           retval = (Node *) aggref;
>
> Index: src/backend/parser/parse_oper.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_oper.c,v
> retrieving revision 1.64
> diff -c -r1.64 parse_oper.c
> *** src/backend/parser/parse_oper.c    26 May 2003 00:11:27 -0000    1.64
> --- src/backend/parser/parse_oper.c    9 Jun 2003 01:31:10 -0000
> ***************
> *** 137,142 ****
> --- 137,169 ----
>   equality_oper(Oid argtype, bool noError)
>   {
>       Operator    optup;
> +     Oid            elem_type = get_element_type(argtype);
> +
> +     if (OidIsValid(elem_type))
> +     {
> +         bool    found = false;
> +         /*
> +          * If the datatype is an array, look for an "=" operator for the
> +          * element datatype.  We require it to be an exact or binary-compatible
> +          * match, since most callers are not prepared to cope with adding any
> +          * run-time type coercion steps.
> +          */
> +         optup = equality_oper(elem_type, true);
> +         if (optup != NULL)
> +         {
> +             found = true;
> +             ReleaseSysCache(optup);
> +         }
> +
> +         if (!found)
> +         {
> +             if (!noError)
> +                 elog(ERROR, "Unable to identify an equality operator for " \
> +                             "array type's element type %s",
> +                              format_type_be(elem_type));
> +             return NULL;
> +         }
> +     }
>
>       /*
>        * Look for an "=" operator for the datatype.  We require it to be
> ***************
> *** 175,180 ****
> --- 202,234 ----
>   ordering_oper(Oid argtype, bool noError)
>   {
>       Operator    optup;
> +     Oid            elem_type = get_element_type(argtype);
> +
> +     if (OidIsValid(elem_type))
> +     {
> +         bool    found = false;
> +         /*
> +          * If the datatype is an array, find the array element type's equality
> +          * operator, and use its lsortop (it *must* be mergejoinable).  We use
> +          * this definition because for sorting and grouping purposes, it's
> +          * important that the equality and ordering operators are consistent.
> +          */
> +         optup = ordering_oper(elem_type, true);
> +         if (optup != NULL)
> +         {
> +             found = true;
> +             ReleaseSysCache(optup);
> +         }
> +
> +         if (!found)
> +         {
> +             if (!noError)
> +                 elog(ERROR, "Unable to identify an ordering operator for " \
> +                             "array type's element type %s",
> +                              format_type_be(elem_type));
> +             return NULL;
> +         }
> +     }
>
>       /*
>        * Find the type's equality operator, and use its lsortop (it *must*
> ***************
> *** 215,220 ****
> --- 269,289 ----
>       Oid            result;
>
>       optup = equality_oper(argtype, false);
> +     result = oprfuncid(optup);
> +     ReleaseSysCache(optup);
> +     return result;
> + }
> +
> + /*
> +  * ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper())
> +  */
> + Oid
> + ordering_oper_funcid(Oid argtype)
> + {
> +     Operator    optup;
> +     Oid            result;
> +
> +     optup = ordering_oper(argtype, false);
>       result = oprfuncid(optup);
>       ReleaseSysCache(optup);
>       return result;
> Index: src/backend/utils/adt/acl.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/acl.c,v
> retrieving revision 1.86
> diff -c -r1.86 acl.c
> *** src/backend/utils/adt/acl.c    24 Jan 2003 21:53:29 -0000    1.86
> --- src/backend/utils/adt/acl.c    9 Jun 2003 01:31:10 -0000
> ***************
> *** 720,732 ****
>       aidat = ACL_DAT(acl);
>       for (i = 0; i < num; ++i)
>       {
> !         if (aip->ai_grantee == aidat[i].ai_grantee &&
> !             aip->ai_privs == aidat[i].ai_privs)
>               PG_RETURN_BOOL(true);
>       }
>       PG_RETURN_BOOL(false);
>   }
>
>
>   /*
>    * has_table_privilege variants
> --- 720,740 ----
>       aidat = ACL_DAT(acl);
>       for (i = 0; i < num; ++i)
>       {
> !         if (aclitemeq(aip, &aidat[i]))
>               PG_RETURN_BOOL(true);
>       }
>       PG_RETURN_BOOL(false);
>   }
>
> + /*
> +  * user-facing version of aclitemeq() for use as the
> +  * aclitem equality operator
> +  */
> + Datum
> + aclitem_eq(PG_FUNCTION_ARGS)
> + {
> +     PG_RETURN_BOOL(aclitemeq(PG_GETARG_ACLITEM_P(0), PG_GETARG_ACLITEM_P(1)));
> + }
>
>   /*
>    * has_table_privilege variants
> Index: src/backend/utils/adt/array_userfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/array_userfuncs.c,v
> retrieving revision 1.1
> diff -c -r1.1 array_userfuncs.c
> *** src/backend/utils/adt/array_userfuncs.c    8 Apr 2003 23:20:02 -0000    1.1
> --- src/backend/utils/adt/array_userfuncs.c    9 Jun 2003 01:46:03 -0000
> ***************
> *** 18,52 ****
>   #include "utils/lsyscache.h"
>   #include "utils/syscache.h"
>
> -
> - /*-----------------------------------------------------------------------------
> -  * singleton_array :
> -  *        Form a multi-dimensional array given one starting element.
> -  *
> -  * - first argument is the datum with which to build the array
> -  * - second argument is the number of dimensions the array should have;
> -  *     defaults to 1 if no second argument is provided
> -  *----------------------------------------------------------------------------
> -  */
> - Datum
> - singleton_array(PG_FUNCTION_ARGS)
> - {
> -     Oid            elem_type = get_fn_expr_argtype(fcinfo, 0);
> -     int            ndims;
> -
> -     if (elem_type == InvalidOid)
> -         elog(ERROR, "Cannot determine input datatype");
> -
> -     if (PG_NARGS() == 2)
> -         ndims = PG_GETARG_INT32(1);
> -     else
> -         ndims = 1;
> -
> -     PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
> -                                                  PG_GETARG_DATUM(0),
> -                                                  ndims));
> - }
> -
>   /*-----------------------------------------------------------------------------
>    * array_push :
>    *        push an element onto either end of a one-dimensional array
> --- 18,23 ----
> ***************
> *** 70,75 ****
> --- 41,47 ----
>       Oid            arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
>       Oid            arg0_elemid;
>       Oid            arg1_elemid;
> +     ArrayMetaState *my_extra;
>
>       if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
>           elog(ERROR, "array_push: cannot determine input data types");
> ***************
> *** 95,122 ****
>           PG_RETURN_NULL();        /* keep compiler quiet */
>       }
>
> !     /* Sanity check: do we have a one-dimensional array */
> !     if (ARR_NDIM(v) != 1)
> !         elog(ERROR, "Arrays greater than one-dimension are not supported");
> !
> !     lb = ARR_LBOUND(v);
> !     dimv = ARR_DIMS(v);
> !     if (arg0_elemid != InvalidOid)
>       {
> !         /* append newelem */
> !         int    ub = dimv[0] + lb[0] - 1;
> !         indx = ub + 1;
>       }
>       else
>       {
> !         /* prepend newelem */
> !         indx = lb[0] - 1;
>       }
>
> !     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
> !     result = array_set(v, 1, &indx, newelem, -1,
> !                        typlen, typbyval, typalign, &isNull);
>
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
> --- 67,127 ----
>           PG_RETURN_NULL();        /* keep compiler quiet */
>       }
>
> !     if (ARR_NDIM(v) == 1)
>       {
> !         lb = ARR_LBOUND(v);
> !         dimv = ARR_DIMS(v);
> !
> !         if (arg0_elemid != InvalidOid)
> !         {
> !             /* append newelem */
> !             int    ub = dimv[0] + lb[0] - 1;
> !             indx = ub + 1;
> !         }
> !         else
> !         {
> !             /* prepend newelem */
> !             indx = lb[0] - 1;
> !         }
>       }
> +     else if (ARR_NDIM(v) == 0)
> +         indx = 1;
>       else
> +         elog(ERROR, "only empty and one-dimensional arrays are supported");
> +
> +
> +     /*
> +      * We arrange to look up info about element type only once per series
> +      * of calls, assuming the element type doesn't change underneath us.
> +      */
> +     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> +     if (my_extra == NULL)
>       {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
>       }
>
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type */
> !         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typalign = my_extra->typalign;
> !     }
> !
> !     result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval,
> !                                                  typalign, &isNull);
>
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
> ***************
> *** 145,157 ****
>
>       /*
>        * We must have one of the following combinations of inputs:
> !      * 1) two arrays with ndims1 == ndims2
> !      * 2) ndims1 == ndims2 - 1
> !      * 3) ndims1 == ndims2 + 1
>        */
>       ndims1 = ARR_NDIM(v1);
>       ndims2 = ARR_NDIM(v2);
>
>       if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
>           elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
>                       "%d dimensions", ndims1, ndims2);
> --- 150,177 ----
>
>       /*
>        * We must have one of the following combinations of inputs:
> !      * 1) one empty array, and one non-empty array
> !      * 2) both arrays empty
> !      * 3) two arrays with ndims1 == ndims2
> !      * 4) ndims1 == ndims2 - 1
> !      * 5) ndims1 == ndims2 + 1
>        */
>       ndims1 = ARR_NDIM(v1);
>       ndims2 = ARR_NDIM(v2);
>
> +     /*
> +      * short circuit - if one input array is empty, and the other is not,
> +      * we return the non-empty one as the result
> +      *
> +      * if both are empty, return the first one
> +      */
> +     if (ndims1 == 0 && ndims2 > 0)
> +         PG_RETURN_ARRAYTYPE_P(v2);
> +
> +     if (ndims2 == 0)
> +         PG_RETURN_ARRAYTYPE_P(v1);
> +
> +     /* the rest fall into combo 2, 3, or 4 */
>       if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
>           elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
>                       "%d dimensions", ndims1, ndims2);
> ***************
> *** 266,412 ****
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
>
> - /*----------------------------------------------------------------------------
> -  * array_accum :
> -  *        accumulator to build a 1-D array from input values -- this can be used
> -  *        to create custom aggregates.
> -  *
> -  * This function is not marked strict, so we have to be careful about nulls.
> -  *----------------------------------------------------------------------------
> -  */
> - Datum
> - array_accum(PG_FUNCTION_ARGS)
> - {
> -     /* return NULL if both arguments are NULL */
> -     if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
> -         PG_RETURN_NULL();
> -
> -     /* create a new 1-D array from the new element if the array is NULL */
> -     if (PG_ARGISNULL(0))
> -     {
> -         Oid            tgt_type = get_fn_expr_rettype(fcinfo);
> -         Oid            tgt_elem_type;
> -
> -         if (tgt_type == InvalidOid)
> -             elog(ERROR, "Cannot determine target array type");
> -         tgt_elem_type = get_element_type(tgt_type);
> -         if (tgt_elem_type == InvalidOid)
> -             elog(ERROR, "Target type is not an array");
> -
> -         PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
> -                                                      PG_GETARG_DATUM(1),
> -                                                      1));
> -     }
> -
> -     /* return the array if the new element is NULL */
> -     if (PG_ARGISNULL(1))
> -         PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0));
> -
> -     /*
> -      * Otherwise this is equivalent to array_push.  We hack the call a little
> -      * so that array_push can see the fn_expr information.
> -      */
> -     return array_push(fcinfo);
> - }
> -
> - /*-----------------------------------------------------------------------------
> -  * array_assign :
> -  *        assign an element of an array to a new value and return the
> -  *        redefined array
> -  *----------------------------------------------------------------------------
> -  */
> - Datum
> - array_assign(PG_FUNCTION_ARGS)
> - {
> -     ArrayType  *v;
> -     int            idx_to_chg;
> -     Datum        newelem;
> -     int           *dimv,
> -                *lb, ub;
> -     ArrayType  *result;
> -     bool        isNull;
> -     Oid            element_type;
> -     int16        typlen;
> -     bool        typbyval;
> -     char        typalign;
> -
> -     v = PG_GETARG_ARRAYTYPE_P(0);
> -     idx_to_chg = PG_GETARG_INT32(1);
> -     newelem = PG_GETARG_DATUM(2);
> -
> -     /* Sanity check: do we have a one-dimensional array */
> -     if (ARR_NDIM(v) != 1)
> -         elog(ERROR, "Arrays greater than one-dimension are not supported");
> -
> -     lb = ARR_LBOUND(v);
> -     dimv = ARR_DIMS(v);
> -     ub = dimv[0] + lb[0] - 1;
> -     if (idx_to_chg < lb[0] || idx_to_chg > ub)
> -         elog(ERROR, "Cannot alter nonexistent array element: %d", idx_to_chg);
> -
> -     element_type = ARR_ELEMTYPE(v);
> -     /* Sanity check: do we have a non-zero element type */
> -     if (element_type == 0)
> -         elog(ERROR, "Invalid array element type: %u", element_type);
> -
> -     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
> -
> -     result = array_set(v, 1, &idx_to_chg, newelem, -1,
> -                        typlen, typbyval, typalign, &isNull);
> -
> -     PG_RETURN_ARRAYTYPE_P(result);
> - }
> -
> - /*-----------------------------------------------------------------------------
> -  * array_subscript :
> -  *        return specific element of an array
> -  *----------------------------------------------------------------------------
> -  */
> - Datum
> - array_subscript(PG_FUNCTION_ARGS)
> - {
> -     ArrayType  *v;
> -     int            idx;
> -     int           *dimv,
> -                *lb, ub;
> -     Datum        result;
> -     bool        isNull;
> -     Oid            element_type;
> -     int16        typlen;
> -     bool        typbyval;
> -     char        typalign;
> -
> -     v = PG_GETARG_ARRAYTYPE_P(0);
> -     idx = PG_GETARG_INT32(1);
> -
> -     /* Sanity check: do we have a one-dimensional array */
> -     if (ARR_NDIM(v) != 1)
> -         elog(ERROR, "Arrays greater than one-dimension are not supported");
> -
> -     lb = ARR_LBOUND(v);
> -     dimv = ARR_DIMS(v);
> -     ub = dimv[0] + lb[0] - 1;
> -     if (idx < lb[0] || idx > ub)
> -         elog(ERROR, "Cannot return nonexistent array element: %d", idx);
> -
> -     element_type = ARR_ELEMTYPE(v);
> -     /* Sanity check: do we have a non-zero element type */
> -     if (element_type == 0)
> -         elog(ERROR, "Invalid array element type: %u", element_type);
> -
> -     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
> -
> -     result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);
> -
> -     PG_RETURN_DATUM(result);
> - }
>
>   /*
> !  * actually does the work for singleton_array(), and array_accum() if it is
> !  * given a null input array.
>    */
>   ArrayType *
> ! create_singleton_array(Oid element_type, Datum element, int ndims)
>   {
>       Datum    dvalues[1];
>       int16    typlen;
> --- 286,300 ----
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
>
>
>   /*
> !  * used by text_to_array() in varlena.c
>    */
>   ArrayType *
> ! create_singleton_array(FunctionCallInfo fcinfo,
> !                        Oid element_type,
> !                        Datum element,
> !                        int ndims)
>   {
>       Datum    dvalues[1];
>       int16    typlen;
> ***************
> *** 415,420 ****
> --- 303,309 ----
>       int        dims[MAXDIM];
>       int        lbs[MAXDIM];
>       int        i;
> +     ArrayMetaState *my_extra;
>
>       if (element_type == 0)
>           elog(ERROR, "Invalid array element type: %u", element_type);
> ***************
> *** 429,435 ****
>           lbs[i] = 1;
>       }
>
> !     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
>       return construct_md_array(dvalues, ndims, dims, lbs, element_type,
>                                 typlen, typbyval, typalign);
> --- 318,352 ----
>           lbs[i] = 1;
>       }
>
> !     /*
> !      * We arrange to look up info about element type only once per series
> !      * of calls, assuming the element type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type */
> !         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typalign = my_extra->typalign;
> !     }
>
>       return construct_md_array(dvalues, ndims, dims, lbs, element_type,
>                                 typlen, typbyval, typalign);
> Index: src/backend/utils/adt/arrayfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v
> retrieving revision 1.89
> diff -c -r1.89 arrayfuncs.c
> *** src/backend/utils/adt/arrayfuncs.c    9 May 2003 23:01:45 -0000    1.89
> --- src/backend/utils/adt/arrayfuncs.c    9 Jun 2003 01:31:10 -0000
> ***************
> *** 21,28 ****
> --- 21,30 ----
>   #include "catalog/pg_type.h"
>   #include "libpq/pqformat.h"
>   #include "parser/parse_coerce.h"
> + #include "parser/parse_oper.h"
>   #include "utils/array.h"
>   #include "utils/builtins.h"
> + #include "utils/datum.h"
>   #include "utils/memutils.h"
>   #include "utils/lsyscache.h"
>   #include "utils/syscache.h"
> ***************
> *** 70,85 ****
>
>   #define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)
>
> - /* I/O function selector for system_cache_lookup */
> - typedef enum IOFuncSelector
> - {
> -     IOFunc_input,
> -     IOFunc_output,
> -     IOFunc_receive,
> -     IOFunc_send
> - } IOFuncSelector;
> -
> -
>   static int    ArrayCount(char *str, int *dim, char typdelim);
>   static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
>                FmgrInfo *inputproc, Oid typelem, int32 typmod,
> --- 72,77 ----
> ***************
> *** 93,102 ****
>   static void CopyArrayEls(char *p, Datum *values, int nitems,
>                int typlen, bool typbyval, char typalign,
>                bool freedata);
> - static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
> -                                 int *typlen, bool *typbyval,
> -                                 char *typdelim, Oid *typelem,
> -                                 Oid *proc, char *typalign);
>   static Datum ArrayCast(char *value, bool byval, int len);
>   static int ArrayCastAndSet(Datum src,
>                   int typlen, bool typbyval, char typalign,
> --- 85,90 ----
> ***************
> *** 119,125 ****
>                      char *destPtr,
>                      int *st, int *endp, char *srcPtr,
>                      int typlen, bool typbyval, char typalign);
> !
>
>   /*---------------------------------------------------------------------
>    * array_in :
> --- 107,113 ----
>                      char *destPtr,
>                      int *st, int *endp, char *srcPtr,
>                      int typlen, bool typbyval, char typalign);
> ! static int array_cmp(FunctionCallInfo fcinfo);
>
>   /*---------------------------------------------------------------------
>    * array_in :
> ***************
> *** 154,165 ****
>                   dim[MAXDIM],
>                   lBound[MAXDIM];
>       char        typalign;
>
> !     /* Get info about element type, including its input conversion proc */
> !     system_cache_lookup(element_type, IOFunc_input,
> !                         &typlen, &typbyval, &typdelim,
> !                         &typelem, &typinput, &typalign);
> !     fmgr_info(typinput, &inputproc);
>
>       /* Make a modifiable copy of the input */
>       /* XXX why are we allocating an extra 2 bytes here? */
> --- 142,190 ----
>                   dim[MAXDIM],
>                   lBound[MAXDIM];
>       char        typalign;
> +     ArrayMetaState *my_extra;
>
> !     /*
> !      * We arrange to look up info about element type, including its input
> !      * conversion proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its input conversion proc */
> !         get_type_metadata(element_type, IOFunc_input,
> !                             &typlen, &typbyval, &typdelim,
> !                             &typelem, &typinput, &typalign);
> !         fmgr_info(typinput, &inputproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typinput;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = inputproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typinput = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         inputproc = my_extra->proc;
> !     }
>
>       /* Make a modifiable copy of the input */
>       /* XXX why are we allocating an extra 2 bytes here? */
> ***************
> *** 636,647 ****
>                   indx[MAXDIM];
>       int            ndim,
>                  *dim;
>
>       element_type = ARR_ELEMTYPE(v);
> !     system_cache_lookup(element_type, IOFunc_output,
> !                         &typlen, &typbyval, &typdelim,
> !                         &typelem, &typoutput, &typalign);
> !     fmgr_info(typoutput, &outputproc);
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> --- 661,711 ----
>                   indx[MAXDIM];
>       int            ndim,
>                  *dim;
> +     ArrayMetaState *my_extra;
>
>       element_type = ARR_ELEMTYPE(v);
> !
> !     /*
> !      * We arrange to look up info about element type, including its input
> !      * conversion proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its output conversion proc */
> !         get_type_metadata(element_type, IOFunc_output,
> !                             &typlen, &typbyval, &typdelim,
> !                             &typelem, &typoutput, &typalign);
> !         fmgr_info(typoutput, &outputproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typoutput;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = outputproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typoutput = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         outputproc = my_extra->proc;
> !     }
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> ***************
> *** 800,805 ****
> --- 864,870 ----
>                   dim[MAXDIM],
>                   lBound[MAXDIM];
>       char        typalign;
> +     ArrayMetaState *my_extra;
>
>       /* Get the array header information */
>       ndim = pq_getmsgint(buf, 4);
> ***************
> *** 831,844 ****
>           PG_RETURN_ARRAYTYPE_P(retval);
>       }
>
> !     /* Get info about element type, including its receive conversion proc */
> !     system_cache_lookup(element_type, IOFunc_receive,
> !                         &typlen, &typbyval, &typdelim,
> !                         &typelem, &typreceive, &typalign);
> !     if (!OidIsValid(typreceive))
> !         elog(ERROR, "No binary input function available for type %s",
> !              format_type_be(element_type));
> !     fmgr_info(typreceive, &receiveproc);
>
>       dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
>                                 typlen, typbyval, typalign,
> --- 896,945 ----
>           PG_RETURN_ARRAYTYPE_P(retval);
>       }
>
> !     /*
> !      * We arrange to look up info about element type, including its receive
> !      * conversion proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its receive conversion proc */
> !         get_type_metadata(element_type, IOFunc_receive,
> !                             &typlen, &typbyval, &typdelim,
> !                             &typelem, &typreceive, &typalign);
> !         if (!OidIsValid(typreceive))
> !             elog(ERROR, "No binary input function available for type %s",
> !                  format_type_be(element_type));
> !         fmgr_info(typreceive, &receiveproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typreceive;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = receiveproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typreceive = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         receiveproc = my_extra->proc;
> !     }
>
>       dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
>                                 typlen, typbyval, typalign,
> ***************
> *** 976,990 ****
>       int            ndim,
>                  *dim;
>       StringInfoData buf;
>
>       /* Get information about the element type and the array dimensions */
>       element_type = ARR_ELEMTYPE(v);
> !     system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
> !                         &typdelim, &typelem, &typsend, &typalign);
> !     if (!OidIsValid(typsend))
> !         elog(ERROR, "No binary output function available for type %s",
> !              format_type_be(element_type));
> !     fmgr_info(typsend, &sendproc);
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> --- 1077,1130 ----
>       int            ndim,
>                  *dim;
>       StringInfoData buf;
> +     ArrayMetaState *my_extra;
>
>       /* Get information about the element type and the array dimensions */
>       element_type = ARR_ELEMTYPE(v);
> !
> !     /*
> !      * We arrange to look up info about element type, including its send
> !      * proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its send proc */
> !         get_type_metadata(element_type, IOFunc_send, &typlen, &typbyval,
> !                             &typdelim, &typelem, &typsend, &typalign);
> !         if (!OidIsValid(typsend))
> !             elog(ERROR, "No binary output function available for type %s",
> !                  format_type_be(element_type));
> !         fmgr_info(typsend, &sendproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typsend;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = sendproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typsend = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         sendproc = my_extra->proc;
> !     }
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> ***************
> *** 1476,1481 ****
> --- 1616,1641 ----
>       array = DatumGetArrayTypeP(PointerGetDatum(array));
>
>       ndim = ARR_NDIM(array);
> +
> +     /*
> +      * if number of dims is zero, i.e. an empty array, create an array
> +      * with nSubscripts dimensions, and set the lower bounds to the supplied
> +      * subscripts
> +      */
> +     if (ndim == 0)
> +     {
> +         Oid        elmtype = ARR_ELEMTYPE(array);
> +
> +         for (i = 0; i < nSubscripts; i++)
> +         {
> +             dim[i] = 1;
> +             lb[i] = indx[i];
> +         }
> +
> +         return construct_md_array(&dataValue, nSubscripts, dim, lb, elmtype,
> +                                                 elmlen, elmbyval, elmalign);
> +     }
> +
>       if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
>           elog(ERROR, "Invalid array subscripts");
>
> ***************
> *** 1632,1637 ****
> --- 1792,1822 ----
>       /* note: we assume srcArray contains no toasted elements */
>
>       ndim = ARR_NDIM(array);
> +
> +     /*
> +      * if number of dims is zero, i.e. an empty array, create an array
> +      * with nSubscripts dimensions, and set the upper and lower bounds
> +      * to the supplied subscripts
> +      */
> +     if (ndim == 0)
> +     {
> +         Datum  *dvalues;
> +         int        nelems;
> +         Oid        elmtype = ARR_ELEMTYPE(array);
> +
> +         deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
> +                                                         &dvalues, &nelems);
> +
> +         for (i = 0; i < nSubscripts; i++)
> +         {
> +             dim[i] = 1 + upperIndx[i] - lowerIndx[i];
> +             lb[i] = lowerIndx[i];
> +         }
> +
> +         return construct_md_array(dvalues, nSubscripts, dim, lb, elmtype,
> +                                                  elmlen, elmbyval, elmalign);
> +     }
> +
>       if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
>           elog(ERROR, "Invalid array subscripts");
>
> ***************
> *** 1811,1816 ****
> --- 1996,2008 ----
>       Oid            typelem;
>       Oid            proc;
>       char       *s;
> +     typedef struct {
> +         ArrayMetaState *inp_extra;
> +         ArrayMetaState *ret_extra;
> +     } am_extra;
> +     am_extra  *my_extra;
> +     ArrayMetaState *inp_extra;
> +     ArrayMetaState *ret_extra;
>
>       /* Get input array */
>       if (fcinfo->nargs < 1)
> ***************
> *** 1829,1839 ****
>       if (nitems <= 0)
>           PG_RETURN_ARRAYTYPE_P(v);
>
> !     /* Lookup source and result types. Unneeded variables are reused. */
> !     system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
> !                         &typdelim, &typelem, &proc, &inp_typalign);
> !     system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
> !                         &typdelim, &typelem, &proc, &typalign);
>
>       /* Allocate temporary array for new values */
>       values = (Datum *) palloc(nitems * sizeof(Datum));
> --- 2021,2101 ----
>       if (nitems <= 0)
>           PG_RETURN_ARRAYTYPE_P(v);
>
> !     /*
> !      * We arrange to look up info about input and return element types only
> !      * once per series of calls, assuming the element type doesn't change
> !      * underneath us.
> !      */
> !     my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(am_extra));
> !         my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
> !
> !         my_extra->inp_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         inp_extra = my_extra->inp_extra;
> !         inp_extra->element_type = InvalidOid;
> !
> !         my_extra->ret_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         ret_extra = my_extra->ret_extra;
> !         ret_extra->element_type = InvalidOid;
> !     }
> !     else
> !     {
> !         inp_extra = my_extra->inp_extra;
> !         ret_extra = my_extra->ret_extra;
> !     }
> !
> !     if (inp_extra->element_type != inpType)
> !     {
> !         /* Lookup source and result types. Unneeded variables are reused. */
> !         get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
> !                             &typdelim, &typelem, &proc, &inp_typalign);
> !
> !         inp_extra->element_type = inpType;
> !         inp_extra->typlen = inp_typlen;
> !         inp_extra->typbyval = inp_typbyval;
> !         inp_extra->typdelim = typdelim;
> !         inp_extra->typelem = typelem;
> !         inp_extra->typiofunc = proc;
> !         inp_extra->typalign = inp_typalign;
> !     }
> !     else
> !     {
> !         inp_typlen = inp_extra->typlen;
> !         inp_typbyval = inp_extra->typbyval;
> !         typdelim = inp_extra->typdelim;
> !         typelem = inp_extra->typelem;
> !         proc = inp_extra->typiofunc;
> !         inp_typalign = inp_extra->typalign;
> !     }
> !
> !     if (ret_extra->element_type != retType)
> !     {
> !         /* Lookup source and result types. Unneeded variables are reused. */
> !         get_type_metadata(retType, IOFunc_input, &typlen, &typbyval,
> !                             &typdelim, &typelem, &proc, &typalign);
> !
> !         ret_extra->element_type = retType;
> !         ret_extra->typlen = typlen;
> !         ret_extra->typbyval = typbyval;
> !         ret_extra->typdelim = typdelim;
> !         ret_extra->typelem = typelem;
> !         ret_extra->typiofunc = proc;
> !         ret_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = ret_extra->typlen;
> !         typbyval = ret_extra->typbyval;
> !         typdelim = ret_extra->typdelim;
> !         typelem = ret_extra->typelem;
> !         proc = ret_extra->typiofunc;
> !         typalign = ret_extra->typalign;
> !     }
>
>       /* Allocate temporary array for new values */
>       values = (Datum *) palloc(nitems * sizeof(Datum));
> ***************
> *** 2049,2056 ****
>    *          compares two arrays for equality
>    * result :
>    *          returns true if the arrays are equal, false otherwise.
> -  *
> -  * XXX bitwise equality is pretty bogus ...
>    *-----------------------------------------------------------------------------
>    */
>   Datum
> --- 2311,2316 ----
> ***************
> *** 2058,2069 ****
>   {
>       ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
>       ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
>       bool        result = true;
>
> !     if (ARR_SIZE(array1) != ARR_SIZE(array2))
> !         result = false;
> !     else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
>           result = false;
>
>       /* Avoid leaking memory when handed toasted input. */
>       PG_FREE_IF_COPY(array1, 0);
> --- 2318,2435 ----
>   {
>       ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
>       ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
> +     char       *p1 = (char *) ARR_DATA_PTR(array1);
> +     char       *p2 = (char *) ARR_DATA_PTR(array2);
> +     int            ndims1 = ARR_NDIM(array1);
> +     int            ndims2 = ARR_NDIM(array2);
> +     int           *dims1 = ARR_DIMS(array1);
> +     int           *dims2 = ARR_DIMS(array2);
> +     int            nitems1 = ArrayGetNItems(ndims1, dims1);
> +     int            nitems2 = ArrayGetNItems(ndims2, dims2);
> +     Oid            element_type = ARR_ELEMTYPE(array1);
> +     FmgrInfo   *ae_fmgr_info = fcinfo->flinfo;
>       bool        result = true;
> +     int            typlen;
> +     bool        typbyval;
> +     char        typdelim;
> +     Oid            typelem;
> +     char        typalign;
> +     Oid            typiofunc;
> +     int            i;
> +     ArrayMetaState *my_extra;
> +     FunctionCallInfoData locfcinfo;
>
> !     /* fast path if the arrays do not have the same number of elements */
> !     if (nitems1 != nitems2)
>           result = false;
> +     else
> +     {
> +         /*
> +          * We arrange to look up the equality function only once per series of
> +          * calls, assuming the element type doesn't change underneath us.
> +          */
> +         my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
> +         if (my_extra == NULL)
> +         {
> +             ae_fmgr_info->fn_extra = MemoryContextAlloc(ae_fmgr_info->fn_mcxt,
> +                                                      sizeof(ArrayMetaState));
> +             my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
> +             my_extra->element_type = InvalidOid;
> +         }
> +
> +         if (my_extra->element_type != element_type)
> +         {
> +             Oid        opfuncid = equality_oper_funcid(element_type);
> +
> +             if (OidIsValid(opfuncid))
> +                 fmgr_info_cxt(opfuncid, &my_extra->proc, ae_fmgr_info->fn_mcxt);
> +             else
> +                 elog(ERROR,
> +                      "array_eq: cannot find equality operator for type: %u",
> +                      element_type);
> +
> +             get_type_metadata(element_type, IOFunc_output,
> +                               &typlen, &typbyval, &typdelim,
> +                               &typelem, &typiofunc, &typalign);
> +
> +             my_extra->element_type = element_type;
> +             my_extra->typlen = typlen;
> +             my_extra->typbyval = typbyval;
> +             my_extra->typdelim = typdelim;
> +             my_extra->typelem = typelem;
> +             my_extra->typiofunc = typiofunc;
> +             my_extra->typalign = typalign;
> +         }
> +         else
> +         {
> +             typlen = my_extra->typlen;
> +             typbyval = my_extra->typbyval;
> +             typdelim = my_extra->typdelim;
> +             typelem = my_extra->typelem;
> +             typiofunc = my_extra->typiofunc;
> +             typalign = my_extra->typalign;
> +         }
> +
> +         /*
> +          * apply the operator to each pair of array elements.
> +          */
> +         MemSet(&locfcinfo, 0, sizeof(locfcinfo));
> +         locfcinfo.flinfo = &my_extra->proc;
> +         locfcinfo.nargs = 2;
> +
> +         /* Loop over source data */
> +         for (i = 0; i < nitems1; i++)
> +         {
> +             Datum    elt1;
> +             Datum    elt2;
> +             bool    oprresult;
> +
> +             /* Get element pair */
> +             elt1 = fetch_att(p1, typbyval, typlen);
> +             elt2 = fetch_att(p2, typbyval, typlen);
> +
> +             p1 = att_addlength(p1, typlen, PointerGetDatum(p1));
> +             p1 = (char *) att_align(p1, typalign);
> +
> +             p2 = att_addlength(p2, typlen, PointerGetDatum(p2));
> +             p2 = (char *) att_align(p2, typalign);
> +
> +             /*
> +              * Apply the operator to the element pair
> +              */
> +             locfcinfo.arg[0] = elt1;
> +             locfcinfo.arg[1] = elt2;
> +             locfcinfo.argnull[0] = false;
> +             locfcinfo.argnull[1] = false;
> +             locfcinfo.isnull = false;
> +             oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
> +             if (!oprresult)
> +             {
> +                 result = false;
> +                 break;
> +             }
> +         }
> +     }
>
>       /* Avoid leaking memory when handed toasted input. */
>       PG_FREE_IF_COPY(array1, 0);
> ***************
> *** 2073,2125 ****
>   }
>
>
> ! /***************************************************************************/
> ! /******************|          Support  Routines              |*****************/
> ! /***************************************************************************/
>
> ! static void
> ! system_cache_lookup(Oid element_type,
> !                     IOFuncSelector which_func,
> !                     int *typlen,
> !                     bool *typbyval,
> !                     char *typdelim,
> !                     Oid *typelem,
> !                     Oid *proc,
> !                     char *typalign)
> ! {
> !     HeapTuple    typeTuple;
> !     Form_pg_type typeStruct;
> !
> !     typeTuple = SearchSysCache(TYPEOID,
> !                                ObjectIdGetDatum(element_type),
> !                                0, 0, 0);
> !     if (!HeapTupleIsValid(typeTuple))
> !         elog(ERROR, "cache lookup failed for type %u", element_type);
> !     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
> !
> !     *typlen = typeStruct->typlen;
> !     *typbyval = typeStruct->typbyval;
> !     *typdelim = typeStruct->typdelim;
> !     *typelem = typeStruct->typelem;
> !     *typalign = typeStruct->typalign;
> !     switch (which_func)
> !     {
> !         case IOFunc_input:
> !             *proc = typeStruct->typinput;
> !             break;
> !         case IOFunc_output:
> !             *proc = typeStruct->typoutput;
> !             break;
> !         case IOFunc_receive:
> !             *proc = typeStruct->typreceive;
> !             break;
> !         case IOFunc_send:
> !             *proc = typeStruct->typsend;
> !             break;
>       }
> !     ReleaseSysCache(typeTuple);
>   }
>
>   /*
>    * Fetch array element at pointer, converted correctly to a Datum
>    */
> --- 2439,2628 ----
>   }
>
>
> ! /*-----------------------------------------------------------------------------
> !  * array-array bool operators:
> !  *        Given two arrays, iterate comparison operators
> !  *        over the array. Uses logic similar to text comparison
> !  *        functions, except element-by-element instead of
> !  *        character-by-character.
> !  *----------------------------------------------------------------------------
> !  */
> ! Datum
> ! array_ne(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
> ! }
>
> ! Datum
> ! array_lt(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
> ! }
> !
> ! Datum
> ! array_gt(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
> ! }
> !
> ! Datum
> ! array_le(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
> ! }
> !
> ! Datum
> ! array_ge(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
> ! }
> !
> ! Datum
> ! btarraycmp(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_INT32(array_cmp(fcinfo));
> ! }
> !
> ! /*
> !  * array_cmp()
> !  * Internal comparison function for arrays.
> !  *
> !  * Returns -1, 0 or 1
> !  */
> ! static int
> ! array_cmp(FunctionCallInfo fcinfo)
> ! {
> !     ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
> !     ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
> !     FmgrInfo   *ac_fmgr_info = fcinfo->flinfo;
> !     Datum        opresult;
> !     int            result = 0;
> !     Oid            element_type = InvalidOid;
> !     int            typlen;
> !     bool        typbyval;
> !     char        typdelim;
> !     Oid            typelem;
> !     char        typalign;
> !     Oid            typiofunc;
> !     Datum       *dvalues1;
> !     int            nelems1;
> !     Datum       *dvalues2;
> !     int            nelems2;
> !     int            min_nelems;
> !     int            i;
> !     typedef struct
> !     {
> !         Oid                element_type;
> !         int                typlen;
> !         bool            typbyval;
> !         char            typdelim;
> !         Oid                typelem;
> !         Oid                typiofunc;
> !         char            typalign;
> !         FmgrInfo        eqproc;
> !         FmgrInfo        ordproc;
> !     } ac_extra;
> !     ac_extra *my_extra;
> !
> !     element_type = ARR_ELEMTYPE(array1);
> !
> !     /*
> !      * We arrange to look up the element type operator function only once
> !      * per series of calls, assuming the element type and opname don't
> !      * change underneath us.
> !      */
> !     my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         ac_fmgr_info->fn_extra = MemoryContextAlloc(ac_fmgr_info->fn_mcxt,
> !                                                          sizeof(ac_extra));
> !         my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         Oid        eqfuncid = equality_oper_funcid(element_type);
> !         Oid        ordfuncid = ordering_oper_funcid(element_type);
> !
> !         fmgr_info_cxt(eqfuncid, &my_extra->eqproc, ac_fmgr_info->fn_mcxt);
> !         fmgr_info_cxt(ordfuncid, &my_extra->ordproc, ac_fmgr_info->fn_mcxt);
> !
> !         if (my_extra->eqproc.fn_nargs != 2)
> !             elog(ERROR, "Equality operator does not take 2 arguments: %u",
> !                                                                  eqfuncid);
> !         if (my_extra->ordproc.fn_nargs != 2)
> !             elog(ERROR, "Ordering operator does not take 2 arguments: %u",
> !                                                                  ordfuncid);
> !
> !         get_type_metadata(element_type, IOFunc_output,
> !                           &typlen, &typbyval, &typdelim,
> !                           &typelem, &typiofunc, &typalign);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = InvalidOid;
> !         my_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typalign = my_extra->typalign;
> !     }
> !
> !     /* extract a C array of arg array datums */
> !     deconstruct_array(array1, element_type, typlen, typbyval, typalign,
> !                                                     &dvalues1, &nelems1);
> !
> !     deconstruct_array(array2, element_type, typlen, typbyval, typalign,
> !                                                     &dvalues2, &nelems2);
> !
> !     min_nelems = Min(nelems1, nelems2);
> !     for (i = 0; i < min_nelems; i++)
> !     {
> !         /* are they equal */
> !         opresult = FunctionCall2(&my_extra->eqproc,
> !                                  dvalues1[i], dvalues2[i]);
> !
> !         if (!DatumGetBool(opresult))
> !         {
> !             /* nope, see if arg1 is less than arg2 */
> !             opresult = FunctionCall2(&my_extra->ordproc,
> !                                      dvalues1[i], dvalues2[i]);
> !             if (DatumGetBool(opresult))
> !             {
> !                 /* arg1 is less than arg2 */
> !                 result = -1;
> !                 break;
> !             }
> !             else
> !             {
> !                 /* arg1 is greater than arg2 */
> !                 result = 1;
> !                 break;
> !             }
> !         }
>       }
> !
> !     if ((result == 0) && (nelems1 != nelems2))
> !         result = (nelems1 < nelems2) ? -1 : 1;
> !
> !     /* Avoid leaking memory when handed toasted input. */
> !     PG_FREE_IF_COPY(array1, 0);
> !     PG_FREE_IF_COPY(array2, 1);
> !
> !     return result;
>   }
>
> +
> + /***************************************************************************/
> + /******************|          Support  Routines              |*****************/
> + /***************************************************************************/
> +
>   /*
>    * Fetch array element at pointer, converted correctly to a Datum
>    */
> ***************
> *** 2423,2428 ****
> --- 2926,2943 ----
>           if (tgt_elem_type == InvalidOid)
>               elog(ERROR, "Target type is not an array");
>
> +         /*
> +          * We don't deal with domain constraints yet, so bail out.
> +          * This isn't currently a problem, because we also don't
> +          * support arrays of domain type elements either. But in the
> +          * future we might. At that point consideration should be given
> +          * to removing the check below and adding a domain constraints
> +          * check to the coercion.
> +          */
> +         if (getBaseType(tgt_elem_type) != tgt_elem_type)
> +             elog(ERROR, "array coercion to domain type elements not " \
> +                         "currently supported");
> +
>           if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
>                                      COERCION_EXPLICIT, &funcId))
>           {
> ***************
> *** 2439,2448 ****
>       }
>
>       /*
> !      * If it's binary-compatible, return the array unmodified.
>        */
>       if (my_extra->coerce_finfo.fn_oid == InvalidOid)
> !         PG_RETURN_ARRAYTYPE_P(src);
>
>       /*
>        * Use array_map to apply the function to each array element.
> --- 2954,2969 ----
>       }
>
>       /*
> !      * If it's binary-compatible, modify the element type in the array header,
> !      * but otherwise leave the array as we received it.
>        */
>       if (my_extra->coerce_finfo.fn_oid == InvalidOid)
> !     {
> !         ArrayType  *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
> !
> !         ARR_ELEMTYPE(result) = my_extra->desttype;
> !         PG_RETURN_ARRAYTYPE_P(result);
> !     }
>
>       /*
>        * Use array_map to apply the function to each array element.
> ***************
> *** 2453,2456 ****
> --- 2974,3092 ----
>       locfcinfo.arg[0] = PointerGetDatum(src);
>
>       return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
> + }
> +
> + /*
> +  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
> +  *
> +  *    astate is working state (NULL on first call)
> +  *    rcontext is where to keep working state
> +  */
> + ArrayBuildState *
> + accumArrayResult(ArrayBuildState *astate,
> +                  Datum dvalue, bool disnull,
> +                  Oid element_type,
> +                  MemoryContext rcontext)
> + {
> +     MemoryContext arr_context,
> +                   oldcontext;
> +
> +     if (astate == NULL)
> +     {
> +         /* First time through --- initialize */
> +
> +         /* Make a temporary context to hold all the junk */
> +         arr_context = AllocSetContextCreate(rcontext,
> +                                             "accumArrayResult",
> +                                             ALLOCSET_DEFAULT_MINSIZE,
> +                                             ALLOCSET_DEFAULT_INITSIZE,
> +                                             ALLOCSET_DEFAULT_MAXSIZE);
> +         oldcontext = MemoryContextSwitchTo(arr_context);
> +         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
> +         astate->mcontext = arr_context;
> +         astate->dvalues = (Datum *)
> +             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
> +         astate->nelems = 0;
> +         astate->element_type = element_type;
> +         get_typlenbyvalalign(element_type,
> +                              &astate->typlen,
> +                              &astate->typbyval,
> +                              &astate->typalign);
> +     }
> +     else
> +     {
> +         oldcontext = MemoryContextSwitchTo(astate->mcontext);
> +         Assert(astate->element_type == element_type);
> +         /* enlarge dvalues[] if needed */
> +         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
> +             astate->dvalues = (Datum *)
> +                 repalloc(astate->dvalues,
> +                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
> +     }
> +
> +     if (disnull)
> +         elog(ERROR, "NULL elements not allowed in Arrays");
> +
> +     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
> +     astate->dvalues[astate->nelems++] =
> +         datumCopy(dvalue, astate->typbyval, astate->typlen);
> +
> +     MemoryContextSwitchTo(oldcontext);
> +
> +     return astate;
> + }
> +
> + /*
> +  * makeArrayResult - produce final result of accumArrayResult
> +  *
> +  *    astate is working state (not NULL)
> +  *    rcontext is where to construct result
> +  */
> + Datum
> + makeArrayResult(ArrayBuildState *astate,
> +                 MemoryContext rcontext)
> + {
> +     int            dims[1];
> +     int            lbs[1];
> +
> +     dims[0] = astate->nelems;
> +     lbs[0] = 1;
> +
> +     return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
> + }
> +
> + /*
> +  * makeMdArrayResult - produce md final result of accumArrayResult
> +  *
> +  *    astate is working state (not NULL)
> +  *    rcontext is where to construct result
> +  */
> + Datum
> + makeMdArrayResult(ArrayBuildState *astate,
> +                 int ndims,
> +                 int *dims,
> +                 int *lbs,
> +                 MemoryContext rcontext)
> + {
> +     ArrayType  *result;
> +     MemoryContext oldcontext;
> +
> +     /* Build the final array result in rcontext */
> +     oldcontext = MemoryContextSwitchTo(rcontext);
> +
> +     result = construct_md_array(astate->dvalues,
> +                                 ndims,
> +                                 dims,
> +                                 lbs,
> +                                 astate->element_type,
> +                                 astate->typlen,
> +                                 astate->typbyval,
> +                                 astate->typalign);
> +
> +     MemoryContextSwitchTo(oldcontext);
> +
> +     /* Clean up all the junk */
> +     MemoryContextDelete(astate->mcontext);
> +
> +     return PointerGetDatum(result);
>   }
> Index: src/backend/utils/adt/varlena.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/varlena.c,v
> retrieving revision 1.98
> diff -c -r1.98 varlena.c
> *** src/backend/utils/adt/varlena.c    15 May 2003 15:50:19 -0000    1.98
> --- src/backend/utils/adt/varlena.c    9 Jun 2003 01:31:10 -0000
> ***************
> *** 19,29 ****
> --- 19,32 ----
>   #include "mb/pg_wchar.h"
>   #include "miscadmin.h"
>   #include "access/tuptoaster.h"
> + #include "catalog/pg_type.h"
>   #include "lib/stringinfo.h"
>   #include "libpq/crypt.h"
>   #include "libpq/pqformat.h"
> + #include "utils/array.h"
>   #include "utils/builtins.h"
>   #include "utils/pg_locale.h"
> + #include "utils/lsyscache.h"
>
>
>   typedef struct varlena unknown;
> ***************
> *** 1983,1990 ****
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else
> ! /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>
> --- 1986,1992 ----
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else                    /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>
> ***************
> *** 2004,2011 ****
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else
> ! /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>       else if ((start_posn != 0) && (end_posn == 0))
> --- 2006,2012 ----
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else                    /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>       else if ((start_posn != 0) && (end_posn == 0))
> ***************
> *** 2026,2031 ****
> --- 2027,2217 ----
>           result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn -
fldsep_len,false); 
>           PG_RETURN_TEXT_P(result_text);
>       }
> + }
> +
> + /*
> +  * text_to_array
> +  * parse input string
> +  * return text array of elements
> +  * based on provided field separator
> +  */
> + Datum
> + text_to_array(PG_FUNCTION_ARGS)
> + {
> +     text       *inputstring = PG_GETARG_TEXT_P(0);
> +     int            inputstring_len = TEXTLEN(inputstring);
> +     text       *fldsep = PG_GETARG_TEXT_P(1);
> +     int            fldsep_len = TEXTLEN(fldsep);
> +     int            fldnum;
> +     int            start_posn = 0;
> +     int            end_posn = 0;
> +     text       *result_text = NULL;
> +     ArrayBuildState *astate = NULL;
> +     MemoryContext oldcontext = CurrentMemoryContext;
> +
> +     /* return NULL for empty input string */
> +     if (inputstring_len < 1)
> +         PG_RETURN_NULL();
> +
> +     /* empty field separator
> +      * return one element, 1D, array using the input string */
> +     if (fldsep_len < 1)
> +         PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
> +                               CStringGetDatum(inputstring), 1));
> +
> +     /* start with end position holding the initial start position */
> +     end_posn = 0;
> +     for (fldnum=1;;fldnum++)    /* field number is 1 based */
> +     {
> +         Datum    dvalue;
> +         bool    disnull = false;
> +
> +         start_posn = end_posn;
> +         end_posn = text_position(PointerGetDatum(inputstring),
> +                                  PointerGetDatum(fldsep),
> +                                  fldnum);
> +
> +         if ((start_posn == 0) && (end_posn == 0))    /* fldsep not found */
> +         {
> +             if (fldnum == 1)
> +             {
> +                 /* first element
> +                  * return one element, 1D, array using the input string */
> +                 PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
> +                                       CStringGetDatum(inputstring), 1));
> +             }
> +             else
> +             {
> +                 /* otherwise create array and exit */
> +                 PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
> +             }
> +         }
> +         else if ((start_posn != 0) && (end_posn == 0))
> +         {
> +             /* last field requested */
> +             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
> +         }
> +         else if ((start_posn == 0) && (end_posn != 0))
> +         {
> +             /* first field requested */
> +             result_text = LEFT(inputstring, fldsep);
> +         }
> +         else
> +         {
> +             /* prior to last field requested */
> +             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn -
start_posn- fldsep_len, false); 
> +         }
> +
> +         /* stash away current value */
> +         dvalue = PointerGetDatum(result_text);
> +         astate = accumArrayResult(astate, dvalue,
> +                                   disnull, TEXTOID, oldcontext);
> +
> +     }
> +
> +     /* never reached -- keep compiler quiet */
> +     PG_RETURN_NULL();
> + }
> +
> + /*
> +  * array_to_text
> +  * concatenate Cstring representation of input array elements
> +  * using provided field separator
> +  */
> + Datum
> + array_to_text(PG_FUNCTION_ARGS)
> + {
> +     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
> +     char       *fldsep = PG_TEXTARG_GET_STR(1);
> +     int            nitems, *dims, ndims;
> +     char       *p;
> +     Oid            element_type;
> +     int            typlen;
> +     bool        typbyval;
> +     char        typdelim;
> +     Oid            typoutput,
> +                 typelem;
> +     FmgrInfo    outputproc;
> +     char        typalign;
> +     StringInfo    result_str = makeStringInfo();
> +     int            i;
> +     ArrayMetaState *my_extra;
> +
> +     p = ARR_DATA_PTR(v);
> +     ndims = ARR_NDIM(v);
> +     dims = ARR_DIMS(v);
> +     nitems = ArrayGetNItems(ndims, dims);
> +
> +     /* if there are no elements, return an empty string */
> +     if (nitems == 0)
> +         PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
> +
> +     element_type = ARR_ELEMTYPE(v);
> +
> +     /*
> +      * We arrange to look up info about element type, including its output
> +      * conversion proc only once per series of calls, assuming the element
> +      * type doesn't change underneath us.
> +      */
> +     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> +     if (my_extra == NULL)
> +     {
> +         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> +                                                      sizeof(ArrayMetaState));
> +         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> +         my_extra->element_type = InvalidOid;
> +     }
> +
> +     if (my_extra->element_type != element_type)
> +     {
> +         /* Get info about element type, including its output conversion proc */
> +         get_type_metadata(element_type, IOFunc_output,
> +                             &typlen, &typbyval, &typdelim,
> +                             &typelem, &typoutput, &typalign);
> +         fmgr_info(typoutput, &outputproc);
> +
> +         my_extra->element_type = element_type;
> +         my_extra->typlen = typlen;
> +         my_extra->typbyval = typbyval;
> +         my_extra->typdelim = typdelim;
> +         my_extra->typelem = typelem;
> +         my_extra->typiofunc = typoutput;
> +         my_extra->typalign = typalign;
> +         my_extra->proc = outputproc;
> +     }
> +     else
> +     {
> +         typlen = my_extra->typlen;
> +         typbyval = my_extra->typbyval;
> +         typdelim = my_extra->typdelim;
> +         typelem = my_extra->typelem;
> +         typoutput = my_extra->typiofunc;
> +         typalign = my_extra->typalign;
> +         outputproc = my_extra->proc;
> +     }
> +
> +     for (i = 0; i < nitems; i++)
> +     {
> +         Datum        itemvalue;
> +         char       *value;
> +
> +         itemvalue = fetch_att(p, typbyval, typlen);
> +
> +         value = DatumGetCString(FunctionCall3(&outputproc,
> +                                               itemvalue,
> +                                               ObjectIdGetDatum(typelem),
> +                                               Int32GetDatum(-1)));
> +
> +         if (i > 0)
> +             appendStringInfo(result_str, "%s%s", fldsep, value);
> +         else
> +             appendStringInfo(result_str, "%s", value);
> +
> +         p = att_addlength(p, typlen, PointerGetDatum(p));
> +         p = (char *) att_align(p, typalign);
> +     }
> +
> +     PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
>   }
>
>   #define HEXBASE 16
> Index: src/backend/utils/cache/lsyscache.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v
> retrieving revision 1.95
> diff -c -r1.95 lsyscache.c
> *** src/backend/utils/cache/lsyscache.c    26 May 2003 00:11:27 -0000    1.95
> --- src/backend/utils/cache/lsyscache.c    9 Jun 2003 01:31:10 -0000
> ***************
> *** 625,630 ****
> --- 625,664 ----
>   }
>
>   /*
> +  * get_func_argtypes
> +  *        Given procedure id, return the function's argument types.
> +  *        Also pass back the number of arguments.
> +  */
> + Oid *
> + get_func_argtypes(Oid funcid, int *nargs)
> + {
> +     HeapTuple        tp;
> +     Form_pg_proc    procstruct;
> +     Oid               *result = NULL;
> +     int                i;
> +
> +     tp = SearchSysCache(PROCOID,
> +                         ObjectIdGetDatum(funcid),
> +                         0, 0, 0);
> +     if (!HeapTupleIsValid(tp))
> +         elog(ERROR, "Function OID %u does not exist", funcid);
> +
> +     procstruct = (Form_pg_proc) GETSTRUCT(tp);
> +     *nargs = (int) procstruct->pronargs;
> +
> +     if (*nargs > 0)
> +     {
> +         result = (Oid *) palloc(*nargs * sizeof(Oid));
> +
> +         for (i = 0; i < *nargs; i++)
> +             result[i] = procstruct->proargtypes[i];
> +     }
> +
> +     ReleaseSysCache(tp);
> +     return result;
> + }
> +
> + /*
>    * get_func_retset
>    *        Given procedure id, return the function's proretset flag.
>    */
> ***************
> *** 994,999 ****
> --- 1028,1083 ----
>       *typbyval = typtup->typbyval;
>       *typalign = typtup->typalign;
>       ReleaseSysCache(tp);
> + }
> +
> + /*
> +  * get_type_metadata
> +  *
> +  *        A six-fer:    given the type OID, return typlen, typbyval, typalign,
> +  *                    typdelim, typelem, IO function Oid. The IO function
> +  *                    returned is controlled by IOFuncSelector
> +  */
> + void
> + get_type_metadata(Oid element_type,
> +                     IOFuncSelector which_func,
> +                     int *typlen,
> +                     bool *typbyval,
> +                     char *typdelim,
> +                     Oid *typelem,
> +                     Oid *proc,
> +                     char *typalign)
> + {
> +     HeapTuple    typeTuple;
> +     Form_pg_type typeStruct;
> +
> +     typeTuple = SearchSysCache(TYPEOID,
> +                                ObjectIdGetDatum(element_type),
> +                                0, 0, 0);
> +     if (!HeapTupleIsValid(typeTuple))
> +         elog(ERROR, "cache lookup failed for type %u", element_type);
> +     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
> +
> +     *typlen = typeStruct->typlen;
> +     *typbyval = typeStruct->typbyval;
> +     *typdelim = typeStruct->typdelim;
> +     *typelem = typeStruct->typelem;
> +     *typalign = typeStruct->typalign;
> +     switch (which_func)
> +     {
> +         case IOFunc_input:
> +             *proc = typeStruct->typinput;
> +             break;
> +         case IOFunc_output:
> +             *proc = typeStruct->typoutput;
> +             break;
> +         case IOFunc_receive:
> +             *proc = typeStruct->typreceive;
> +             break;
> +         case IOFunc_send:
> +             *proc = typeStruct->typsend;
> +             break;
> +     }
> +     ReleaseSysCache(typeTuple);
>   }
>
>   #ifdef NOT_USED
> Index: src/backend/utils/fmgr/fmgr.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/fmgr/fmgr.c,v
> retrieving revision 1.68
> diff -c -r1.68 fmgr.c
> *** src/backend/utils/fmgr/fmgr.c    8 Apr 2003 23:20:02 -0000    1.68
> --- src/backend/utils/fmgr/fmgr.c    9 Jun 2003 01:31:10 -0000
> ***************
> *** 1673,1675 ****
> --- 1673,1701 ----
>
>       return exprType((Node *) nth(argnum, args));
>   }
> +
> + /*
> +  * Get the OID of the function or operator
> +  *
> +  * Returns InvalidOid if information is not available
> +  */
> + Oid
> + get_fn_expr_functype(FunctionCallInfo fcinfo)
> + {
> +     Node   *expr;
> +
> +     /*
> +      * can't return anything useful if we have no FmgrInfo or if
> +      * its fn_expr node has not been initialized
> +      */
> +     if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
> +         return InvalidOid;
> +
> +     expr = fcinfo->flinfo->fn_expr;
> +     if (IsA(expr, FuncExpr))
> +         return ((FuncExpr *) expr)->funcid;
> +     else if (IsA(expr, OpExpr))
> +         return ((OpExpr *) expr)->opno;
> +     else
> +         return InvalidOid;
> + }
> Index: src/include/fmgr.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/fmgr.h,v
> retrieving revision 1.27
> diff -c -r1.27 fmgr.h
> *** src/include/fmgr.h    8 Apr 2003 23:20:04 -0000    1.27
> --- src/include/fmgr.h    9 Jun 2003 01:31:10 -0000
> ***************
> *** 18,23 ****
> --- 18,24 ----
>   #ifndef FMGR_H
>   #define FMGR_H
>
> + #include "nodes/nodes.h"
>
>   /*
>    * All functions that can be called directly by fmgr must have this signature.
> ***************
> *** 372,385 ****
>                    Datum arg6, Datum arg7, Datum arg8,
>                    Datum arg9);
>
> -
>   /*
>    * Routines in fmgr.c
>    */
>   extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
> ! extern Oid    fmgr_internal_function(const char *proname);
> ! extern Oid    get_fn_expr_rettype(FunctionCallInfo fcinfo);
> ! extern Oid    get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
>
>   /*
>    * Routines in dfmgr.c
> --- 373,386 ----
>                    Datum arg6, Datum arg7, Datum arg8,
>                    Datum arg9);
>
>   /*
>    * Routines in fmgr.c
>    */
>   extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
> ! extern Oid fmgr_internal_function(const char *proname);
> ! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo);
> ! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
> ! extern Oid get_fn_expr_functype(FunctionCallInfo fcinfo);
>
>   /*
>    * Routines in dfmgr.c
> Index: src/include/catalog/pg_amop.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amop.h,v
> retrieving revision 1.49
> diff -c -r1.49 pg_amop.h
> *** src/include/catalog/pg_amop.h    26 May 2003 00:11:27 -0000    1.49
> --- src/include/catalog/pg_amop.h    9 Jun 2003 01:31:10 -0000
> ***************
> *** 418,423 ****
> --- 418,432 ----
>   DATA(insert (    2098 4 f 2335 ));
>   DATA(insert (    2098 5 f 2336 ));
>
> + /*
> +  *    btree array_ops
> +  */
> +
> + DATA(insert (     397 1 f 1072 ));
> + DATA(insert (     397 2 f 1074 ));
> + DATA(insert (     397 3 f 1070 ));
> + DATA(insert (     397 4 f 1075 ));
> + DATA(insert (     397 5 f 1073 ));
>
>   /*
>    *    hash index _ops
> Index: src/include/catalog/pg_amproc.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amproc.h,v
> retrieving revision 1.37
> diff -c -r1.37 pg_amproc.h
> *** src/include/catalog/pg_amproc.h    26 May 2003 00:11:27 -0000    1.37
> --- src/include/catalog/pg_amproc.h    9 Jun 2003 01:31:10 -0000
> ***************
> *** 78,83 ****
> --- 78,84 ----
>
>
>   /* btree */
> + DATA(insert (     397 1  398 ));
>   DATA(insert (     421 1    357 ));
>   DATA(insert (     423 1 1596 ));
>   DATA(insert (     424 1 1693 ));
> Index: src/include/catalog/pg_opclass.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_opclass.h,v
> retrieving revision 1.50
> diff -c -r1.50 pg_opclass.h
> *** src/include/catalog/pg_opclass.h    28 May 2003 16:04:00 -0000    1.50
> --- src/include/catalog/pg_opclass.h    9 Jun 2003 02:48:16 -0000
> ***************
> *** 87,92 ****
> --- 87,94 ----
>    */
>
>   DATA(insert OID =  421 (    403        abstime_ops        PGNSP PGUID  702 t 0 ));
> + DATA(insert OID =  397 (    403        array_ops        PGNSP PGUID 2277 t 0 ));
> + #define ARRAY_BTREE_OPS_OID 397
>   DATA(insert OID =  422 (    402        bigbox_ops        PGNSP PGUID  603 f 0 ));
>   DATA(insert OID =  423 (    403        bit_ops            PGNSP PGUID 1560 t 0 ));
>   DATA(insert OID =  424 (    403        bool_ops        PGNSP PGUID   16 t 0 ));
> Index: src/include/catalog/pg_operator.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_operator.h,v
> retrieving revision 1.114
> diff -c -r1.114 pg_operator.h
> *** src/include/catalog/pg_operator.h    26 May 2003 00:11:27 -0000    1.114
> --- src/include/catalog/pg_operator.h    9 Jun 2003 01:31:10 -0000
> ***************
> *** 116,125 ****
>   DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
>   DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 
>
> ! DATA(insert OID = 329 (  "="       PGNSP PGUID b f 2277 2277 16   329 0 0 0    0 0 array_eq     eqsel eqjoinsel ));
> ! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277   0 0 0 0    0 0 array_append   -       -
));
> ! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277   0 0 0 0    0 0 array_prepend  -       -
));
> ! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277   0 0 0 0    0 0 array_cat      -       -
));
>
>   DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
>   DATA(insert OID = 353 (  "="       PGNSP PGUID b t    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
> --- 116,130 ----
>   DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
>   DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 
>
> ! DATA(insert OID = 1070 (  "="       PGNSP PGUID b f 2277 2277 16 1070 1071  1072 1072 1072 1073 array_eq eqsel
eqjoinsel)); 
> ! DATA(insert OID = 1071 (  "<>"       PGNSP PGUID b f 2277 2277 16 1071 1070  0 0 0 0 array_ne neqsel neqjoinsel ));
> ! DATA(insert OID = 1072 (  "<"       PGNSP PGUID b f 2277 2277 16 1073 1075  0 0 0 0 array_lt scalarltsel
scalarltjoinsel)); 
> ! DATA(insert OID = 1073 (  ">"       PGNSP PGUID b f 2277 2277 16 1072 1074  0 0 0 0 array_gt scalargtsel
scalargtjoinsel)); 
> ! DATA(insert OID = 1074 (  "<="       PGNSP PGUID b f 2277 2277 16 1075 1073  0 0 0 0 array_le scalarltsel
scalarltjoinsel)); 
> ! DATA(insert OID = 1075 (  ">="       PGNSP PGUID b f 2277 2277 16 1074 1072  0 0 0 0 array_ge scalargtsel
scalargtjoinsel)); 
> ! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277 0 0  0 0 0 0 array_append   -       -     ));
> ! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277 0 0  0 0 0 0 array_prepend  -       -     ));
> ! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277 0 0  0 0 0 0 array_cat      -       -     ));
>
>   DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
>   DATA(insert OID = 353 (  "="       PGNSP PGUID b t    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
> ***************
> *** 425,430 ****
> --- 430,436 ----
>   DATA(insert OID = 966 (  "+"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - ));
>   DATA(insert OID = 967 (  "-"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclremove - - ));
>   DATA(insert OID = 968 (  "~"       PGNSP PGUID b f 1034 1033     16 0 0 0 0 0 0 aclcontains - - ));
> + DATA(insert OID = 974 (  "="       PGNSP PGUID b f 1033 1033     16 0 0 0 0 0 0 aclitemeq eqsel eqjoinsel ));
>
>   /* additional geometric operators - thomas 1997-07-09 */
>   DATA(insert OID =  969 (  "@@"       PGNSP PGUID l f    0  601    600    0  0 0 0 0 0 lseg_center - - ));
> Index: src/include/catalog/pg_proc.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v
> retrieving revision 1.302
> diff -c -r1.302 pg_proc.h
> *** src/include/catalog/pg_proc.h    26 May 2003 00:11:27 -0000    1.302
> --- src/include/catalog/pg_proc.h    9 Jun 2003 01:52:28 -0000
> ***************
> *** 758,763 ****
> --- 758,765 ----
>   DESCR("btree less-equal-greater");
>   DATA(insert OID = 360 (  bttextcmp           PGNSP PGUID 12 f f t f i 2 23 "25 25"    bttextcmp - _null_ ));
>   DESCR("btree less-equal-greater");
> + DATA(insert OID = 398 (  btarraycmp           PGNSP PGUID 12 f f t f i 2 23 "2277 2277"    btarraycmp - _null_ ));
> + DESCR("btree less-equal-greater");
>
>   DATA(insert OID = 361 (  lseg_distance       PGNSP PGUID 12 f f t f i 2 701 "601 601"  lseg_distance - _null_ ));
>   DESCR("distance between");
> ***************
> *** 984,997 ****
>   DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
>   DESCR("greater-than-or-equal");
>
> - DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
> - DESCR("array equal");
> -
>   DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
>   DESCR("current user name");
>   DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
>   DESCR("session user name");
>
>   DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
>   DESCR("array dimensions");
>   DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
> --- 986,1008 ----
>   DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
>   DESCR("greater-than-or-equal");
>
>   DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
>   DESCR("current user name");
>   DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
>   DESCR("session user name");
>
> + DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
> + DESCR("array equal");
> + DATA(insert OID = 390 (  array_ne           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ne - _null_ ));
> + DESCR("array not equal");
> + DATA(insert OID = 391 (  array_lt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_lt - _null_ ));
> + DESCR("array less than");
> + DATA(insert OID = 392 (  array_gt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_gt - _null_ ));
> + DESCR("array greater than");
> + DATA(insert OID = 393 (  array_le           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_le - _null_ ));
> + DESCR("array less than or equal");
> + DATA(insert OID = 396 (  array_ge           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ge - _null_ ));
> + DESCR("array greater than or equal");
>   DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
>   DESCR("array dimensions");
>   DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
> ***************
> *** 1002,1023 ****
>   DESCR("array lower dimension");
>   DATA(insert OID = 2092 (  array_upper       PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ ));
>   DESCR("array upper dimension");
> - DATA(insert OID = 377 (  singleton_array  PGNSP PGUID 12 f f t f i 1 2277 "2283" singleton_array - _null_ ));
> - DESCR("create array from single element");
>   DATA(insert OID = 378 (  array_append       PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ ));
>   DESCR("append element onto end of array");
>   DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
>   DESCR("prepend element onto front of array");
> - DATA(insert OID = 380 (  array_accum       PGNSP PGUID 12 f f f f i 2 2277 "2277 2283" array_accum - _null_ ));
> - DESCR("push element onto end of array, creating array if needed");
> - DATA(insert OID = 381 (  array_assign       PGNSP PGUID 12 f f t f i 3 2277 "2277 23 2283" array_assign - _null_
));
> - DESCR("assign specific array element");
> - DATA(insert OID = 382 (  array_subscript  PGNSP PGUID 12 f f t f i 2 2283 "2277 23" array_subscript - _null_ ));
> - DESCR("return specific array element");
>   DATA(insert OID = 383 (  array_cat           PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ ));
>   DESCR("concatenate two arrays");
>   DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
>   DESCR("coerce array type to another array type");
>
>   DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
>   DESCR("I/O");
> --- 1013,1030 ----
>   DESCR("array lower dimension");
>   DATA(insert OID = 2092 (  array_upper       PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ ));
>   DESCR("array upper dimension");
>   DATA(insert OID = 378 (  array_append       PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ ));
>   DESCR("append element onto end of array");
>   DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
>   DESCR("prepend element onto front of array");
>   DATA(insert OID = 383 (  array_cat           PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ ));
>   DESCR("concatenate two arrays");
>   DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
>   DESCR("coerce array type to another array type");
> + DATA(insert OID = 394 (  string_to_array   PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ ));
> + DESCR("split delimited text into text[]");
> + DATA(insert OID = 395 (  array_to_string   PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ ));
> + DESCR("concatenate array elements, using delimiter, into text");
>
>   DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
>   DESCR("I/O");
> ***************
> *** 1318,1323 ****
> --- 1325,1332 ----
>   DESCR("remove ACL item");
>   DATA(insert OID = 1037 (  aclcontains       PGNSP PGUID 12 f f t f s 2 16 "1034 1033"    aclcontains - _null_ ));
>   DESCR("does ACL contain item?");
> + DATA(insert OID = 1062 (  aclitemeq           PGNSP PGUID 12 f f t f s 2 16 "1033 1033"    aclitem_eq - _null_ ));
> + DESCR("equality operator for ACL items");
>   DATA(insert OID = 1038 (  seteval           PGNSP PGUID 12 f f t t v 1 23 "26"  seteval - _null_ ));
>   DESCR("internal function supporting PostQuel-style sets");
>   DATA(insert OID = 1044 (  bpcharin           PGNSP PGUID 12 f f t f i 3 1042 "2275 26 23" bpcharin - _null_ ));
> Index: src/include/nodes/primnodes.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v
> retrieving revision 1.82
> diff -c -r1.82 primnodes.h
> *** src/include/nodes/primnodes.h    6 May 2003 00:20:33 -0000    1.82
> --- src/include/nodes/primnodes.h    9 Jun 2003 01:31:10 -0000
> ***************
> *** 225,230 ****
> --- 225,231 ----
>       Expr       *target;            /* expression we are aggregating on */
>       bool        aggstar;        /* TRUE if argument was really '*' */
>       bool        aggdistinct;    /* TRUE if it's agg(DISTINCT ...) */
> +     List       *args;            /* arguments to the aggregate */
>   } Aggref;
>
>   /* ----------------
> ***************
> *** 357,371 ****
>   /* ----------------
>    * SubLink
>    *
> !  * A SubLink represents a subselect appearing in an expression, and in some
> !  * cases also the combining operator(s) just above it.    The subLinkType
> !  * indicates the form of the expression represented:
>    *    EXISTS_SUBLINK        EXISTS(SELECT ...)
>    *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
>    *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
>    *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
>    *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
>    *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
>    * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
>    * same length as the subselect's targetlist.  MULTIEXPR will *always* have
>    * a list with more than one entry; if the subselect has just one target
> --- 358,376 ----
>   /* ----------------
>    * SubLink
>    *
> !  * A SubLink represents a subselect, or an expression, appearing in an
> !  * expression, and in some cases also the combining operator(s) just above
> !  * it.    The subLinkType indicates the form of the expression represented:
>    *    EXISTS_SUBLINK        EXISTS(SELECT ...)
>    *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
>    *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
>    *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
>    *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
>    *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
> +  * If an expression is used in place of the subselect, it is transformed
> +  * into a simple "(SELECT expr)" in gram.y. This is to allow arrays to be
> +  * used as if they were the result of a single column subselect. If the
> +  * expression is scalar, it is treated as a one element array.
>    * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
>    * same length as the subselect's targetlist.  MULTIEXPR will *always* have
>    * a list with more than one entry; if the subselect has just one target
> ***************
> *** 414,419 ****
> --- 419,426 ----
>       SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
>       bool        useOr;            /* TRUE to combine column results with
>                                    * "OR" not "AND" */
> +     bool        isExpr;            /* TRUE if the subselect is really derived
> +                                  * from a single expression */
>       List       *lefthand;        /* list of outer-query expressions on the
>                                    * left */
>       List       *operName;        /* originally specified operator name */
> ***************
> *** 455,460 ****
> --- 462,476 ----
>       SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
>       bool        useOr;            /* TRUE to combine column results with
>                                    * "OR" not "AND" */
> +     bool        isExpr;            /* TRUE if the subselect is really derived
> +                                  * from a single expression */
> +     /* runtime cache for single array expressions */
> +     Oid            exprtype;        /* array and element type, and other info
> +                                  * needed deconstruct the array */
> +     Oid            elemtype;
> +     int16        elmlen;
> +     bool        elmbyval;
> +     char        elmalign;
>       /* The combining operators, transformed to executable expressions: */
>       List       *exprs;            /* list of OpExpr expression trees */
>       List       *paramIds;        /* IDs of Params embedded in the above */
> Index: src/include/optimizer/clauses.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/optimizer/clauses.h,v
> retrieving revision 1.63
> diff -c -r1.63 clauses.h
> *** src/include/optimizer/clauses.h    28 May 2003 16:04:02 -0000    1.63
> --- src/include/optimizer/clauses.h    9 Jun 2003 01:31:10 -0000
> ***************
> *** 28,33 ****
> --- 28,36 ----
>   extern Node *get_leftop(Expr *clause);
>   extern Node *get_rightop(Expr *clause);
>
> + extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
> +                                     CoercionForm funcformat, List *funcargs);
> +
>   extern bool not_clause(Node *clause);
>   extern Expr *make_notclause(Expr *notclause);
>   extern Expr *get_notclausearg(Expr *notclause);
> Index: src/include/parser/parse_oper.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_oper.h,v
> retrieving revision 1.25
> diff -c -r1.25 parse_oper.h
> *** src/include/parser/parse_oper.h    29 Apr 2003 22:13:11 -0000    1.25
> --- src/include/parser/parse_oper.h    9 Jun 2003 01:31:10 -0000
> ***************
> *** 44,49 ****
> --- 44,50 ----
>   /* Convenience routines for common calls on the above */
>   extern Oid    compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError);
>   extern Oid    equality_oper_funcid(Oid argtype);
> + extern Oid  ordering_oper_funcid(Oid argtype);
>   extern Oid    ordering_oper_opid(Oid argtype);
>
>   /* Extract operator OID or underlying-function OID from an Operator tuple */
> Index: src/include/utils/acl.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/acl.h,v
> retrieving revision 1.51
> diff -c -r1.51 acl.h
> *** src/include/utils/acl.h    23 Jan 2003 23:39:07 -0000    1.51
> --- src/include/utils/acl.h    9 Jun 2003 01:31:10 -0000
> ***************
> *** 188,193 ****
> --- 188,194 ----
>   extern Datum aclinsert(PG_FUNCTION_ARGS);
>   extern Datum aclremove(PG_FUNCTION_ARGS);
>   extern Datum aclcontains(PG_FUNCTION_ARGS);
> + extern Datum aclitem_eq(PG_FUNCTION_ARGS);
>
>   /*
>    * prototypes for functions in aclchk.c
> Index: src/include/utils/array.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v
> retrieving revision 1.38
> diff -c -r1.38 array.h
> *** src/include/utils/array.h    8 May 2003 22:19:57 -0000    1.38
> --- src/include/utils/array.h    9 Jun 2003 01:47:03 -0000
> ***************
> *** 32,37 ****
> --- 32,68 ----
>       Oid            elemtype;        /* element type OID */
>   } ArrayType;
>
> + typedef struct ArrayBuildState
> + {
> +     MemoryContext mcontext;        /* where all the temp stuff is kept */
> +     Datum       *dvalues;        /* array of accumulated Datums */
> +     /*
> +      * The allocated size of dvalues[] is always a multiple of
> +      * ARRAY_ELEMS_CHUNKSIZE
> +      */
> + #define ARRAY_ELEMS_CHUNKSIZE    64
> +     int            nelems;            /* number of valid Datums in dvalues[] */
> +     Oid            element_type;    /* data type of the Datums */
> +     int16        typlen;            /* needed info about datatype */
> +     bool        typbyval;
> +     char        typalign;
> + } ArrayBuildState;
> +
> + /*
> +  * structure to cache type metadata needed for array manipulation
> +  */
> + typedef struct ArrayMetaState
> + {
> +     Oid                element_type;
> +     int                typlen;
> +     bool            typbyval;
> +     char            typdelim;
> +     Oid                typelem;
> +     Oid                typiofunc;
> +     char            typalign;
> +     FmgrInfo        proc;
> + } ArrayMetaState;
> +
>   /*
>    * fmgr macros for array objects
>    */
> ***************
> *** 86,96 ****
>   extern Datum array_send(PG_FUNCTION_ARGS);
>   extern Datum array_length_coerce(PG_FUNCTION_ARGS);
>   extern Datum array_eq(PG_FUNCTION_ARGS);
>   extern Datum array_dims(PG_FUNCTION_ARGS);
>   extern Datum array_lower(PG_FUNCTION_ARGS);
>   extern Datum array_upper(PG_FUNCTION_ARGS);
> - extern Datum array_assign(PG_FUNCTION_ARGS);
> - extern Datum array_subscript(PG_FUNCTION_ARGS);
>   extern Datum array_type_coerce(PG_FUNCTION_ARGS);
>
>   extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
> --- 117,131 ----
>   extern Datum array_send(PG_FUNCTION_ARGS);
>   extern Datum array_length_coerce(PG_FUNCTION_ARGS);
>   extern Datum array_eq(PG_FUNCTION_ARGS);
> + extern Datum array_ne(PG_FUNCTION_ARGS);
> + extern Datum array_lt(PG_FUNCTION_ARGS);
> + extern Datum array_gt(PG_FUNCTION_ARGS);
> + extern Datum array_le(PG_FUNCTION_ARGS);
> + extern Datum array_ge(PG_FUNCTION_ARGS);
> + extern Datum btarraycmp(PG_FUNCTION_ARGS);
>   extern Datum array_dims(PG_FUNCTION_ARGS);
>   extern Datum array_lower(PG_FUNCTION_ARGS);
>   extern Datum array_upper(PG_FUNCTION_ARGS);
>   extern Datum array_type_coerce(PG_FUNCTION_ARGS);
>
>   extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
> ***************
> *** 124,130 ****
>                     Oid elmtype,
>                     int elmlen, bool elmbyval, char elmalign,
>                     Datum **elemsp, int *nelemsp);
> !
>
>   /*
>    * prototypes for functions defined in arrayutils.c
> --- 159,172 ----
>                     Oid elmtype,
>                     int elmlen, bool elmbyval, char elmalign,
>                     Datum **elemsp, int *nelemsp);
> ! extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
> !                                          Datum dvalue, bool disnull,
> !                                          Oid element_type,
> !                                          MemoryContext rcontext);
> ! extern Datum makeArrayResult(ArrayBuildState *astate,
> !                              MemoryContext rcontext);
> ! extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
> !                                int *dims, int *lbs, MemoryContext rcontext);
>
>   /*
>    * prototypes for functions defined in arrayutils.c
> ***************
> *** 141,152 ****
>   /*
>    * prototypes for functions defined in array_userfuncs.c
>    */
> - extern Datum singleton_array(PG_FUNCTION_ARGS);
>   extern Datum array_push(PG_FUNCTION_ARGS);
> - extern Datum array_accum(PG_FUNCTION_ARGS);
>   extern Datum array_cat(PG_FUNCTION_ARGS);
>
> ! extern ArrayType *create_singleton_array(Oid element_type,
>                                            Datum element,
>                                            int ndims);
>
> --- 183,193 ----
>   /*
>    * prototypes for functions defined in array_userfuncs.c
>    */
>   extern Datum array_push(PG_FUNCTION_ARGS);
>   extern Datum array_cat(PG_FUNCTION_ARGS);
>
> ! extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
> !                                          Oid element_type,
>                                            Datum element,
>                                            int ndims);
>
> Index: src/include/utils/builtins.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/builtins.h,v
> retrieving revision 1.219
> diff -c -r1.219 builtins.h
> *** src/include/utils/builtins.h    26 May 2003 00:11:28 -0000    1.219
> --- src/include/utils/builtins.h    9 Jun 2003 01:31:10 -0000
> ***************
> *** 530,535 ****
> --- 530,537 ----
>                         List **namelist);
>   extern Datum replace_text(PG_FUNCTION_ARGS);
>   extern Datum split_text(PG_FUNCTION_ARGS);
> + extern Datum text_to_array(PG_FUNCTION_ARGS);
> + extern Datum array_to_text(PG_FUNCTION_ARGS);
>   extern Datum to_hex32(PG_FUNCTION_ARGS);
>   extern Datum to_hex64(PG_FUNCTION_ARGS);
>   extern Datum md5_text(PG_FUNCTION_ARGS);
> Index: src/include/utils/lsyscache.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v
> retrieving revision 1.70
> diff -c -r1.70 lsyscache.h
> *** src/include/utils/lsyscache.h    26 May 2003 00:11:28 -0000    1.70
> --- src/include/utils/lsyscache.h    9 Jun 2003 01:31:10 -0000
> ***************
> *** 15,20 ****
> --- 15,29 ----
>
>   #include "access/htup.h"
>
> + /* I/O function selector for system_cache_lookup */
> + typedef enum IOFuncSelector
> + {
> +     IOFunc_input,
> +     IOFunc_output,
> +     IOFunc_receive,
> +     IOFunc_send
> + } IOFuncSelector;
> +
>   extern bool op_in_opclass(Oid opno, Oid opclass);
>   extern bool op_requires_recheck(Oid opno, Oid opclass);
>   extern Oid    get_opclass_member(Oid opclass, int16 strategy);
> ***************
> *** 39,44 ****
> --- 48,54 ----
>   extern RegProcedure get_oprjoin(Oid opno);
>   extern char *get_func_name(Oid funcid);
>   extern Oid    get_func_rettype(Oid funcid);
> + extern Oid *get_func_argtypes(Oid funcid, int *nargs);
>   extern bool get_func_retset(Oid funcid);
>   extern bool func_strict(Oid funcid);
>   extern char func_volatile(Oid funcid);
> ***************
> *** 54,59 ****
> --- 64,77 ----
>   extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
>   extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
>                        char *typalign);
> + extern void get_type_metadata(Oid element_type,
> +                                 IOFuncSelector which_func,
> +                                 int *typlen,
> +                                 bool *typbyval,
> +                                 char *typdelim,
> +                                 Oid *typelem,
> +                                 Oid *proc,
> +                                 char *typalign);
>   extern char get_typstorage(Oid typid);
>   extern int32 get_typtypmod(Oid typid);
>   extern Node *get_typdefault(Oid typid);
> Index: src/interfaces/ecpg/preproc/preproc.y
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/preproc.y,v
> retrieving revision 1.227
> diff -c -r1.227 preproc.y
> *** src/interfaces/ecpg/preproc/preproc.y    30 May 2003 13:22:02 -0000    1.227
> --- src/interfaces/ecpg/preproc/preproc.y    9 Jun 2003 01:42:58 -0000
> ***************
> *** 4549,4555 ****
>                   $3.type_enum != ECPGt_char &&
>                       $3.type_enum != ECPGt_unsigned_char &&
>                   atoi(this->type->type_index) >= 0)
> !                 mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
>
>               types = this;
>           }
> --- 4549,4555 ----
>                   $3.type_enum != ECPGt_char &&
>                       $3.type_enum != ECPGt_unsigned_char &&
>                   atoi(this->type->type_index) >= 0)
> !                 mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
>
>               types = this;
>           }
> ***************
> *** 5372,5378 ****
>                       $5.type_enum != ECPGt_char &&
>                       $5.type_enum != ECPGt_unsigned_char &&
>                       atoi(this->type->type_index) >= 0)
> !                     mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
>
>                   types = this;
>               }
> --- 5372,5378 ----
>                       $5.type_enum != ECPGt_char &&
>                       $5.type_enum != ECPGt_unsigned_char &&
>                       atoi(this->type->type_index) >= 0)
> !                     mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
>
>                   types = this;
>               }
> ***************
> *** 5439,5445 ****
>
>                       default:
>                           if (atoi(length) >= 0)
> !                             mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data
types");
>
>                           if (atoi(dimension) < 0)
>                               type = ECPGmake_simple_type($5.type_enum, make_str("1"));
> --- 5439,5445 ----
>
>                       default:
>                           if (atoi(length) >= 0)
> !                             mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data
types");
>
>                           if (atoi(dimension) < 0)
>                               type = ECPGmake_simple_type($5.type_enum, make_str("1"));
> Index: src/interfaces/ecpg/preproc/type.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/type.c,v
> retrieving revision 1.51
> diff -c -r1.51 type.c
> *** src/interfaces/ecpg/preproc/type.c    29 May 2003 13:59:26 -0000    1.51
> --- src/interfaces/ecpg/preproc/type.c    9 Jun 2003 01:42:58 -0000
> ***************
> *** 493,499 ****
>                   switch (type->u.element->type)
>                   {
>                       case ECPGt_array:
> !                         yyerror("internal error, found multi-dimensional array\n");
>                           break;
>                       case ECPGt_struct:
>                       case ECPGt_union:
> --- 493,499 ----
>                   switch (type->u.element->type)
>                   {
>                       case ECPGt_array:
> !                         yyerror("internal error, found multidimensional array\n");
>                           break;
>                       case ECPGt_struct:
>                       case ECPGt_union:
> Index: src/interfaces/ecpg/preproc/variable.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/variable.c,v
> retrieving revision 1.20
> diff -c -r1.20 variable.c
> *** src/interfaces/ecpg/preproc/variable.c    29 May 2003 13:59:26 -0000    1.20
> --- src/interfaces/ecpg/preproc/variable.c    9 Jun 2003 01:42:58 -0000
> ***************
> *** 405,411 ****
>       if (atoi(type_index) >= 0)
>       {
>           if (atoi(*length) >= 0)
> !             mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
>           *length = type_index;
>       }
> --- 405,411 ----
>       if (atoi(type_index) >= 0)
>       {
>           if (atoi(*length) >= 0)
> !             mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
>           *length = type_index;
>       }
> ***************
> *** 413,419 ****
>       if (atoi(type_dimension) >= 0)
>       {
>           if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
> !             mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
>           if (atoi(*dimension) >= 0)
>               *length = *dimension;
> --- 413,419 ----
>       if (atoi(type_dimension) >= 0)
>       {
>           if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
> !             mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
>           if (atoi(*dimension) >= 0)
>               *length = *dimension;
> ***************
> *** 432,441 ****
>           mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");
>
>       if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
> !         mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
>       if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
> !         mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
>       switch (type_enum)
>       {
> --- 432,441 ----
>           mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");
>
>       if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
> !         mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
>       if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
> !         mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
>       switch (type_enum)
>       {
> ***************
> *** 449,455 ****
>               }
>
>               if (atoi(*length) >= 0)
> !                 mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for structures");
>
>               break;
>           case ECPGt_varchar:
> --- 449,455 ----
>               }
>
>               if (atoi(*length) >= 0)
> !                 mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for structures");
>
>               break;
>           case ECPGt_varchar:
> ***************
> *** 494,500 ****
>               }
>
>               if (atoi(*length) >= 0)
> !                 mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for simple data types");
>
>               break;
>       }
> --- 494,500 ----
>               }
>
>               if (atoi(*length) >= 0)
> !                 mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for simple data types");
>
>               break;
>       }
> Index: src/test/regress/expected/arrays.out
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/test/regress/expected/arrays.out,v
> retrieving revision 1.11
> diff -c -r1.11 arrays.out
> *** src/test/regress/expected/arrays.out    8 Apr 2003 23:20:04 -0000    1.11
> --- src/test/regress/expected/arrays.out    9 Jun 2003 04:43:05 -0000
> ***************
> *** 178,196 ****
>   (1 row)
>
>   -- functions
> ! SELECT singleton_array(42) AS "{42}";
> !  {42}
> ! ------
> !  {42}
> ! (1 row)
> !
> ! SELECT array_append(singleton_array(42), 6) AS "{42,6}";
>    {42,6}
>   --------
>    {42,6}
>   (1 row)
>
> ! SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
>    {6,42}
>   --------
>    {6,42}
> --- 178,190 ----
>   (1 row)
>
>   -- functions
> ! SELECT array_append(array[42], 6) AS "{42,6}";
>    {42,6}
>   --------
>    {42,6}
>   (1 row)
>
> ! SELECT array_prepend(6, array[42]) AS "{6,42}";
>    {6,42}
>   --------
>    {6,42}
> ***************
> *** 212,235 ****
>    {{3,4},{5,6},{1,2}}
>   ---------------------
>    {{3,4},{5,6},{1,2}}
> - (1 row)
> -
> - SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
> -  1.2
> - -----
> -  1.2
> - (1 row)
> -
> - SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
> -  {1.1,9.99,1.3}
> - ----------------
> -  {1.1,9.99,1.3}
> - (1 row)
> -
> - SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
> -  9.99
> - ------
> -  9.99
>   (1 row)
>
>   -- operators
> --- 206,211 ----
> Index: src/test/regress/sql/arrays.sql
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/test/regress/sql/arrays.sql,v
> retrieving revision 1.10
> diff -c -r1.10 arrays.sql
> *** src/test/regress/sql/arrays.sql    8 Apr 2003 23:20:04 -0000    1.10
> --- src/test/regress/sql/arrays.sql    9 Jun 2003 04:42:20 -0000
> ***************
> *** 130,144 ****
>   SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
>
>   -- functions
> ! SELECT singleton_array(42) AS "{42}";
> ! SELECT array_append(singleton_array(42), 6) AS "{42,6}";
> ! SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
>   SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}";
>   SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
>   SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
> - SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
> - SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
> - SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
>
>   -- operators
>   SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
> --- 130,140 ----
>   SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
>
>   -- functions
> ! SELECT array_append(array[42], 6) AS "{42,6}";
> ! SELECT array_prepend(6, array[42]) AS "{6,42}";
>   SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}";
>   SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
>   SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
>
>   -- operators
>   SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Re: array support patch phase 1 patch

From
Joe Conway
Date:
Joe Conway wrote:
> The attached patch addresses Peter's concerns, subject to our agreements
> above. I.e, the changes are:

The previous patch was no longer applying cleanly, so here is an update.
Applies and compiles clean on cvs tip, passes all regression tests.
Please apply.

Joe

Index: doc/src/sgml/array.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
retrieving revision 1.25
diff -c -r1.25 array.sgml
*** doc/src/sgml/array.sgml    13 Mar 2003 01:30:26 -0000    1.25
--- doc/src/sgml/array.sgml    16 Jun 2003 23:45:51 -0000
***************
*** 60,73 ****
  </programlisting>
   </para>

   <note>
    <para>
!    A limitation of the present array implementation is that individual
!    elements of an array cannot be SQL null values.  The entire array can be set
!    to null, but you can't have an array with some elements null and some
!    not.  Fixing this is on the to-do list.
    </para>
   </note>
   </sect2>

   <sect2>
--- 60,133 ----
  </programlisting>
   </para>

+  <para>
+   A limitation of the present array implementation is that individual
+   elements of an array cannot be SQL null values.  The entire array can be set
+   to null, but you can't have an array with some elements null and some
+   not.
+  </para>
+  <para>
+   This can lead to surprising results. For example, the result of the
+   previous two inserts looks like this:
+ <programlisting>
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |      schedule
+ -------+---------------------------+--------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting},{""}}
+  Carol | {20000,25000,25000,25000} | {{talk},{meeting}}
+ (2 rows)
+ </programlisting>
+   Because the <literal>[2][2]</literal> element of
+   <structfield>schedule</structfield> is missing in each of the
+   <command>INSERT</command> statements, the <literal>[1][2]</literal>
+   element is discarded.
+  </para>
+
+  <note>
+   <para>
+    Fixing this is on the to-do list.
+   </para>
+  </note>
+
+  <para>
+   The <command>ARRAY</command> expression syntax may also be used:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Bill',
+     ARRAY[10000, 10000, 10000, 10000],
+     ARRAY[['meeting', 'lunch'], ['','']]);
+
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting', '']]);
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |           schedule
+ -------+---------------------------+-------------------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}}
+  Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}}
+ (2 rows)
+ </programlisting>
+   Note that with this syntax, multidimensional arrays must have matching
+   extents for each dimension. This eliminates the missing-array-elements
+   problem above. For example:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting']]);
+ ERROR:  Multidimensional arrays must have array expressions with matching dimensions
+ </programlisting>
+   Also notice that string literals are single quoted instead of double quoted.
+  </para>
+
   <note>
    <para>
!    The examples in the rest of this section are based on the
!    <command>ARRAY</command> expression syntax <command>INSERT</command>s.
    </para>
   </note>
+
   </sect2>

   <sect2>
***************
*** 132,142 ****
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the
!   form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified.
   </para>

   <para>
--- 192,221 ----
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified; another example follows:
! <programlisting>
! SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
!          schedule
! ---------------------------
!  {{meeting,lunch},{"",""}}
! (1 row)
! </programlisting>
!  </para>
!
!  <para>
!   Additionally, we can also access a single arbitrary array element of
!   a one-dimensional array with the <function>array_subscript</function>
!   function:
! <programlisting>
! SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
!  array_subscript
! -----------------
!            10000
! (1 row)
! </programlisting>
   </para>

   <para>
***************
*** 147,153 ****
      WHERE name = 'Carol';
  </programlisting>

!   or updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
--- 226,248 ----
      WHERE name = 'Carol';
  </programlisting>

!   or using the <command>ARRAY</command> expression syntax:
!
! <programlisting>
! UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
!     WHERE name = 'Carol';
! </programlisting>
!
!   <note>
!    <para>
!     Anywhere you can use the <quote>curly braces</quote> array syntax,
!     you can also use the <command>ARRAY</command> expression syntax. The
!     remainder of this section will illustrate only one or the other, but
!     not both.
!    </para>
!   </note>
!
!   An array may also be updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
***************
*** 160,165 ****
--- 255,268 ----
  UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
      WHERE name = 'Carol';
  </programlisting>
+
+   A one-dimensional array may also be updated with the
+   <function>array_assign</function> function:
+
+ <programlisting>
+ UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000)
+     WHERE name = 'Bill';
+ </programListing>
   </para>

   <para>
***************
*** 179,184 ****
--- 282,369 ----
   </para>

   <para>
+   An array can also be enlarged by using the concatenation operator,
+   <command>||</command>.
+ <programlisting>
+ SELECT ARRAY[1,2] || ARRAY[3,4];
+    ?column?
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
+       ?column?
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ (1 row)
+ </programlisting>
+
+   The concatenation operator allows a single element to be pushed on to the
+   beginning or end of a one-dimensional array. It also allows two
+   <replaceable>N</>-dimensional arrays, or an <replaceable>N</>-dimensional
+   and an <replaceable>N+1</>-dimensional array. In the former case, the two
+   <replaceable>N</>-dimension arrays become outer elements of an
+   <replaceable>N+1</>-dimensional array. In the latter, the
+   <replaceable>N</>-dimensional array is added as either the first or last
+   outer element of the <replaceable>N+1</>-dimensional array.
+
+   The array is extended in the direction of the push. Hence, by pushing
+   onto the beginning of an array with a one-based subscript, a zero-based
+   subscript array is created:
+
+ <programlisting>
+ SELECT array_dims(t.f) FROM (SELECT 1 || ARRAY[2,3] AS f) AS t;
+  array_dims
+ ------------
+  [0:2]
+ (1 row)
+ </programlisting>
+  </para>
+
+  <para>
+   An array can also be enlarged by using the functions
+   <function>array_prepend</function>, <function>array_append</function>,
+   or <function>array_cat</function>. The first two only support one-dimensional
+   arrays, but <function>array_cat</function> supports multidimensional arrays.
+
+   Note that the concatenation operator discussed above is preferred over
+   direct use of these functions. In fact, the functions are primarily for use
+   in implementing the concatenation operator. However, they may be directly
+   useful in the creation of user-defined aggregates. Some examples:
+
+ <programlisting>
+ SELECT array_prepend(1, ARRAY[2,3]);
+  array_prepend
+ ---------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_append(ARRAY[1,2], 3);
+  array_append
+ --------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
+    array_cat
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
+       array_cat
+ ---------------------
+  {{1,2},{3,4},{5,6}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
+       array_cat
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ </programlisting>
+  </para>
+
+  <para>
    The syntax for <command>CREATE TABLE</command> allows fixed-length
    arrays to be defined:

***************
*** 194,199 ****
--- 379,394 ----
   </para>

   <para>
+   An alternative syntax for one-dimensional arrays may be used.
+   <structfield>pay_by_quarter</structfield> could have been defined as:
+ <programlisting>
+     pay_by_quarter  integer ARRAY[4],
+ </programlisting>
+   This syntax may <emphasis>only</emphasis> be used with the integer
+   constant to denote the array size.
+  </para>
+
+  <para>
    Actually, the current implementation does not enforce the declared
    number of dimensions either.  Arrays of a particular element type are
    all considered to be of the same type, regardless of size or number
***************
*** 300,305 ****
--- 495,566 ----
     is not ignored, however: after skipping leading whitespace, everything
     up to the next right brace or delimiter is taken as the item value.
    </para>
+
+   <para>
+    As illustrated earlier in this chapter, arrays may also be represented
+    using the <command>ARRAY</command> expression syntax. This representation
+    of an array value consists of items that are interpreted according to the
+    I/O conversion rules for the array's element type, plus decoration that
+    indicates the array structure. The decoration consists of the keyword
+    <command>ARRAY</command> and square brackets (<literal>[</> and
+    <literal>]</>) around the array values, plus delimiter characters between
+    adjacent items. The delimiter character is always a comma (<literal>,</>).
+    When representing multidimensional arrays, the keyword
+    <command>ARRAY</command> is only necessary for the outer level. For example,
+    <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as:
+ <programlisting>
+ SELECT ARRAY[['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   or it also could be written as:
+ <programlisting>
+ SELECT ARRAY[ARRAY['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   </para>
+
+   <para>
+    A final method to represent an array, is through an
+    <command>ARRAY</command> sub-select expression. For example:
+ <programlisting>
+ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
+                           ?column?
+ -------------------------------------------------------------
+  {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
+ (1 row)
+ </programlisting>
+   The sub-select may <emphasis>only</emphasis> return a single column. The
+   resulting one-dimensional array will have an element for each row in the
+   sub-select result, with an element type matching that of the sub-select's
+   target column.
+   </para>
+
+   <para>
+    Arrays may be cast from one type to another in similar fashion to other
+    data types:
+
+ <programlisting>
+ SELECT ARRAY[1,2,3]::oid[];
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+
+ SELECT CAST(ARRAY[1,2,3] AS float8[]);
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+ </programlisting>
+
+   </para>
+
   </sect2>

   <sect2>
***************
*** 316,321 ****
--- 577,590 ----
     Alternatively, you can use backslash-escaping to protect all data characters
     that would otherwise be taken as array syntax or ignorable white space.
    </para>
+
+  <note>
+   <para>
+    The discussion in the preceding paragraph with respect to double quoting does
+    not pertain to the <command>ARRAY</command> expression syntax. In that case,
+    each element is quoted exactly as any other literal value of the element type.
+   </para>
+  </note>

    <para>
     The array output routine will put double quotes around element values
Index: doc/src/sgml/func.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v
retrieving revision 1.154
diff -c -r1.154 func.sgml
*** doc/src/sgml/func.sgml    5 May 2003 15:08:49 -0000    1.154
--- doc/src/sgml/func.sgml    16 Jun 2003 23:45:51 -0000
***************
*** 6962,6967 ****
--- 6962,7164 ----

    </sect1>

+  <sect1 id="functions-array">
+   <title>Array Functions</title>
+
+   <para>
+    <xref linkend="array-operators-table"> shows the operators
+    available for the <type>array</type> types.
+   </para>
+
+     <table id="array-operators-table">
+      <title><type>array</type> Operators</title>
+      <tgroup cols="4">
+       <thead>
+        <row>
+     <entry>Operator</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry> <literal>=</literal> </entry>
+     <entry>equals</entry>
+     <entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry>
+     <entry><literal>t</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>element-to-array concatenation</entry>
+     <entry><literal>3 || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{3,4,5,6}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-element concatenation</entry>
+     <entry><literal>ARRAY[4,5,6] || 7</literal></entry>
+     <entry><literal>{4,5,6,7}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+
+   <para>
+    <xref linkend="array-functions-table"> shows the functions
+    available for use with array types. See <xref linkend="arrays">
+    for more discussion and examples for the use of these functions.
+   </para>
+
+     <table id="array-functions-table">
+      <title><type>array</type> Functions</title>
+      <tgroup cols="5">
+       <thead>
+        <row>
+     <entry>Function</entry>
+     <entry>Return Type</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry>
+      <literal>
+       <function>array_append</function>
+       (<type>anyarray</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the end of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_append(ARRAY[1,2], 3)</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_cat</function>
+       (<type>anyarray</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      concatenate two arrays, returning <literal>NULL</literal>
+      for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_dims</function>
+       (<type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      returns a text representation of array dimension lower and upper bounds,
+      generating an ERROR for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry>
+     <entry><literal>[1:2][1:3]</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_lower</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns lower bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry>
+     <entry><literal>0</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_prepend</function>
+       (<type>anyelement</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the beginning of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_to_string</function>
+       (<type>anyarray</type>, <type>text</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      concatenates array elements using provided delimiter, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_to_string(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry>
+     <entry><literal>1.10~^~2.20~^~3.30</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_upper</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns upper bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry>
+     <entry><literal>4</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>string_to_array</function>
+       (<type>text</type>, <type>text</type>)
+      </literal>
+     </entry>
+     <entry><type>text[]</type></entry>
+     <entry>
+      splits string into array elements using provided delimiter, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>string_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry>
+     <entry><literal>{1.1,2.2,3.3}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+   </sect1>

   <sect1 id="functions-aggregate">
    <title>Aggregate Functions</title>
Index: src/backend/catalog/pg_aggregate.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_aggregate.c,v
retrieving revision 1.56
diff -c -r1.56 pg_aggregate.c
*** src/backend/catalog/pg_aggregate.c    18 Sep 2002 21:35:20 -0000    1.56
--- src/backend/catalog/pg_aggregate.c    16 Jun 2003 23:45:51 -0000
***************
*** 50,59 ****
      Oid            finalfn = InvalidOid;    /* can be omitted */
      Oid            finaltype;
      Oid            fnArgs[FUNC_MAX_ARGS];
!     int            nargs;
      Oid            procOid;
      TupleDesc    tupDesc;
      int            i;
      ObjectAddress myself,
                  referenced;

--- 50,65 ----
      Oid            finalfn = InvalidOid;    /* can be omitted */
      Oid            finaltype;
      Oid            fnArgs[FUNC_MAX_ARGS];
!     int            nargs_transfn;
!     int            nargs_finalfn;
      Oid            procOid;
      TupleDesc    tupDesc;
      int            i;
+     Oid            rettype;
+     Oid           *true_oid_array_transfn;
+     Oid           *true_oid_array_finalfn;
+     bool        retset;
+     FuncDetailCode fdresult;
      ObjectAddress myself,
                  referenced;

***************
*** 68,91 ****
      MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
      fnArgs[0] = aggTransType;
      if (aggBaseType == ANYOID)
!         nargs = 1;
      else
      {
          fnArgs[1] = aggBaseType;
!         nargs = 2;
      }
!     transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
      if (!OidIsValid(transfn))
!         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
      tup = SearchSysCache(PROCOID,
                           ObjectIdGetDatum(transfn),
                           0, 0, 0);
      if (!HeapTupleIsValid(tup))
!         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
      proc = (Form_pg_proc) GETSTRUCT(tup);
-     if (proc->prorettype != aggTransType)
-         elog(ERROR, "return type of transition function %s is not %s",
-          NameListToString(aggtransfnName), format_type_be(aggTransType));

      /*
       * If the transfn is strict and the initval is NULL, make sure input
--- 74,122 ----
      MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
      fnArgs[0] = aggTransType;
      if (aggBaseType == ANYOID)
!         nargs_transfn = 1;
      else
      {
          fnArgs[1] = aggBaseType;
!         nargs_transfn = 2;
      }
!
!     /*
!      * func_get_detail looks up the function in the catalogs, does
!      * disambiguation for polymorphic functions, handles inheritance, and
!      * returns the funcid and type and set or singleton status of the
!      * function's return value.  it also returns the true argument types
!      * to the function.
!      */
!     fdresult = func_get_detail(aggtransfnName, NIL, nargs_transfn, fnArgs,
!                                &transfn, &rettype, &retset,
!                                &true_oid_array_transfn);
!
!     /* only valid case is a normal function */
!     if (fdresult != FUNCDETAIL_NORMAL)
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
      if (!OidIsValid(transfn))
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
!     /*
!      * enforce consistency with ANYARRAY and ANYELEMENT argument
!      * and return types, possibly modifying return type along the way
!      */
!     rettype = enforce_generic_type_consistency(fnArgs, true_oid_array_transfn,
!                                                        nargs_transfn, rettype);
!
!     if (rettype != aggTransType)
!         elog(ERROR, "return type of transition function %s is not %s",
!          NameListToString(aggtransfnName), format_type_be(aggTransType));
!
      tup = SearchSysCache(PROCOID,
                           ObjectIdGetDatum(transfn),
                           0, 0, 0);
      if (!HeapTupleIsValid(tup))
!         func_error("AggregateCreate", aggtransfnName,
!                         nargs_transfn, fnArgs, NULL);
      proc = (Form_pg_proc) GETSTRUCT(tup);

      /*
       * If the transfn is strict and the initval is NULL, make sure input
***************
*** 105,121 ****
      {
          MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
          fnArgs[0] = aggTransType;
!         finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
!         if (!OidIsValid(finalfn))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!         tup = SearchSysCache(PROCOID,
!                              ObjectIdGetDatum(finalfn),
!                              0, 0, 0);
!         if (!HeapTupleIsValid(tup))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!         proc = (Form_pg_proc) GETSTRUCT(tup);
!         finaltype = proc->prorettype;
!         ReleaseSysCache(tup);
      }
      else
      {
--- 136,161 ----
      {
          MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
          fnArgs[0] = aggTransType;
!         nargs_finalfn = 1;
!
!         fdresult = func_get_detail(aggfinalfnName, NIL, 1, fnArgs,
!                                    &finalfn, &rettype, &retset,
!                                    &true_oid_array_finalfn);
!
!         /* only valid case is a normal function */
!         if (fdresult != FUNCDETAIL_NORMAL)
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!
!         if (!OidIsValid(finalfn))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!
!         /*
!          * enforce consistency with ANYARRAY and ANYELEMENT argument
!          * and return types, possibly modifying return type along the way
!          */
!         finaltype = enforce_generic_type_consistency(fnArgs,
!                                                      true_oid_array_finalfn,
!                                                      nargs_finalfn, rettype);
      }
      else
      {
***************
*** 125,130 ****
--- 165,191 ----
          finaltype = aggTransType;
      }
      Assert(OidIsValid(finaltype));
+
+     /*
+      * special disallowed cases:
+      * 1)    if finaltype is polymorphic, basetype cannot be ANY
+      * 2)    if finaltype is polymorphic, both args to transfn must be
+      *        polymorphic
+      */
+     if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
+     {
+         if (aggBaseType == ANYOID)
+             elog(ERROR, "aggregate with base type ANY must have a " \
+                         "non-polymorphic return type");
+
+         if (nargs_transfn > 1 && (
+             (true_oid_array_transfn[0] != ANYARRAYOID &&
+              true_oid_array_transfn[0] != ANYELEMENTOID) ||
+             (true_oid_array_transfn[1] != ANYARRAYOID &&
+              true_oid_array_transfn[1] != ANYELEMENTOID)))
+             elog(ERROR, "aggregate with polymorphic return type requires " \
+                         "state function with both arguments polymorphic");
+     }

      /*
       * Everything looks okay.  Try to create the pg_proc entry for the
Index: src/backend/commands/aggregatecmds.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/aggregatecmds.c,v
retrieving revision 1.5
diff -c -r1.5 aggregatecmds.c
*** src/backend/commands/aggregatecmds.c    4 Sep 2002 20:31:14 -0000    1.5
--- src/backend/commands/aggregatecmds.c    16 Jun 2003 23:45:51 -0000
***************
*** 119,125 ****
          baseTypeId = typenameTypeId(baseType);

      transTypeId = typenameTypeId(transType);
!     if (get_typtype(transTypeId) == 'p')
          elog(ERROR, "Aggregate transition datatype cannot be %s",
               format_type_be(transTypeId));

--- 119,127 ----
          baseTypeId = typenameTypeId(baseType);

      transTypeId = typenameTypeId(transType);
!     if (get_typtype(transTypeId) == 'p' &&
!         transTypeId != ANYARRAYOID &&
!         transTypeId != ANYELEMENTOID)
          elog(ERROR, "Aggregate transition datatype cannot be %s",
               format_type_be(transTypeId));

Index: src/backend/executor/execQual.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/execQual.c,v
retrieving revision 1.130
diff -c -r1.130 execQual.c
*** src/backend/executor/execQual.c    28 May 2003 22:32:49 -0000    1.130
--- src/backend/executor/execQual.c    16 Jun 2003 23:45:51 -0000
***************
*** 1528,1544 ****
              {
                  /* Check other sub-arrays are compatible */
                  if (elem_ndims != ARR_NDIM(array))
!                     elog(ERROR, "Multiple dimension arrays must have array "
                           "expressions with matching number of dimensions");

                  if (memcmp(elem_dims, ARR_DIMS(array),
                             elem_ndims * sizeof(int)) != 0)
!                     elog(ERROR, "Multiple dimension arrays must have array "
                           "expressions with matching dimensions");

                  if (memcmp(elem_lbs, ARR_LBOUND(array),
                             elem_ndims * sizeof(int)) != 0)
!                     elog(ERROR, "Multiple dimension arrays must have array "
                           "expressions with matching dimensions");
              }

--- 1528,1544 ----
              {
                  /* Check other sub-arrays are compatible */
                  if (elem_ndims != ARR_NDIM(array))
!                     elog(ERROR, "Multidimensional arrays must have array "
                           "expressions with matching number of dimensions");

                  if (memcmp(elem_dims, ARR_DIMS(array),
                             elem_ndims * sizeof(int)) != 0)
!                     elog(ERROR, "Multidimensional arrays must have array "
                           "expressions with matching dimensions");

                  if (memcmp(elem_lbs, ARR_LBOUND(array),
                             elem_ndims * sizeof(int)) != 0)
!                     elog(ERROR, "Multidimensional arrays must have array "
                           "expressions with matching dimensions");
              }

Index: src/backend/executor/nodeAgg.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeAgg.c,v
retrieving revision 1.106
diff -c -r1.106 nodeAgg.c
*** src/backend/executor/nodeAgg.c    6 Jun 2003 15:04:01 -0000    1.106
--- src/backend/executor/nodeAgg.c    16 Jun 2003 23:45:51 -0000
***************
*** 58,63 ****
--- 58,64 ----
  #include "executor/executor.h"
  #include "executor/nodeAgg.h"
  #include "miscadmin.h"
+ #include "nodes/makefuncs.h"
  #include "optimizer/clauses.h"
  #include "parser/parse_coerce.h"
  #include "parser/parse_expr.h"
***************
*** 212,218 ****
  static void agg_fill_hash_table(AggState *aggstate);
  static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
  static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
!

  /*
   * Initialize all aggregates for a new group of input values.
--- 213,219 ----
  static void agg_fill_hash_table(AggState *aggstate);
  static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
  static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
! static Oid resolve_type(Oid type_to_resolve, Oid context_type);

  /*
   * Initialize all aggregates for a new group of input values.
***************
*** 351,364 ****
      fcinfo.context = NULL;
      fcinfo.resultinfo = NULL;
      fcinfo.isnull = false;
-
      fcinfo.flinfo = &peraggstate->transfn;
      fcinfo.nargs = 2;
      fcinfo.arg[0] = pergroupstate->transValue;
      fcinfo.argnull[0] = pergroupstate->transValueIsNull;
      fcinfo.arg[1] = newVal;
      fcinfo.argnull[1] = isNull;
-
      newVal = FunctionCallInvoke(&fcinfo);

      /*
--- 352,363 ----
***************
*** 1176,1182 ****
--- 1175,1195 ----
          AclResult    aclresult;
          Oid            transfn_oid,
                      finalfn_oid;
+         FuncExpr   *transfnexpr,
+                    *finalfnexpr;
          Datum        textInitVal;
+         List       *fargs;
+         Oid            agg_rt_type;
+         Oid           *transfn_arg_types;
+         List       *transfn_args = NIL;
+         int            transfn_nargs;
+         Oid            transfn_ret_type;
+         Oid           *finalfn_arg_types = NULL;
+         List       *finalfn_args = NIL;
+         Oid            finalfn_ret_type = InvalidOid;
+         int            finalfn_nargs = 0;
+         Node       *arg0;
+         Node       *arg1;
          int            i;

          /* Planner should have assigned aggregate to correct level */
***************
*** 1227,1232 ****
--- 1240,1405 ----
                          &peraggstate->transtypeLen,
                          &peraggstate->transtypeByVal);

+         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
+         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
+
+         /* get the runtime aggregate argument type */
+         fargs = aggref->args;
+         agg_rt_type = exprType((Node *) nth(0, fargs));
+
+         /* get the transition function argument and return types */
+         transfn_ret_type = get_func_rettype(transfn_oid);
+         transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs);
+
+         /* resolve any polymorphic types */
+         if (transfn_nargs == 2)
+         /* base type was not ANY */
+         {
+             if (transfn_arg_types[1] == ANYARRAYOID ||
+                 transfn_arg_types[1] == ANYELEMENTOID)
+                 transfn_arg_types[1] = agg_rt_type;
+
+             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+                                                         agg_rt_type);
+
+             /*
+              * Build arg list to use on the transfn FuncExpr node. We really
+              * only care that the node type is correct so that the transfn
+              * can discover the actual argument types at runtime using
+              * get_fn_expr_argtype()
+              */
+             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
+                                                             -1, COERCE_DONTCARE);
+             arg1 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[1],
+                                                             -1, COERCE_DONTCARE);
+             transfn_args = makeList2(arg0, arg1);
+
+             /*
+              * the state transition function always returns the same type
+              * as its first argument
+              */
+             if (transfn_ret_type == ANYARRAYOID ||
+                 transfn_ret_type == ANYELEMENTOID)
+                 transfn_ret_type = transfn_arg_types[0];
+         }
+         else if (transfn_nargs == 1)
+         /*
+          * base type was ANY, therefore the aggregate return type should
+          * be non-polymorphic
+          */
+         {
+             Oid    finaltype = get_func_rettype(aggref->aggfnoid);
+
+             /*
+              * this should have been prevented in AggregateCreate,
+              * but check anyway
+              */
+             if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
+                 elog(ERROR, "aggregate with base type ANY must have a " \
+                             "non-polymorphic return type");
+
+             /* see if we have a final function */
+             if (OidIsValid(finalfn_oid))
+             {
+                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
+                 if (finalfn_nargs != 1)
+                     elog(ERROR, "final function takes unexpected number " \
+                                 "of arguments: %d", finalfn_nargs);
+
+                 /*
+                  * final function argument is always the same as the state
+                  * function return type
+                  */
+                 if (finalfn_arg_types[0] != ANYARRAYOID &&
+                     finalfn_arg_types[0] != ANYELEMENTOID)
+                 {
+                     /* if it is not ambiguous, use it */
+                     transfn_ret_type = finalfn_arg_types[0];
+                 }
+                 else
+                 {
+                     /* if it is ambiguous, try to derive it */
+                     finalfn_ret_type = finaltype;
+                     finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0],
+                                                             finalfn_ret_type);
+                     transfn_ret_type = finalfn_arg_types[0];
+                 }
+             }
+             else
+                 transfn_ret_type = finaltype;
+
+             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+                                                         transfn_ret_type);
+
+             /*
+              * Build arg list to use on the transfn FuncExpr node. We really
+              * only care that the node type is correct so that the transfn
+              * can discover the actual argument types at runtime using
+              * get_fn_expr_argtype()
+              */
+             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
+                                                             -1, COERCE_DONTCARE);
+             transfn_args = makeList1(arg0);
+         }
+         else
+             elog(ERROR, "state transition function takes unexpected number " \
+                         "of arguments: %d", transfn_nargs);
+
+         if (OidIsValid(finalfn_oid))
+         {
+             /* get the final function argument and return types */
+             if (finalfn_ret_type == InvalidOid)
+                 finalfn_ret_type = get_func_rettype(finalfn_oid);
+
+             if (!finalfn_arg_types)
+             {
+                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
+                 if (finalfn_nargs != 1)
+                     elog(ERROR, "final function takes unexpected number " \
+                                 "of arguments: %d", finalfn_nargs);
+             }
+
+             /*
+              * final function argument is always the same as the state
+              * function return type, which by now should have been resolved
+              */
+             if (finalfn_arg_types[0] == ANYARRAYOID ||
+                 finalfn_arg_types[0] == ANYELEMENTOID)
+                 finalfn_arg_types[0] = transfn_ret_type;
+
+             /*
+              * Build arg list to use on the finalfn FuncExpr node. We really
+              * only care that the node type is correct so that the finalfn
+              * can discover the actual argument type at runtime using
+              * get_fn_expr_argtype()
+              */
+             arg0 = (Node *) makeRelabelType((Expr *) NULL, finalfn_arg_types[0],
+                                                             -1, COERCE_DONTCARE);
+             finalfn_args = makeList1(arg0);
+
+             finalfn_ret_type = resolve_type(finalfn_ret_type,
+                                                 finalfn_arg_types[0]);
+         }
+
+         fmgr_info(transfn_oid, &peraggstate->transfn);
+         transfnexpr = (FuncExpr *) make_funcclause(transfn_oid,
+                           transfn_ret_type,
+                           false,            /* cannot be a set */
+                           COERCE_DONTCARE,    /* to match any user expr */
+                           transfn_args);
+         peraggstate->transfn.fn_expr = (Node *) transfnexpr;
+
+         if (OidIsValid(finalfn_oid))
+         {
+             fmgr_info(finalfn_oid, &peraggstate->finalfn);
+             finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid,
+                           finalfn_ret_type,
+                           false,            /* cannot be a set */
+                           COERCE_DONTCARE,    /* to match any user expr */
+                           finalfn_args);
+             peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
+         }
+
          /*
           * initval is potentially null, so don't try to access it as a
           * struct field. Must do it the hard way with SysCacheGetAttr.
***************
*** 1239,1252 ****
              peraggstate->initValue = (Datum) 0;
          else
              peraggstate->initValue = GetAggInitVal(textInitVal,
!                                                    aggform->aggtranstype);
!
!         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
!         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
!
!         fmgr_info(transfn_oid, &peraggstate->transfn);
!         if (OidIsValid(finalfn_oid))
!             fmgr_info(finalfn_oid, &peraggstate->finalfn);

          /*
           * If the transfn is strict and the initval is NULL, make sure
--- 1412,1418 ----
              peraggstate->initValue = (Datum) 0;
          else
              peraggstate->initValue = GetAggInitVal(textInitVal,
!                                                    transfn_arg_types[0]);

          /*
           * If the transfn is strict and the initval is NULL, make sure
***************
*** 1457,1460 ****
--- 1623,1659 ----
      elog(ERROR, "Aggregate function %u called as normal function",
           fcinfo->flinfo->fn_oid);
      return (Datum) 0;            /* keep compiler quiet */
+ }
+
+ static Oid
+ resolve_type(Oid type_to_resolve, Oid context_type)
+ {
+     Oid        resolved_type;
+
+     if (context_type == ANYARRAYOID || context_type == ANYELEMENTOID)
+         resolved_type = type_to_resolve;
+     else if (type_to_resolve == ANYARRAYOID)
+     /* any array */
+     {
+         Oid        context_type_arraytype = get_array_type(context_type);
+
+         if (context_type_arraytype != InvalidOid)
+             resolved_type = context_type_arraytype;
+         else
+             resolved_type = context_type;
+     }
+     else if (type_to_resolve == ANYELEMENTOID)
+     /* any element */
+     {
+         Oid        context_type_elemtype = get_element_type(context_type);
+
+         if (context_type_elemtype != InvalidOid)
+             resolved_type = context_type_elemtype;
+         else
+             resolved_type = context_type;
+     }
+     else
+         resolved_type = type_to_resolve;
+
+     return resolved_type;
  }
Index: src/backend/executor/nodeSubplan.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v
retrieving revision 1.46
diff -c -r1.46 nodeSubplan.c
*** src/backend/executor/nodeSubplan.c    6 Jun 2003 15:04:01 -0000    1.46
--- src/backend/executor/nodeSubplan.c    16 Jun 2003 23:45:51 -0000
***************
*** 28,50 ****
  #include "utils/datum.h"
  #include "utils/lsyscache.h"

-
- typedef struct ArrayBuildState
- {
-     MemoryContext mcontext;        /* where all the temp stuff is kept */
-     Datum       *dvalues;        /* array of accumulated Datums */
-     /*
-      * The allocated size of dvalues[] is always a multiple of
-      * ARRAY_ELEMS_CHUNKSIZE
-      */
- #define ARRAY_ELEMS_CHUNKSIZE    64
-     int            nelems;            /* number of valid Datums in dvalues[] */
-     Oid            element_type;    /* data type of the Datums */
-     int16        typlen;            /* needed info about datatype */
-     bool        typbyval;
-     char        typalign;
- } ArrayBuildState;
-
  static Datum ExecHashSubPlan(SubPlanState *node,
                               ExprContext *econtext,
                               bool *isNull);
--- 28,33 ----
***************
*** 54,66 ****
  static void buildSubPlanHash(SubPlanState *node);
  static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
  static bool tupleAllNulls(HeapTuple tuple);
- static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
-                                          Datum dvalue, bool disnull,
-                                          Oid element_type,
-                                          MemoryContext rcontext);
- static Datum makeArrayResult(ArrayBuildState *astate,
-                              MemoryContext rcontext);
-

  /* ----------------------------------------------------------------
   *        ExecSubPlan
--- 37,42 ----
***************
*** 224,229 ****
--- 200,206 ----
      PlanState  *planstate = node->planstate;
      SubLinkType subLinkType = subplan->subLinkType;
      bool        useOr = subplan->useOr;
+     bool        isExpr = subplan->isExpr;
      MemoryContext oldcontext;
      TupleTableSlot *slot;
      Datum        result;
***************
*** 294,299 ****
--- 271,281 ----
          bool        rownull = false;
          int            col = 1;
          List       *plst;
+         int            numelems;
+         int            elemnum;
+         Datum        dvalue;
+         Datum       *dvalues = NULL;
+         bool        disnull;

          if (subLinkType == EXISTS_SUBLINK)
          {
***************
*** 331,339 ****

          if (subLinkType == ARRAY_SUBLINK)
          {
-             Datum    dvalue;
-             bool    disnull;
-
              found = true;
              /* stash away current value */
              dvalue = heap_getattr(tup, 1, tdesc, &disnull);
--- 313,318 ----
***************
*** 351,448 ****
          found = true;

          /*
!          * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
!          * operators for columns of tuple.
           */
!         plst = subplan->paramIds;
!         foreach(lst, node->exprs)
          {
!             ExprState  *exprstate = (ExprState *) lfirst(lst);
!             int            paramid = lfirsti(plst);
!             ParamExecData *prmdata;
!             Datum        expresult;
!             bool        expnull;
!
!             /*
!              * Load up the Param representing this column of the sub-select.
!              */
!             prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
!             Assert(prmdata->execPlan == NULL);
!             prmdata->value = heap_getattr(tup, col, tdesc,
!                                           &(prmdata->isnull));

!             /*
!              * Now we can eval the combining operator for this column.
!              */
!             expresult = ExecEvalExprSwitchContext(exprstate, econtext,
!                                                   &expnull, NULL);
!
!             /*
!              * Combine the result into the row result as appropriate.
!              */
!             if (col == 1)
              {
!                 rowresult = expresult;
!                 rownull = expnull;
              }
!             else if (useOr)
              {
!                 /* combine within row per OR semantics */
!                 if (expnull)
!                     rownull = true;
!                 else if (DatumGetBool(expresult))
                  {
!                     rowresult = BoolGetDatum(true);
!                     rownull = false;
!                     break;        /* needn't look at any more columns */
                  }
              }
              else
              {
!                 /* combine within row per AND semantics */
!                 if (expnull)
!                     rownull = true;
!                 else if (!DatumGetBool(expresult))
!                 {
!                     rowresult = BoolGetDatum(false);
!                     rownull = false;
!                     break;        /* needn't look at any more columns */
!                 }
              }

-             plst = lnext(plst);
-             col++;
          }

!         if (subLinkType == ANY_SUBLINK)
          {
!             /* combine across rows per OR semantics */
!             if (rownull)
!                 *isNull = true;
!             else if (DatumGetBool(rowresult))
              {
!                 result = BoolGetDatum(true);
!                 *isNull = false;
!                 break;            /* needn't look at any more rows */
              }
!         }
!         else if (subLinkType == ALL_SUBLINK)
!         {
!             /* combine across rows per AND semantics */
!             if (rownull)
!                 *isNull = true;
!             else if (!DatumGetBool(rowresult))
              {
!                 result = BoolGetDatum(false);
!                 *isNull = false;
!                 break;            /* needn't look at any more rows */
              }
-         }
-         else
-         {
-             /* must be MULTIEXPR_SUBLINK */
-             result = rowresult;
-             *isNull = rownull;
          }
      }

--- 330,492 ----
          found = true;

          /*
!          * When isExpr is true, we have either a scalar expression or an
!          * array. In the former case, this is no different than the !isExpr
!          * case. In the latter case, iterate over the elements as if they
!          * were from multiple input tuples.
           */
!         if (!isExpr)
!             numelems = 1;
!         else
          {
!             Oid        expr_typeid = tdesc->attrs[0]->atttypid;

!             if (expr_typeid != subplan->exprtype)
              {
!                 subplan->exprtype = expr_typeid;
!                 subplan->elemtype = get_element_type(expr_typeid);
!
!                 if (subplan->elemtype != InvalidOid)
!                     get_typlenbyvalalign(subplan->elemtype,
!                                          &subplan->elmlen,
!                                          &subplan->elmbyval,
!                                          &subplan->elmalign);
              }
!
!             /* get current value */
!             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
!
!             /* XXX this will need work if/when arrays support NULL elements */
!             if (!disnull)
              {
!                 if (subplan->elemtype != InvalidOid)
!                 {
!                     ArrayType   *v = DatumGetArrayTypeP(dvalue);
!
!                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
!                                       subplan->elmbyval, subplan->elmalign,
!                                         &dvalues, &numelems);
!                 }
!                 else
                  {
!                     numelems = 1;
!                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                     dvalues[0] = dvalue;
                  }
              }
              else
              {
!                 numelems = 1;
!                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                 dvalues[0] = (Datum) 0;
              }

          }

!         for (elemnum = 0; elemnum < numelems; elemnum++)
          {
!             /*
!              * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
!              * operators for columns of tuple.
!              */
!             col = 1;
!             plst = subplan->paramIds;
!             foreach(lst, node->exprs)
              {
!                 ExprState  *exprstate = (ExprState *) lfirst(lst);
!                 int            paramid = lfirsti(plst);
!                 ParamExecData *prmdata;
!                 Datum        expresult;
!                 bool        expnull;
!
!                 /*
!                  * Load up the Param representing this column of the sub-select.
!                  */
!                 prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
!                 Assert(prmdata->execPlan == NULL);
!
!                 if (!isExpr)
!                     prmdata->value = heap_getattr(tup, col, tdesc,
!                                                   &(prmdata->isnull));
!                 else
!                 {
!                     prmdata->value = dvalues[elemnum];
!                     prmdata->isnull = disnull;
!                 }
!
!                 /*
!                  * Now we can eval the combining operator for this column.
!                  */
!                 expresult = ExecEvalExprSwitchContext(exprstate, econtext,
!                                                       &expnull, NULL);
!
!                 /*
!                  * Combine the result into the row result as appropriate.
!                  */
!                 if (col == 1)
!                 {
!                     rowresult = expresult;
!                     rownull = expnull;
!                 }
!                 else if (useOr)
!                 {
!                     /* combine within row per OR semantics */
!                     if (expnull)
!                         rownull = true;
!                     else if (DatumGetBool(expresult))
!                     {
!                         rowresult = BoolGetDatum(true);
!                         rownull = false;
!                         break;        /* needn't look at any more columns */
!                     }
!                 }
!                 else
!                 {
!                     /* combine within row per AND semantics */
!                     if (expnull)
!                         rownull = true;
!                     else if (!DatumGetBool(expresult))
!                     {
!                         rowresult = BoolGetDatum(false);
!                         rownull = false;
!                         break;        /* needn't look at any more columns */
!                     }
!                 }
!
!                 plst = lnext(plst);
!                 col++;
              }
!
!             if (subLinkType == ANY_SUBLINK)
              {
!                 /* combine across rows per OR semantics */
!                 if (rownull)
!                     *isNull = true;
!                 else if (DatumGetBool(rowresult))
!                 {
!                     result = BoolGetDatum(true);
!                     *isNull = false;
!                     break;            /* needn't look at any more rows */
!                 }
!             }
!             else if (subLinkType == ALL_SUBLINK)
!             {
!                 /* combine across rows per AND semantics */
!                 if (rownull)
!                     *isNull = true;
!                 else if (!DatumGetBool(rowresult))
!                 {
!                     result = BoolGetDatum(false);
!                     *isNull = false;
!                     break;            /* needn't look at any more rows */
!                 }
!             }
!             else
!             {
!                 /* must be MULTIEXPR_SUBLINK */
!                 result = rowresult;
!                 *isNull = rownull;
              }
          }
      }

***************
*** 480,485 ****
--- 524,530 ----
  buildSubPlanHash(SubPlanState *node)
  {
      SubPlan       *subplan = (SubPlan *) node->xprstate.expr;
+     bool        isExpr = subplan->isExpr;
      PlanState  *planstate = node->planstate;
      int            ncols = length(node->exprs);
      ExprContext *innerecontext = node->innerecontext;
***************
*** 487,492 ****
--- 532,538 ----
      MemoryContext oldcontext;
      int            nbuckets;
      TupleTableSlot *slot;
+     TupleTableSlot *arrslot = NULL;

      Assert(subplan->subLinkType == ANY_SUBLINK);
      Assert(!subplan->useOr);
***************
*** 564,606 ****
      {
          HeapTuple    tup = slot->val;
          TupleDesc    tdesc = slot->ttc_tupleDescriptor;
!         int            col = 1;
          List       *plst;
          bool        isnew;

          /*
!          * Load up the Params representing the raw sub-select outputs,
!          * then form the projection tuple to store in the hashtable.
           */
!         foreach(plst, subplan->paramIds)
          {
!             int            paramid = lfirsti(plst);
!             ParamExecData *prmdata;

!             prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
!             Assert(prmdata->execPlan == NULL);
!             prmdata->value = heap_getattr(tup, col, tdesc,
!                                           &(prmdata->isnull));
!             col++;
!         }
!         slot = ExecProject(node->projRight, NULL);
!         tup = slot->val;

-         /*
-          * If result contains any nulls, store separately or not at all.
-          * (Since we know the projection tuple has no junk columns, we
-          * can just look at the overall hasnull info bit, instead of
-          * groveling through the columns.)
-          */
-         if (HeapTupleNoNulls(tup))
-         {
-             (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
-             node->havehashrows = true;
          }
!         else if (node->hashnulls)
          {
!             (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
!             node->havenullrows = true;
          }

          /*
--- 610,748 ----
      {
          HeapTuple    tup = slot->val;
          TupleDesc    tdesc = slot->ttc_tupleDescriptor;
!         TupleDesc    arrtdesc = NULL;
          List       *plst;
          bool        isnew;
+         int            numelems;
+         int            elemnum;
+         Datum        dvalue;
+         Datum       *dvalues = NULL;
+         bool        disnull;

          /*
!          * When isExpr is true, we have either a scalar expression or an
!          * array. In the former case, this is no different than the !isExpr
!          * case. In the latter case, iterate over the elements as if they
!          * were from multiple input tuples.
           */
!         if (!isExpr)
!             numelems = 1;
!         else
          {
!             Oid        expr_typeid = tdesc->attrs[0]->atttypid;

!             if (expr_typeid != subplan->exprtype)
!             {
!                 subplan->exprtype = expr_typeid;
!                 subplan->elemtype = get_element_type(expr_typeid);
!
!                 if (subplan->elemtype != InvalidOid)
!                     get_typlenbyvalalign(subplan->elemtype,
!                                          &subplan->elmlen,
!                                          &subplan->elmbyval,
!                                          &subplan->elmalign);
!             }
!
!             /* get current value */
!             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
!
!             if (subplan->elemtype != InvalidOid)
!             {
!                 TupleTable    tupleTable;
!                 ArrayType   *v = DatumGetArrayTypeP(dvalue);
!
!                 arrtdesc = CreateTemplateTupleDesc(1, false);
!                 TupleDescInitEntry(arrtdesc, 1, "elem", subplan->elemtype,
!                                                             -1, 0, false);
!
!                 tupleTable = ExecCreateTupleTable(1);
!                 arrslot = ExecAllocTableSlot(tupleTable);
!                 ExecSetSlotDescriptor(arrslot, arrtdesc, true);
!
!                 /* XXX this will need work if/when arrays support NULL elements */
!                 if (!disnull)
!                 {
!                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
!                                       subplan->elmbyval, subplan->elmalign,
!                                         &dvalues, &numelems);
!                 }
!                 else
!                 {
!                     numelems = 1;
!                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                     dvalues[0] = (Datum) 0;
!                 }
!             }
!             else
!             {
!                 numelems = 1;
!                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                 dvalues[0] = dvalue;
!             }

          }
!
!         for (elemnum = 0; elemnum < numelems; elemnum++)
          {
!             int    col = 1;
!
!             if (!isExpr || subplan->elemtype == InvalidOid)
!             {
!                 /*
!                  * Load up the Params representing the raw sub-select outputs,
!                  * then form the projection tuple to store in the hashtable.
!                  */
!                 foreach(plst, subplan->paramIds)
!                 {
!                     int            paramid = lfirsti(plst);
!                     ParamExecData *prmdata;
!
!                     prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
!                     Assert(prmdata->execPlan == NULL);
!
!                     prmdata->value = heap_getattr(tup, col, tdesc,
!                                                   &(prmdata->isnull));
!
!                     col++;
!                 }
!                 slot = ExecProject(node->projRight, NULL);
!                 tup = slot->val;
!             }
!             else
!             {
!                 /*
!                  * For array type expressions, we need to build up our own
!                  * tuple and slot
!                  */
!                 char        nullflag;
!
!                 nullflag = disnull ? 'n' : ' ';
!                 tup = heap_formtuple(arrtdesc, &dvalues[elemnum], &nullflag);
!                 arrslot = ExecStoreTuple(tup, arrslot, InvalidBuffer, true);
!             }
!
!             /*
!              * If result contains any nulls, store separately or not at all.
!              * (Since we know the projection tuple has no junk columns, we
!              * can just look at the overall hasnull info bit, instead of
!              * groveling through the columns.)
!              */
!             if (HeapTupleNoNulls(tup))
!             {
!                 if (!isExpr)
!                     (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
!                 else
!                     (void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew);
!                 node->havehashrows = true;
!             }
!             else if (node->hashnulls)
!             {
!                 if (!isExpr)
!                     (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
!                 else
!                     (void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew);
!                 node->havenullrows = true;
!             }
          }

          /*
***************
*** 617,622 ****
--- 759,766 ----
       * have the potential for a double free attempt.
       */
      ExecClearTuple(node->projRight->pi_slot);
+     if (arrslot)
+         ExecClearTuple(arrslot);

      MemoryContextSwitchTo(oldcontext);
  }
***************
*** 1086,1187 ****
          prm->execPlan = node;
          parent->chgParam = bms_add_member(parent->chgParam, paramid);
      }
- }
-
- /*
-  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
-  *
-  *    astate is working state (NULL on first call)
-  *    rcontext is where to keep working state
-  */
- static ArrayBuildState *
- accumArrayResult(ArrayBuildState *astate,
-                  Datum dvalue, bool disnull,
-                  Oid element_type,
-                  MemoryContext rcontext)
- {
-     MemoryContext arr_context,
-                   oldcontext;
-
-     if (astate == NULL)
-     {
-         /* First time through --- initialize */
-
-         /* Make a temporary context to hold all the junk */
-         arr_context = AllocSetContextCreate(rcontext,
-                                             "ARRAY_SUBLINK Result",
-                                             ALLOCSET_DEFAULT_MINSIZE,
-                                             ALLOCSET_DEFAULT_INITSIZE,
-                                             ALLOCSET_DEFAULT_MAXSIZE);
-         oldcontext = MemoryContextSwitchTo(arr_context);
-         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
-         astate->mcontext = arr_context;
-         astate->dvalues = (Datum *)
-             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
-         astate->nelems = 0;
-         astate->element_type = element_type;
-         get_typlenbyvalalign(element_type,
-                              &astate->typlen,
-                              &astate->typbyval,
-                              &astate->typalign);
-     }
-     else
-     {
-         oldcontext = MemoryContextSwitchTo(astate->mcontext);
-         Assert(astate->element_type == element_type);
-         /* enlarge dvalues[] if needed */
-         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
-             astate->dvalues = (Datum *)
-                 repalloc(astate->dvalues,
-                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
-     }
-
-     if (disnull)
-         elog(ERROR, "NULL elements not allowed in Arrays");
-
-     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
-     astate->dvalues[astate->nelems++] =
-         datumCopy(dvalue, astate->typbyval, astate->typlen);
-
-     MemoryContextSwitchTo(oldcontext);
-
-     return astate;
- }
-
- /*
-  * makeArrayResult - produce final result of ARRAY_SUBLINK
-  *
-  *    astate is working state (not NULL)
-  *    rcontext is where to construct result
-  */
- static Datum
- makeArrayResult(ArrayBuildState *astate,
-                 MemoryContext rcontext)
- {
-     ArrayType  *result;
-     int            dims[1];
-     int            lbs[1];
-     MemoryContext oldcontext;
-
-     /* Build the final array result in rcontext */
-     oldcontext = MemoryContextSwitchTo(rcontext);
-
-     dims[0] = astate->nelems;
-     lbs[0] = 1;
-
-     result = construct_md_array(astate->dvalues,
-                                 1,
-                                 dims,
-                                 lbs,
-                                 astate->element_type,
-                                 astate->typlen,
-                                 astate->typbyval,
-                                 astate->typalign);
-
-     MemoryContextSwitchTo(oldcontext);
-
-     /* Clean up all the junk */
-     MemoryContextDelete(astate->mcontext);
-
-     return PointerGetDatum(result);
  }
--- 1230,1233 ----
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.252
diff -c -r1.252 copyfuncs.c
*** src/backend/nodes/copyfuncs.c    6 Jun 2003 15:04:02 -0000    1.252
--- src/backend/nodes/copyfuncs.c    16 Jun 2003 23:45:51 -0000
***************
*** 728,733 ****
--- 728,734 ----
      COPY_SCALAR_FIELD(agglevelsup);
      COPY_SCALAR_FIELD(aggstar);
      COPY_SCALAR_FIELD(aggdistinct);
+     COPY_NODE_FIELD(args);

      return newnode;
  }
***************
*** 826,831 ****
--- 827,833 ----

      COPY_SCALAR_FIELD(subLinkType);
      COPY_SCALAR_FIELD(useOr);
+     COPY_SCALAR_FIELD(isExpr);
      COPY_NODE_FIELD(lefthand);
      COPY_NODE_FIELD(operName);
      COPY_OIDLIST_FIELD(operOids);
***************
*** 844,849 ****
--- 846,857 ----

      COPY_SCALAR_FIELD(subLinkType);
      COPY_SCALAR_FIELD(useOr);
+     COPY_SCALAR_FIELD(isExpr);
+     COPY_SCALAR_FIELD(exprtype);
+     COPY_SCALAR_FIELD(elemtype);
+     COPY_SCALAR_FIELD(elmlen);
+     COPY_SCALAR_FIELD(elmbyval);
+     COPY_SCALAR_FIELD(elmalign);
      COPY_NODE_FIELD(exprs);
      COPY_INTLIST_FIELD(paramIds);
      COPY_NODE_FIELD(plan);
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.195
diff -c -r1.195 equalfuncs.c
*** src/backend/nodes/equalfuncs.c    6 Jun 2003 15:04:02 -0000    1.195
--- src/backend/nodes/equalfuncs.c    16 Jun 2003 23:45:51 -0000
***************
*** 205,210 ****
--- 205,211 ----
      COMPARE_SCALAR_FIELD(agglevelsup);
      COMPARE_SCALAR_FIELD(aggstar);
      COMPARE_SCALAR_FIELD(aggdistinct);
+     COMPARE_NODE_FIELD(args);

      return true;
  }
***************
*** 301,306 ****
--- 302,308 ----
  {
      COMPARE_SCALAR_FIELD(subLinkType);
      COMPARE_SCALAR_FIELD(useOr);
+     COMPARE_SCALAR_FIELD(isExpr);
      COMPARE_NODE_FIELD(lefthand);
      COMPARE_NODE_FIELD(operName);
      COMPARE_OIDLIST_FIELD(operOids);
***************
*** 314,319 ****
--- 316,327 ----
  {
      COMPARE_SCALAR_FIELD(subLinkType);
      COMPARE_SCALAR_FIELD(useOr);
+     COMPARE_SCALAR_FIELD(isExpr);
+     COMPARE_SCALAR_FIELD(exprtype);
+     COMPARE_SCALAR_FIELD(elemtype);
+     COMPARE_SCALAR_FIELD(elmlen);
+     COMPARE_SCALAR_FIELD(elmbyval);
+     COMPARE_SCALAR_FIELD(elmalign);
      COMPARE_NODE_FIELD(exprs);
      COMPARE_INTLIST_FIELD(paramIds);
      /* should compare plans, but have to settle for comparing plan IDs */
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v
retrieving revision 1.208
diff -c -r1.208 outfuncs.c
*** src/backend/nodes/outfuncs.c    15 Jun 2003 22:51:45 -0000    1.208
--- src/backend/nodes/outfuncs.c    16 Jun 2003 23:45:51 -0000
***************
*** 616,621 ****
--- 616,622 ----
      WRITE_UINT_FIELD(agglevelsup);
      WRITE_BOOL_FIELD(aggstar);
      WRITE_BOOL_FIELD(aggdistinct);
+     WRITE_NODE_FIELD(args);
  }

  static void
***************
*** 701,706 ****
--- 702,708 ----

      WRITE_ENUM_FIELD(subLinkType, SubLinkType);
      WRITE_BOOL_FIELD(useOr);
+     WRITE_BOOL_FIELD(isExpr);
      WRITE_NODE_FIELD(lefthand);
      WRITE_NODE_FIELD(operName);
      WRITE_OIDLIST_FIELD(operOids);
***************
*** 714,719 ****
--- 716,727 ----

      WRITE_ENUM_FIELD(subLinkType, SubLinkType);
      WRITE_BOOL_FIELD(useOr);
+     WRITE_BOOL_FIELD(isExpr);
+     WRITE_OID_FIELD(exprtype);
+     WRITE_OID_FIELD(elemtype);
+     WRITE_INT_FIELD(elmlen);
+     WRITE_BOOL_FIELD(elmbyval);
+     WRITE_CHAR_FIELD(elmalign);
      WRITE_NODE_FIELD(exprs);
      WRITE_INTLIST_FIELD(paramIds);
      WRITE_NODE_FIELD(plan);
Index: src/backend/nodes/readfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v
retrieving revision 1.154
diff -c -r1.154 readfuncs.c
*** src/backend/nodes/readfuncs.c    6 Jun 2003 15:04:02 -0000    1.154
--- src/backend/nodes/readfuncs.c    16 Jun 2003 23:45:51 -0000
***************
*** 416,421 ****
--- 416,422 ----
      READ_UINT_FIELD(agglevelsup);
      READ_BOOL_FIELD(aggstar);
      READ_BOOL_FIELD(aggdistinct);
+     READ_NODE_FIELD(args);

      READ_DONE();
  }
***************
*** 545,550 ****
--- 546,552 ----

      READ_ENUM_FIELD(subLinkType, SubLinkType);
      READ_BOOL_FIELD(useOr);
+     READ_BOOL_FIELD(isExpr);
      READ_NODE_FIELD(lefthand);
      READ_NODE_FIELD(operName);
      READ_OIDLIST_FIELD(operOids);
Index: src/backend/optimizer/plan/subselect.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/plan/subselect.c,v
retrieving revision 1.76
diff -c -r1.76 subselect.c
*** src/backend/optimizer/plan/subselect.c    6 Jun 2003 15:04:02 -0000    1.76
--- src/backend/optimizer/plan/subselect.c    16 Jun 2003 23:45:51 -0000
***************
*** 83,89 ****

  static List *convert_sublink_opers(List *lefthand, List *operOids,
                                     List *targetlist, int rtindex,
!                                    List **righthandIds);
  static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
  static Node *replace_correlation_vars_mutator(Node *node, void *context);
  static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
--- 83,89 ----

  static List *convert_sublink_opers(List *lefthand, List *operOids,
                                     List *targetlist, int rtindex,
!                                    bool isExpr, List **righthandIds);
  static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
  static Node *replace_correlation_vars_mutator(Node *node, void *context);
  static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
***************
*** 299,304 ****
--- 299,310 ----
       */
      node->subLinkType = slink->subLinkType;
      node->useOr = slink->useOr;
+     node->isExpr = slink->isExpr;
+     node->exprtype = InvalidOid;
+     node->elemtype = InvalidOid;
+     node->elmlen = 0;
+     node->elmbyval = false;
+     node->elmalign = '\0';
      node->exprs = NIL;
      node->paramIds = NIL;
      node->useHashTable = false;
***************
*** 374,380 ****
          exprs = convert_sublink_opers(lefthand,
                                        slink->operOids,
                                        plan->targetlist,
!                                       0,
                                        &node->paramIds);
          node->setParam = listCopy(node->paramIds);
          PlannerInitPlan = lappend(PlannerInitPlan, node);
--- 380,386 ----
          exprs = convert_sublink_opers(lefthand,
                                        slink->operOids,
                                        plan->targetlist,
!                                       0, node->isExpr,
                                        &node->paramIds);
          node->setParam = listCopy(node->paramIds);
          PlannerInitPlan = lappend(PlannerInitPlan, node);
***************
*** 457,463 ****
          node->exprs = convert_sublink_opers(lefthand,
                                              slink->operOids,
                                              plan->targetlist,
!                                             0,
                                              &node->paramIds);

          /*
--- 463,469 ----
          node->exprs = convert_sublink_opers(lefthand,
                                              slink->operOids,
                                              plan->targetlist,
!                                             0, node->isExpr,
                                              &node->paramIds);

          /*
***************
*** 499,505 ****
  static List *
  convert_sublink_opers(List *lefthand, List *operOids,
                        List *targetlist, int rtindex,
!                       List **righthandIds)
  {
      List       *result = NIL;
      List       *lst;
--- 505,511 ----
  static List *
  convert_sublink_opers(List *lefthand, List *operOids,
                        List *targetlist, int rtindex,
!                       bool isExpr, List **righthandIds)
  {
      List       *result = NIL;
      List       *lst;
***************
*** 554,566 ****
           * are not expecting to have to resolve unknown Params, so
           * it's okay to pass a null pstate.)
           */
!         result = lappend(result,
!                          make_op_expr(NULL,
!                                       tup,
!                                       leftop,
!                                       rightop,
!                                       exprType(leftop),
!                                       te->resdom->restype));

          ReleaseSysCache(tup);

--- 560,597 ----
           * are not expecting to have to resolve unknown Params, so
           * it's okay to pass a null pstate.)
           */
!         if (!isExpr)
!         {
!             result = lappend(result,
!                              make_op_expr(NULL,
!                                           tup,
!                                           leftop,
!                                           rightop,
!                                           exprType(leftop),
!                                           te->resdom->restype));
!         }
!         else
!         {
!             Oid        exprtype = te->resdom->restype;
!             Oid        elemtype = get_element_type(exprtype);
!
!             if (elemtype != InvalidOid)
!                 result = lappend(result,
!                                  make_op_expr(NULL,
!                                               tup,
!                                               leftop,
!                                               rightop,
!                                               exprType(leftop),
!                                               elemtype));
!             else
!                 result = lappend(result,
!                                  make_op_expr(NULL,
!                                               tup,
!                                               leftop,
!                                               rightop,
!                                               exprType(leftop),
!                                               exprtype));
!         }

          ReleaseSysCache(tup);

***************
*** 671,683 ****
      /*
       * The sublink type must be "= ANY" --- that is, an IN operator.
       * (We require the operator name to be unqualified, which may be
!      * overly paranoid, or may not be.)
       */
      if (sublink->subLinkType != ANY_SUBLINK)
          return NULL;
      if (length(sublink->operName) != 1 ||
          strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
          return NULL;
      /*
       * The sub-select must not refer to any Vars of the parent query.
       * (Vars of higher levels should be okay, though.)
--- 702,718 ----
      /*
       * The sublink type must be "= ANY" --- that is, an IN operator.
       * (We require the operator name to be unqualified, which may be
!      * overly paranoid, or may not be.) It must not be an Expression
!      * sublink.
       */
      if (sublink->subLinkType != ANY_SUBLINK)
          return NULL;
      if (length(sublink->operName) != 1 ||
          strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
          return NULL;
+     if (sublink->isExpr)
+         return NULL;
+
      /*
       * The sub-select must not refer to any Vars of the parent query.
       * (Vars of higher levels should be okay, though.)
***************
*** 730,736 ****
      exprs = convert_sublink_opers(sublink->lefthand,
                                    sublink->operOids,
                                    subselect->targetList,
!                                   rtindex,
                                    &ininfo->sub_targetlist);
      return (Node *) make_ands_explicit(exprs);
  }
--- 765,771 ----
      exprs = convert_sublink_opers(sublink->lefthand,
                                    sublink->operOids,
                                    subselect->targetList,
!                                   rtindex, sublink->isExpr,
                                    &ininfo->sub_targetlist);
      return (Node *) make_ands_explicit(exprs);
  }
Index: src/backend/optimizer/util/clauses.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/util/clauses.c,v
retrieving revision 1.139
diff -c -r1.139 clauses.c
*** src/backend/optimizer/util/clauses.c    6 Jun 2003 15:04:02 -0000    1.139
--- src/backend/optimizer/util/clauses.c    16 Jun 2003 23:45:51 -0000
***************
*** 133,138 ****
--- 133,160 ----
  }

  /*****************************************************************************
+  *              FUNCTION clause functions
+  *****************************************************************************/
+
+ /*
+  * make_funcclause
+  *        Creates a function clause given its function info and argument list.
+  */
+ Expr *
+ make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
+                             CoercionForm funcformat, List *funcargs)
+ {
+     FuncExpr   *expr = makeNode(FuncExpr);
+
+     expr->funcid = funcid;
+     expr->funcresulttype = funcresulttype;
+     expr->funcretset = funcretset;
+     expr->funcformat = funcformat;
+     expr->args = funcargs;
+     return (Expr *) expr;
+ }
+
+ /*****************************************************************************
   *        NOT clause functions
   *****************************************************************************/

Index: src/backend/parser/gram.y
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/gram.y,v
retrieving revision 2.416
diff -c -r2.416 gram.y
*** src/backend/parser/gram.y    29 May 2003 20:40:36 -0000    2.416
--- src/backend/parser/gram.y    16 Jun 2003 23:45:51 -0000
***************
*** 5490,5495 ****
--- 5490,5496 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ANY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = makeList1(makeString("="));
                      n->subselect = $3;
***************
*** 5500,5505 ****
--- 5501,5507 ----
                      /* Make an IN node */
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ANY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = makeList1(makeString("="));
                      n->subselect = $4;
***************
*** 5511,5516 ****
--- 5513,5519 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = $3;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = $2;
                      n->subselect = $4;
***************
*** 5521,5526 ****
--- 5524,5530 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = MULTIEXPR_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = $2;
                      n->subselect = $3;
***************
*** 5904,5909 ****
--- 5908,5914 ----
                      {
                              SubLink *n = (SubLink *)$3;
                              n->subLinkType = ANY_SUBLINK;
+                             n->isExpr = false;
                              n->lefthand = makeList1($1);
                              n->operName = makeList1(makeString("="));
                              $$ = (Node *)n;
***************
*** 5931,5936 ****
--- 5936,5942 ----
                      {
                          /* Make an IN node */
                          SubLink *n = (SubLink *)$4;
+                         n->isExpr = false;
                          n->subLinkType = ANY_SUBLINK;
                          n->lefthand = makeList1($1);
                          n->operName = makeList1(makeString("="));
***************
*** 5957,5967 ****
--- 5963,6000 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = $3;
+                     n->isExpr = false;
                      n->lefthand = makeList1($1);
                      n->operName = $2;
                      n->subselect = $4;
                      $$ = (Node *)n;
                  }
+             | a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op
+                 {
+                     SubLink *n = makeNode(SubLink);
+                     SelectStmt *s = makeNode(SelectStmt);
+                     ResTarget *r = makeNode(ResTarget);
+
+                     r->name = NULL;
+                     r->indirection = NIL;
+                     r->val = (Node *)$5;
+
+                     s->distinctClause = NIL;
+                     s->targetList = makeList1(r);
+                     s->into = NULL;
+                     s->intoColNames = NIL;
+                     s->fromClause = NIL;
+                     s->whereClause = NULL;
+                     s->groupClause = NIL;
+                     s->havingClause = NULL;
+
+                     n->subLinkType = $3;
+                     n->isExpr = true;
+                     n->lefthand = makeList1($1);
+                     n->operName = $2;
+                     n->subselect = (Node *) s;
+                     $$ = (Node *)n;
+                 }
              | UNIQUE select_with_parens %prec Op
                  {
                      /* Not sure how to get rid of the parentheses
***************
*** 6538,6543 ****
--- 6571,6577 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = EXPR_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $1;
***************
*** 6547,6552 ****
--- 6581,6587 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = EXISTS_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $2;
***************
*** 6556,6561 ****
--- 6591,6597 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ARRAY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $2;
***************
*** 6730,6735 ****
--- 6766,6772 ----
  in_expr:    select_with_parens
                  {
                      SubLink *n = makeNode(SubLink);
+                     n->isExpr = false;
                      n->subselect = $1;
                      /* other fields will be filled later */
                      $$ = (Node *)n;
Index: src/backend/parser/parse_coerce.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v
retrieving revision 2.97
diff -c -r2.97 parse_coerce.c
*** src/backend/parser/parse_coerce.c    26 May 2003 00:11:27 -0000    2.97
--- src/backend/parser/parse_coerce.c    16 Jun 2003 23:45:51 -0000
***************
*** 859,865 ****
      /* Get the element type based on the array type, if we have one */
      if (OidIsValid(array_typeid))
      {
!         array_typelem = get_element_type(array_typeid);
          if (!OidIsValid(array_typelem))
              elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
                   format_type_be(array_typeid));
--- 859,869 ----
      /* Get the element type based on the array type, if we have one */
      if (OidIsValid(array_typeid))
      {
!         if (array_typeid != ANYARRAYOID)
!             array_typelem = get_element_type(array_typeid);
!         else
!             array_typelem = ANYELEMENTOID;
!
          if (!OidIsValid(array_typelem))
              elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
                   format_type_be(array_typeid));
***************
*** 919,925 ****
      {
          if (!OidIsValid(array_typeid))
          {
!             array_typeid = get_array_type(elem_typeid);
              if (!OidIsValid(array_typeid))
                  elog(ERROR, "Cannot find array type for datatype %s",
                       format_type_be(elem_typeid));
--- 923,933 ----
      {
          if (!OidIsValid(array_typeid))
          {
!             if (elem_typeid != ANYELEMENTOID)
!                 array_typeid = get_array_type(elem_typeid);
!             else
!                 array_typeid = ANYARRAYOID;
!
              if (!OidIsValid(array_typeid))
                  elog(ERROR, "Cannot find array type for datatype %s",
                       format_type_be(elem_typeid));
***************
*** 1169,1174 ****
--- 1177,1187 ----
      /* Somewhat-fast path for domain -> base type case */
      if (srctype == targettype)
          return true;
+
+     /* Last of the fast-paths: check for matching polymorphic arrays */
+     if (targettype == ANYARRAYOID)
+         if (get_element_type(srctype) != InvalidOid)
+             return true;

      /* Else look in pg_cast */
      tuple = SearchSysCache(CASTSOURCETARGET,
Index: src/backend/parser/parse_expr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_expr.c,v
retrieving revision 1.148
diff -c -r1.148 parse_expr.c
*** src/backend/parser/parse_expr.c    29 Apr 2003 22:13:10 -0000    1.148
--- src/backend/parser/parse_expr.c    16 Jun 2003 23:45:51 -0000
***************
*** 436,441 ****
--- 436,442 ----
                      sublink->operName = NIL;
                      sublink->operOids = NIL;
                      sublink->useOr = FALSE;
+                     sublink->isExpr = FALSE;
                  }
                  else if (sublink->subLinkType == EXPR_SUBLINK ||
                           sublink->subLinkType == ARRAY_SUBLINK)
***************
*** 463,468 ****
--- 464,470 ----
                      sublink->operName = NIL;
                      sublink->operOids = NIL;
                      sublink->useOr = FALSE;
+                     sublink->isExpr = FALSE;
                  }
                  else
                  {
***************
*** 538,547 ****
                           * here, because make_subplan() will insert type
                           * coercion calls if needed.
                           */
!                         optup = oper(op,
!                                      exprType(lexpr),
!                                      exprType((Node *) tent->expr),
!                                      false);
                          opform = (Form_pg_operator) GETSTRUCT(optup);

                          if (opform->oprresult != BOOLOID)
--- 540,569 ----
                           * here, because make_subplan() will insert type
                           * coercion calls if needed.
                           */
!                         if (!sublink->isExpr)
!                         {
!                             optup = oper(op,
!                                          exprType(lexpr),
!                                          exprType((Node *) tent->expr),
!                                          false);
!                         }
!                         else
!                         {
!                             Oid        exprtype = exprType((Node *) tent->expr);
!                             Oid        elemtype = get_element_type(exprtype);
!
!                             if (elemtype != InvalidOid)
!                                 optup = oper(op,
!                                              exprType(lexpr),
!                                              elemtype,
!                                              false);
!                             else
!                                 optup = oper(op,
!                                              exprType(lexpr),
!                                              exprtype,
!                                              false);
!                         }
!
                          opform = (Form_pg_operator) GETSTRUCT(optup);

                          if (opform->oprresult != BOOLOID)
***************
*** 743,749 ****
                          ArrayExpr  *e = (ArrayExpr *) lfirst(element);

                          if (!IsA(e, ArrayExpr))
!                             elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions");
                          if (ndims == 0)
                              ndims = e->ndims;
                          else if (e->ndims != ndims)
--- 765,771 ----
                          ArrayExpr  *e = (ArrayExpr *) lfirst(element);

                          if (!IsA(e, ArrayExpr))
!                             elog(ERROR, "Multidimensional ARRAY[] must be built from nested array expressions");
                          if (ndims == 0)
                              ndims = e->ndims;
                          else if (e->ndims != ndims)
Index: src/backend/parser/parse_func.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_func.c,v
retrieving revision 1.149
diff -c -r1.149 parse_func.c
*** src/backend/parser/parse_func.c    6 Jun 2003 15:04:02 -0000    1.149
--- src/backend/parser/parse_func.c    16 Jun 2003 23:45:51 -0000
***************
*** 336,341 ****
--- 336,342 ----
          aggref->target = lfirst(fargs);
          aggref->aggstar = agg_star;
          aggref->aggdistinct = agg_distinct;
+         aggref->args = fargs;

          /* parse_agg.c does additional aggregate-specific processing */
          transformAggregateCall(pstate, aggref);
Index: src/backend/parser/parse_oper.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_oper.c,v
retrieving revision 1.64
diff -c -r1.64 parse_oper.c
*** src/backend/parser/parse_oper.c    26 May 2003 00:11:27 -0000    1.64
--- src/backend/parser/parse_oper.c    16 Jun 2003 23:45:51 -0000
***************
*** 137,142 ****
--- 137,169 ----
  equality_oper(Oid argtype, bool noError)
  {
      Operator    optup;
+     Oid            elem_type = get_element_type(argtype);
+
+     if (OidIsValid(elem_type))
+     {
+         bool    found = false;
+         /*
+          * If the datatype is an array, look for an "=" operator for the
+          * element datatype.  We require it to be an exact or binary-compatible
+          * match, since most callers are not prepared to cope with adding any
+          * run-time type coercion steps.
+          */
+         optup = equality_oper(elem_type, true);
+         if (optup != NULL)
+         {
+             found = true;
+             ReleaseSysCache(optup);
+         }
+
+         if (!found)
+         {
+             if (!noError)
+                 elog(ERROR, "Unable to identify an equality operator for " \
+                             "array type's element type %s",
+                              format_type_be(elem_type));
+             return NULL;
+         }
+     }

      /*
       * Look for an "=" operator for the datatype.  We require it to be
***************
*** 175,180 ****
--- 202,234 ----
  ordering_oper(Oid argtype, bool noError)
  {
      Operator    optup;
+     Oid            elem_type = get_element_type(argtype);
+
+     if (OidIsValid(elem_type))
+     {
+         bool    found = false;
+         /*
+          * If the datatype is an array, find the array element type's equality
+          * operator, and use its lsortop (it *must* be mergejoinable).  We use
+          * this definition because for sorting and grouping purposes, it's
+          * important that the equality and ordering operators are consistent.
+          */
+         optup = ordering_oper(elem_type, true);
+         if (optup != NULL)
+         {
+             found = true;
+             ReleaseSysCache(optup);
+         }
+
+         if (!found)
+         {
+             if (!noError)
+                 elog(ERROR, "Unable to identify an ordering operator for " \
+                             "array type's element type %s",
+                              format_type_be(elem_type));
+             return NULL;
+         }
+     }

      /*
       * Find the type's equality operator, and use its lsortop (it *must*
***************
*** 215,220 ****
--- 269,289 ----
      Oid            result;

      optup = equality_oper(argtype, false);
+     result = oprfuncid(optup);
+     ReleaseSysCache(optup);
+     return result;
+ }
+
+ /*
+  * ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper())
+  */
+ Oid
+ ordering_oper_funcid(Oid argtype)
+ {
+     Operator    optup;
+     Oid            result;
+
+     optup = ordering_oper(argtype, false);
      result = oprfuncid(optup);
      ReleaseSysCache(optup);
      return result;
Index: src/backend/utils/adt/acl.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/acl.c,v
retrieving revision 1.88
diff -c -r1.88 acl.c
*** src/backend/utils/adt/acl.c    11 Jun 2003 09:23:55 -0000    1.88
--- src/backend/utils/adt/acl.c    16 Jun 2003 21:19:53 -0000
***************
*** 427,432 ****
--- 427,441 ----
          a1->ai_grantor == a2->ai_grantor;
  }

+ /*
+  * user-facing version of aclitemeq() for use as the
+  * aclitem equality operator
+  */
+ Datum
+ aclitem_eq(PG_FUNCTION_ARGS)
+ {
+     PG_RETURN_BOOL(aclitemeq(PG_GETARG_ACLITEM_P(0), PG_GETARG_ACLITEM_P(1)));
+ }

  /*
   * acldefault()  --- create an ACL describing default access permissions
Index: src/backend/utils/adt/array_userfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/array_userfuncs.c,v
retrieving revision 1.1
diff -c -r1.1 array_userfuncs.c
*** src/backend/utils/adt/array_userfuncs.c    8 Apr 2003 23:20:02 -0000    1.1
--- src/backend/utils/adt/array_userfuncs.c    16 Jun 2003 23:45:51 -0000
***************
*** 18,52 ****
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"

-
- /*-----------------------------------------------------------------------------
-  * singleton_array :
-  *        Form a multi-dimensional array given one starting element.
-  *
-  * - first argument is the datum with which to build the array
-  * - second argument is the number of dimensions the array should have;
-  *     defaults to 1 if no second argument is provided
-  *----------------------------------------------------------------------------
-  */
- Datum
- singleton_array(PG_FUNCTION_ARGS)
- {
-     Oid            elem_type = get_fn_expr_argtype(fcinfo, 0);
-     int            ndims;
-
-     if (elem_type == InvalidOid)
-         elog(ERROR, "Cannot determine input datatype");
-
-     if (PG_NARGS() == 2)
-         ndims = PG_GETARG_INT32(1);
-     else
-         ndims = 1;
-
-     PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
-                                                  PG_GETARG_DATUM(0),
-                                                  ndims));
- }
-
  /*-----------------------------------------------------------------------------
   * array_push :
   *        push an element onto either end of a one-dimensional array
--- 18,23 ----
***************
*** 70,75 ****
--- 41,47 ----
      Oid            arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
      Oid            arg0_elemid;
      Oid            arg1_elemid;
+     ArrayMetaState *my_extra;

      if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
          elog(ERROR, "array_push: cannot determine input data types");
***************
*** 95,122 ****
          PG_RETURN_NULL();        /* keep compiler quiet */
      }

!     /* Sanity check: do we have a one-dimensional array */
!     if (ARR_NDIM(v) != 1)
!         elog(ERROR, "Arrays greater than one-dimension are not supported");
!
!     lb = ARR_LBOUND(v);
!     dimv = ARR_DIMS(v);
!     if (arg0_elemid != InvalidOid)
      {
!         /* append newelem */
!         int    ub = dimv[0] + lb[0] - 1;
!         indx = ub + 1;
      }
      else
      {
!         /* prepend newelem */
!         indx = lb[0] - 1;
      }

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

!     result = array_set(v, 1, &indx, newelem, -1,
!                        typlen, typbyval, typalign, &isNull);

      PG_RETURN_ARRAYTYPE_P(result);
  }
--- 67,127 ----
          PG_RETURN_NULL();        /* keep compiler quiet */
      }

!     if (ARR_NDIM(v) == 1)
      {
!         lb = ARR_LBOUND(v);
!         dimv = ARR_DIMS(v);
!
!         if (arg0_elemid != InvalidOid)
!         {
!             /* append newelem */
!             int    ub = dimv[0] + lb[0] - 1;
!             indx = ub + 1;
!         }
!         else
!         {
!             /* prepend newelem */
!             indx = lb[0] - 1;
!         }
      }
+     else if (ARR_NDIM(v) == 0)
+         indx = 1;
      else
+         elog(ERROR, "only empty and one-dimensional arrays are supported");
+
+
+     /*
+      * We arrange to look up info about element type only once per series
+      * of calls, assuming the element type doesn't change underneath us.
+      */
+     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+     if (my_extra == NULL)
      {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
      }

!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }
!
!     result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval,
!                                                  typalign, &isNull);

      PG_RETURN_ARRAYTYPE_P(result);
  }
***************
*** 145,157 ****

      /*
       * We must have one of the following combinations of inputs:
!      * 1) two arrays with ndims1 == ndims2
!      * 2) ndims1 == ndims2 - 1
!      * 3) ndims1 == ndims2 + 1
       */
      ndims1 = ARR_NDIM(v1);
      ndims2 = ARR_NDIM(v2);

      if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
          elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
                      "%d dimensions", ndims1, ndims2);
--- 150,177 ----

      /*
       * We must have one of the following combinations of inputs:
!      * 1) one empty array, and one non-empty array
!      * 2) both arrays empty
!      * 3) two arrays with ndims1 == ndims2
!      * 4) ndims1 == ndims2 - 1
!      * 5) ndims1 == ndims2 + 1
       */
      ndims1 = ARR_NDIM(v1);
      ndims2 = ARR_NDIM(v2);

+     /*
+      * short circuit - if one input array is empty, and the other is not,
+      * we return the non-empty one as the result
+      *
+      * if both are empty, return the first one
+      */
+     if (ndims1 == 0 && ndims2 > 0)
+         PG_RETURN_ARRAYTYPE_P(v2);
+
+     if (ndims2 == 0)
+         PG_RETURN_ARRAYTYPE_P(v1);
+
+     /* the rest fall into combo 2, 3, or 4 */
      if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
          elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
                      "%d dimensions", ndims1, ndims2);
***************
*** 266,412 ****
      PG_RETURN_ARRAYTYPE_P(result);
  }

- /*----------------------------------------------------------------------------
-  * array_accum :
-  *        accumulator to build a 1-D array from input values -- this can be used
-  *        to create custom aggregates.
-  *
-  * This function is not marked strict, so we have to be careful about nulls.
-  *----------------------------------------------------------------------------
-  */
- Datum
- array_accum(PG_FUNCTION_ARGS)
- {
-     /* return NULL if both arguments are NULL */
-     if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
-         PG_RETURN_NULL();
-
-     /* create a new 1-D array from the new element if the array is NULL */
-     if (PG_ARGISNULL(0))
-     {
-         Oid            tgt_type = get_fn_expr_rettype(fcinfo);
-         Oid            tgt_elem_type;
-
-         if (tgt_type == InvalidOid)
-             elog(ERROR, "Cannot determine target array type");
-         tgt_elem_type = get_element_type(tgt_type);
-         if (tgt_elem_type == InvalidOid)
-             elog(ERROR, "Target type is not an array");
-
-         PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
-                                                      PG_GETARG_DATUM(1),
-                                                      1));
-     }
-
-     /* return the array if the new element is NULL */
-     if (PG_ARGISNULL(1))
-         PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0));
-
-     /*
-      * Otherwise this is equivalent to array_push.  We hack the call a little
-      * so that array_push can see the fn_expr information.
-      */
-     return array_push(fcinfo);
- }
-
- /*-----------------------------------------------------------------------------
-  * array_assign :
-  *        assign an element of an array to a new value and return the
-  *        redefined array
-  *----------------------------------------------------------------------------
-  */
- Datum
- array_assign(PG_FUNCTION_ARGS)
- {
-     ArrayType  *v;
-     int            idx_to_chg;
-     Datum        newelem;
-     int           *dimv,
-                *lb, ub;
-     ArrayType  *result;
-     bool        isNull;
-     Oid            element_type;
-     int16        typlen;
-     bool        typbyval;
-     char        typalign;
-
-     v = PG_GETARG_ARRAYTYPE_P(0);
-     idx_to_chg = PG_GETARG_INT32(1);
-     newelem = PG_GETARG_DATUM(2);
-
-     /* Sanity check: do we have a one-dimensional array */
-     if (ARR_NDIM(v) != 1)
-         elog(ERROR, "Arrays greater than one-dimension are not supported");
-
-     lb = ARR_LBOUND(v);
-     dimv = ARR_DIMS(v);
-     ub = dimv[0] + lb[0] - 1;
-     if (idx_to_chg < lb[0] || idx_to_chg > ub)
-         elog(ERROR, "Cannot alter nonexistent array element: %d", idx_to_chg);
-
-     element_type = ARR_ELEMTYPE(v);
-     /* Sanity check: do we have a non-zero element type */
-     if (element_type == 0)
-         elog(ERROR, "Invalid array element type: %u", element_type);
-
-     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
-
-     result = array_set(v, 1, &idx_to_chg, newelem, -1,
-                        typlen, typbyval, typalign, &isNull);
-
-     PG_RETURN_ARRAYTYPE_P(result);
- }
-
- /*-----------------------------------------------------------------------------
-  * array_subscript :
-  *        return specific element of an array
-  *----------------------------------------------------------------------------
-  */
- Datum
- array_subscript(PG_FUNCTION_ARGS)
- {
-     ArrayType  *v;
-     int            idx;
-     int           *dimv,
-                *lb, ub;
-     Datum        result;
-     bool        isNull;
-     Oid            element_type;
-     int16        typlen;
-     bool        typbyval;
-     char        typalign;
-
-     v = PG_GETARG_ARRAYTYPE_P(0);
-     idx = PG_GETARG_INT32(1);
-
-     /* Sanity check: do we have a one-dimensional array */
-     if (ARR_NDIM(v) != 1)
-         elog(ERROR, "Arrays greater than one-dimension are not supported");
-
-     lb = ARR_LBOUND(v);
-     dimv = ARR_DIMS(v);
-     ub = dimv[0] + lb[0] - 1;
-     if (idx < lb[0] || idx > ub)
-         elog(ERROR, "Cannot return nonexistent array element: %d", idx);
-
-     element_type = ARR_ELEMTYPE(v);
-     /* Sanity check: do we have a non-zero element type */
-     if (element_type == 0)
-         elog(ERROR, "Invalid array element type: %u", element_type);
-
-     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
-
-     result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);
-
-     PG_RETURN_DATUM(result);
- }

  /*
!  * actually does the work for singleton_array(), and array_accum() if it is
!  * given a null input array.
   */
  ArrayType *
! create_singleton_array(Oid element_type, Datum element, int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
--- 286,300 ----
      PG_RETURN_ARRAYTYPE_P(result);
  }


  /*
!  * used by text_to_array() in varlena.c
   */
  ArrayType *
! create_singleton_array(FunctionCallInfo fcinfo,
!                        Oid element_type,
!                        Datum element,
!                        int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
***************
*** 415,420 ****
--- 303,309 ----
      int        dims[MAXDIM];
      int        lbs[MAXDIM];
      int        i;
+     ArrayMetaState *my_extra;

      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);
***************
*** 429,435 ****
          lbs[i] = 1;
      }

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      return construct_md_array(dvalues, ndims, dims, lbs, element_type,
                                typlen, typbyval, typalign);
--- 318,352 ----
          lbs[i] = 1;
      }

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      return construct_md_array(dvalues, ndims, dims, lbs, element_type,
                                typlen, typbyval, typalign);
Index: src/backend/utils/adt/arrayfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v
retrieving revision 1.89
diff -c -r1.89 arrayfuncs.c
*** src/backend/utils/adt/arrayfuncs.c    9 May 2003 23:01:45 -0000    1.89
--- src/backend/utils/adt/arrayfuncs.c    16 Jun 2003 23:45:51 -0000
***************
*** 21,28 ****
--- 21,30 ----
  #include "catalog/pg_type.h"
  #include "libpq/pqformat.h"
  #include "parser/parse_coerce.h"
+ #include "parser/parse_oper.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
+ #include "utils/datum.h"
  #include "utils/memutils.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
***************
*** 70,85 ****

  #define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)

- /* I/O function selector for system_cache_lookup */
- typedef enum IOFuncSelector
- {
-     IOFunc_input,
-     IOFunc_output,
-     IOFunc_receive,
-     IOFunc_send
- } IOFuncSelector;
-
-
  static int    ArrayCount(char *str, int *dim, char typdelim);
  static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
               FmgrInfo *inputproc, Oid typelem, int32 typmod,
--- 72,77 ----
***************
*** 93,102 ****
  static void CopyArrayEls(char *p, Datum *values, int nitems,
               int typlen, bool typbyval, char typalign,
               bool freedata);
- static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
-                                 int *typlen, bool *typbyval,
-                                 char *typdelim, Oid *typelem,
-                                 Oid *proc, char *typalign);
  static Datum ArrayCast(char *value, bool byval, int len);
  static int ArrayCastAndSet(Datum src,
                  int typlen, bool typbyval, char typalign,
--- 85,90 ----
***************
*** 119,125 ****
                     char *destPtr,
                     int *st, int *endp, char *srcPtr,
                     int typlen, bool typbyval, char typalign);
!

  /*---------------------------------------------------------------------
   * array_in :
--- 107,113 ----
                     char *destPtr,
                     int *st, int *endp, char *srcPtr,
                     int typlen, bool typbyval, char typalign);
! static int array_cmp(FunctionCallInfo fcinfo);

  /*---------------------------------------------------------------------
   * array_in :
***************
*** 154,165 ****
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;

!     /* Get info about element type, including its input conversion proc */
!     system_cache_lookup(element_type, IOFunc_input,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typinput, &typalign);
!     fmgr_info(typinput, &inputproc);

      /* Make a modifiable copy of the input */
      /* XXX why are we allocating an extra 2 bytes here? */
--- 142,190 ----
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;
+     ArrayMetaState *my_extra;

!     /*
!      * We arrange to look up info about element type, including its input
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its input conversion proc */
!         get_type_metadata(element_type, IOFunc_input,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typinput, &typalign);
!         fmgr_info(typinput, &inputproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typinput;
!         my_extra->typalign = typalign;
!         my_extra->proc = inputproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typinput = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         inputproc = my_extra->proc;
!     }

      /* Make a modifiable copy of the input */
      /* XXX why are we allocating an extra 2 bytes here? */
***************
*** 636,647 ****
                  indx[MAXDIM];
      int            ndim,
                 *dim;

      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_output,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typoutput, &typalign);
!     fmgr_info(typoutput, &outputproc);

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
--- 661,711 ----
                  indx[MAXDIM];
      int            ndim,
                 *dim;
+     ArrayMetaState *my_extra;

      element_type = ARR_ELEMTYPE(v);
!
!     /*
!      * We arrange to look up info about element type, including its input
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its output conversion proc */
!         get_type_metadata(element_type, IOFunc_output,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typoutput, &typalign);
!         fmgr_info(typoutput, &outputproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typoutput;
!         my_extra->typalign = typalign;
!         my_extra->proc = outputproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typoutput = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         outputproc = my_extra->proc;
!     }

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
***************
*** 800,805 ****
--- 864,870 ----
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;
+     ArrayMetaState *my_extra;

      /* Get the array header information */
      ndim = pq_getmsgint(buf, 4);
***************
*** 831,844 ****
          PG_RETURN_ARRAYTYPE_P(retval);
      }

!     /* Get info about element type, including its receive conversion proc */
!     system_cache_lookup(element_type, IOFunc_receive,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typreceive, &typalign);
!     if (!OidIsValid(typreceive))
!         elog(ERROR, "No binary input function available for type %s",
!              format_type_be(element_type));
!     fmgr_info(typreceive, &receiveproc);

      dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
                                typlen, typbyval, typalign,
--- 896,945 ----
          PG_RETURN_ARRAYTYPE_P(retval);
      }

!     /*
!      * We arrange to look up info about element type, including its receive
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its receive conversion proc */
!         get_type_metadata(element_type, IOFunc_receive,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typreceive, &typalign);
!         if (!OidIsValid(typreceive))
!             elog(ERROR, "No binary input function available for type %s",
!                  format_type_be(element_type));
!         fmgr_info(typreceive, &receiveproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typreceive;
!         my_extra->typalign = typalign;
!         my_extra->proc = receiveproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typreceive = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         receiveproc = my_extra->proc;
!     }

      dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
                                typlen, typbyval, typalign,
***************
*** 976,990 ****
      int            ndim,
                 *dim;
      StringInfoData buf;

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
!                         &typdelim, &typelem, &typsend, &typalign);
!     if (!OidIsValid(typsend))
!         elog(ERROR, "No binary output function available for type %s",
!              format_type_be(element_type));
!     fmgr_info(typsend, &sendproc);

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
--- 1077,1130 ----
      int            ndim,
                 *dim;
      StringInfoData buf;
+     ArrayMetaState *my_extra;

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!
!     /*
!      * We arrange to look up info about element type, including its send
!      * proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its send proc */
!         get_type_metadata(element_type, IOFunc_send, &typlen, &typbyval,
!                             &typdelim, &typelem, &typsend, &typalign);
!         if (!OidIsValid(typsend))
!             elog(ERROR, "No binary output function available for type %s",
!                  format_type_be(element_type));
!         fmgr_info(typsend, &sendproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typsend;
!         my_extra->typalign = typalign;
!         my_extra->proc = sendproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typsend = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         sendproc = my_extra->proc;
!     }

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
***************
*** 1476,1481 ****
--- 1616,1641 ----
      array = DatumGetArrayTypeP(PointerGetDatum(array));

      ndim = ARR_NDIM(array);
+
+     /*
+      * if number of dims is zero, i.e. an empty array, create an array
+      * with nSubscripts dimensions, and set the lower bounds to the supplied
+      * subscripts
+      */
+     if (ndim == 0)
+     {
+         Oid        elmtype = ARR_ELEMTYPE(array);
+
+         for (i = 0; i < nSubscripts; i++)
+         {
+             dim[i] = 1;
+             lb[i] = indx[i];
+         }
+
+         return construct_md_array(&dataValue, nSubscripts, dim, lb, elmtype,
+                                                 elmlen, elmbyval, elmalign);
+     }
+
      if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
          elog(ERROR, "Invalid array subscripts");

***************
*** 1632,1637 ****
--- 1792,1822 ----
      /* note: we assume srcArray contains no toasted elements */

      ndim = ARR_NDIM(array);
+
+     /*
+      * if number of dims is zero, i.e. an empty array, create an array
+      * with nSubscripts dimensions, and set the upper and lower bounds
+      * to the supplied subscripts
+      */
+     if (ndim == 0)
+     {
+         Datum  *dvalues;
+         int        nelems;
+         Oid        elmtype = ARR_ELEMTYPE(array);
+
+         deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
+                                                         &dvalues, &nelems);
+
+         for (i = 0; i < nSubscripts; i++)
+         {
+             dim[i] = 1 + upperIndx[i] - lowerIndx[i];
+             lb[i] = lowerIndx[i];
+         }
+
+         return construct_md_array(dvalues, nSubscripts, dim, lb, elmtype,
+                                                  elmlen, elmbyval, elmalign);
+     }
+
      if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
          elog(ERROR, "Invalid array subscripts");

***************
*** 1811,1816 ****
--- 1996,2008 ----
      Oid            typelem;
      Oid            proc;
      char       *s;
+     typedef struct {
+         ArrayMetaState *inp_extra;
+         ArrayMetaState *ret_extra;
+     } am_extra;
+     am_extra  *my_extra;
+     ArrayMetaState *inp_extra;
+     ArrayMetaState *ret_extra;

      /* Get input array */
      if (fcinfo->nargs < 1)
***************
*** 1829,1839 ****
      if (nitems <= 0)
          PG_RETURN_ARRAYTYPE_P(v);

!     /* Lookup source and result types. Unneeded variables are reused. */
!     system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
!                         &typdelim, &typelem, &proc, &inp_typalign);
!     system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
!                         &typdelim, &typelem, &proc, &typalign);

      /* Allocate temporary array for new values */
      values = (Datum *) palloc(nitems * sizeof(Datum));
--- 2021,2101 ----
      if (nitems <= 0)
          PG_RETURN_ARRAYTYPE_P(v);

!     /*
!      * We arrange to look up info about input and return element types only
!      * once per series of calls, assuming the element type doesn't change
!      * underneath us.
!      */
!     my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(am_extra));
!         my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
!
!         my_extra->inp_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         inp_extra = my_extra->inp_extra;
!         inp_extra->element_type = InvalidOid;
!
!         my_extra->ret_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         ret_extra = my_extra->ret_extra;
!         ret_extra->element_type = InvalidOid;
!     }
!     else
!     {
!         inp_extra = my_extra->inp_extra;
!         ret_extra = my_extra->ret_extra;
!     }
!
!     if (inp_extra->element_type != inpType)
!     {
!         /* Lookup source and result types. Unneeded variables are reused. */
!         get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
!                             &typdelim, &typelem, &proc, &inp_typalign);
!
!         inp_extra->element_type = inpType;
!         inp_extra->typlen = inp_typlen;
!         inp_extra->typbyval = inp_typbyval;
!         inp_extra->typdelim = typdelim;
!         inp_extra->typelem = typelem;
!         inp_extra->typiofunc = proc;
!         inp_extra->typalign = inp_typalign;
!     }
!     else
!     {
!         inp_typlen = inp_extra->typlen;
!         inp_typbyval = inp_extra->typbyval;
!         typdelim = inp_extra->typdelim;
!         typelem = inp_extra->typelem;
!         proc = inp_extra->typiofunc;
!         inp_typalign = inp_extra->typalign;
!     }
!
!     if (ret_extra->element_type != retType)
!     {
!         /* Lookup source and result types. Unneeded variables are reused. */
!         get_type_metadata(retType, IOFunc_input, &typlen, &typbyval,
!                             &typdelim, &typelem, &proc, &typalign);
!
!         ret_extra->element_type = retType;
!         ret_extra->typlen = typlen;
!         ret_extra->typbyval = typbyval;
!         ret_extra->typdelim = typdelim;
!         ret_extra->typelem = typelem;
!         ret_extra->typiofunc = proc;
!         ret_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = ret_extra->typlen;
!         typbyval = ret_extra->typbyval;
!         typdelim = ret_extra->typdelim;
!         typelem = ret_extra->typelem;
!         proc = ret_extra->typiofunc;
!         typalign = ret_extra->typalign;
!     }

      /* Allocate temporary array for new values */
      values = (Datum *) palloc(nitems * sizeof(Datum));
***************
*** 2049,2056 ****
   *          compares two arrays for equality
   * result :
   *          returns true if the arrays are equal, false otherwise.
-  *
-  * XXX bitwise equality is pretty bogus ...
   *-----------------------------------------------------------------------------
   */
  Datum
--- 2311,2316 ----
***************
*** 2058,2069 ****
  {
      ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
      ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
      bool        result = true;

!     if (ARR_SIZE(array1) != ARR_SIZE(array2))
!         result = false;
!     else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
          result = false;

      /* Avoid leaking memory when handed toasted input. */
      PG_FREE_IF_COPY(array1, 0);
--- 2318,2435 ----
  {
      ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
      ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
+     char       *p1 = (char *) ARR_DATA_PTR(array1);
+     char       *p2 = (char *) ARR_DATA_PTR(array2);
+     int            ndims1 = ARR_NDIM(array1);
+     int            ndims2 = ARR_NDIM(array2);
+     int           *dims1 = ARR_DIMS(array1);
+     int           *dims2 = ARR_DIMS(array2);
+     int            nitems1 = ArrayGetNItems(ndims1, dims1);
+     int            nitems2 = ArrayGetNItems(ndims2, dims2);
+     Oid            element_type = ARR_ELEMTYPE(array1);
+     FmgrInfo   *ae_fmgr_info = fcinfo->flinfo;
      bool        result = true;
+     int            typlen;
+     bool        typbyval;
+     char        typdelim;
+     Oid            typelem;
+     char        typalign;
+     Oid            typiofunc;
+     int            i;
+     ArrayMetaState *my_extra;
+     FunctionCallInfoData locfcinfo;

!     /* fast path if the arrays do not have the same number of elements */
!     if (nitems1 != nitems2)
          result = false;
+     else
+     {
+         /*
+          * We arrange to look up the equality function only once per series of
+          * calls, assuming the element type doesn't change underneath us.
+          */
+         my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
+         if (my_extra == NULL)
+         {
+             ae_fmgr_info->fn_extra = MemoryContextAlloc(ae_fmgr_info->fn_mcxt,
+                                                      sizeof(ArrayMetaState));
+             my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
+             my_extra->element_type = InvalidOid;
+         }
+
+         if (my_extra->element_type != element_type)
+         {
+             Oid        opfuncid = equality_oper_funcid(element_type);
+
+             if (OidIsValid(opfuncid))
+                 fmgr_info_cxt(opfuncid, &my_extra->proc, ae_fmgr_info->fn_mcxt);
+             else
+                 elog(ERROR,
+                      "array_eq: cannot find equality operator for type: %u",
+                      element_type);
+
+             get_type_metadata(element_type, IOFunc_output,
+                               &typlen, &typbyval, &typdelim,
+                               &typelem, &typiofunc, &typalign);
+
+             my_extra->element_type = element_type;
+             my_extra->typlen = typlen;
+             my_extra->typbyval = typbyval;
+             my_extra->typdelim = typdelim;
+             my_extra->typelem = typelem;
+             my_extra->typiofunc = typiofunc;
+             my_extra->typalign = typalign;
+         }
+         else
+         {
+             typlen = my_extra->typlen;
+             typbyval = my_extra->typbyval;
+             typdelim = my_extra->typdelim;
+             typelem = my_extra->typelem;
+             typiofunc = my_extra->typiofunc;
+             typalign = my_extra->typalign;
+         }
+
+         /*
+          * apply the operator to each pair of array elements.
+          */
+         MemSet(&locfcinfo, 0, sizeof(locfcinfo));
+         locfcinfo.flinfo = &my_extra->proc;
+         locfcinfo.nargs = 2;
+
+         /* Loop over source data */
+         for (i = 0; i < nitems1; i++)
+         {
+             Datum    elt1;
+             Datum    elt2;
+             bool    oprresult;
+
+             /* Get element pair */
+             elt1 = fetch_att(p1, typbyval, typlen);
+             elt2 = fetch_att(p2, typbyval, typlen);
+
+             p1 = att_addlength(p1, typlen, PointerGetDatum(p1));
+             p1 = (char *) att_align(p1, typalign);
+
+             p2 = att_addlength(p2, typlen, PointerGetDatum(p2));
+             p2 = (char *) att_align(p2, typalign);
+
+             /*
+              * Apply the operator to the element pair
+              */
+             locfcinfo.arg[0] = elt1;
+             locfcinfo.arg[1] = elt2;
+             locfcinfo.argnull[0] = false;
+             locfcinfo.argnull[1] = false;
+             locfcinfo.isnull = false;
+             oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+             if (!oprresult)
+             {
+                 result = false;
+                 break;
+             }
+         }
+     }

      /* Avoid leaking memory when handed toasted input. */
      PG_FREE_IF_COPY(array1, 0);
***************
*** 2073,2125 ****
  }


! /***************************************************************************/
! /******************|          Support  Routines              |*****************/
! /***************************************************************************/

! static void
! system_cache_lookup(Oid element_type,
!                     IOFuncSelector which_func,
!                     int *typlen,
!                     bool *typbyval,
!                     char *typdelim,
!                     Oid *typelem,
!                     Oid *proc,
!                     char *typalign)
! {
!     HeapTuple    typeTuple;
!     Form_pg_type typeStruct;
!
!     typeTuple = SearchSysCache(TYPEOID,
!                                ObjectIdGetDatum(element_type),
!                                0, 0, 0);
!     if (!HeapTupleIsValid(typeTuple))
!         elog(ERROR, "cache lookup failed for type %u", element_type);
!     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
!
!     *typlen = typeStruct->typlen;
!     *typbyval = typeStruct->typbyval;
!     *typdelim = typeStruct->typdelim;
!     *typelem = typeStruct->typelem;
!     *typalign = typeStruct->typalign;
!     switch (which_func)
!     {
!         case IOFunc_input:
!             *proc = typeStruct->typinput;
!             break;
!         case IOFunc_output:
!             *proc = typeStruct->typoutput;
!             break;
!         case IOFunc_receive:
!             *proc = typeStruct->typreceive;
!             break;
!         case IOFunc_send:
!             *proc = typeStruct->typsend;
!             break;
      }
!     ReleaseSysCache(typeTuple);
  }

  /*
   * Fetch array element at pointer, converted correctly to a Datum
   */
--- 2439,2628 ----
  }


! /*-----------------------------------------------------------------------------
!  * array-array bool operators:
!  *        Given two arrays, iterate comparison operators
!  *        over the array. Uses logic similar to text comparison
!  *        functions, except element-by-element instead of
!  *        character-by-character.
!  *----------------------------------------------------------------------------
!  */
! Datum
! array_ne(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
! }

! Datum
! array_lt(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
! }
!
! Datum
! array_gt(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
! }
!
! Datum
! array_le(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
! }
!
! Datum
! array_ge(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
! }
!
! Datum
! btarraycmp(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_INT32(array_cmp(fcinfo));
! }
!
! /*
!  * array_cmp()
!  * Internal comparison function for arrays.
!  *
!  * Returns -1, 0 or 1
!  */
! static int
! array_cmp(FunctionCallInfo fcinfo)
! {
!     ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
!     ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
!     FmgrInfo   *ac_fmgr_info = fcinfo->flinfo;
!     Datum        opresult;
!     int            result = 0;
!     Oid            element_type = InvalidOid;
!     int            typlen;
!     bool        typbyval;
!     char        typdelim;
!     Oid            typelem;
!     char        typalign;
!     Oid            typiofunc;
!     Datum       *dvalues1;
!     int            nelems1;
!     Datum       *dvalues2;
!     int            nelems2;
!     int            min_nelems;
!     int            i;
!     typedef struct
!     {
!         Oid                element_type;
!         int                typlen;
!         bool            typbyval;
!         char            typdelim;
!         Oid                typelem;
!         Oid                typiofunc;
!         char            typalign;
!         FmgrInfo        eqproc;
!         FmgrInfo        ordproc;
!     } ac_extra;
!     ac_extra *my_extra;
!
!     element_type = ARR_ELEMTYPE(array1);
!
!     /*
!      * We arrange to look up the element type operator function only once
!      * per series of calls, assuming the element type and opname don't
!      * change underneath us.
!      */
!     my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
!     if (my_extra == NULL)
!     {
!         ac_fmgr_info->fn_extra = MemoryContextAlloc(ac_fmgr_info->fn_mcxt,
!                                                          sizeof(ac_extra));
!         my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         Oid        eqfuncid = equality_oper_funcid(element_type);
!         Oid        ordfuncid = ordering_oper_funcid(element_type);
!
!         fmgr_info_cxt(eqfuncid, &my_extra->eqproc, ac_fmgr_info->fn_mcxt);
!         fmgr_info_cxt(ordfuncid, &my_extra->ordproc, ac_fmgr_info->fn_mcxt);
!
!         if (my_extra->eqproc.fn_nargs != 2)
!             elog(ERROR, "Equality operator does not take 2 arguments: %u",
!                                                                  eqfuncid);
!         if (my_extra->ordproc.fn_nargs != 2)
!             elog(ERROR, "Ordering operator does not take 2 arguments: %u",
!                                                                  ordfuncid);
!
!         get_type_metadata(element_type, IOFunc_output,
!                           &typlen, &typbyval, &typdelim,
!                           &typelem, &typiofunc, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = InvalidOid;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }
!
!     /* extract a C array of arg array datums */
!     deconstruct_array(array1, element_type, typlen, typbyval, typalign,
!                                                     &dvalues1, &nelems1);
!
!     deconstruct_array(array2, element_type, typlen, typbyval, typalign,
!                                                     &dvalues2, &nelems2);
!
!     min_nelems = Min(nelems1, nelems2);
!     for (i = 0; i < min_nelems; i++)
!     {
!         /* are they equal */
!         opresult = FunctionCall2(&my_extra->eqproc,
!                                  dvalues1[i], dvalues2[i]);
!
!         if (!DatumGetBool(opresult))
!         {
!             /* nope, see if arg1 is less than arg2 */
!             opresult = FunctionCall2(&my_extra->ordproc,
!                                      dvalues1[i], dvalues2[i]);
!             if (DatumGetBool(opresult))
!             {
!                 /* arg1 is less than arg2 */
!                 result = -1;
!                 break;
!             }
!             else
!             {
!                 /* arg1 is greater than arg2 */
!                 result = 1;
!                 break;
!             }
!         }
      }
!
!     if ((result == 0) && (nelems1 != nelems2))
!         result = (nelems1 < nelems2) ? -1 : 1;
!
!     /* Avoid leaking memory when handed toasted input. */
!     PG_FREE_IF_COPY(array1, 0);
!     PG_FREE_IF_COPY(array2, 1);
!
!     return result;
  }

+
+ /***************************************************************************/
+ /******************|          Support  Routines              |*****************/
+ /***************************************************************************/
+
  /*
   * Fetch array element at pointer, converted correctly to a Datum
   */
***************
*** 2423,2428 ****
--- 2926,2943 ----
          if (tgt_elem_type == InvalidOid)
              elog(ERROR, "Target type is not an array");

+         /*
+          * We don't deal with domain constraints yet, so bail out.
+          * This isn't currently a problem, because we also don't
+          * support arrays of domain type elements either. But in the
+          * future we might. At that point consideration should be given
+          * to removing the check below and adding a domain constraints
+          * check to the coercion.
+          */
+         if (getBaseType(tgt_elem_type) != tgt_elem_type)
+             elog(ERROR, "array coercion to domain type elements not " \
+                         "currently supported");
+
          if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
                                     COERCION_EXPLICIT, &funcId))
          {
***************
*** 2439,2448 ****
      }

      /*
!      * If it's binary-compatible, return the array unmodified.
       */
      if (my_extra->coerce_finfo.fn_oid == InvalidOid)
!         PG_RETURN_ARRAYTYPE_P(src);

      /*
       * Use array_map to apply the function to each array element.
--- 2954,2969 ----
      }

      /*
!      * If it's binary-compatible, modify the element type in the array header,
!      * but otherwise leave the array as we received it.
       */
      if (my_extra->coerce_finfo.fn_oid == InvalidOid)
!     {
!         ArrayType  *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
!
!         ARR_ELEMTYPE(result) = my_extra->desttype;
!         PG_RETURN_ARRAYTYPE_P(result);
!     }

      /*
       * Use array_map to apply the function to each array element.
***************
*** 2453,2456 ****
--- 2974,3092 ----
      locfcinfo.arg[0] = PointerGetDatum(src);

      return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
+ }
+
+ /*
+  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
+  *
+  *    astate is working state (NULL on first call)
+  *    rcontext is where to keep working state
+  */
+ ArrayBuildState *
+ accumArrayResult(ArrayBuildState *astate,
+                  Datum dvalue, bool disnull,
+                  Oid element_type,
+                  MemoryContext rcontext)
+ {
+     MemoryContext arr_context,
+                   oldcontext;
+
+     if (astate == NULL)
+     {
+         /* First time through --- initialize */
+
+         /* Make a temporary context to hold all the junk */
+         arr_context = AllocSetContextCreate(rcontext,
+                                             "accumArrayResult",
+                                             ALLOCSET_DEFAULT_MINSIZE,
+                                             ALLOCSET_DEFAULT_INITSIZE,
+                                             ALLOCSET_DEFAULT_MAXSIZE);
+         oldcontext = MemoryContextSwitchTo(arr_context);
+         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
+         astate->mcontext = arr_context;
+         astate->dvalues = (Datum *)
+             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
+         astate->nelems = 0;
+         astate->element_type = element_type;
+         get_typlenbyvalalign(element_type,
+                              &astate->typlen,
+                              &astate->typbyval,
+                              &astate->typalign);
+     }
+     else
+     {
+         oldcontext = MemoryContextSwitchTo(astate->mcontext);
+         Assert(astate->element_type == element_type);
+         /* enlarge dvalues[] if needed */
+         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
+             astate->dvalues = (Datum *)
+                 repalloc(astate->dvalues,
+                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
+     }
+
+     if (disnull)
+         elog(ERROR, "NULL elements not allowed in Arrays");
+
+     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
+     astate->dvalues[astate->nelems++] =
+         datumCopy(dvalue, astate->typbyval, astate->typlen);
+
+     MemoryContextSwitchTo(oldcontext);
+
+     return astate;
+ }
+
+ /*
+  * makeArrayResult - produce final result of accumArrayResult
+  *
+  *    astate is working state (not NULL)
+  *    rcontext is where to construct result
+  */
+ Datum
+ makeArrayResult(ArrayBuildState *astate,
+                 MemoryContext rcontext)
+ {
+     int            dims[1];
+     int            lbs[1];
+
+     dims[0] = astate->nelems;
+     lbs[0] = 1;
+
+     return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
+ }
+
+ /*
+  * makeMdArrayResult - produce md final result of accumArrayResult
+  *
+  *    astate is working state (not NULL)
+  *    rcontext is where to construct result
+  */
+ Datum
+ makeMdArrayResult(ArrayBuildState *astate,
+                 int ndims,
+                 int *dims,
+                 int *lbs,
+                 MemoryContext rcontext)
+ {
+     ArrayType  *result;
+     MemoryContext oldcontext;
+
+     /* Build the final array result in rcontext */
+     oldcontext = MemoryContextSwitchTo(rcontext);
+
+     result = construct_md_array(astate->dvalues,
+                                 ndims,
+                                 dims,
+                                 lbs,
+                                 astate->element_type,
+                                 astate->typlen,
+                                 astate->typbyval,
+                                 astate->typalign);
+
+     MemoryContextSwitchTo(oldcontext);
+
+     /* Clean up all the junk */
+     MemoryContextDelete(astate->mcontext);
+
+     return PointerGetDatum(result);
  }
Index: src/backend/utils/adt/varlena.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/varlena.c,v
retrieving revision 1.98
diff -c -r1.98 varlena.c
*** src/backend/utils/adt/varlena.c    15 May 2003 15:50:19 -0000    1.98
--- src/backend/utils/adt/varlena.c    16 Jun 2003 23:45:51 -0000
***************
*** 19,29 ****
--- 19,32 ----
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
  #include "access/tuptoaster.h"
+ #include "catalog/pg_type.h"
  #include "lib/stringinfo.h"
  #include "libpq/crypt.h"
  #include "libpq/pqformat.h"
+ #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/pg_locale.h"
+ #include "utils/lsyscache.h"


  typedef struct varlena unknown;
***************
*** 1983,1990 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

--- 1986,1992 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

***************
*** 2004,2011 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
--- 2006,2012 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
***************
*** 2026,2031 ****
--- 2027,2217 ----
          result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn -
fldsep_len,false); 
          PG_RETURN_TEXT_P(result_text);
      }
+ }
+
+ /*
+  * text_to_array
+  * parse input string
+  * return text array of elements
+  * based on provided field separator
+  */
+ Datum
+ text_to_array(PG_FUNCTION_ARGS)
+ {
+     text       *inputstring = PG_GETARG_TEXT_P(0);
+     int            inputstring_len = TEXTLEN(inputstring);
+     text       *fldsep = PG_GETARG_TEXT_P(1);
+     int            fldsep_len = TEXTLEN(fldsep);
+     int            fldnum;
+     int            start_posn = 0;
+     int            end_posn = 0;
+     text       *result_text = NULL;
+     ArrayBuildState *astate = NULL;
+     MemoryContext oldcontext = CurrentMemoryContext;
+
+     /* return NULL for empty input string */
+     if (inputstring_len < 1)
+         PG_RETURN_NULL();
+
+     /* empty field separator
+      * return one element, 1D, array using the input string */
+     if (fldsep_len < 1)
+         PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+                               CStringGetDatum(inputstring), 1));
+
+     /* start with end position holding the initial start position */
+     end_posn = 0;
+     for (fldnum=1;;fldnum++)    /* field number is 1 based */
+     {
+         Datum    dvalue;
+         bool    disnull = false;
+
+         start_posn = end_posn;
+         end_posn = text_position(PointerGetDatum(inputstring),
+                                  PointerGetDatum(fldsep),
+                                  fldnum);
+
+         if ((start_posn == 0) && (end_posn == 0))    /* fldsep not found */
+         {
+             if (fldnum == 1)
+             {
+                 /* first element
+                  * return one element, 1D, array using the input string */
+                 PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+                                       CStringGetDatum(inputstring), 1));
+             }
+             else
+             {
+                 /* otherwise create array and exit */
+                 PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
+             }
+         }
+         else if ((start_posn != 0) && (end_posn == 0))
+         {
+             /* last field requested */
+             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
+         }
+         else if ((start_posn == 0) && (end_posn != 0))
+         {
+             /* first field requested */
+             result_text = LEFT(inputstring, fldsep);
+         }
+         else
+         {
+             /* prior to last field requested */
+             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn
-fldsep_len, false); 
+         }
+
+         /* stash away current value */
+         dvalue = PointerGetDatum(result_text);
+         astate = accumArrayResult(astate, dvalue,
+                                   disnull, TEXTOID, oldcontext);
+
+     }
+
+     /* never reached -- keep compiler quiet */
+     PG_RETURN_NULL();
+ }
+
+ /*
+  * array_to_text
+  * concatenate Cstring representation of input array elements
+  * using provided field separator
+  */
+ Datum
+ array_to_text(PG_FUNCTION_ARGS)
+ {
+     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
+     char       *fldsep = PG_TEXTARG_GET_STR(1);
+     int            nitems, *dims, ndims;
+     char       *p;
+     Oid            element_type;
+     int            typlen;
+     bool        typbyval;
+     char        typdelim;
+     Oid            typoutput,
+                 typelem;
+     FmgrInfo    outputproc;
+     char        typalign;
+     StringInfo    result_str = makeStringInfo();
+     int            i;
+     ArrayMetaState *my_extra;
+
+     p = ARR_DATA_PTR(v);
+     ndims = ARR_NDIM(v);
+     dims = ARR_DIMS(v);
+     nitems = ArrayGetNItems(ndims, dims);
+
+     /* if there are no elements, return an empty string */
+     if (nitems == 0)
+         PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
+
+     element_type = ARR_ELEMTYPE(v);
+
+     /*
+      * We arrange to look up info about element type, including its output
+      * conversion proc only once per series of calls, assuming the element
+      * type doesn't change underneath us.
+      */
+     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+     if (my_extra == NULL)
+     {
+         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+                                                      sizeof(ArrayMetaState));
+         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+         my_extra->element_type = InvalidOid;
+     }
+
+     if (my_extra->element_type != element_type)
+     {
+         /* Get info about element type, including its output conversion proc */
+         get_type_metadata(element_type, IOFunc_output,
+                             &typlen, &typbyval, &typdelim,
+                             &typelem, &typoutput, &typalign);
+         fmgr_info(typoutput, &outputproc);
+
+         my_extra->element_type = element_type;
+         my_extra->typlen = typlen;
+         my_extra->typbyval = typbyval;
+         my_extra->typdelim = typdelim;
+         my_extra->typelem = typelem;
+         my_extra->typiofunc = typoutput;
+         my_extra->typalign = typalign;
+         my_extra->proc = outputproc;
+     }
+     else
+     {
+         typlen = my_extra->typlen;
+         typbyval = my_extra->typbyval;
+         typdelim = my_extra->typdelim;
+         typelem = my_extra->typelem;
+         typoutput = my_extra->typiofunc;
+         typalign = my_extra->typalign;
+         outputproc = my_extra->proc;
+     }
+
+     for (i = 0; i < nitems; i++)
+     {
+         Datum        itemvalue;
+         char       *value;
+
+         itemvalue = fetch_att(p, typbyval, typlen);
+
+         value = DatumGetCString(FunctionCall3(&outputproc,
+                                               itemvalue,
+                                               ObjectIdGetDatum(typelem),
+                                               Int32GetDatum(-1)));
+
+         if (i > 0)
+             appendStringInfo(result_str, "%s%s", fldsep, value);
+         else
+             appendStringInfo(result_str, "%s", value);
+
+         p = att_addlength(p, typlen, PointerGetDatum(p));
+         p = (char *) att_align(p, typalign);
+     }
+
+     PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
  }

  #define HEXBASE 16
Index: src/backend/utils/cache/lsyscache.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v
retrieving revision 1.95
diff -c -r1.95 lsyscache.c
*** src/backend/utils/cache/lsyscache.c    26 May 2003 00:11:27 -0000    1.95
--- src/backend/utils/cache/lsyscache.c    16 Jun 2003 23:45:51 -0000
***************
*** 625,630 ****
--- 625,664 ----
  }

  /*
+  * get_func_argtypes
+  *        Given procedure id, return the function's argument types.
+  *        Also pass back the number of arguments.
+  */
+ Oid *
+ get_func_argtypes(Oid funcid, int *nargs)
+ {
+     HeapTuple        tp;
+     Form_pg_proc    procstruct;
+     Oid               *result = NULL;
+     int                i;
+
+     tp = SearchSysCache(PROCOID,
+                         ObjectIdGetDatum(funcid),
+                         0, 0, 0);
+     if (!HeapTupleIsValid(tp))
+         elog(ERROR, "Function OID %u does not exist", funcid);
+
+     procstruct = (Form_pg_proc) GETSTRUCT(tp);
+     *nargs = (int) procstruct->pronargs;
+
+     if (*nargs > 0)
+     {
+         result = (Oid *) palloc(*nargs * sizeof(Oid));
+
+         for (i = 0; i < *nargs; i++)
+             result[i] = procstruct->proargtypes[i];
+     }
+
+     ReleaseSysCache(tp);
+     return result;
+ }
+
+ /*
   * get_func_retset
   *        Given procedure id, return the function's proretset flag.
   */
***************
*** 994,999 ****
--- 1028,1083 ----
      *typbyval = typtup->typbyval;
      *typalign = typtup->typalign;
      ReleaseSysCache(tp);
+ }
+
+ /*
+  * get_type_metadata
+  *
+  *        A six-fer:    given the type OID, return typlen, typbyval, typalign,
+  *                    typdelim, typelem, IO function Oid. The IO function
+  *                    returned is controlled by IOFuncSelector
+  */
+ void
+ get_type_metadata(Oid element_type,
+                     IOFuncSelector which_func,
+                     int *typlen,
+                     bool *typbyval,
+                     char *typdelim,
+                     Oid *typelem,
+                     Oid *proc,
+                     char *typalign)
+ {
+     HeapTuple    typeTuple;
+     Form_pg_type typeStruct;
+
+     typeTuple = SearchSysCache(TYPEOID,
+                                ObjectIdGetDatum(element_type),
+                                0, 0, 0);
+     if (!HeapTupleIsValid(typeTuple))
+         elog(ERROR, "cache lookup failed for type %u", element_type);
+     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+
+     *typlen = typeStruct->typlen;
+     *typbyval = typeStruct->typbyval;
+     *typdelim = typeStruct->typdelim;
+     *typelem = typeStruct->typelem;
+     *typalign = typeStruct->typalign;
+     switch (which_func)
+     {
+         case IOFunc_input:
+             *proc = typeStruct->typinput;
+             break;
+         case IOFunc_output:
+             *proc = typeStruct->typoutput;
+             break;
+         case IOFunc_receive:
+             *proc = typeStruct->typreceive;
+             break;
+         case IOFunc_send:
+             *proc = typeStruct->typsend;
+             break;
+     }
+     ReleaseSysCache(typeTuple);
  }

  #ifdef NOT_USED
Index: src/backend/utils/fmgr/fmgr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/fmgr/fmgr.c,v
retrieving revision 1.68
diff -c -r1.68 fmgr.c
*** src/backend/utils/fmgr/fmgr.c    8 Apr 2003 23:20:02 -0000    1.68
--- src/backend/utils/fmgr/fmgr.c    16 Jun 2003 23:45:51 -0000
***************
*** 1673,1675 ****
--- 1673,1701 ----

      return exprType((Node *) nth(argnum, args));
  }
+
+ /*
+  * Get the OID of the function or operator
+  *
+  * Returns InvalidOid if information is not available
+  */
+ Oid
+ get_fn_expr_functype(FunctionCallInfo fcinfo)
+ {
+     Node   *expr;
+
+     /*
+      * can't return anything useful if we have no FmgrInfo or if
+      * its fn_expr node has not been initialized
+      */
+     if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
+         return InvalidOid;
+
+     expr = fcinfo->flinfo->fn_expr;
+     if (IsA(expr, FuncExpr))
+         return ((FuncExpr *) expr)->funcid;
+     else if (IsA(expr, OpExpr))
+         return ((OpExpr *) expr)->opno;
+     else
+         return InvalidOid;
+ }
Index: src/include/fmgr.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/fmgr.h,v
retrieving revision 1.27
diff -c -r1.27 fmgr.h
*** src/include/fmgr.h    8 Apr 2003 23:20:04 -0000    1.27
--- src/include/fmgr.h    16 Jun 2003 23:45:51 -0000
***************
*** 18,23 ****
--- 18,24 ----
  #ifndef FMGR_H
  #define FMGR_H

+ #include "nodes/nodes.h"

  /*
   * All functions that can be called directly by fmgr must have this signature.
***************
*** 372,385 ****
                   Datum arg6, Datum arg7, Datum arg8,
                   Datum arg9);

-
  /*
   * Routines in fmgr.c
   */
  extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
! extern Oid    fmgr_internal_function(const char *proname);
! extern Oid    get_fn_expr_rettype(FunctionCallInfo fcinfo);
! extern Oid    get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);

  /*
   * Routines in dfmgr.c
--- 373,386 ----
                   Datum arg6, Datum arg7, Datum arg8,
                   Datum arg9);

  /*
   * Routines in fmgr.c
   */
  extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
! extern Oid fmgr_internal_function(const char *proname);
! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo);
! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
! extern Oid get_fn_expr_functype(FunctionCallInfo fcinfo);

  /*
   * Routines in dfmgr.c
Index: src/include/catalog/pg_amop.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amop.h,v
retrieving revision 1.49
diff -c -r1.49 pg_amop.h
*** src/include/catalog/pg_amop.h    26 May 2003 00:11:27 -0000    1.49
--- src/include/catalog/pg_amop.h    16 Jun 2003 23:45:51 -0000
***************
*** 418,423 ****
--- 418,432 ----
  DATA(insert (    2098 4 f 2335 ));
  DATA(insert (    2098 5 f 2336 ));

+ /*
+  *    btree array_ops
+  */
+
+ DATA(insert (     397 1 f 1072 ));
+ DATA(insert (     397 2 f 1074 ));
+ DATA(insert (     397 3 f 1070 ));
+ DATA(insert (     397 4 f 1075 ));
+ DATA(insert (     397 5 f 1073 ));

  /*
   *    hash index _ops
Index: src/include/catalog/pg_amproc.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amproc.h,v
retrieving revision 1.37
diff -c -r1.37 pg_amproc.h
*** src/include/catalog/pg_amproc.h    26 May 2003 00:11:27 -0000    1.37
--- src/include/catalog/pg_amproc.h    16 Jun 2003 23:45:51 -0000
***************
*** 78,83 ****
--- 78,84 ----


  /* btree */
+ DATA(insert (     397 1  398 ));
  DATA(insert (     421 1    357 ));
  DATA(insert (     423 1 1596 ));
  DATA(insert (     424 1 1693 ));
Index: src/include/catalog/pg_opclass.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_opclass.h,v
retrieving revision 1.50
diff -c -r1.50 pg_opclass.h
*** src/include/catalog/pg_opclass.h    28 May 2003 16:04:00 -0000    1.50
--- src/include/catalog/pg_opclass.h    16 Jun 2003 23:45:51 -0000
***************
*** 87,92 ****
--- 87,94 ----
   */

  DATA(insert OID =  421 (    403        abstime_ops        PGNSP PGUID  702 t 0 ));
+ DATA(insert OID =  397 (    403        array_ops        PGNSP PGUID 2277 t 0 ));
+ #define ARRAY_BTREE_OPS_OID 397
  DATA(insert OID =  422 (    402        bigbox_ops        PGNSP PGUID  603 f 0 ));
  DATA(insert OID =  423 (    403        bit_ops            PGNSP PGUID 1560 t 0 ));
  DATA(insert OID =  424 (    403        bool_ops        PGNSP PGUID   16 t 0 ));
Index: src/include/catalog/pg_operator.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_operator.h,v
retrieving revision 1.114
diff -c -r1.114 pg_operator.h
*** src/include/catalog/pg_operator.h    26 May 2003 00:11:27 -0000    1.114
--- src/include/catalog/pg_operator.h    16 Jun 2003 23:45:51 -0000
***************
*** 116,125 ****
  DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
  DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 

! DATA(insert OID = 329 (  "="       PGNSP PGUID b f 2277 2277 16   329 0 0 0    0 0 array_eq     eqsel eqjoinsel ));
! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277   0 0 0 0    0 0 array_append   -       -     ));
! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277   0 0 0 0    0 0 array_prepend  -       -     ));
! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277   0 0 0 0    0 0 array_cat      -       -     ));

  DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
  DATA(insert OID = 353 (  "="       PGNSP PGUID b t    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
--- 116,130 ----
  DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
  DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 

! DATA(insert OID = 1070 (  "="       PGNSP PGUID b f 2277 2277 16 1070 1071  1072 1072 1072 1073 array_eq eqsel
eqjoinsel)); 
! DATA(insert OID = 1071 (  "<>"       PGNSP PGUID b f 2277 2277 16 1071 1070  0 0 0 0 array_ne neqsel neqjoinsel ));
! DATA(insert OID = 1072 (  "<"       PGNSP PGUID b f 2277 2277 16 1073 1075  0 0 0 0 array_lt scalarltsel
scalarltjoinsel)); 
! DATA(insert OID = 1073 (  ">"       PGNSP PGUID b f 2277 2277 16 1072 1074  0 0 0 0 array_gt scalargtsel
scalargtjoinsel)); 
! DATA(insert OID = 1074 (  "<="       PGNSP PGUID b f 2277 2277 16 1075 1073  0 0 0 0 array_le scalarltsel
scalarltjoinsel)); 
! DATA(insert OID = 1075 (  ">="       PGNSP PGUID b f 2277 2277 16 1074 1072  0 0 0 0 array_ge scalargtsel
scalargtjoinsel)); 
! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277 0 0  0 0 0 0 array_append   -       -     ));
! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277 0 0  0 0 0 0 array_prepend  -       -     ));
! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277 0 0  0 0 0 0 array_cat      -       -     ));

  DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
  DATA(insert OID = 353 (  "="       PGNSP PGUID b t    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
***************
*** 425,430 ****
--- 430,436 ----
  DATA(insert OID = 966 (  "+"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - ));
  DATA(insert OID = 967 (  "-"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclremove - - ));
  DATA(insert OID = 968 (  "~"       PGNSP PGUID b f 1034 1033     16 0 0 0 0 0 0 aclcontains - - ));
+ DATA(insert OID = 974 (  "="       PGNSP PGUID b f 1033 1033     16 0 0 0 0 0 0 aclitemeq eqsel eqjoinsel ));

  /* additional geometric operators - thomas 1997-07-09 */
  DATA(insert OID =  969 (  "@@"       PGNSP PGUID l f    0  601    600    0  0 0 0 0 0 lseg_center - - ));
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v
retrieving revision 1.303
diff -c -r1.303 pg_proc.h
*** src/include/catalog/pg_proc.h    11 Jun 2003 09:23:55 -0000    1.303
--- src/include/catalog/pg_proc.h    16 Jun 2003 23:45:51 -0000
***************
*** 758,763 ****
--- 758,765 ----
  DESCR("btree less-equal-greater");
  DATA(insert OID = 360 (  bttextcmp           PGNSP PGUID 12 f f t f i 2 23 "25 25"    bttextcmp - _null_ ));
  DESCR("btree less-equal-greater");
+ DATA(insert OID = 398 (  btarraycmp           PGNSP PGUID 12 f f t f i 2 23 "2277 2277"    btarraycmp - _null_ ));
+ DESCR("btree less-equal-greater");

  DATA(insert OID = 361 (  lseg_distance       PGNSP PGUID 12 f f t f i 2 701 "601 601"  lseg_distance - _null_ ));
  DESCR("distance between");
***************
*** 984,997 ****
  DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
  DESCR("greater-than-or-equal");

- DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
- DESCR("array equal");
-
  DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
  DESCR("current user name");
  DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
  DESCR("session user name");

  DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
  DESCR("array dimensions");
  DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
--- 986,1008 ----
  DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
  DESCR("greater-than-or-equal");

  DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
  DESCR("current user name");
  DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
  DESCR("session user name");

+ DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
+ DESCR("array equal");
+ DATA(insert OID = 390 (  array_ne           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ne - _null_ ));
+ DESCR("array not equal");
+ DATA(insert OID = 391 (  array_lt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_lt - _null_ ));
+ DESCR("array less than");
+ DATA(insert OID = 392 (  array_gt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_gt - _null_ ));
+ DESCR("array greater than");
+ DATA(insert OID = 393 (  array_le           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_le - _null_ ));
+ DESCR("array less than or equal");
+ DATA(insert OID = 396 (  array_ge           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ge - _null_ ));
+ DESCR("array greater than or equal");
  DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
  DESCR("array dimensions");
  DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
***************
*** 1002,1023 ****
  DESCR("array lower dimension");
  DATA(insert OID = 2092 (  array_upper       PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ ));
  DESCR("array upper dimension");
- DATA(insert OID = 377 (  singleton_array  PGNSP PGUID 12 f f t f i 1 2277 "2283" singleton_array - _null_ ));
- DESCR("create array from single element");
  DATA(insert OID = 378 (  array_append       PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ ));
  DESCR("append element onto end of array");
  DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
  DESCR("prepend element onto front of array");
- DATA(insert OID = 380 (  array_accum       PGNSP PGUID 12 f f f f i 2 2277 "2277 2283" array_accum - _null_ ));
- DESCR("push element onto end of array, creating array if needed");
- DATA(insert OID = 381 (  array_assign       PGNSP PGUID 12 f f t f i 3 2277 "2277 23 2283" array_assign - _null_ ));
- DESCR("assign specific array element");
- DATA(insert OID = 382 (  array_subscript  PGNSP PGUID 12 f f t f i 2 2283 "2277 23" array_subscript - _null_ ));
- DESCR("return specific array element");
  DATA(insert OID = 383 (  array_cat           PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ ));
  DESCR("concatenate two arrays");
  DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
  DESCR("coerce array type to another array type");

  DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
  DESCR("I/O");
--- 1013,1030 ----
  DESCR("array lower dimension");
  DATA(insert OID = 2092 (  array_upper       PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ ));
  DESCR("array upper dimension");
  DATA(insert OID = 378 (  array_append       PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ ));
  DESCR("append element onto end of array");
  DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
  DESCR("prepend element onto front of array");
  DATA(insert OID = 383 (  array_cat           PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ ));
  DESCR("concatenate two arrays");
  DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
  DESCR("coerce array type to another array type");
+ DATA(insert OID = 394 (  string_to_array   PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ ));
+ DESCR("split delimited text into text[]");
+ DATA(insert OID = 395 (  array_to_string   PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ ));
+ DESCR("concatenate array elements, using delimiter, into text");

  DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
  DESCR("I/O");
***************
*** 1318,1323 ****
--- 1325,1332 ----
  DESCR("remove ACL item");
  DATA(insert OID = 1037 (  aclcontains       PGNSP PGUID 12 f f t f s 2 16 "1034 1033"    aclcontains - _null_ ));
  DESCR("does ACL contain item?");
+ DATA(insert OID = 1062 (  aclitemeq           PGNSP PGUID 12 f f t f s 2 16 "1033 1033"    aclitem_eq - _null_ ));
+ DESCR("equality operator for ACL items");
  DATA(insert OID = 1365 (  makeaclitem       PGNSP PGUID 12 f f t f s 5 1033 "23 23 23 25 16"    makeaclitem - _null_
));
  DESCR("make ACL item");
  DATA(insert OID = 1038 (  seteval           PGNSP PGUID 12 f f t t v 1 23 "26"  seteval - _null_ ));
Index: src/include/nodes/primnodes.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v
retrieving revision 1.83
diff -c -r1.83 primnodes.h
*** src/include/nodes/primnodes.h    6 Jun 2003 15:04:03 -0000    1.83
--- src/include/nodes/primnodes.h    16 Jun 2003 23:45:51 -0000
***************
*** 226,231 ****
--- 226,232 ----
      Index        agglevelsup;    /* > 0 if agg belongs to outer query */
      bool        aggstar;        /* TRUE if argument was really '*' */
      bool        aggdistinct;    /* TRUE if it's agg(DISTINCT ...) */
+     List       *args;            /* arguments to the aggregate */
  } Aggref;

  /* ----------------
***************
*** 358,372 ****
  /* ----------------
   * SubLink
   *
!  * A SubLink represents a subselect appearing in an expression, and in some
!  * cases also the combining operator(s) just above it.    The subLinkType
!  * indicates the form of the expression represented:
   *    EXISTS_SUBLINK        EXISTS(SELECT ...)
   *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
   *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
   *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
   *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
   *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
   * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
   * same length as the subselect's targetlist.  MULTIEXPR will *always* have
   * a list with more than one entry; if the subselect has just one target
--- 359,377 ----
  /* ----------------
   * SubLink
   *
!  * A SubLink represents a subselect, or an expression, appearing in an
!  * expression, and in some cases also the combining operator(s) just above
!  * it.    The subLinkType indicates the form of the expression represented:
   *    EXISTS_SUBLINK        EXISTS(SELECT ...)
   *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
   *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
   *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
   *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
   *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
+  * If an expression is used in place of the subselect, it is transformed
+  * into a simple "(SELECT expr)" in gram.y. This is to allow arrays to be
+  * used as if they were the result of a single column subselect. If the
+  * expression is scalar, it is treated as a one element array.
   * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
   * same length as the subselect's targetlist.  MULTIEXPR will *always* have
   * a list with more than one entry; if the subselect has just one target
***************
*** 415,420 ****
--- 420,427 ----
      SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
      bool        useOr;            /* TRUE to combine column results with
                                   * "OR" not "AND" */
+     bool        isExpr;            /* TRUE if the subselect is really derived
+                                  * from a single expression */
      List       *lefthand;        /* list of outer-query expressions on the
                                   * left */
      List       *operName;        /* originally specified operator name */
***************
*** 456,461 ****
--- 463,477 ----
      SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
      bool        useOr;            /* TRUE to combine column results with
                                   * "OR" not "AND" */
+     bool        isExpr;            /* TRUE if the subselect is really derived
+                                  * from a single expression */
+     /* runtime cache for single array expressions */
+     Oid            exprtype;        /* array and element type, and other info
+                                  * needed deconstruct the array */
+     Oid            elemtype;
+     int16        elmlen;
+     bool        elmbyval;
+     char        elmalign;
      /* The combining operators, transformed to executable expressions: */
      List       *exprs;            /* list of OpExpr expression trees */
      List       *paramIds;        /* IDs of Params embedded in the above */
Index: src/include/optimizer/clauses.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/optimizer/clauses.h,v
retrieving revision 1.63
diff -c -r1.63 clauses.h
*** src/include/optimizer/clauses.h    28 May 2003 16:04:02 -0000    1.63
--- src/include/optimizer/clauses.h    16 Jun 2003 23:45:51 -0000
***************
*** 28,33 ****
--- 28,36 ----
  extern Node *get_leftop(Expr *clause);
  extern Node *get_rightop(Expr *clause);

+ extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
+                                     CoercionForm funcformat, List *funcargs);
+
  extern bool not_clause(Node *clause);
  extern Expr *make_notclause(Expr *notclause);
  extern Expr *get_notclausearg(Expr *notclause);
Index: src/include/parser/parse_oper.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_oper.h,v
retrieving revision 1.25
diff -c -r1.25 parse_oper.h
*** src/include/parser/parse_oper.h    29 Apr 2003 22:13:11 -0000    1.25
--- src/include/parser/parse_oper.h    16 Jun 2003 23:45:51 -0000
***************
*** 44,49 ****
--- 44,50 ----
  /* Convenience routines for common calls on the above */
  extern Oid    compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError);
  extern Oid    equality_oper_funcid(Oid argtype);
+ extern Oid  ordering_oper_funcid(Oid argtype);
  extern Oid    ordering_oper_opid(Oid argtype);

  /* Extract operator OID or underlying-function OID from an Operator tuple */
Index: src/include/utils/acl.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/acl.h,v
retrieving revision 1.52
diff -c -r1.52 acl.h
*** src/include/utils/acl.h    11 Jun 2003 09:23:55 -0000    1.52
--- src/include/utils/acl.h    16 Jun 2003 23:41:46 -0000
***************
*** 192,197 ****
--- 192,198 ----
  extern Datum aclremove(PG_FUNCTION_ARGS);
  extern Datum aclcontains(PG_FUNCTION_ARGS);
  extern Datum makeaclitem(PG_FUNCTION_ARGS);
+ extern Datum aclitem_eq(PG_FUNCTION_ARGS);

  /*
   * prototypes for functions in aclchk.c
Index: src/include/utils/array.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v
retrieving revision 1.38
diff -c -r1.38 array.h
*** src/include/utils/array.h    8 May 2003 22:19:57 -0000    1.38
--- src/include/utils/array.h    16 Jun 2003 23:45:51 -0000
***************
*** 32,37 ****
--- 32,68 ----
      Oid            elemtype;        /* element type OID */
  } ArrayType;

+ typedef struct ArrayBuildState
+ {
+     MemoryContext mcontext;        /* where all the temp stuff is kept */
+     Datum       *dvalues;        /* array of accumulated Datums */
+     /*
+      * The allocated size of dvalues[] is always a multiple of
+      * ARRAY_ELEMS_CHUNKSIZE
+      */
+ #define ARRAY_ELEMS_CHUNKSIZE    64
+     int            nelems;            /* number of valid Datums in dvalues[] */
+     Oid            element_type;    /* data type of the Datums */
+     int16        typlen;            /* needed info about datatype */
+     bool        typbyval;
+     char        typalign;
+ } ArrayBuildState;
+
+ /*
+  * structure to cache type metadata needed for array manipulation
+  */
+ typedef struct ArrayMetaState
+ {
+     Oid                element_type;
+     int                typlen;
+     bool            typbyval;
+     char            typdelim;
+     Oid                typelem;
+     Oid                typiofunc;
+     char            typalign;
+     FmgrInfo        proc;
+ } ArrayMetaState;
+
  /*
   * fmgr macros for array objects
   */
***************
*** 86,96 ****
  extern Datum array_send(PG_FUNCTION_ARGS);
  extern Datum array_length_coerce(PG_FUNCTION_ARGS);
  extern Datum array_eq(PG_FUNCTION_ARGS);
  extern Datum array_dims(PG_FUNCTION_ARGS);
  extern Datum array_lower(PG_FUNCTION_ARGS);
  extern Datum array_upper(PG_FUNCTION_ARGS);
- extern Datum array_assign(PG_FUNCTION_ARGS);
- extern Datum array_subscript(PG_FUNCTION_ARGS);
  extern Datum array_type_coerce(PG_FUNCTION_ARGS);

  extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
--- 117,131 ----
  extern Datum array_send(PG_FUNCTION_ARGS);
  extern Datum array_length_coerce(PG_FUNCTION_ARGS);
  extern Datum array_eq(PG_FUNCTION_ARGS);
+ extern Datum array_ne(PG_FUNCTION_ARGS);
+ extern Datum array_lt(PG_FUNCTION_ARGS);
+ extern Datum array_gt(PG_FUNCTION_ARGS);
+ extern Datum array_le(PG_FUNCTION_ARGS);
+ extern Datum array_ge(PG_FUNCTION_ARGS);
+ extern Datum btarraycmp(PG_FUNCTION_ARGS);
  extern Datum array_dims(PG_FUNCTION_ARGS);
  extern Datum array_lower(PG_FUNCTION_ARGS);
  extern Datum array_upper(PG_FUNCTION_ARGS);
  extern Datum array_type_coerce(PG_FUNCTION_ARGS);

  extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
***************
*** 124,130 ****
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
!

  /*
   * prototypes for functions defined in arrayutils.c
--- 159,172 ----
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
! extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
!                                          Datum dvalue, bool disnull,
!                                          Oid element_type,
!                                          MemoryContext rcontext);
! extern Datum makeArrayResult(ArrayBuildState *astate,
!                              MemoryContext rcontext);
! extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
!                                int *dims, int *lbs, MemoryContext rcontext);

  /*
   * prototypes for functions defined in arrayutils.c
***************
*** 141,152 ****
  /*
   * prototypes for functions defined in array_userfuncs.c
   */
- extern Datum singleton_array(PG_FUNCTION_ARGS);
  extern Datum array_push(PG_FUNCTION_ARGS);
- extern Datum array_accum(PG_FUNCTION_ARGS);
  extern Datum array_cat(PG_FUNCTION_ARGS);

! extern ArrayType *create_singleton_array(Oid element_type,
                                           Datum element,
                                           int ndims);

--- 183,193 ----
  /*
   * prototypes for functions defined in array_userfuncs.c
   */
  extern Datum array_push(PG_FUNCTION_ARGS);
  extern Datum array_cat(PG_FUNCTION_ARGS);

! extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
!                                          Oid element_type,
                                           Datum element,
                                           int ndims);

Index: src/include/utils/builtins.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/builtins.h,v
retrieving revision 1.219
diff -c -r1.219 builtins.h
*** src/include/utils/builtins.h    26 May 2003 00:11:28 -0000    1.219
--- src/include/utils/builtins.h    16 Jun 2003 23:45:51 -0000
***************
*** 530,535 ****
--- 530,537 ----
                        List **namelist);
  extern Datum replace_text(PG_FUNCTION_ARGS);
  extern Datum split_text(PG_FUNCTION_ARGS);
+ extern Datum text_to_array(PG_FUNCTION_ARGS);
+ extern Datum array_to_text(PG_FUNCTION_ARGS);
  extern Datum to_hex32(PG_FUNCTION_ARGS);
  extern Datum to_hex64(PG_FUNCTION_ARGS);
  extern Datum md5_text(PG_FUNCTION_ARGS);
Index: src/include/utils/lsyscache.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v
retrieving revision 1.70
diff -c -r1.70 lsyscache.h
*** src/include/utils/lsyscache.h    26 May 2003 00:11:28 -0000    1.70
--- src/include/utils/lsyscache.h    16 Jun 2003 23:45:51 -0000
***************
*** 15,20 ****
--- 15,29 ----

  #include "access/htup.h"

+ /* I/O function selector for system_cache_lookup */
+ typedef enum IOFuncSelector
+ {
+     IOFunc_input,
+     IOFunc_output,
+     IOFunc_receive,
+     IOFunc_send
+ } IOFuncSelector;
+
  extern bool op_in_opclass(Oid opno, Oid opclass);
  extern bool op_requires_recheck(Oid opno, Oid opclass);
  extern Oid    get_opclass_member(Oid opclass, int16 strategy);
***************
*** 39,44 ****
--- 48,54 ----
  extern RegProcedure get_oprjoin(Oid opno);
  extern char *get_func_name(Oid funcid);
  extern Oid    get_func_rettype(Oid funcid);
+ extern Oid *get_func_argtypes(Oid funcid, int *nargs);
  extern bool get_func_retset(Oid funcid);
  extern bool func_strict(Oid funcid);
  extern char func_volatile(Oid funcid);
***************
*** 54,59 ****
--- 64,77 ----
  extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
  extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
                       char *typalign);
+ extern void get_type_metadata(Oid element_type,
+                                 IOFuncSelector which_func,
+                                 int *typlen,
+                                 bool *typbyval,
+                                 char *typdelim,
+                                 Oid *typelem,
+                                 Oid *proc,
+                                 char *typalign);
  extern char get_typstorage(Oid typid);
  extern int32 get_typtypmod(Oid typid);
  extern Node *get_typdefault(Oid typid);
Index: src/interfaces/ecpg/preproc/preproc.y
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/preproc.y,v
retrieving revision 1.232
diff -c -r1.232 preproc.y
*** src/interfaces/ecpg/preproc/preproc.y    16 Jun 2003 16:58:11 -0000    1.232
--- src/interfaces/ecpg/preproc/preproc.y    16 Jun 2003 23:45:51 -0000
***************
*** 4592,4598 ****
                  $3.type_enum != ECPGt_char &&
                      $3.type_enum != ECPGt_unsigned_char &&
                  atoi(this->type->type_index) >= 0)
!                 mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");

              types = this;
          }
--- 4592,4598 ----
                  $3.type_enum != ECPGt_char &&
                      $3.type_enum != ECPGt_unsigned_char &&
                  atoi(this->type->type_index) >= 0)
!                 mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");

              types = this;
          }
***************
*** 5424,5430 ****
                      $5.type_enum != ECPGt_char &&
                      $5.type_enum != ECPGt_unsigned_char &&
                      atoi(this->type->type_index) >= 0)
!                     mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");

                  types = this;
              }
--- 5424,5430 ----
                      $5.type_enum != ECPGt_char &&
                      $5.type_enum != ECPGt_unsigned_char &&
                      atoi(this->type->type_index) >= 0)
!                     mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");

                  types = this;
              }
***************
*** 5491,5497 ****

                      default:
                          if (atoi(length) >= 0)
!                             mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data
types");

                          if (atoi(dimension) < 0)
                              type = ECPGmake_simple_type($5.type_enum, make_str("1"));
--- 5491,5497 ----

                      default:
                          if (atoi(length) >= 0)
!                             mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data
types");

                          if (atoi(dimension) < 0)
                              type = ECPGmake_simple_type($5.type_enum, make_str("1"));
Index: src/interfaces/ecpg/preproc/type.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/type.c,v
retrieving revision 1.51
diff -c -r1.51 type.c
*** src/interfaces/ecpg/preproc/type.c    29 May 2003 13:59:26 -0000    1.51
--- src/interfaces/ecpg/preproc/type.c    16 Jun 2003 23:45:51 -0000
***************
*** 493,499 ****
                  switch (type->u.element->type)
                  {
                      case ECPGt_array:
!                         yyerror("internal error, found multi-dimensional array\n");
                          break;
                      case ECPGt_struct:
                      case ECPGt_union:
--- 493,499 ----
                  switch (type->u.element->type)
                  {
                      case ECPGt_array:
!                         yyerror("internal error, found multidimensional array\n");
                          break;
                      case ECPGt_struct:
                      case ECPGt_union:
Index: src/interfaces/ecpg/preproc/variable.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/variable.c,v
retrieving revision 1.21
diff -c -r1.21 variable.c
*** src/interfaces/ecpg/preproc/variable.c    11 Jun 2003 06:39:13 -0000    1.21
--- src/interfaces/ecpg/preproc/variable.c    16 Jun 2003 23:45:51 -0000
***************
*** 436,442 ****
      if (atoi(type_index) >= 0)
      {
          if (atoi(*length) >= 0)
!             mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");

          *length = type_index;
      }
--- 436,442 ----
      if (atoi(type_index) >= 0)
      {
          if (atoi(*length) >= 0)
!             mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");

          *length = type_index;
      }
***************
*** 444,450 ****
      if (atoi(type_dimension) >= 0)
      {
          if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
!             mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");

          if (atoi(*dimension) >= 0)
              *length = *dimension;
--- 444,450 ----
      if (atoi(type_dimension) >= 0)
      {
          if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
!             mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");

          if (atoi(*dimension) >= 0)
              *length = *dimension;
***************
*** 463,472 ****
          mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");

      if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
!         mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");

      if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
!         mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");

      switch (type_enum)
      {
--- 463,472 ----
          mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");

      if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
!         mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");

      if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
!         mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");

      switch (type_enum)
      {
***************
*** 480,486 ****
              }

              if (atoi(*length) >= 0)
!                 mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for structures");

              break;
          case ECPGt_varchar:
--- 480,486 ----
              }

              if (atoi(*length) >= 0)
!                 mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for structures");

              break;
          case ECPGt_varchar:
***************
*** 525,531 ****
              }

              if (atoi(*length) >= 0)
!                 mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for simple data types");

              break;
      }
--- 525,531 ----
              }

              if (atoi(*length) >= 0)
!                 mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for simple data types");

              break;
      }
Index: src/test/regress/expected/arrays.out
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/test/regress/expected/arrays.out,v
retrieving revision 1.11
diff -c -r1.11 arrays.out
*** src/test/regress/expected/arrays.out    8 Apr 2003 23:20:04 -0000    1.11
--- src/test/regress/expected/arrays.out    16 Jun 2003 23:45:51 -0000
***************
*** 178,196 ****
  (1 row)

  -- functions
! SELECT singleton_array(42) AS "{42}";
!  {42}
! ------
!  {42}
! (1 row)
!
! SELECT array_append(singleton_array(42), 6) AS "{42,6}";
   {42,6}
  --------
   {42,6}
  (1 row)

! SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
   {6,42}
  --------
   {6,42}
--- 178,190 ----
  (1 row)

  -- functions
! SELECT array_append(array[42], 6) AS "{42,6}";
   {42,6}
  --------
   {42,6}
  (1 row)

! SELECT array_prepend(6, array[42]) AS "{6,42}";
   {6,42}
  --------
   {6,42}
***************
*** 212,235 ****
   {{3,4},{5,6},{1,2}}
  ---------------------
   {{3,4},{5,6},{1,2}}
- (1 row)
-
- SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
-  1.2
- -----
-  1.2
- (1 row)
-
- SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
-  {1.1,9.99,1.3}
- ----------------
-  {1.1,9.99,1.3}
- (1 row)
-
- SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
-  9.99
- ------
-  9.99
  (1 row)

  -- operators
--- 206,211 ----
Index: src/test/regress/sql/arrays.sql
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/test/regress/sql/arrays.sql,v
retrieving revision 1.10
diff -c -r1.10 arrays.sql
*** src/test/regress/sql/arrays.sql    8 Apr 2003 23:20:04 -0000    1.10
--- src/test/regress/sql/arrays.sql    16 Jun 2003 23:45:51 -0000
***************
*** 130,144 ****
  SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";

  -- functions
! SELECT singleton_array(42) AS "{42}";
! SELECT array_append(singleton_array(42), 6) AS "{42,6}";
! SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
  SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}";
  SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
  SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
- SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
- SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
- SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;

  -- operators
  SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
--- 130,140 ----
  SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";

  -- functions
! SELECT array_append(array[42], 6) AS "{42,6}";
! SELECT array_prepend(6, array[42]) AS "{6,42}";
  SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}";
  SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
  SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";

  -- operators
  SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];

Re: array support patch phase 1 patch

From
Bruce Momjian
Date:
Your patch has been added to the PostgreSQL unapplied patches list at:

    http://momjian.postgresql.org/cgi-bin/pgpatches

I will try to apply it within the next 48 hours.

---------------------------------------------------------------------------


Joe Conway wrote:
> Joe Conway wrote:
> > The attached patch addresses Peter's concerns, subject to our agreements
> > above. I.e, the changes are:
>
> The previous patch was no longer applying cleanly, so here is an update.
> Applies and compiles clean on cvs tip, passes all regression tests.
> Please apply.
>
> Joe
>

> Index: doc/src/sgml/array.sgml
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
> retrieving revision 1.25
> diff -c -r1.25 array.sgml
> *** doc/src/sgml/array.sgml    13 Mar 2003 01:30:26 -0000    1.25
> --- doc/src/sgml/array.sgml    16 Jun 2003 23:45:51 -0000
> ***************
> *** 60,73 ****
>   </programlisting>
>    </para>
>
>    <note>
>     <para>
> !    A limitation of the present array implementation is that individual
> !    elements of an array cannot be SQL null values.  The entire array can be set
> !    to null, but you can't have an array with some elements null and some
> !    not.  Fixing this is on the to-do list.
>     </para>
>    </note>
>    </sect2>
>
>    <sect2>
> --- 60,133 ----
>   </programlisting>
>    </para>
>
> +  <para>
> +   A limitation of the present array implementation is that individual
> +   elements of an array cannot be SQL null values.  The entire array can be set
> +   to null, but you can't have an array with some elements null and some
> +   not.
> +  </para>
> +  <para>
> +   This can lead to surprising results. For example, the result of the
> +   previous two inserts looks like this:
> + <programlisting>
> + SELECT * FROM sal_emp;
> +  name  |      pay_by_quarter       |      schedule
> + -------+---------------------------+--------------------
> +  Bill  | {10000,10000,10000,10000} | {{meeting},{""}}
> +  Carol | {20000,25000,25000,25000} | {{talk},{meeting}}
> + (2 rows)
> + </programlisting>
> +   Because the <literal>[2][2]</literal> element of
> +   <structfield>schedule</structfield> is missing in each of the
> +   <command>INSERT</command> statements, the <literal>[1][2]</literal>
> +   element is discarded.
> +  </para>
> +
> +  <note>
> +   <para>
> +    Fixing this is on the to-do list.
> +   </para>
> +  </note>
> +
> +  <para>
> +   The <command>ARRAY</command> expression syntax may also be used:
> + <programlisting>
> + INSERT INTO sal_emp
> +     VALUES ('Bill',
> +     ARRAY[10000, 10000, 10000, 10000],
> +     ARRAY[['meeting', 'lunch'], ['','']]);
> +
> + INSERT INTO sal_emp
> +     VALUES ('Carol',
> +     ARRAY[20000, 25000, 25000, 25000],
> +     ARRAY[['talk', 'consult'], ['meeting', '']]);
> + SELECT * FROM sal_emp;
> +  name  |      pay_by_quarter       |           schedule
> + -------+---------------------------+-------------------------------
> +  Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}}
> +  Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}}
> + (2 rows)
> + </programlisting>
> +   Note that with this syntax, multidimensional arrays must have matching
> +   extents for each dimension. This eliminates the missing-array-elements
> +   problem above. For example:
> + <programlisting>
> + INSERT INTO sal_emp
> +     VALUES ('Carol',
> +     ARRAY[20000, 25000, 25000, 25000],
> +     ARRAY[['talk', 'consult'], ['meeting']]);
> + ERROR:  Multidimensional arrays must have array expressions with matching dimensions
> + </programlisting>
> +   Also notice that string literals are single quoted instead of double quoted.
> +  </para>
> +
>    <note>
>     <para>
> !    The examples in the rest of this section are based on the
> !    <command>ARRAY</command> expression syntax <command>INSERT</command>s.
>     </para>
>    </note>
> +
>    </sect2>
>
>    <sect2>
> ***************
> *** 132,142 ****
>   </programlisting>
>
>     with the same result.  An array subscripting operation is always taken to
> !   represent an array slice if any of the subscripts are written in the
> !   form
>     <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
>     A lower bound of 1 is assumed for any subscript where only one value
> !   is specified.
>    </para>
>
>    <para>
> --- 192,221 ----
>   </programlisting>
>
>     with the same result.  An array subscripting operation is always taken to
> !   represent an array slice if any of the subscripts are written in the form
>     <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
>     A lower bound of 1 is assumed for any subscript where only one value
> !   is specified; another example follows:
> ! <programlisting>
> ! SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
> !          schedule
> ! ---------------------------
> !  {{meeting,lunch},{"",""}}
> ! (1 row)
> ! </programlisting>
> !  </para>
> !
> !  <para>
> !   Additionally, we can also access a single arbitrary array element of
> !   a one-dimensional array with the <function>array_subscript</function>
> !   function:
> ! <programlisting>
> ! SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
> !  array_subscript
> ! -----------------
> !            10000
> ! (1 row)
> ! </programlisting>
>    </para>
>
>    <para>
> ***************
> *** 147,153 ****
>       WHERE name = 'Carol';
>   </programlisting>
>
> !   or updated at a single element:
>
>   <programlisting>
>   UPDATE sal_emp SET pay_by_quarter[4] = 15000
> --- 226,248 ----
>       WHERE name = 'Carol';
>   </programlisting>
>
> !   or using the <command>ARRAY</command> expression syntax:
> !
> ! <programlisting>
> ! UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
> !     WHERE name = 'Carol';
> ! </programlisting>
> !
> !   <note>
> !    <para>
> !     Anywhere you can use the <quote>curly braces</quote> array syntax,
> !     you can also use the <command>ARRAY</command> expression syntax. The
> !     remainder of this section will illustrate only one or the other, but
> !     not both.
> !    </para>
> !   </note>
> !
> !   An array may also be updated at a single element:
>
>   <programlisting>
>   UPDATE sal_emp SET pay_by_quarter[4] = 15000
> ***************
> *** 160,165 ****
> --- 255,268 ----
>   UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
>       WHERE name = 'Carol';
>   </programlisting>
> +
> +   A one-dimensional array may also be updated with the
> +   <function>array_assign</function> function:
> +
> + <programlisting>
> + UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000)
> +     WHERE name = 'Bill';
> + </programListing>
>    </para>
>
>    <para>
> ***************
> *** 179,184 ****
> --- 282,369 ----
>    </para>
>
>    <para>
> +   An array can also be enlarged by using the concatenation operator,
> +   <command>||</command>.
> + <programlisting>
> + SELECT ARRAY[1,2] || ARRAY[3,4];
> +    ?column?
> + ---------------
> +  {{1,2},{3,4}}
> + (1 row)
> +
> + SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
> +       ?column?
> + ---------------------
> +  {{5,6},{1,2},{3,4}}
> + (1 row)
> + </programlisting>
> +
> +   The concatenation operator allows a single element to be pushed on to the
> +   beginning or end of a one-dimensional array. It also allows two
> +   <replaceable>N</>-dimensional arrays, or an <replaceable>N</>-dimensional
> +   and an <replaceable>N+1</>-dimensional array. In the former case, the two
> +   <replaceable>N</>-dimension arrays become outer elements of an
> +   <replaceable>N+1</>-dimensional array. In the latter, the
> +   <replaceable>N</>-dimensional array is added as either the first or last
> +   outer element of the <replaceable>N+1</>-dimensional array.
> +
> +   The array is extended in the direction of the push. Hence, by pushing
> +   onto the beginning of an array with a one-based subscript, a zero-based
> +   subscript array is created:
> +
> + <programlisting>
> + SELECT array_dims(t.f) FROM (SELECT 1 || ARRAY[2,3] AS f) AS t;
> +  array_dims
> + ------------
> +  [0:2]
> + (1 row)
> + </programlisting>
> +  </para>
> +
> +  <para>
> +   An array can also be enlarged by using the functions
> +   <function>array_prepend</function>, <function>array_append</function>,
> +   or <function>array_cat</function>. The first two only support one-dimensional
> +   arrays, but <function>array_cat</function> supports multidimensional arrays.
> +
> +   Note that the concatenation operator discussed above is preferred over
> +   direct use of these functions. In fact, the functions are primarily for use
> +   in implementing the concatenation operator. However, they may be directly
> +   useful in the creation of user-defined aggregates. Some examples:
> +
> + <programlisting>
> + SELECT array_prepend(1, ARRAY[2,3]);
> +  array_prepend
> + ---------------
> +  {1,2,3}
> + (1 row)
> +
> + SELECT array_append(ARRAY[1,2], 3);
> +  array_append
> + --------------
> +  {1,2,3}
> + (1 row)
> +
> + SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
> +    array_cat
> + ---------------
> +  {{1,2},{3,4}}
> + (1 row)
> +
> + SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
> +       array_cat
> + ---------------------
> +  {{1,2},{3,4},{5,6}}
> + (1 row)
> +
> + SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
> +       array_cat
> + ---------------------
> +  {{5,6},{1,2},{3,4}}
> + </programlisting>
> +  </para>
> +
> +  <para>
>     The syntax for <command>CREATE TABLE</command> allows fixed-length
>     arrays to be defined:
>
> ***************
> *** 194,199 ****
> --- 379,394 ----
>    </para>
>
>    <para>
> +   An alternative syntax for one-dimensional arrays may be used.
> +   <structfield>pay_by_quarter</structfield> could have been defined as:
> + <programlisting>
> +     pay_by_quarter  integer ARRAY[4],
> + </programlisting>
> +   This syntax may <emphasis>only</emphasis> be used with the integer
> +   constant to denote the array size.
> +  </para>
> +
> +  <para>
>     Actually, the current implementation does not enforce the declared
>     number of dimensions either.  Arrays of a particular element type are
>     all considered to be of the same type, regardless of size or number
> ***************
> *** 300,305 ****
> --- 495,566 ----
>      is not ignored, however: after skipping leading whitespace, everything
>      up to the next right brace or delimiter is taken as the item value.
>     </para>
> +
> +   <para>
> +    As illustrated earlier in this chapter, arrays may also be represented
> +    using the <command>ARRAY</command> expression syntax. This representation
> +    of an array value consists of items that are interpreted according to the
> +    I/O conversion rules for the array's element type, plus decoration that
> +    indicates the array structure. The decoration consists of the keyword
> +    <command>ARRAY</command> and square brackets (<literal>[</> and
> +    <literal>]</>) around the array values, plus delimiter characters between
> +    adjacent items. The delimiter character is always a comma (<literal>,</>).
> +    When representing multidimensional arrays, the keyword
> +    <command>ARRAY</command> is only necessary for the outer level. For example,
> +    <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as:
> + <programlisting>
> + SELECT ARRAY[['hello world', 'happy birthday']];
> +                array
> + ------------------------------------
> +  {{"hello world","happy birthday"}}
> + (1 row)
> + </programlisting>
> +   or it also could be written as:
> + <programlisting>
> + SELECT ARRAY[ARRAY['hello world', 'happy birthday']];
> +                array
> + ------------------------------------
> +  {{"hello world","happy birthday"}}
> + (1 row)
> + </programlisting>
> +   </para>
> +
> +   <para>
> +    A final method to represent an array, is through an
> +    <command>ARRAY</command> sub-select expression. For example:
> + <programlisting>
> + SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
> +                           ?column?
> + -------------------------------------------------------------
> +  {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
> + (1 row)
> + </programlisting>
> +   The sub-select may <emphasis>only</emphasis> return a single column. The
> +   resulting one-dimensional array will have an element for each row in the
> +   sub-select result, with an element type matching that of the sub-select's
> +   target column.
> +   </para>
> +
> +   <para>
> +    Arrays may be cast from one type to another in similar fashion to other
> +    data types:
> +
> + <programlisting>
> + SELECT ARRAY[1,2,3]::oid[];
> +   array
> + ---------
> +  {1,2,3}
> + (1 row)
> +
> + SELECT CAST(ARRAY[1,2,3] AS float8[]);
> +   array
> + ---------
> +  {1,2,3}
> + (1 row)
> + </programlisting>
> +
> +   </para>
> +
>    </sect2>
>
>    <sect2>
> ***************
> *** 316,321 ****
> --- 577,590 ----
>      Alternatively, you can use backslash-escaping to protect all data characters
>      that would otherwise be taken as array syntax or ignorable white space.
>     </para>
> +
> +  <note>
> +   <para>
> +    The discussion in the preceding paragraph with respect to double quoting does
> +    not pertain to the <command>ARRAY</command> expression syntax. In that case,
> +    each element is quoted exactly as any other literal value of the element type.
> +   </para>
> +  </note>
>
>     <para>
>      The array output routine will put double quotes around element values
> Index: doc/src/sgml/func.sgml
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v
> retrieving revision 1.154
> diff -c -r1.154 func.sgml
> *** doc/src/sgml/func.sgml    5 May 2003 15:08:49 -0000    1.154
> --- doc/src/sgml/func.sgml    16 Jun 2003 23:45:51 -0000
> ***************
> *** 6962,6967 ****
> --- 6962,7164 ----
>
>     </sect1>
>
> +  <sect1 id="functions-array">
> +   <title>Array Functions</title>
> +
> +   <para>
> +    <xref linkend="array-operators-table"> shows the operators
> +    available for the <type>array</type> types.
> +   </para>
> +
> +     <table id="array-operators-table">
> +      <title><type>array</type> Operators</title>
> +      <tgroup cols="4">
> +       <thead>
> +        <row>
> +     <entry>Operator</entry>
> +     <entry>Description</entry>
> +     <entry>Example</entry>
> +     <entry>Result</entry>
> +        </row>
> +       </thead>
> +       <tbody>
> +        <row>
> +     <entry> <literal>=</literal> </entry>
> +     <entry>equals</entry>
> +     <entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry>
> +     <entry><literal>t</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>array-to-array concatenation</entry>
> +     <entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry>
> +     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>array-to-array concatenation</entry>
> +     <entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry>
> +     <entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>element-to-array concatenation</entry>
> +     <entry><literal>3 || ARRAY[4,5,6]</literal></entry>
> +     <entry><literal>{3,4,5,6}</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>array-to-element concatenation</entry>
> +     <entry><literal>ARRAY[4,5,6] || 7</literal></entry>
> +     <entry><literal>{4,5,6,7}</literal></entry>
> +        </row>
> +       </tbody>
> +      </tgroup>
> +     </table>
> +
> +   <para>
> +    <xref linkend="array-functions-table"> shows the functions
> +    available for use with array types. See <xref linkend="arrays">
> +    for more discussion and examples for the use of these functions.
> +   </para>
> +
> +     <table id="array-functions-table">
> +      <title><type>array</type> Functions</title>
> +      <tgroup cols="5">
> +       <thead>
> +        <row>
> +     <entry>Function</entry>
> +     <entry>Return Type</entry>
> +     <entry>Description</entry>
> +     <entry>Example</entry>
> +     <entry>Result</entry>
> +        </row>
> +       </thead>
> +       <tbody>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_append</function>
> +       (<type>anyarray</type>, <type>anyelement</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>anyarray</type></entry>
> +     <entry>
> +      append an element to the end of an array, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_append(ARRAY[1,2], 3)</literal></entry>
> +     <entry><literal>{1,2,3}</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_cat</function>
> +       (<type>anyarray</type>, <type>anyarray</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>anyarray</type></entry>
> +     <entry>
> +      concatenate two arrays, returning <literal>NULL</literal>
> +      for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry>
> +     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_dims</function>
> +       (<type>anyarray</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>text</type></entry>
> +     <entry>
> +      returns a text representation of array dimension lower and upper bounds,
> +      generating an ERROR for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry>
> +     <entry><literal>[1:2][1:3]</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_lower</function>
> +       (<type>anyarray</type>, <type>integer</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>integer</type></entry>
> +     <entry>
> +      returns lower bound of the requested array dimension, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry>
> +     <entry><literal>0</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_prepend</function>
> +       (<type>anyelement</type>, <type>anyarray</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>anyarray</type></entry>
> +     <entry>
> +      append an element to the beginning of an array, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry>
> +     <entry><literal>{1,2,3}</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_to_string</function>
> +       (<type>anyarray</type>, <type>text</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>text</type></entry>
> +     <entry>
> +      concatenates array elements using provided delimiter, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_to_string(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry>
> +     <entry><literal>1.10~^~2.20~^~3.30</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_upper</function>
> +       (<type>anyarray</type>, <type>integer</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>integer</type></entry>
> +     <entry>
> +      returns upper bound of the requested array dimension, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry>
> +     <entry><literal>4</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>string_to_array</function>
> +       (<type>text</type>, <type>text</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>text[]</type></entry>
> +     <entry>
> +      splits string into array elements using provided delimiter, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>string_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry>
> +     <entry><literal>{1.1,2.2,3.3}</literal></entry>
> +        </row>
> +       </tbody>
> +      </tgroup>
> +     </table>
> +   </sect1>
>
>    <sect1 id="functions-aggregate">
>     <title>Aggregate Functions</title>
> Index: src/backend/catalog/pg_aggregate.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_aggregate.c,v
> retrieving revision 1.56
> diff -c -r1.56 pg_aggregate.c
> *** src/backend/catalog/pg_aggregate.c    18 Sep 2002 21:35:20 -0000    1.56
> --- src/backend/catalog/pg_aggregate.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 50,59 ****
>       Oid            finalfn = InvalidOid;    /* can be omitted */
>       Oid            finaltype;
>       Oid            fnArgs[FUNC_MAX_ARGS];
> !     int            nargs;
>       Oid            procOid;
>       TupleDesc    tupDesc;
>       int            i;
>       ObjectAddress myself,
>                   referenced;
>
> --- 50,65 ----
>       Oid            finalfn = InvalidOid;    /* can be omitted */
>       Oid            finaltype;
>       Oid            fnArgs[FUNC_MAX_ARGS];
> !     int            nargs_transfn;
> !     int            nargs_finalfn;
>       Oid            procOid;
>       TupleDesc    tupDesc;
>       int            i;
> +     Oid            rettype;
> +     Oid           *true_oid_array_transfn;
> +     Oid           *true_oid_array_finalfn;
> +     bool        retset;
> +     FuncDetailCode fdresult;
>       ObjectAddress myself,
>                   referenced;
>
> ***************
> *** 68,91 ****
>       MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>       fnArgs[0] = aggTransType;
>       if (aggBaseType == ANYOID)
> !         nargs = 1;
>       else
>       {
>           fnArgs[1] = aggBaseType;
> !         nargs = 2;
>       }
> !     transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
>       if (!OidIsValid(transfn))
> !         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
>       tup = SearchSysCache(PROCOID,
>                            ObjectIdGetDatum(transfn),
>                            0, 0, 0);
>       if (!HeapTupleIsValid(tup))
> !         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
>       proc = (Form_pg_proc) GETSTRUCT(tup);
> -     if (proc->prorettype != aggTransType)
> -         elog(ERROR, "return type of transition function %s is not %s",
> -          NameListToString(aggtransfnName), format_type_be(aggTransType));
>
>       /*
>        * If the transfn is strict and the initval is NULL, make sure input
> --- 74,122 ----
>       MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>       fnArgs[0] = aggTransType;
>       if (aggBaseType == ANYOID)
> !         nargs_transfn = 1;
>       else
>       {
>           fnArgs[1] = aggBaseType;
> !         nargs_transfn = 2;
>       }
> !
> !     /*
> !      * func_get_detail looks up the function in the catalogs, does
> !      * disambiguation for polymorphic functions, handles inheritance, and
> !      * returns the funcid and type and set or singleton status of the
> !      * function's return value.  it also returns the true argument types
> !      * to the function.
> !      */
> !     fdresult = func_get_detail(aggtransfnName, NIL, nargs_transfn, fnArgs,
> !                                &transfn, &rettype, &retset,
> !                                &true_oid_array_transfn);
> !
> !     /* only valid case is a normal function */
> !     if (fdresult != FUNCDETAIL_NORMAL)
> !         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
> !
>       if (!OidIsValid(transfn))
> !         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
> !
> !     /*
> !      * enforce consistency with ANYARRAY and ANYELEMENT argument
> !      * and return types, possibly modifying return type along the way
> !      */
> !     rettype = enforce_generic_type_consistency(fnArgs, true_oid_array_transfn,
> !                                                        nargs_transfn, rettype);
> !
> !     if (rettype != aggTransType)
> !         elog(ERROR, "return type of transition function %s is not %s",
> !          NameListToString(aggtransfnName), format_type_be(aggTransType));
> !
>       tup = SearchSysCache(PROCOID,
>                            ObjectIdGetDatum(transfn),
>                            0, 0, 0);
>       if (!HeapTupleIsValid(tup))
> !         func_error("AggregateCreate", aggtransfnName,
> !                         nargs_transfn, fnArgs, NULL);
>       proc = (Form_pg_proc) GETSTRUCT(tup);
>
>       /*
>        * If the transfn is strict and the initval is NULL, make sure input
> ***************
> *** 105,121 ****
>       {
>           MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>           fnArgs[0] = aggTransType;
> !         finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
> !         if (!OidIsValid(finalfn))
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !         tup = SearchSysCache(PROCOID,
> !                              ObjectIdGetDatum(finalfn),
> !                              0, 0, 0);
> !         if (!HeapTupleIsValid(tup))
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !         proc = (Form_pg_proc) GETSTRUCT(tup);
> !         finaltype = proc->prorettype;
> !         ReleaseSysCache(tup);
>       }
>       else
>       {
> --- 136,161 ----
>       {
>           MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>           fnArgs[0] = aggTransType;
> !         nargs_finalfn = 1;
> !
> !         fdresult = func_get_detail(aggfinalfnName, NIL, 1, fnArgs,
> !                                    &finalfn, &rettype, &retset,
> !                                    &true_oid_array_finalfn);
> !
> !         /* only valid case is a normal function */
> !         if (fdresult != FUNCDETAIL_NORMAL)
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !
> !         if (!OidIsValid(finalfn))
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !
> !         /*
> !          * enforce consistency with ANYARRAY and ANYELEMENT argument
> !          * and return types, possibly modifying return type along the way
> !          */
> !         finaltype = enforce_generic_type_consistency(fnArgs,
> !                                                      true_oid_array_finalfn,
> !                                                      nargs_finalfn, rettype);
>       }
>       else
>       {
> ***************
> *** 125,130 ****
> --- 165,191 ----
>           finaltype = aggTransType;
>       }
>       Assert(OidIsValid(finaltype));
> +
> +     /*
> +      * special disallowed cases:
> +      * 1)    if finaltype is polymorphic, basetype cannot be ANY
> +      * 2)    if finaltype is polymorphic, both args to transfn must be
> +      *        polymorphic
> +      */
> +     if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
> +     {
> +         if (aggBaseType == ANYOID)
> +             elog(ERROR, "aggregate with base type ANY must have a " \
> +                         "non-polymorphic return type");
> +
> +         if (nargs_transfn > 1 && (
> +             (true_oid_array_transfn[0] != ANYARRAYOID &&
> +              true_oid_array_transfn[0] != ANYELEMENTOID) ||
> +             (true_oid_array_transfn[1] != ANYARRAYOID &&
> +              true_oid_array_transfn[1] != ANYELEMENTOID)))
> +             elog(ERROR, "aggregate with polymorphic return type requires " \
> +                         "state function with both arguments polymorphic");
> +     }
>
>       /*
>        * Everything looks okay.  Try to create the pg_proc entry for the
> Index: src/backend/commands/aggregatecmds.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/aggregatecmds.c,v
> retrieving revision 1.5
> diff -c -r1.5 aggregatecmds.c
> *** src/backend/commands/aggregatecmds.c    4 Sep 2002 20:31:14 -0000    1.5
> --- src/backend/commands/aggregatecmds.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 119,125 ****
>           baseTypeId = typenameTypeId(baseType);
>
>       transTypeId = typenameTypeId(transType);
> !     if (get_typtype(transTypeId) == 'p')
>           elog(ERROR, "Aggregate transition datatype cannot be %s",
>                format_type_be(transTypeId));
>
> --- 119,127 ----
>           baseTypeId = typenameTypeId(baseType);
>
>       transTypeId = typenameTypeId(transType);
> !     if (get_typtype(transTypeId) == 'p' &&
> !         transTypeId != ANYARRAYOID &&
> !         transTypeId != ANYELEMENTOID)
>           elog(ERROR, "Aggregate transition datatype cannot be %s",
>                format_type_be(transTypeId));
>
> Index: src/backend/executor/execQual.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/execQual.c,v
> retrieving revision 1.130
> diff -c -r1.130 execQual.c
> *** src/backend/executor/execQual.c    28 May 2003 22:32:49 -0000    1.130
> --- src/backend/executor/execQual.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 1528,1544 ****
>               {
>                   /* Check other sub-arrays are compatible */
>                   if (elem_ndims != ARR_NDIM(array))
> !                     elog(ERROR, "Multiple dimension arrays must have array "
>                            "expressions with matching number of dimensions");
>
>                   if (memcmp(elem_dims, ARR_DIMS(array),
>                              elem_ndims * sizeof(int)) != 0)
> !                     elog(ERROR, "Multiple dimension arrays must have array "
>                            "expressions with matching dimensions");
>
>                   if (memcmp(elem_lbs, ARR_LBOUND(array),
>                              elem_ndims * sizeof(int)) != 0)
> !                     elog(ERROR, "Multiple dimension arrays must have array "
>                            "expressions with matching dimensions");
>               }
>
> --- 1528,1544 ----
>               {
>                   /* Check other sub-arrays are compatible */
>                   if (elem_ndims != ARR_NDIM(array))
> !                     elog(ERROR, "Multidimensional arrays must have array "
>                            "expressions with matching number of dimensions");
>
>                   if (memcmp(elem_dims, ARR_DIMS(array),
>                              elem_ndims * sizeof(int)) != 0)
> !                     elog(ERROR, "Multidimensional arrays must have array "
>                            "expressions with matching dimensions");
>
>                   if (memcmp(elem_lbs, ARR_LBOUND(array),
>                              elem_ndims * sizeof(int)) != 0)
> !                     elog(ERROR, "Multidimensional arrays must have array "
>                            "expressions with matching dimensions");
>               }
>
> Index: src/backend/executor/nodeAgg.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeAgg.c,v
> retrieving revision 1.106
> diff -c -r1.106 nodeAgg.c
> *** src/backend/executor/nodeAgg.c    6 Jun 2003 15:04:01 -0000    1.106
> --- src/backend/executor/nodeAgg.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 58,63 ****
> --- 58,64 ----
>   #include "executor/executor.h"
>   #include "executor/nodeAgg.h"
>   #include "miscadmin.h"
> + #include "nodes/makefuncs.h"
>   #include "optimizer/clauses.h"
>   #include "parser/parse_coerce.h"
>   #include "parser/parse_expr.h"
> ***************
> *** 212,218 ****
>   static void agg_fill_hash_table(AggState *aggstate);
>   static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
>   static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
> !
>
>   /*
>    * Initialize all aggregates for a new group of input values.
> --- 213,219 ----
>   static void agg_fill_hash_table(AggState *aggstate);
>   static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
>   static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
> ! static Oid resolve_type(Oid type_to_resolve, Oid context_type);
>
>   /*
>    * Initialize all aggregates for a new group of input values.
> ***************
> *** 351,364 ****
>       fcinfo.context = NULL;
>       fcinfo.resultinfo = NULL;
>       fcinfo.isnull = false;
> -
>       fcinfo.flinfo = &peraggstate->transfn;
>       fcinfo.nargs = 2;
>       fcinfo.arg[0] = pergroupstate->transValue;
>       fcinfo.argnull[0] = pergroupstate->transValueIsNull;
>       fcinfo.arg[1] = newVal;
>       fcinfo.argnull[1] = isNull;
> -
>       newVal = FunctionCallInvoke(&fcinfo);
>
>       /*
> --- 352,363 ----
> ***************
> *** 1176,1182 ****
> --- 1175,1195 ----
>           AclResult    aclresult;
>           Oid            transfn_oid,
>                       finalfn_oid;
> +         FuncExpr   *transfnexpr,
> +                    *finalfnexpr;
>           Datum        textInitVal;
> +         List       *fargs;
> +         Oid            agg_rt_type;
> +         Oid           *transfn_arg_types;
> +         List       *transfn_args = NIL;
> +         int            transfn_nargs;
> +         Oid            transfn_ret_type;
> +         Oid           *finalfn_arg_types = NULL;
> +         List       *finalfn_args = NIL;
> +         Oid            finalfn_ret_type = InvalidOid;
> +         int            finalfn_nargs = 0;
> +         Node       *arg0;
> +         Node       *arg1;
>           int            i;
>
>           /* Planner should have assigned aggregate to correct level */
> ***************
> *** 1227,1232 ****
> --- 1240,1405 ----
>                           &peraggstate->transtypeLen,
>                           &peraggstate->transtypeByVal);
>
> +         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
> +         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
> +
> +         /* get the runtime aggregate argument type */
> +         fargs = aggref->args;
> +         agg_rt_type = exprType((Node *) nth(0, fargs));
> +
> +         /* get the transition function argument and return types */
> +         transfn_ret_type = get_func_rettype(transfn_oid);
> +         transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs);
> +
> +         /* resolve any polymorphic types */
> +         if (transfn_nargs == 2)
> +         /* base type was not ANY */
> +         {
> +             if (transfn_arg_types[1] == ANYARRAYOID ||
> +                 transfn_arg_types[1] == ANYELEMENTOID)
> +                 transfn_arg_types[1] = agg_rt_type;
> +
> +             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
> +                                                         agg_rt_type);
> +
> +             /*
> +              * Build arg list to use on the transfn FuncExpr node. We really
> +              * only care that the node type is correct so that the transfn
> +              * can discover the actual argument types at runtime using
> +              * get_fn_expr_argtype()
> +              */
> +             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
> +                                                             -1, COERCE_DONTCARE);
> +             arg1 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[1],
> +                                                             -1, COERCE_DONTCARE);
> +             transfn_args = makeList2(arg0, arg1);
> +
> +             /*
> +              * the state transition function always returns the same type
> +              * as its first argument
> +              */
> +             if (transfn_ret_type == ANYARRAYOID ||
> +                 transfn_ret_type == ANYELEMENTOID)
> +                 transfn_ret_type = transfn_arg_types[0];
> +         }
> +         else if (transfn_nargs == 1)
> +         /*
> +          * base type was ANY, therefore the aggregate return type should
> +          * be non-polymorphic
> +          */
> +         {
> +             Oid    finaltype = get_func_rettype(aggref->aggfnoid);
> +
> +             /*
> +              * this should have been prevented in AggregateCreate,
> +              * but check anyway
> +              */
> +             if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
> +                 elog(ERROR, "aggregate with base type ANY must have a " \
> +                             "non-polymorphic return type");
> +
> +             /* see if we have a final function */
> +             if (OidIsValid(finalfn_oid))
> +             {
> +                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
> +                 if (finalfn_nargs != 1)
> +                     elog(ERROR, "final function takes unexpected number " \
> +                                 "of arguments: %d", finalfn_nargs);
> +
> +                 /*
> +                  * final function argument is always the same as the state
> +                  * function return type
> +                  */
> +                 if (finalfn_arg_types[0] != ANYARRAYOID &&
> +                     finalfn_arg_types[0] != ANYELEMENTOID)
> +                 {
> +                     /* if it is not ambiguous, use it */
> +                     transfn_ret_type = finalfn_arg_types[0];
> +                 }
> +                 else
> +                 {
> +                     /* if it is ambiguous, try to derive it */
> +                     finalfn_ret_type = finaltype;
> +                     finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0],
> +                                                             finalfn_ret_type);
> +                     transfn_ret_type = finalfn_arg_types[0];
> +                 }
> +             }
> +             else
> +                 transfn_ret_type = finaltype;
> +
> +             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
> +                                                         transfn_ret_type);
> +
> +             /*
> +              * Build arg list to use on the transfn FuncExpr node. We really
> +              * only care that the node type is correct so that the transfn
> +              * can discover the actual argument types at runtime using
> +              * get_fn_expr_argtype()
> +              */
> +             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
> +                                                             -1, COERCE_DONTCARE);
> +             transfn_args = makeList1(arg0);
> +         }
> +         else
> +             elog(ERROR, "state transition function takes unexpected number " \
> +                         "of arguments: %d", transfn_nargs);
> +
> +         if (OidIsValid(finalfn_oid))
> +         {
> +             /* get the final function argument and return types */
> +             if (finalfn_ret_type == InvalidOid)
> +                 finalfn_ret_type = get_func_rettype(finalfn_oid);
> +
> +             if (!finalfn_arg_types)
> +             {
> +                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
> +                 if (finalfn_nargs != 1)
> +                     elog(ERROR, "final function takes unexpected number " \
> +                                 "of arguments: %d", finalfn_nargs);
> +             }
> +
> +             /*
> +              * final function argument is always the same as the state
> +              * function return type, which by now should have been resolved
> +              */
> +             if (finalfn_arg_types[0] == ANYARRAYOID ||
> +                 finalfn_arg_types[0] == ANYELEMENTOID)
> +                 finalfn_arg_types[0] = transfn_ret_type;
> +
> +             /*
> +              * Build arg list to use on the finalfn FuncExpr node. We really
> +              * only care that the node type is correct so that the finalfn
> +              * can discover the actual argument type at runtime using
> +              * get_fn_expr_argtype()
> +              */
> +             arg0 = (Node *) makeRelabelType((Expr *) NULL, finalfn_arg_types[0],
> +                                                             -1, COERCE_DONTCARE);
> +             finalfn_args = makeList1(arg0);
> +
> +             finalfn_ret_type = resolve_type(finalfn_ret_type,
> +                                                 finalfn_arg_types[0]);
> +         }
> +
> +         fmgr_info(transfn_oid, &peraggstate->transfn);
> +         transfnexpr = (FuncExpr *) make_funcclause(transfn_oid,
> +                           transfn_ret_type,
> +                           false,            /* cannot be a set */
> +                           COERCE_DONTCARE,    /* to match any user expr */
> +                           transfn_args);
> +         peraggstate->transfn.fn_expr = (Node *) transfnexpr;
> +
> +         if (OidIsValid(finalfn_oid))
> +         {
> +             fmgr_info(finalfn_oid, &peraggstate->finalfn);
> +             finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid,
> +                           finalfn_ret_type,
> +                           false,            /* cannot be a set */
> +                           COERCE_DONTCARE,    /* to match any user expr */
> +                           finalfn_args);
> +             peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
> +         }
> +
>           /*
>            * initval is potentially null, so don't try to access it as a
>            * struct field. Must do it the hard way with SysCacheGetAttr.
> ***************
> *** 1239,1252 ****
>               peraggstate->initValue = (Datum) 0;
>           else
>               peraggstate->initValue = GetAggInitVal(textInitVal,
> !                                                    aggform->aggtranstype);
> !
> !         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
> !         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
> !
> !         fmgr_info(transfn_oid, &peraggstate->transfn);
> !         if (OidIsValid(finalfn_oid))
> !             fmgr_info(finalfn_oid, &peraggstate->finalfn);
>
>           /*
>            * If the transfn is strict and the initval is NULL, make sure
> --- 1412,1418 ----
>               peraggstate->initValue = (Datum) 0;
>           else
>               peraggstate->initValue = GetAggInitVal(textInitVal,
> !                                                    transfn_arg_types[0]);
>
>           /*
>            * If the transfn is strict and the initval is NULL, make sure
> ***************
> *** 1457,1460 ****
> --- 1623,1659 ----
>       elog(ERROR, "Aggregate function %u called as normal function",
>            fcinfo->flinfo->fn_oid);
>       return (Datum) 0;            /* keep compiler quiet */
> + }
> +
> + static Oid
> + resolve_type(Oid type_to_resolve, Oid context_type)
> + {
> +     Oid        resolved_type;
> +
> +     if (context_type == ANYARRAYOID || context_type == ANYELEMENTOID)
> +         resolved_type = type_to_resolve;
> +     else if (type_to_resolve == ANYARRAYOID)
> +     /* any array */
> +     {
> +         Oid        context_type_arraytype = get_array_type(context_type);
> +
> +         if (context_type_arraytype != InvalidOid)
> +             resolved_type = context_type_arraytype;
> +         else
> +             resolved_type = context_type;
> +     }
> +     else if (type_to_resolve == ANYELEMENTOID)
> +     /* any element */
> +     {
> +         Oid        context_type_elemtype = get_element_type(context_type);
> +
> +         if (context_type_elemtype != InvalidOid)
> +             resolved_type = context_type_elemtype;
> +         else
> +             resolved_type = context_type;
> +     }
> +     else
> +         resolved_type = type_to_resolve;
> +
> +     return resolved_type;
>   }
> Index: src/backend/executor/nodeSubplan.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v
> retrieving revision 1.46
> diff -c -r1.46 nodeSubplan.c
> *** src/backend/executor/nodeSubplan.c    6 Jun 2003 15:04:01 -0000    1.46
> --- src/backend/executor/nodeSubplan.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 28,50 ****
>   #include "utils/datum.h"
>   #include "utils/lsyscache.h"
>
> -
> - typedef struct ArrayBuildState
> - {
> -     MemoryContext mcontext;        /* where all the temp stuff is kept */
> -     Datum       *dvalues;        /* array of accumulated Datums */
> -     /*
> -      * The allocated size of dvalues[] is always a multiple of
> -      * ARRAY_ELEMS_CHUNKSIZE
> -      */
> - #define ARRAY_ELEMS_CHUNKSIZE    64
> -     int            nelems;            /* number of valid Datums in dvalues[] */
> -     Oid            element_type;    /* data type of the Datums */
> -     int16        typlen;            /* needed info about datatype */
> -     bool        typbyval;
> -     char        typalign;
> - } ArrayBuildState;
> -
>   static Datum ExecHashSubPlan(SubPlanState *node,
>                                ExprContext *econtext,
>                                bool *isNull);
> --- 28,33 ----
> ***************
> *** 54,66 ****
>   static void buildSubPlanHash(SubPlanState *node);
>   static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
>   static bool tupleAllNulls(HeapTuple tuple);
> - static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
> -                                          Datum dvalue, bool disnull,
> -                                          Oid element_type,
> -                                          MemoryContext rcontext);
> - static Datum makeArrayResult(ArrayBuildState *astate,
> -                              MemoryContext rcontext);
> -
>
>   /* ----------------------------------------------------------------
>    *        ExecSubPlan
> --- 37,42 ----
> ***************
> *** 224,229 ****
> --- 200,206 ----
>       PlanState  *planstate = node->planstate;
>       SubLinkType subLinkType = subplan->subLinkType;
>       bool        useOr = subplan->useOr;
> +     bool        isExpr = subplan->isExpr;
>       MemoryContext oldcontext;
>       TupleTableSlot *slot;
>       Datum        result;
> ***************
> *** 294,299 ****
> --- 271,281 ----
>           bool        rownull = false;
>           int            col = 1;
>           List       *plst;
> +         int            numelems;
> +         int            elemnum;
> +         Datum        dvalue;
> +         Datum       *dvalues = NULL;
> +         bool        disnull;
>
>           if (subLinkType == EXISTS_SUBLINK)
>           {
> ***************
> *** 331,339 ****
>
>           if (subLinkType == ARRAY_SUBLINK)
>           {
> -             Datum    dvalue;
> -             bool    disnull;
> -
>               found = true;
>               /* stash away current value */
>               dvalue = heap_getattr(tup, 1, tdesc, &disnull);
> --- 313,318 ----
> ***************
> *** 351,448 ****
>           found = true;
>
>           /*
> !          * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
> !          * operators for columns of tuple.
>            */
> !         plst = subplan->paramIds;
> !         foreach(lst, node->exprs)
>           {
> !             ExprState  *exprstate = (ExprState *) lfirst(lst);
> !             int            paramid = lfirsti(plst);
> !             ParamExecData *prmdata;
> !             Datum        expresult;
> !             bool        expnull;
> !
> !             /*
> !              * Load up the Param representing this column of the sub-select.
> !              */
> !             prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
> !             Assert(prmdata->execPlan == NULL);
> !             prmdata->value = heap_getattr(tup, col, tdesc,
> !                                           &(prmdata->isnull));
>
> !             /*
> !              * Now we can eval the combining operator for this column.
> !              */
> !             expresult = ExecEvalExprSwitchContext(exprstate, econtext,
> !                                                   &expnull, NULL);
> !
> !             /*
> !              * Combine the result into the row result as appropriate.
> !              */
> !             if (col == 1)
>               {
> !                 rowresult = expresult;
> !                 rownull = expnull;
>               }
> !             else if (useOr)
>               {
> !                 /* combine within row per OR semantics */
> !                 if (expnull)
> !                     rownull = true;
> !                 else if (DatumGetBool(expresult))
>                   {
> !                     rowresult = BoolGetDatum(true);
> !                     rownull = false;
> !                     break;        /* needn't look at any more columns */
>                   }
>               }
>               else
>               {
> !                 /* combine within row per AND semantics */
> !                 if (expnull)
> !                     rownull = true;
> !                 else if (!DatumGetBool(expresult))
> !                 {
> !                     rowresult = BoolGetDatum(false);
> !                     rownull = false;
> !                     break;        /* needn't look at any more columns */
> !                 }
>               }
>
> -             plst = lnext(plst);
> -             col++;
>           }
>
> !         if (subLinkType == ANY_SUBLINK)
>           {
> !             /* combine across rows per OR semantics */
> !             if (rownull)
> !                 *isNull = true;
> !             else if (DatumGetBool(rowresult))
>               {
> !                 result = BoolGetDatum(true);
> !                 *isNull = false;
> !                 break;            /* needn't look at any more rows */
>               }
> !         }
> !         else if (subLinkType == ALL_SUBLINK)
> !         {
> !             /* combine across rows per AND semantics */
> !             if (rownull)
> !                 *isNull = true;
> !             else if (!DatumGetBool(rowresult))
>               {
> !                 result = BoolGetDatum(false);
> !                 *isNull = false;
> !                 break;            /* needn't look at any more rows */
>               }
> -         }
> -         else
> -         {
> -             /* must be MULTIEXPR_SUBLINK */
> -             result = rowresult;
> -             *isNull = rownull;
>           }
>       }
>
> --- 330,492 ----
>           found = true;
>
>           /*
> !          * When isExpr is true, we have either a scalar expression or an
> !          * array. In the former case, this is no different than the !isExpr
> !          * case. In the latter case, iterate over the elements as if they
> !          * were from multiple input tuples.
>            */
> !         if (!isExpr)
> !             numelems = 1;
> !         else
>           {
> !             Oid        expr_typeid = tdesc->attrs[0]->atttypid;
>
> !             if (expr_typeid != subplan->exprtype)
>               {
> !                 subplan->exprtype = expr_typeid;
> !                 subplan->elemtype = get_element_type(expr_typeid);
> !
> !                 if (subplan->elemtype != InvalidOid)
> !                     get_typlenbyvalalign(subplan->elemtype,
> !                                          &subplan->elmlen,
> !                                          &subplan->elmbyval,
> !                                          &subplan->elmalign);
>               }
> !
> !             /* get current value */
> !             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
> !
> !             /* XXX this will need work if/when arrays support NULL elements */
> !             if (!disnull)
>               {
> !                 if (subplan->elemtype != InvalidOid)
> !                 {
> !                     ArrayType   *v = DatumGetArrayTypeP(dvalue);
> !
> !                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
> !                                       subplan->elmbyval, subplan->elmalign,
> !                                         &dvalues, &numelems);
> !                 }
> !                 else
>                   {
> !                     numelems = 1;
> !                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                     dvalues[0] = dvalue;
>                   }
>               }
>               else
>               {
> !                 numelems = 1;
> !                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                 dvalues[0] = (Datum) 0;
>               }
>
>           }
>
> !         for (elemnum = 0; elemnum < numelems; elemnum++)
>           {
> !             /*
> !              * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
> !              * operators for columns of tuple.
> !              */
> !             col = 1;
> !             plst = subplan->paramIds;
> !             foreach(lst, node->exprs)
>               {
> !                 ExprState  *exprstate = (ExprState *) lfirst(lst);
> !                 int            paramid = lfirsti(plst);
> !                 ParamExecData *prmdata;
> !                 Datum        expresult;
> !                 bool        expnull;
> !
> !                 /*
> !                  * Load up the Param representing this column of the sub-select.
> !                  */
> !                 prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
> !                 Assert(prmdata->execPlan == NULL);
> !
> !                 if (!isExpr)
> !                     prmdata->value = heap_getattr(tup, col, tdesc,
> !                                                   &(prmdata->isnull));
> !                 else
> !                 {
> !                     prmdata->value = dvalues[elemnum];
> !                     prmdata->isnull = disnull;
> !                 }
> !
> !                 /*
> !                  * Now we can eval the combining operator for this column.
> !                  */
> !                 expresult = ExecEvalExprSwitchContext(exprstate, econtext,
> !                                                       &expnull, NULL);
> !
> !                 /*
> !                  * Combine the result into the row result as appropriate.
> !                  */
> !                 if (col == 1)
> !                 {
> !                     rowresult = expresult;
> !                     rownull = expnull;
> !                 }
> !                 else if (useOr)
> !                 {
> !                     /* combine within row per OR semantics */
> !                     if (expnull)
> !                         rownull = true;
> !                     else if (DatumGetBool(expresult))
> !                     {
> !                         rowresult = BoolGetDatum(true);
> !                         rownull = false;
> !                         break;        /* needn't look at any more columns */
> !                     }
> !                 }
> !                 else
> !                 {
> !                     /* combine within row per AND semantics */
> !                     if (expnull)
> !                         rownull = true;
> !                     else if (!DatumGetBool(expresult))
> !                     {
> !                         rowresult = BoolGetDatum(false);
> !                         rownull = false;
> !                         break;        /* needn't look at any more columns */
> !                     }
> !                 }
> !
> !                 plst = lnext(plst);
> !                 col++;
>               }
> !
> !             if (subLinkType == ANY_SUBLINK)
>               {
> !                 /* combine across rows per OR semantics */
> !                 if (rownull)
> !                     *isNull = true;
> !                 else if (DatumGetBool(rowresult))
> !                 {
> !                     result = BoolGetDatum(true);
> !                     *isNull = false;
> !                     break;            /* needn't look at any more rows */
> !                 }
> !             }
> !             else if (subLinkType == ALL_SUBLINK)
> !             {
> !                 /* combine across rows per AND semantics */
> !                 if (rownull)
> !                     *isNull = true;
> !                 else if (!DatumGetBool(rowresult))
> !                 {
> !                     result = BoolGetDatum(false);
> !                     *isNull = false;
> !                     break;            /* needn't look at any more rows */
> !                 }
> !             }
> !             else
> !             {
> !                 /* must be MULTIEXPR_SUBLINK */
> !                 result = rowresult;
> !                 *isNull = rownull;
>               }
>           }
>       }
>
> ***************
> *** 480,485 ****
> --- 524,530 ----
>   buildSubPlanHash(SubPlanState *node)
>   {
>       SubPlan       *subplan = (SubPlan *) node->xprstate.expr;
> +     bool        isExpr = subplan->isExpr;
>       PlanState  *planstate = node->planstate;
>       int            ncols = length(node->exprs);
>       ExprContext *innerecontext = node->innerecontext;
> ***************
> *** 487,492 ****
> --- 532,538 ----
>       MemoryContext oldcontext;
>       int            nbuckets;
>       TupleTableSlot *slot;
> +     TupleTableSlot *arrslot = NULL;
>
>       Assert(subplan->subLinkType == ANY_SUBLINK);
>       Assert(!subplan->useOr);
> ***************
> *** 564,606 ****
>       {
>           HeapTuple    tup = slot->val;
>           TupleDesc    tdesc = slot->ttc_tupleDescriptor;
> !         int            col = 1;
>           List       *plst;
>           bool        isnew;
>
>           /*
> !          * Load up the Params representing the raw sub-select outputs,
> !          * then form the projection tuple to store in the hashtable.
>            */
> !         foreach(plst, subplan->paramIds)
>           {
> !             int            paramid = lfirsti(plst);
> !             ParamExecData *prmdata;
>
> !             prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
> !             Assert(prmdata->execPlan == NULL);
> !             prmdata->value = heap_getattr(tup, col, tdesc,
> !                                           &(prmdata->isnull));
> !             col++;
> !         }
> !         slot = ExecProject(node->projRight, NULL);
> !         tup = slot->val;
>
> -         /*
> -          * If result contains any nulls, store separately or not at all.
> -          * (Since we know the projection tuple has no junk columns, we
> -          * can just look at the overall hasnull info bit, instead of
> -          * groveling through the columns.)
> -          */
> -         if (HeapTupleNoNulls(tup))
> -         {
> -             (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
> -             node->havehashrows = true;
>           }
> !         else if (node->hashnulls)
>           {
> !             (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
> !             node->havenullrows = true;
>           }
>
>           /*
> --- 610,748 ----
>       {
>           HeapTuple    tup = slot->val;
>           TupleDesc    tdesc = slot->ttc_tupleDescriptor;
> !         TupleDesc    arrtdesc = NULL;
>           List       *plst;
>           bool        isnew;
> +         int            numelems;
> +         int            elemnum;
> +         Datum        dvalue;
> +         Datum       *dvalues = NULL;
> +         bool        disnull;
>
>           /*
> !          * When isExpr is true, we have either a scalar expression or an
> !          * array. In the former case, this is no different than the !isExpr
> !          * case. In the latter case, iterate over the elements as if they
> !          * were from multiple input tuples.
>            */
> !         if (!isExpr)
> !             numelems = 1;
> !         else
>           {
> !             Oid        expr_typeid = tdesc->attrs[0]->atttypid;
>
> !             if (expr_typeid != subplan->exprtype)
> !             {
> !                 subplan->exprtype = expr_typeid;
> !                 subplan->elemtype = get_element_type(expr_typeid);
> !
> !                 if (subplan->elemtype != InvalidOid)
> !                     get_typlenbyvalalign(subplan->elemtype,
> !                                          &subplan->elmlen,
> !                                          &subplan->elmbyval,
> !                                          &subplan->elmalign);
> !             }
> !
> !             /* get current value */
> !             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
> !
> !             if (subplan->elemtype != InvalidOid)
> !             {
> !                 TupleTable    tupleTable;
> !                 ArrayType   *v = DatumGetArrayTypeP(dvalue);
> !
> !                 arrtdesc = CreateTemplateTupleDesc(1, false);
> !                 TupleDescInitEntry(arrtdesc, 1, "elem", subplan->elemtype,
> !                                                             -1, 0, false);
> !
> !                 tupleTable = ExecCreateTupleTable(1);
> !                 arrslot = ExecAllocTableSlot(tupleTable);
> !                 ExecSetSlotDescriptor(arrslot, arrtdesc, true);
> !
> !                 /* XXX this will need work if/when arrays support NULL elements */
> !                 if (!disnull)
> !                 {
> !                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
> !                                       subplan->elmbyval, subplan->elmalign,
> !                                         &dvalues, &numelems);
> !                 }
> !                 else
> !                 {
> !                     numelems = 1;
> !                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                     dvalues[0] = (Datum) 0;
> !                 }
> !             }
> !             else
> !             {
> !                 numelems = 1;
> !                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                 dvalues[0] = dvalue;
> !             }
>
>           }
> !
> !         for (elemnum = 0; elemnum < numelems; elemnum++)
>           {
> !             int    col = 1;
> !
> !             if (!isExpr || subplan->elemtype == InvalidOid)
> !             {
> !                 /*
> !                  * Load up the Params representing the raw sub-select outputs,
> !                  * then form the projection tuple to store in the hashtable.
> !                  */
> !                 foreach(plst, subplan->paramIds)
> !                 {
> !                     int            paramid = lfirsti(plst);
> !                     ParamExecData *prmdata;
> !
> !                     prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
> !                     Assert(prmdata->execPlan == NULL);
> !
> !                     prmdata->value = heap_getattr(tup, col, tdesc,
> !                                                   &(prmdata->isnull));
> !
> !                     col++;
> !                 }
> !                 slot = ExecProject(node->projRight, NULL);
> !                 tup = slot->val;
> !             }
> !             else
> !             {
> !                 /*
> !                  * For array type expressions, we need to build up our own
> !                  * tuple and slot
> !                  */
> !                 char        nullflag;
> !
> !                 nullflag = disnull ? 'n' : ' ';
> !                 tup = heap_formtuple(arrtdesc, &dvalues[elemnum], &nullflag);
> !                 arrslot = ExecStoreTuple(tup, arrslot, InvalidBuffer, true);
> !             }
> !
> !             /*
> !              * If result contains any nulls, store separately or not at all.
> !              * (Since we know the projection tuple has no junk columns, we
> !              * can just look at the overall hasnull info bit, instead of
> !              * groveling through the columns.)
> !              */
> !             if (HeapTupleNoNulls(tup))
> !             {
> !                 if (!isExpr)
> !                     (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
> !                 else
> !                     (void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew);
> !                 node->havehashrows = true;
> !             }
> !             else if (node->hashnulls)
> !             {
> !                 if (!isExpr)
> !                     (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
> !                 else
> !                     (void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew);
> !                 node->havenullrows = true;
> !             }
>           }
>
>           /*
> ***************
> *** 617,622 ****
> --- 759,766 ----
>        * have the potential for a double free attempt.
>        */
>       ExecClearTuple(node->projRight->pi_slot);
> +     if (arrslot)
> +         ExecClearTuple(arrslot);
>
>       MemoryContextSwitchTo(oldcontext);
>   }
> ***************
> *** 1086,1187 ****
>           prm->execPlan = node;
>           parent->chgParam = bms_add_member(parent->chgParam, paramid);
>       }
> - }
> -
> - /*
> -  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
> -  *
> -  *    astate is working state (NULL on first call)
> -  *    rcontext is where to keep working state
> -  */
> - static ArrayBuildState *
> - accumArrayResult(ArrayBuildState *astate,
> -                  Datum dvalue, bool disnull,
> -                  Oid element_type,
> -                  MemoryContext rcontext)
> - {
> -     MemoryContext arr_context,
> -                   oldcontext;
> -
> -     if (astate == NULL)
> -     {
> -         /* First time through --- initialize */
> -
> -         /* Make a temporary context to hold all the junk */
> -         arr_context = AllocSetContextCreate(rcontext,
> -                                             "ARRAY_SUBLINK Result",
> -                                             ALLOCSET_DEFAULT_MINSIZE,
> -                                             ALLOCSET_DEFAULT_INITSIZE,
> -                                             ALLOCSET_DEFAULT_MAXSIZE);
> -         oldcontext = MemoryContextSwitchTo(arr_context);
> -         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
> -         astate->mcontext = arr_context;
> -         astate->dvalues = (Datum *)
> -             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
> -         astate->nelems = 0;
> -         astate->element_type = element_type;
> -         get_typlenbyvalalign(element_type,
> -                              &astate->typlen,
> -                              &astate->typbyval,
> -                              &astate->typalign);
> -     }
> -     else
> -     {
> -         oldcontext = MemoryContextSwitchTo(astate->mcontext);
> -         Assert(astate->element_type == element_type);
> -         /* enlarge dvalues[] if needed */
> -         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
> -             astate->dvalues = (Datum *)
> -                 repalloc(astate->dvalues,
> -                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
> -     }
> -
> -     if (disnull)
> -         elog(ERROR, "NULL elements not allowed in Arrays");
> -
> -     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
> -     astate->dvalues[astate->nelems++] =
> -         datumCopy(dvalue, astate->typbyval, astate->typlen);
> -
> -     MemoryContextSwitchTo(oldcontext);
> -
> -     return astate;
> - }
> -
> - /*
> -  * makeArrayResult - produce final result of ARRAY_SUBLINK
> -  *
> -  *    astate is working state (not NULL)
> -  *    rcontext is where to construct result
> -  */
> - static Datum
> - makeArrayResult(ArrayBuildState *astate,
> -                 MemoryContext rcontext)
> - {
> -     ArrayType  *result;
> -     int            dims[1];
> -     int            lbs[1];
> -     MemoryContext oldcontext;
> -
> -     /* Build the final array result in rcontext */
> -     oldcontext = MemoryContextSwitchTo(rcontext);
> -
> -     dims[0] = astate->nelems;
> -     lbs[0] = 1;
> -
> -     result = construct_md_array(astate->dvalues,
> -                                 1,
> -                                 dims,
> -                                 lbs,
> -                                 astate->element_type,
> -                                 astate->typlen,
> -                                 astate->typbyval,
> -                                 astate->typalign);
> -
> -     MemoryContextSwitchTo(oldcontext);
> -
> -     /* Clean up all the junk */
> -     MemoryContextDelete(astate->mcontext);
> -
> -     return PointerGetDatum(result);
>   }
> --- 1230,1233 ----
> Index: src/backend/nodes/copyfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
> retrieving revision 1.252
> diff -c -r1.252 copyfuncs.c
> *** src/backend/nodes/copyfuncs.c    6 Jun 2003 15:04:02 -0000    1.252
> --- src/backend/nodes/copyfuncs.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 728,733 ****
> --- 728,734 ----
>       COPY_SCALAR_FIELD(agglevelsup);
>       COPY_SCALAR_FIELD(aggstar);
>       COPY_SCALAR_FIELD(aggdistinct);
> +     COPY_NODE_FIELD(args);
>
>       return newnode;
>   }
> ***************
> *** 826,831 ****
> --- 827,833 ----
>
>       COPY_SCALAR_FIELD(subLinkType);
>       COPY_SCALAR_FIELD(useOr);
> +     COPY_SCALAR_FIELD(isExpr);
>       COPY_NODE_FIELD(lefthand);
>       COPY_NODE_FIELD(operName);
>       COPY_OIDLIST_FIELD(operOids);
> ***************
> *** 844,849 ****
> --- 846,857 ----
>
>       COPY_SCALAR_FIELD(subLinkType);
>       COPY_SCALAR_FIELD(useOr);
> +     COPY_SCALAR_FIELD(isExpr);
> +     COPY_SCALAR_FIELD(exprtype);
> +     COPY_SCALAR_FIELD(elemtype);
> +     COPY_SCALAR_FIELD(elmlen);
> +     COPY_SCALAR_FIELD(elmbyval);
> +     COPY_SCALAR_FIELD(elmalign);
>       COPY_NODE_FIELD(exprs);
>       COPY_INTLIST_FIELD(paramIds);
>       COPY_NODE_FIELD(plan);
> Index: src/backend/nodes/equalfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v
> retrieving revision 1.195
> diff -c -r1.195 equalfuncs.c
> *** src/backend/nodes/equalfuncs.c    6 Jun 2003 15:04:02 -0000    1.195
> --- src/backend/nodes/equalfuncs.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 205,210 ****
> --- 205,211 ----
>       COMPARE_SCALAR_FIELD(agglevelsup);
>       COMPARE_SCALAR_FIELD(aggstar);
>       COMPARE_SCALAR_FIELD(aggdistinct);
> +     COMPARE_NODE_FIELD(args);
>
>       return true;
>   }
> ***************
> *** 301,306 ****
> --- 302,308 ----
>   {
>       COMPARE_SCALAR_FIELD(subLinkType);
>       COMPARE_SCALAR_FIELD(useOr);
> +     COMPARE_SCALAR_FIELD(isExpr);
>       COMPARE_NODE_FIELD(lefthand);
>       COMPARE_NODE_FIELD(operName);
>       COMPARE_OIDLIST_FIELD(operOids);
> ***************
> *** 314,319 ****
> --- 316,327 ----
>   {
>       COMPARE_SCALAR_FIELD(subLinkType);
>       COMPARE_SCALAR_FIELD(useOr);
> +     COMPARE_SCALAR_FIELD(isExpr);
> +     COMPARE_SCALAR_FIELD(exprtype);
> +     COMPARE_SCALAR_FIELD(elemtype);
> +     COMPARE_SCALAR_FIELD(elmlen);
> +     COMPARE_SCALAR_FIELD(elmbyval);
> +     COMPARE_SCALAR_FIELD(elmalign);
>       COMPARE_NODE_FIELD(exprs);
>       COMPARE_INTLIST_FIELD(paramIds);
>       /* should compare plans, but have to settle for comparing plan IDs */
> Index: src/backend/nodes/outfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v
> retrieving revision 1.208
> diff -c -r1.208 outfuncs.c
> *** src/backend/nodes/outfuncs.c    15 Jun 2003 22:51:45 -0000    1.208
> --- src/backend/nodes/outfuncs.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 616,621 ****
> --- 616,622 ----
>       WRITE_UINT_FIELD(agglevelsup);
>       WRITE_BOOL_FIELD(aggstar);
>       WRITE_BOOL_FIELD(aggdistinct);
> +     WRITE_NODE_FIELD(args);
>   }
>
>   static void
> ***************
> *** 701,706 ****
> --- 702,708 ----
>
>       WRITE_ENUM_FIELD(subLinkType, SubLinkType);
>       WRITE_BOOL_FIELD(useOr);
> +     WRITE_BOOL_FIELD(isExpr);
>       WRITE_NODE_FIELD(lefthand);
>       WRITE_NODE_FIELD(operName);
>       WRITE_OIDLIST_FIELD(operOids);
> ***************
> *** 714,719 ****
> --- 716,727 ----
>
>       WRITE_ENUM_FIELD(subLinkType, SubLinkType);
>       WRITE_BOOL_FIELD(useOr);
> +     WRITE_BOOL_FIELD(isExpr);
> +     WRITE_OID_FIELD(exprtype);
> +     WRITE_OID_FIELD(elemtype);
> +     WRITE_INT_FIELD(elmlen);
> +     WRITE_BOOL_FIELD(elmbyval);
> +     WRITE_CHAR_FIELD(elmalign);
>       WRITE_NODE_FIELD(exprs);
>       WRITE_INTLIST_FIELD(paramIds);
>       WRITE_NODE_FIELD(plan);
> Index: src/backend/nodes/readfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v
> retrieving revision 1.154
> diff -c -r1.154 readfuncs.c
> *** src/backend/nodes/readfuncs.c    6 Jun 2003 15:04:02 -0000    1.154
> --- src/backend/nodes/readfuncs.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 416,421 ****
> --- 416,422 ----
>       READ_UINT_FIELD(agglevelsup);
>       READ_BOOL_FIELD(aggstar);
>       READ_BOOL_FIELD(aggdistinct);
> +     READ_NODE_FIELD(args);
>
>       READ_DONE();
>   }
> ***************
> *** 545,550 ****
> --- 546,552 ----
>
>       READ_ENUM_FIELD(subLinkType, SubLinkType);
>       READ_BOOL_FIELD(useOr);
> +     READ_BOOL_FIELD(isExpr);
>       READ_NODE_FIELD(lefthand);
>       READ_NODE_FIELD(operName);
>       READ_OIDLIST_FIELD(operOids);
> Index: src/backend/optimizer/plan/subselect.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/plan/subselect.c,v
> retrieving revision 1.76
> diff -c -r1.76 subselect.c
> *** src/backend/optimizer/plan/subselect.c    6 Jun 2003 15:04:02 -0000    1.76
> --- src/backend/optimizer/plan/subselect.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 83,89 ****
>
>   static List *convert_sublink_opers(List *lefthand, List *operOids,
>                                      List *targetlist, int rtindex,
> !                                    List **righthandIds);
>   static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
>   static Node *replace_correlation_vars_mutator(Node *node, void *context);
>   static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
> --- 83,89 ----
>
>   static List *convert_sublink_opers(List *lefthand, List *operOids,
>                                      List *targetlist, int rtindex,
> !                                    bool isExpr, List **righthandIds);
>   static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
>   static Node *replace_correlation_vars_mutator(Node *node, void *context);
>   static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
> ***************
> *** 299,304 ****
> --- 299,310 ----
>        */
>       node->subLinkType = slink->subLinkType;
>       node->useOr = slink->useOr;
> +     node->isExpr = slink->isExpr;
> +     node->exprtype = InvalidOid;
> +     node->elemtype = InvalidOid;
> +     node->elmlen = 0;
> +     node->elmbyval = false;
> +     node->elmalign = '\0';
>       node->exprs = NIL;
>       node->paramIds = NIL;
>       node->useHashTable = false;
> ***************
> *** 374,380 ****
>           exprs = convert_sublink_opers(lefthand,
>                                         slink->operOids,
>                                         plan->targetlist,
> !                                       0,
>                                         &node->paramIds);
>           node->setParam = listCopy(node->paramIds);
>           PlannerInitPlan = lappend(PlannerInitPlan, node);
> --- 380,386 ----
>           exprs = convert_sublink_opers(lefthand,
>                                         slink->operOids,
>                                         plan->targetlist,
> !                                       0, node->isExpr,
>                                         &node->paramIds);
>           node->setParam = listCopy(node->paramIds);
>           PlannerInitPlan = lappend(PlannerInitPlan, node);
> ***************
> *** 457,463 ****
>           node->exprs = convert_sublink_opers(lefthand,
>                                               slink->operOids,
>                                               plan->targetlist,
> !                                             0,
>                                               &node->paramIds);
>
>           /*
> --- 463,469 ----
>           node->exprs = convert_sublink_opers(lefthand,
>                                               slink->operOids,
>                                               plan->targetlist,
> !                                             0, node->isExpr,
>                                               &node->paramIds);
>
>           /*
> ***************
> *** 499,505 ****
>   static List *
>   convert_sublink_opers(List *lefthand, List *operOids,
>                         List *targetlist, int rtindex,
> !                       List **righthandIds)
>   {
>       List       *result = NIL;
>       List       *lst;
> --- 505,511 ----
>   static List *
>   convert_sublink_opers(List *lefthand, List *operOids,
>                         List *targetlist, int rtindex,
> !                       bool isExpr, List **righthandIds)
>   {
>       List       *result = NIL;
>       List       *lst;
> ***************
> *** 554,566 ****
>            * are not expecting to have to resolve unknown Params, so
>            * it's okay to pass a null pstate.)
>            */
> !         result = lappend(result,
> !                          make_op_expr(NULL,
> !                                       tup,
> !                                       leftop,
> !                                       rightop,
> !                                       exprType(leftop),
> !                                       te->resdom->restype));
>
>           ReleaseSysCache(tup);
>
> --- 560,597 ----
>            * are not expecting to have to resolve unknown Params, so
>            * it's okay to pass a null pstate.)
>            */
> !         if (!isExpr)
> !         {
> !             result = lappend(result,
> !                              make_op_expr(NULL,
> !                                           tup,
> !                                           leftop,
> !                                           rightop,
> !                                           exprType(leftop),
> !                                           te->resdom->restype));
> !         }
> !         else
> !         {
> !             Oid        exprtype = te->resdom->restype;
> !             Oid        elemtype = get_element_type(exprtype);
> !
> !             if (elemtype != InvalidOid)
> !                 result = lappend(result,
> !                                  make_op_expr(NULL,
> !                                               tup,
> !                                               leftop,
> !                                               rightop,
> !                                               exprType(leftop),
> !                                               elemtype));
> !             else
> !                 result = lappend(result,
> !                                  make_op_expr(NULL,
> !                                               tup,
> !                                               leftop,
> !                                               rightop,
> !                                               exprType(leftop),
> !                                               exprtype));
> !         }
>
>           ReleaseSysCache(tup);
>
> ***************
> *** 671,683 ****
>       /*
>        * The sublink type must be "= ANY" --- that is, an IN operator.
>        * (We require the operator name to be unqualified, which may be
> !      * overly paranoid, or may not be.)
>        */
>       if (sublink->subLinkType != ANY_SUBLINK)
>           return NULL;
>       if (length(sublink->operName) != 1 ||
>           strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
>           return NULL;
>       /*
>        * The sub-select must not refer to any Vars of the parent query.
>        * (Vars of higher levels should be okay, though.)
> --- 702,718 ----
>       /*
>        * The sublink type must be "= ANY" --- that is, an IN operator.
>        * (We require the operator name to be unqualified, which may be
> !      * overly paranoid, or may not be.) It must not be an Expression
> !      * sublink.
>        */
>       if (sublink->subLinkType != ANY_SUBLINK)
>           return NULL;
>       if (length(sublink->operName) != 1 ||
>           strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
>           return NULL;
> +     if (sublink->isExpr)
> +         return NULL;
> +
>       /*
>        * The sub-select must not refer to any Vars of the parent query.
>        * (Vars of higher levels should be okay, though.)
> ***************
> *** 730,736 ****
>       exprs = convert_sublink_opers(sublink->lefthand,
>                                     sublink->operOids,
>                                     subselect->targetList,
> !                                   rtindex,
>                                     &ininfo->sub_targetlist);
>       return (Node *) make_ands_explicit(exprs);
>   }
> --- 765,771 ----
>       exprs = convert_sublink_opers(sublink->lefthand,
>                                     sublink->operOids,
>                                     subselect->targetList,
> !                                   rtindex, sublink->isExpr,
>                                     &ininfo->sub_targetlist);
>       return (Node *) make_ands_explicit(exprs);
>   }
> Index: src/backend/optimizer/util/clauses.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/util/clauses.c,v
> retrieving revision 1.139
> diff -c -r1.139 clauses.c
> *** src/backend/optimizer/util/clauses.c    6 Jun 2003 15:04:02 -0000    1.139
> --- src/backend/optimizer/util/clauses.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 133,138 ****
> --- 133,160 ----
>   }
>
>   /*****************************************************************************
> +  *              FUNCTION clause functions
> +  *****************************************************************************/
> +
> + /*
> +  * make_funcclause
> +  *        Creates a function clause given its function info and argument list.
> +  */
> + Expr *
> + make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
> +                             CoercionForm funcformat, List *funcargs)
> + {
> +     FuncExpr   *expr = makeNode(FuncExpr);
> +
> +     expr->funcid = funcid;
> +     expr->funcresulttype = funcresulttype;
> +     expr->funcretset = funcretset;
> +     expr->funcformat = funcformat;
> +     expr->args = funcargs;
> +     return (Expr *) expr;
> + }
> +
> + /*****************************************************************************
>    *        NOT clause functions
>    *****************************************************************************/
>
> Index: src/backend/parser/gram.y
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/gram.y,v
> retrieving revision 2.416
> diff -c -r2.416 gram.y
> *** src/backend/parser/gram.y    29 May 2003 20:40:36 -0000    2.416
> --- src/backend/parser/gram.y    16 Jun 2003 23:45:51 -0000
> ***************
> *** 5490,5495 ****
> --- 5490,5496 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = ANY_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = makeList1(makeString("="));
>                       n->subselect = $3;
> ***************
> *** 5500,5505 ****
> --- 5501,5507 ----
>                       /* Make an IN node */
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = ANY_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = makeList1(makeString("="));
>                       n->subselect = $4;
> ***************
> *** 5511,5516 ****
> --- 5513,5519 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = $3;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = $2;
>                       n->subselect = $4;
> ***************
> *** 5521,5526 ****
> --- 5524,5530 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = MULTIEXPR_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = $2;
>                       n->subselect = $3;
> ***************
> *** 5904,5909 ****
> --- 5908,5914 ----
>                       {
>                               SubLink *n = (SubLink *)$3;
>                               n->subLinkType = ANY_SUBLINK;
> +                             n->isExpr = false;
>                               n->lefthand = makeList1($1);
>                               n->operName = makeList1(makeString("="));
>                               $$ = (Node *)n;
> ***************
> *** 5931,5936 ****
> --- 5936,5942 ----
>                       {
>                           /* Make an IN node */
>                           SubLink *n = (SubLink *)$4;
> +                         n->isExpr = false;
>                           n->subLinkType = ANY_SUBLINK;
>                           n->lefthand = makeList1($1);
>                           n->operName = makeList1(makeString("="));
> ***************
> *** 5957,5967 ****
> --- 5963,6000 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = $3;
> +                     n->isExpr = false;
>                       n->lefthand = makeList1($1);
>                       n->operName = $2;
>                       n->subselect = $4;
>                       $$ = (Node *)n;
>                   }
> +             | a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op
> +                 {
> +                     SubLink *n = makeNode(SubLink);
> +                     SelectStmt *s = makeNode(SelectStmt);
> +                     ResTarget *r = makeNode(ResTarget);
> +
> +                     r->name = NULL;
> +                     r->indirection = NIL;
> +                     r->val = (Node *)$5;
> +
> +                     s->distinctClause = NIL;
> +                     s->targetList = makeList1(r);
> +                     s->into = NULL;
> +                     s->intoColNames = NIL;
> +                     s->fromClause = NIL;
> +                     s->whereClause = NULL;
> +                     s->groupClause = NIL;
> +                     s->havingClause = NULL;
> +
> +                     n->subLinkType = $3;
> +                     n->isExpr = true;
> +                     n->lefthand = makeList1($1);
> +                     n->operName = $2;
> +                     n->subselect = (Node *) s;
> +                     $$ = (Node *)n;
> +                 }
>               | UNIQUE select_with_parens %prec Op
>                   {
>                       /* Not sure how to get rid of the parentheses
> ***************
> *** 6538,6543 ****
> --- 6571,6577 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = EXPR_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = NIL;
>                       n->operName = NIL;
>                       n->subselect = $1;
> ***************
> *** 6547,6552 ****
> --- 6581,6587 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = EXISTS_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = NIL;
>                       n->operName = NIL;
>                       n->subselect = $2;
> ***************
> *** 6556,6561 ****
> --- 6591,6597 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = ARRAY_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = NIL;
>                       n->operName = NIL;
>                       n->subselect = $2;
> ***************
> *** 6730,6735 ****
> --- 6766,6772 ----
>   in_expr:    select_with_parens
>                   {
>                       SubLink *n = makeNode(SubLink);
> +                     n->isExpr = false;
>                       n->subselect = $1;
>                       /* other fields will be filled later */
>                       $$ = (Node *)n;
> Index: src/backend/parser/parse_coerce.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v
> retrieving revision 2.97
> diff -c -r2.97 parse_coerce.c
> *** src/backend/parser/parse_coerce.c    26 May 2003 00:11:27 -0000    2.97
> --- src/backend/parser/parse_coerce.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 859,865 ****
>       /* Get the element type based on the array type, if we have one */
>       if (OidIsValid(array_typeid))
>       {
> !         array_typelem = get_element_type(array_typeid);
>           if (!OidIsValid(array_typelem))
>               elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
>                    format_type_be(array_typeid));
> --- 859,869 ----
>       /* Get the element type based on the array type, if we have one */
>       if (OidIsValid(array_typeid))
>       {
> !         if (array_typeid != ANYARRAYOID)
> !             array_typelem = get_element_type(array_typeid);
> !         else
> !             array_typelem = ANYELEMENTOID;
> !
>           if (!OidIsValid(array_typelem))
>               elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
>                    format_type_be(array_typeid));
> ***************
> *** 919,925 ****
>       {
>           if (!OidIsValid(array_typeid))
>           {
> !             array_typeid = get_array_type(elem_typeid);
>               if (!OidIsValid(array_typeid))
>                   elog(ERROR, "Cannot find array type for datatype %s",
>                        format_type_be(elem_typeid));
> --- 923,933 ----
>       {
>           if (!OidIsValid(array_typeid))
>           {
> !             if (elem_typeid != ANYELEMENTOID)
> !                 array_typeid = get_array_type(elem_typeid);
> !             else
> !                 array_typeid = ANYARRAYOID;
> !
>               if (!OidIsValid(array_typeid))
>                   elog(ERROR, "Cannot find array type for datatype %s",
>                        format_type_be(elem_typeid));
> ***************
> *** 1169,1174 ****
> --- 1177,1187 ----
>       /* Somewhat-fast path for domain -> base type case */
>       if (srctype == targettype)
>           return true;
> +
> +     /* Last of the fast-paths: check for matching polymorphic arrays */
> +     if (targettype == ANYARRAYOID)
> +         if (get_element_type(srctype) != InvalidOid)
> +             return true;
>
>       /* Else look in pg_cast */
>       tuple = SearchSysCache(CASTSOURCETARGET,
> Index: src/backend/parser/parse_expr.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_expr.c,v
> retrieving revision 1.148
> diff -c -r1.148 parse_expr.c
> *** src/backend/parser/parse_expr.c    29 Apr 2003 22:13:10 -0000    1.148
> --- src/backend/parser/parse_expr.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 436,441 ****
> --- 436,442 ----
>                       sublink->operName = NIL;
>                       sublink->operOids = NIL;
>                       sublink->useOr = FALSE;
> +                     sublink->isExpr = FALSE;
>                   }
>                   else if (sublink->subLinkType == EXPR_SUBLINK ||
>                            sublink->subLinkType == ARRAY_SUBLINK)
> ***************
> *** 463,468 ****
> --- 464,470 ----
>                       sublink->operName = NIL;
>                       sublink->operOids = NIL;
>                       sublink->useOr = FALSE;
> +                     sublink->isExpr = FALSE;
>                   }
>                   else
>                   {
> ***************
> *** 538,547 ****
>                            * here, because make_subplan() will insert type
>                            * coercion calls if needed.
>                            */
> !                         optup = oper(op,
> !                                      exprType(lexpr),
> !                                      exprType((Node *) tent->expr),
> !                                      false);
>                           opform = (Form_pg_operator) GETSTRUCT(optup);
>
>                           if (opform->oprresult != BOOLOID)
> --- 540,569 ----
>                            * here, because make_subplan() will insert type
>                            * coercion calls if needed.
>                            */
> !                         if (!sublink->isExpr)
> !                         {
> !                             optup = oper(op,
> !                                          exprType(lexpr),
> !                                          exprType((Node *) tent->expr),
> !                                          false);
> !                         }
> !                         else
> !                         {
> !                             Oid        exprtype = exprType((Node *) tent->expr);
> !                             Oid        elemtype = get_element_type(exprtype);
> !
> !                             if (elemtype != InvalidOid)
> !                                 optup = oper(op,
> !                                              exprType(lexpr),
> !                                              elemtype,
> !                                              false);
> !                             else
> !                                 optup = oper(op,
> !                                              exprType(lexpr),
> !                                              exprtype,
> !                                              false);
> !                         }
> !
>                           opform = (Form_pg_operator) GETSTRUCT(optup);
>
>                           if (opform->oprresult != BOOLOID)
> ***************
> *** 743,749 ****
>                           ArrayExpr  *e = (ArrayExpr *) lfirst(element);
>
>                           if (!IsA(e, ArrayExpr))
> !                             elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions");
>                           if (ndims == 0)
>                               ndims = e->ndims;
>                           else if (e->ndims != ndims)
> --- 765,771 ----
>                           ArrayExpr  *e = (ArrayExpr *) lfirst(element);
>
>                           if (!IsA(e, ArrayExpr))
> !                             elog(ERROR, "Multidimensional ARRAY[] must be built from nested array expressions");
>                           if (ndims == 0)
>                               ndims = e->ndims;
>                           else if (e->ndims != ndims)
> Index: src/backend/parser/parse_func.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_func.c,v
> retrieving revision 1.149
> diff -c -r1.149 parse_func.c
> *** src/backend/parser/parse_func.c    6 Jun 2003 15:04:02 -0000    1.149
> --- src/backend/parser/parse_func.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 336,341 ****
> --- 336,342 ----
>           aggref->target = lfirst(fargs);
>           aggref->aggstar = agg_star;
>           aggref->aggdistinct = agg_distinct;
> +         aggref->args = fargs;
>
>           /* parse_agg.c does additional aggregate-specific processing */
>           transformAggregateCall(pstate, aggref);
> Index: src/backend/parser/parse_oper.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_oper.c,v
> retrieving revision 1.64
> diff -c -r1.64 parse_oper.c
> *** src/backend/parser/parse_oper.c    26 May 2003 00:11:27 -0000    1.64
> --- src/backend/parser/parse_oper.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 137,142 ****
> --- 137,169 ----
>   equality_oper(Oid argtype, bool noError)
>   {
>       Operator    optup;
> +     Oid            elem_type = get_element_type(argtype);
> +
> +     if (OidIsValid(elem_type))
> +     {
> +         bool    found = false;
> +         /*
> +          * If the datatype is an array, look for an "=" operator for the
> +          * element datatype.  We require it to be an exact or binary-compatible
> +          * match, since most callers are not prepared to cope with adding any
> +          * run-time type coercion steps.
> +          */
> +         optup = equality_oper(elem_type, true);
> +         if (optup != NULL)
> +         {
> +             found = true;
> +             ReleaseSysCache(optup);
> +         }
> +
> +         if (!found)
> +         {
> +             if (!noError)
> +                 elog(ERROR, "Unable to identify an equality operator for " \
> +                             "array type's element type %s",
> +                              format_type_be(elem_type));
> +             return NULL;
> +         }
> +     }
>
>       /*
>        * Look for an "=" operator for the datatype.  We require it to be
> ***************
> *** 175,180 ****
> --- 202,234 ----
>   ordering_oper(Oid argtype, bool noError)
>   {
>       Operator    optup;
> +     Oid            elem_type = get_element_type(argtype);
> +
> +     if (OidIsValid(elem_type))
> +     {
> +         bool    found = false;
> +         /*
> +          * If the datatype is an array, find the array element type's equality
> +          * operator, and use its lsortop (it *must* be mergejoinable).  We use
> +          * this definition because for sorting and grouping purposes, it's
> +          * important that the equality and ordering operators are consistent.
> +          */
> +         optup = ordering_oper(elem_type, true);
> +         if (optup != NULL)
> +         {
> +             found = true;
> +             ReleaseSysCache(optup);
> +         }
> +
> +         if (!found)
> +         {
> +             if (!noError)
> +                 elog(ERROR, "Unable to identify an ordering operator for " \
> +                             "array type's element type %s",
> +                              format_type_be(elem_type));
> +             return NULL;
> +         }
> +     }
>
>       /*
>        * Find the type's equality operator, and use its lsortop (it *must*
> ***************
> *** 215,220 ****
> --- 269,289 ----
>       Oid            result;
>
>       optup = equality_oper(argtype, false);
> +     result = oprfuncid(optup);
> +     ReleaseSysCache(optup);
> +     return result;
> + }
> +
> + /*
> +  * ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper())
> +  */
> + Oid
> + ordering_oper_funcid(Oid argtype)
> + {
> +     Operator    optup;
> +     Oid            result;
> +
> +     optup = ordering_oper(argtype, false);
>       result = oprfuncid(optup);
>       ReleaseSysCache(optup);
>       return result;
> Index: src/backend/utils/adt/acl.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/acl.c,v
> retrieving revision 1.88
> diff -c -r1.88 acl.c
> *** src/backend/utils/adt/acl.c    11 Jun 2003 09:23:55 -0000    1.88
> --- src/backend/utils/adt/acl.c    16 Jun 2003 21:19:53 -0000
> ***************
> *** 427,432 ****
> --- 427,441 ----
>           a1->ai_grantor == a2->ai_grantor;
>   }
>
> + /*
> +  * user-facing version of aclitemeq() for use as the
> +  * aclitem equality operator
> +  */
> + Datum
> + aclitem_eq(PG_FUNCTION_ARGS)
> + {
> +     PG_RETURN_BOOL(aclitemeq(PG_GETARG_ACLITEM_P(0), PG_GETARG_ACLITEM_P(1)));
> + }
>
>   /*
>    * acldefault()  --- create an ACL describing default access permissions
> Index: src/backend/utils/adt/array_userfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/array_userfuncs.c,v
> retrieving revision 1.1
> diff -c -r1.1 array_userfuncs.c
> *** src/backend/utils/adt/array_userfuncs.c    8 Apr 2003 23:20:02 -0000    1.1
> --- src/backend/utils/adt/array_userfuncs.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 18,52 ****
>   #include "utils/lsyscache.h"
>   #include "utils/syscache.h"
>
> -
> - /*-----------------------------------------------------------------------------
> -  * singleton_array :
> -  *        Form a multi-dimensional array given one starting element.
> -  *
> -  * - first argument is the datum with which to build the array
> -  * - second argument is the number of dimensions the array should have;
> -  *     defaults to 1 if no second argument is provided
> -  *----------------------------------------------------------------------------
> -  */
> - Datum
> - singleton_array(PG_FUNCTION_ARGS)
> - {
> -     Oid            elem_type = get_fn_expr_argtype(fcinfo, 0);
> -     int            ndims;
> -
> -     if (elem_type == InvalidOid)
> -         elog(ERROR, "Cannot determine input datatype");
> -
> -     if (PG_NARGS() == 2)
> -         ndims = PG_GETARG_INT32(1);
> -     else
> -         ndims = 1;
> -
> -     PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
> -                                                  PG_GETARG_DATUM(0),
> -                                                  ndims));
> - }
> -
>   /*-----------------------------------------------------------------------------
>    * array_push :
>    *        push an element onto either end of a one-dimensional array
> --- 18,23 ----
> ***************
> *** 70,75 ****
> --- 41,47 ----
>       Oid            arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
>       Oid            arg0_elemid;
>       Oid            arg1_elemid;
> +     ArrayMetaState *my_extra;
>
>       if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
>           elog(ERROR, "array_push: cannot determine input data types");
> ***************
> *** 95,122 ****
>           PG_RETURN_NULL();        /* keep compiler quiet */
>       }
>
> !     /* Sanity check: do we have a one-dimensional array */
> !     if (ARR_NDIM(v) != 1)
> !         elog(ERROR, "Arrays greater than one-dimension are not supported");
> !
> !     lb = ARR_LBOUND(v);
> !     dimv = ARR_DIMS(v);
> !     if (arg0_elemid != InvalidOid)
>       {
> !         /* append newelem */
> !         int    ub = dimv[0] + lb[0] - 1;
> !         indx = ub + 1;
>       }
>       else
>       {
> !         /* prepend newelem */
> !         indx = lb[0] - 1;
>       }
>
> !     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
> !     result = array_set(v, 1, &indx, newelem, -1,
> !                        typlen, typbyval, typalign, &isNull);
>
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
> --- 67,127 ----
>           PG_RETURN_NULL();        /* keep compiler quiet */
>       }
>
> !     if (ARR_NDIM(v) == 1)
>       {
> !         lb = ARR_LBOUND(v);
> !         dimv = ARR_DIMS(v);
> !
> !         if (arg0_elemid != InvalidOid)
> !         {
> !             /* append newelem */
> !             int    ub = dimv[0] + lb[0] - 1;
> !             indx = ub + 1;
> !         }
> !         else
> !         {
> !             /* prepend newelem */
> !             indx = lb[0] - 1;
> !         }
>       }
> +     else if (ARR_NDIM(v) == 0)
> +         indx = 1;
>       else
> +         elog(ERROR, "only empty and one-dimensional arrays are supported");
> +
> +
> +     /*
> +      * We arrange to look up info about element type only once per series
> +      * of calls, assuming the element type doesn't change underneath us.
> +      */
> +     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> +     if (my_extra == NULL)
>       {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
>       }
>
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type */
> !         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typalign = my_extra->typalign;
> !     }
> !
> !     result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval,
> !                                                  typalign, &isNull);
>
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
> ***************
> *** 145,157 ****
>
>       /*
>        * We must have one of the following combinations of inputs:
> !      * 1) two arrays with ndims1 == ndims2
> !      * 2) ndims1 == ndims2 - 1
> !      * 3) ndims1 == ndims2 + 1
>        */
>       ndims1 = ARR_NDIM(v1);
>       ndims2 = ARR_NDIM(v2);
>
>       if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
>           elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
>                       "%d dimensions", ndims1, ndims2);
> --- 150,177 ----
>
>       /*
>        * We must have one of the following combinations of inputs:
> !      * 1) one empty array, and one non-empty array
> !      * 2) both arrays empty
> !      * 3) two arrays with ndims1 == ndims2
> !      * 4) ndims1 == ndims2 - 1
> !      * 5) ndims1 == ndims2 + 1
>        */
>       ndims1 = ARR_NDIM(v1);
>       ndims2 = ARR_NDIM(v2);
>
> +     /*
> +      * short circuit - if one input array is empty, and the other is not,
> +      * we return the non-empty one as the result
> +      *
> +      * if both are empty, return the first one
> +      */
> +     if (ndims1 == 0 && ndims2 > 0)
> +         PG_RETURN_ARRAYTYPE_P(v2);
> +
> +     if (ndims2 == 0)
> +         PG_RETURN_ARRAYTYPE_P(v1);
> +
> +     /* the rest fall into combo 2, 3, or 4 */
>       if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
>           elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
>                       "%d dimensions", ndims1, ndims2);
> ***************
> *** 266,412 ****
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
>
> - /*----------------------------------------------------------------------------
> -  * array_accum :
> -  *        accumulator to build a 1-D array from input values -- this can be used
> -  *        to create custom aggregates.
> -  *
> -  * This function is not marked strict, so we have to be careful about nulls.
> -  *----------------------------------------------------------------------------
> -  */
> - Datum
> - array_accum(PG_FUNCTION_ARGS)
> - {
> -     /* return NULL if both arguments are NULL */
> -     if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
> -         PG_RETURN_NULL();
> -
> -     /* create a new 1-D array from the new element if the array is NULL */
> -     if (PG_ARGISNULL(0))
> -     {
> -         Oid            tgt_type = get_fn_expr_rettype(fcinfo);
> -         Oid            tgt_elem_type;
> -
> -         if (tgt_type == InvalidOid)
> -             elog(ERROR, "Cannot determine target array type");
> -         tgt_elem_type = get_element_type(tgt_type);
> -         if (tgt_elem_type == InvalidOid)
> -             elog(ERROR, "Target type is not an array");
> -
> -         PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
> -                                                      PG_GETARG_DATUM(1),
> -                                                      1));
> -     }
> -
> -     /* return the array if the new element is NULL */
> -     if (PG_ARGISNULL(1))
> -         PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0));
> -
> -     /*
> -      * Otherwise this is equivalent to array_push.  We hack the call a little
> -      * so that array_push can see the fn_expr information.
> -      */
> -     return array_push(fcinfo);
> - }
> -
> - /*-----------------------------------------------------------------------------
> -  * array_assign :
> -  *        assign an element of an array to a new value and return the
> -  *        redefined array
> -  *----------------------------------------------------------------------------
> -  */
> - Datum
> - array_assign(PG_FUNCTION_ARGS)
> - {
> -     ArrayType  *v;
> -     int            idx_to_chg;
> -     Datum        newelem;
> -     int           *dimv,
> -                *lb, ub;
> -     ArrayType  *result;
> -     bool        isNull;
> -     Oid            element_type;
> -     int16        typlen;
> -     bool        typbyval;
> -     char        typalign;
> -
> -     v = PG_GETARG_ARRAYTYPE_P(0);
> -     idx_to_chg = PG_GETARG_INT32(1);
> -     newelem = PG_GETARG_DATUM(2);
> -
> -     /* Sanity check: do we have a one-dimensional array */
> -     if (ARR_NDIM(v) != 1)
> -         elog(ERROR, "Arrays greater than one-dimension are not supported");
> -
> -     lb = ARR_LBOUND(v);
> -     dimv = ARR_DIMS(v);
> -     ub = dimv[0] + lb[0] - 1;
> -     if (idx_to_chg < lb[0] || idx_to_chg > ub)
> -         elog(ERROR, "Cannot alter nonexistent array element: %d", idx_to_chg);
> -
> -     element_type = ARR_ELEMTYPE(v);
> -     /* Sanity check: do we have a non-zero element type */
> -     if (element_type == 0)
> -         elog(ERROR, "Invalid array element type: %u", element_type);
> -
> -     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
> -
> -     result = array_set(v, 1, &idx_to_chg, newelem, -1,
> -                        typlen, typbyval, typalign, &isNull);
> -
> -     PG_RETURN_ARRAYTYPE_P(result);
> - }
> -
> - /*-----------------------------------------------------------------------------
> -  * array_subscript :
> -  *        return specific element of an array
> -  *----------------------------------------------------------------------------
> -  */
> - Datum
> - array_subscript(PG_FUNCTION_ARGS)
> - {
> -     ArrayType  *v;
> -     int            idx;
> -     int           *dimv,
> -                *lb, ub;
> -     Datum        result;
> -     bool        isNull;
> -     Oid            element_type;
> -     int16        typlen;
> -     bool        typbyval;
> -     char        typalign;
> -
> -     v = PG_GETARG_ARRAYTYPE_P(0);
> -     idx = PG_GETARG_INT32(1);
> -
> -     /* Sanity check: do we have a one-dimensional array */
> -     if (ARR_NDIM(v) != 1)
> -         elog(ERROR, "Arrays greater than one-dimension are not supported");
> -
> -     lb = ARR_LBOUND(v);
> -     dimv = ARR_DIMS(v);
> -     ub = dimv[0] + lb[0] - 1;
> -     if (idx < lb[0] || idx > ub)
> -         elog(ERROR, "Cannot return nonexistent array element: %d", idx);
> -
> -     element_type = ARR_ELEMTYPE(v);
> -     /* Sanity check: do we have a non-zero element type */
> -     if (element_type == 0)
> -         elog(ERROR, "Invalid array element type: %u", element_type);
> -
> -     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
> -
> -     result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);
> -
> -     PG_RETURN_DATUM(result);
> - }
>
>   /*
> !  * actually does the work for singleton_array(), and array_accum() if it is
> !  * given a null input array.
>    */
>   ArrayType *
> ! create_singleton_array(Oid element_type, Datum element, int ndims)
>   {
>       Datum    dvalues[1];
>       int16    typlen;
> --- 286,300 ----
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
>
>
>   /*
> !  * used by text_to_array() in varlena.c
>    */
>   ArrayType *
> ! create_singleton_array(FunctionCallInfo fcinfo,
> !                        Oid element_type,
> !                        Datum element,
> !                        int ndims)
>   {
>       Datum    dvalues[1];
>       int16    typlen;
> ***************
> *** 415,420 ****
> --- 303,309 ----
>       int        dims[MAXDIM];
>       int        lbs[MAXDIM];
>       int        i;
> +     ArrayMetaState *my_extra;
>
>       if (element_type == 0)
>           elog(ERROR, "Invalid array element type: %u", element_type);
> ***************
> *** 429,435 ****
>           lbs[i] = 1;
>       }
>
> !     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
>       return construct_md_array(dvalues, ndims, dims, lbs, element_type,
>                                 typlen, typbyval, typalign);
> --- 318,352 ----
>           lbs[i] = 1;
>       }
>
> !     /*
> !      * We arrange to look up info about element type only once per series
> !      * of calls, assuming the element type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type */
> !         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typalign = my_extra->typalign;
> !     }
>
>       return construct_md_array(dvalues, ndims, dims, lbs, element_type,
>                                 typlen, typbyval, typalign);
> Index: src/backend/utils/adt/arrayfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v
> retrieving revision 1.89
> diff -c -r1.89 arrayfuncs.c
> *** src/backend/utils/adt/arrayfuncs.c    9 May 2003 23:01:45 -0000    1.89
> --- src/backend/utils/adt/arrayfuncs.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 21,28 ****
> --- 21,30 ----
>   #include "catalog/pg_type.h"
>   #include "libpq/pqformat.h"
>   #include "parser/parse_coerce.h"
> + #include "parser/parse_oper.h"
>   #include "utils/array.h"
>   #include "utils/builtins.h"
> + #include "utils/datum.h"
>   #include "utils/memutils.h"
>   #include "utils/lsyscache.h"
>   #include "utils/syscache.h"
> ***************
> *** 70,85 ****
>
>   #define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)
>
> - /* I/O function selector for system_cache_lookup */
> - typedef enum IOFuncSelector
> - {
> -     IOFunc_input,
> -     IOFunc_output,
> -     IOFunc_receive,
> -     IOFunc_send
> - } IOFuncSelector;
> -
> -
>   static int    ArrayCount(char *str, int *dim, char typdelim);
>   static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
>                FmgrInfo *inputproc, Oid typelem, int32 typmod,
> --- 72,77 ----
> ***************
> *** 93,102 ****
>   static void CopyArrayEls(char *p, Datum *values, int nitems,
>                int typlen, bool typbyval, char typalign,
>                bool freedata);
> - static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
> -                                 int *typlen, bool *typbyval,
> -                                 char *typdelim, Oid *typelem,
> -                                 Oid *proc, char *typalign);
>   static Datum ArrayCast(char *value, bool byval, int len);
>   static int ArrayCastAndSet(Datum src,
>                   int typlen, bool typbyval, char typalign,
> --- 85,90 ----
> ***************
> *** 119,125 ****
>                      char *destPtr,
>                      int *st, int *endp, char *srcPtr,
>                      int typlen, bool typbyval, char typalign);
> !
>
>   /*---------------------------------------------------------------------
>    * array_in :
> --- 107,113 ----
>                      char *destPtr,
>                      int *st, int *endp, char *srcPtr,
>                      int typlen, bool typbyval, char typalign);
> ! static int array_cmp(FunctionCallInfo fcinfo);
>
>   /*---------------------------------------------------------------------
>    * array_in :
> ***************
> *** 154,165 ****
>                   dim[MAXDIM],
>                   lBound[MAXDIM];
>       char        typalign;
>
> !     /* Get info about element type, including its input conversion proc */
> !     system_cache_lookup(element_type, IOFunc_input,
> !                         &typlen, &typbyval, &typdelim,
> !                         &typelem, &typinput, &typalign);
> !     fmgr_info(typinput, &inputproc);
>
>       /* Make a modifiable copy of the input */
>       /* XXX why are we allocating an extra 2 bytes here? */
> --- 142,190 ----
>                   dim[MAXDIM],
>                   lBound[MAXDIM];
>       char        typalign;
> +     ArrayMetaState *my_extra;
>
> !     /*
> !      * We arrange to look up info about element type, including its input
> !      * conversion proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its input conversion proc */
> !         get_type_metadata(element_type, IOFunc_input,
> !                             &typlen, &typbyval, &typdelim,
> !                             &typelem, &typinput, &typalign);
> !         fmgr_info(typinput, &inputproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typinput;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = inputproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typinput = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         inputproc = my_extra->proc;
> !     }
>
>       /* Make a modifiable copy of the input */
>       /* XXX why are we allocating an extra 2 bytes here? */
> ***************
> *** 636,647 ****
>                   indx[MAXDIM];
>       int            ndim,
>                  *dim;
>
>       element_type = ARR_ELEMTYPE(v);
> !     system_cache_lookup(element_type, IOFunc_output,
> !                         &typlen, &typbyval, &typdelim,
> !                         &typelem, &typoutput, &typalign);
> !     fmgr_info(typoutput, &outputproc);
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> --- 661,711 ----
>                   indx[MAXDIM];
>       int            ndim,
>                  *dim;
> +     ArrayMetaState *my_extra;
>
>       element_type = ARR_ELEMTYPE(v);
> !
> !     /*
> !      * We arrange to look up info about element type, including its input
> !      * conversion proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its output conversion proc */
> !         get_type_metadata(element_type, IOFunc_output,
> !                             &typlen, &typbyval, &typdelim,
> !                             &typelem, &typoutput, &typalign);
> !         fmgr_info(typoutput, &outputproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typoutput;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = outputproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typoutput = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         outputproc = my_extra->proc;
> !     }
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> ***************
> *** 800,805 ****
> --- 864,870 ----
>                   dim[MAXDIM],
>                   lBound[MAXDIM];
>       char        typalign;
> +     ArrayMetaState *my_extra;
>
>       /* Get the array header information */
>       ndim = pq_getmsgint(buf, 4);
> ***************
> *** 831,844 ****
>           PG_RETURN_ARRAYTYPE_P(retval);
>       }
>
> !     /* Get info about element type, including its receive conversion proc */
> !     system_cache_lookup(element_type, IOFunc_receive,
> !                         &typlen, &typbyval, &typdelim,
> !                         &typelem, &typreceive, &typalign);
> !     if (!OidIsValid(typreceive))
> !         elog(ERROR, "No binary input function available for type %s",
> !              format_type_be(element_type));
> !     fmgr_info(typreceive, &receiveproc);
>
>       dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
>                                 typlen, typbyval, typalign,
> --- 896,945 ----
>           PG_RETURN_ARRAYTYPE_P(retval);
>       }
>
> !     /*
> !      * We arrange to look up info about element type, including its receive
> !      * conversion proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its receive conversion proc */
> !         get_type_metadata(element_type, IOFunc_receive,
> !                             &typlen, &typbyval, &typdelim,
> !                             &typelem, &typreceive, &typalign);
> !         if (!OidIsValid(typreceive))
> !             elog(ERROR, "No binary input function available for type %s",
> !                  format_type_be(element_type));
> !         fmgr_info(typreceive, &receiveproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typreceive;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = receiveproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typreceive = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         receiveproc = my_extra->proc;
> !     }
>
>       dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
>                                 typlen, typbyval, typalign,
> ***************
> *** 976,990 ****
>       int            ndim,
>                  *dim;
>       StringInfoData buf;
>
>       /* Get information about the element type and the array dimensions */
>       element_type = ARR_ELEMTYPE(v);
> !     system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
> !                         &typdelim, &typelem, &typsend, &typalign);
> !     if (!OidIsValid(typsend))
> !         elog(ERROR, "No binary output function available for type %s",
> !              format_type_be(element_type));
> !     fmgr_info(typsend, &sendproc);
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> --- 1077,1130 ----
>       int            ndim,
>                  *dim;
>       StringInfoData buf;
> +     ArrayMetaState *my_extra;
>
>       /* Get information about the element type and the array dimensions */
>       element_type = ARR_ELEMTYPE(v);
> !
> !     /*
> !      * We arrange to look up info about element type, including its send
> !      * proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its send proc */
> !         get_type_metadata(element_type, IOFunc_send, &typlen, &typbyval,
> !                             &typdelim, &typelem, &typsend, &typalign);
> !         if (!OidIsValid(typsend))
> !             elog(ERROR, "No binary output function available for type %s",
> !                  format_type_be(element_type));
> !         fmgr_info(typsend, &sendproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typsend;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = sendproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typsend = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         sendproc = my_extra->proc;
> !     }
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> ***************
> *** 1476,1481 ****
> --- 1616,1641 ----
>       array = DatumGetArrayTypeP(PointerGetDatum(array));
>
>       ndim = ARR_NDIM(array);
> +
> +     /*
> +      * if number of dims is zero, i.e. an empty array, create an array
> +      * with nSubscripts dimensions, and set the lower bounds to the supplied
> +      * subscripts
> +      */
> +     if (ndim == 0)
> +     {
> +         Oid        elmtype = ARR_ELEMTYPE(array);
> +
> +         for (i = 0; i < nSubscripts; i++)
> +         {
> +             dim[i] = 1;
> +             lb[i] = indx[i];
> +         }
> +
> +         return construct_md_array(&dataValue, nSubscripts, dim, lb, elmtype,
> +                                                 elmlen, elmbyval, elmalign);
> +     }
> +
>       if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
>           elog(ERROR, "Invalid array subscripts");
>
> ***************
> *** 1632,1637 ****
> --- 1792,1822 ----
>       /* note: we assume srcArray contains no toasted elements */
>
>       ndim = ARR_NDIM(array);
> +
> +     /*
> +      * if number of dims is zero, i.e. an empty array, create an array
> +      * with nSubscripts dimensions, and set the upper and lower bounds
> +      * to the supplied subscripts
> +      */
> +     if (ndim == 0)
> +     {
> +         Datum  *dvalues;
> +         int        nelems;
> +         Oid        elmtype = ARR_ELEMTYPE(array);
> +
> +         deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
> +                                                         &dvalues, &nelems);
> +
> +         for (i = 0; i < nSubscripts; i++)
> +         {
> +             dim[i] = 1 + upperIndx[i] - lowerIndx[i];
> +             lb[i] = lowerIndx[i];
> +         }
> +
> +         return construct_md_array(dvalues, nSubscripts, dim, lb, elmtype,
> +                                                  elmlen, elmbyval, elmalign);
> +     }
> +
>       if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
>           elog(ERROR, "Invalid array subscripts");
>
> ***************
> *** 1811,1816 ****
> --- 1996,2008 ----
>       Oid            typelem;
>       Oid            proc;
>       char       *s;
> +     typedef struct {
> +         ArrayMetaState *inp_extra;
> +         ArrayMetaState *ret_extra;
> +     } am_extra;
> +     am_extra  *my_extra;
> +     ArrayMetaState *inp_extra;
> +     ArrayMetaState *ret_extra;
>
>       /* Get input array */
>       if (fcinfo->nargs < 1)
> ***************
> *** 1829,1839 ****
>       if (nitems <= 0)
>           PG_RETURN_ARRAYTYPE_P(v);
>
> !     /* Lookup source and result types. Unneeded variables are reused. */
> !     system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
> !                         &typdelim, &typelem, &proc, &inp_typalign);
> !     system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
> !                         &typdelim, &typelem, &proc, &typalign);
>
>       /* Allocate temporary array for new values */
>       values = (Datum *) palloc(nitems * sizeof(Datum));
> --- 2021,2101 ----
>       if (nitems <= 0)
>           PG_RETURN_ARRAYTYPE_P(v);
>
> !     /*
> !      * We arrange to look up info about input and return element types only
> !      * once per series of calls, assuming the element type doesn't change
> !      * underneath us.
> !      */
> !     my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(am_extra));
> !         my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
> !
> !         my_extra->inp_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         inp_extra = my_extra->inp_extra;
> !         inp_extra->element_type = InvalidOid;
> !
> !         my_extra->ret_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         ret_extra = my_extra->ret_extra;
> !         ret_extra->element_type = InvalidOid;
> !     }
> !     else
> !     {
> !         inp_extra = my_extra->inp_extra;
> !         ret_extra = my_extra->ret_extra;
> !     }
> !
> !     if (inp_extra->element_type != inpType)
> !     {
> !         /* Lookup source and result types. Unneeded variables are reused. */
> !         get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
> !                             &typdelim, &typelem, &proc, &inp_typalign);
> !
> !         inp_extra->element_type = inpType;
> !         inp_extra->typlen = inp_typlen;
> !         inp_extra->typbyval = inp_typbyval;
> !         inp_extra->typdelim = typdelim;
> !         inp_extra->typelem = typelem;
> !         inp_extra->typiofunc = proc;
> !         inp_extra->typalign = inp_typalign;
> !     }
> !     else
> !     {
> !         inp_typlen = inp_extra->typlen;
> !         inp_typbyval = inp_extra->typbyval;
> !         typdelim = inp_extra->typdelim;
> !         typelem = inp_extra->typelem;
> !         proc = inp_extra->typiofunc;
> !         inp_typalign = inp_extra->typalign;
> !     }
> !
> !     if (ret_extra->element_type != retType)
> !     {
> !         /* Lookup source and result types. Unneeded variables are reused. */
> !         get_type_metadata(retType, IOFunc_input, &typlen, &typbyval,
> !                             &typdelim, &typelem, &proc, &typalign);
> !
> !         ret_extra->element_type = retType;
> !         ret_extra->typlen = typlen;
> !         ret_extra->typbyval = typbyval;
> !         ret_extra->typdelim = typdelim;
> !         ret_extra->typelem = typelem;
> !         ret_extra->typiofunc = proc;
> !         ret_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = ret_extra->typlen;
> !         typbyval = ret_extra->typbyval;
> !         typdelim = ret_extra->typdelim;
> !         typelem = ret_extra->typelem;
> !         proc = ret_extra->typiofunc;
> !         typalign = ret_extra->typalign;
> !     }
>
>       /* Allocate temporary array for new values */
>       values = (Datum *) palloc(nitems * sizeof(Datum));
> ***************
> *** 2049,2056 ****
>    *          compares two arrays for equality
>    * result :
>    *          returns true if the arrays are equal, false otherwise.
> -  *
> -  * XXX bitwise equality is pretty bogus ...
>    *-----------------------------------------------------------------------------
>    */
>   Datum
> --- 2311,2316 ----
> ***************
> *** 2058,2069 ****
>   {
>       ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
>       ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
>       bool        result = true;
>
> !     if (ARR_SIZE(array1) != ARR_SIZE(array2))
> !         result = false;
> !     else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
>           result = false;
>
>       /* Avoid leaking memory when handed toasted input. */
>       PG_FREE_IF_COPY(array1, 0);
> --- 2318,2435 ----
>   {
>       ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
>       ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
> +     char       *p1 = (char *) ARR_DATA_PTR(array1);
> +     char       *p2 = (char *) ARR_DATA_PTR(array2);
> +     int            ndims1 = ARR_NDIM(array1);
> +     int            ndims2 = ARR_NDIM(array2);
> +     int           *dims1 = ARR_DIMS(array1);
> +     int           *dims2 = ARR_DIMS(array2);
> +     int            nitems1 = ArrayGetNItems(ndims1, dims1);
> +     int            nitems2 = ArrayGetNItems(ndims2, dims2);
> +     Oid            element_type = ARR_ELEMTYPE(array1);
> +     FmgrInfo   *ae_fmgr_info = fcinfo->flinfo;
>       bool        result = true;
> +     int            typlen;
> +     bool        typbyval;
> +     char        typdelim;
> +     Oid            typelem;
> +     char        typalign;
> +     Oid            typiofunc;
> +     int            i;
> +     ArrayMetaState *my_extra;
> +     FunctionCallInfoData locfcinfo;
>
> !     /* fast path if the arrays do not have the same number of elements */
> !     if (nitems1 != nitems2)
>           result = false;
> +     else
> +     {
> +         /*
> +          * We arrange to look up the equality function only once per series of
> +          * calls, assuming the element type doesn't change underneath us.
> +          */
> +         my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
> +         if (my_extra == NULL)
> +         {
> +             ae_fmgr_info->fn_extra = MemoryContextAlloc(ae_fmgr_info->fn_mcxt,
> +                                                      sizeof(ArrayMetaState));
> +             my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
> +             my_extra->element_type = InvalidOid;
> +         }
> +
> +         if (my_extra->element_type != element_type)
> +         {
> +             Oid        opfuncid = equality_oper_funcid(element_type);
> +
> +             if (OidIsValid(opfuncid))
> +                 fmgr_info_cxt(opfuncid, &my_extra->proc, ae_fmgr_info->fn_mcxt);
> +             else
> +                 elog(ERROR,
> +                      "array_eq: cannot find equality operator for type: %u",
> +                      element_type);
> +
> +             get_type_metadata(element_type, IOFunc_output,
> +                               &typlen, &typbyval, &typdelim,
> +                               &typelem, &typiofunc, &typalign);
> +
> +             my_extra->element_type = element_type;
> +             my_extra->typlen = typlen;
> +             my_extra->typbyval = typbyval;
> +             my_extra->typdelim = typdelim;
> +             my_extra->typelem = typelem;
> +             my_extra->typiofunc = typiofunc;
> +             my_extra->typalign = typalign;
> +         }
> +         else
> +         {
> +             typlen = my_extra->typlen;
> +             typbyval = my_extra->typbyval;
> +             typdelim = my_extra->typdelim;
> +             typelem = my_extra->typelem;
> +             typiofunc = my_extra->typiofunc;
> +             typalign = my_extra->typalign;
> +         }
> +
> +         /*
> +          * apply the operator to each pair of array elements.
> +          */
> +         MemSet(&locfcinfo, 0, sizeof(locfcinfo));
> +         locfcinfo.flinfo = &my_extra->proc;
> +         locfcinfo.nargs = 2;
> +
> +         /* Loop over source data */
> +         for (i = 0; i < nitems1; i++)
> +         {
> +             Datum    elt1;
> +             Datum    elt2;
> +             bool    oprresult;
> +
> +             /* Get element pair */
> +             elt1 = fetch_att(p1, typbyval, typlen);
> +             elt2 = fetch_att(p2, typbyval, typlen);
> +
> +             p1 = att_addlength(p1, typlen, PointerGetDatum(p1));
> +             p1 = (char *) att_align(p1, typalign);
> +
> +             p2 = att_addlength(p2, typlen, PointerGetDatum(p2));
> +             p2 = (char *) att_align(p2, typalign);
> +
> +             /*
> +              * Apply the operator to the element pair
> +              */
> +             locfcinfo.arg[0] = elt1;
> +             locfcinfo.arg[1] = elt2;
> +             locfcinfo.argnull[0] = false;
> +             locfcinfo.argnull[1] = false;
> +             locfcinfo.isnull = false;
> +             oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
> +             if (!oprresult)
> +             {
> +                 result = false;
> +                 break;
> +             }
> +         }
> +     }
>
>       /* Avoid leaking memory when handed toasted input. */
>       PG_FREE_IF_COPY(array1, 0);
> ***************
> *** 2073,2125 ****
>   }
>
>
> ! /***************************************************************************/
> ! /******************|          Support  Routines              |*****************/
> ! /***************************************************************************/
>
> ! static void
> ! system_cache_lookup(Oid element_type,
> !                     IOFuncSelector which_func,
> !                     int *typlen,
> !                     bool *typbyval,
> !                     char *typdelim,
> !                     Oid *typelem,
> !                     Oid *proc,
> !                     char *typalign)
> ! {
> !     HeapTuple    typeTuple;
> !     Form_pg_type typeStruct;
> !
> !     typeTuple = SearchSysCache(TYPEOID,
> !                                ObjectIdGetDatum(element_type),
> !                                0, 0, 0);
> !     if (!HeapTupleIsValid(typeTuple))
> !         elog(ERROR, "cache lookup failed for type %u", element_type);
> !     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
> !
> !     *typlen = typeStruct->typlen;
> !     *typbyval = typeStruct->typbyval;
> !     *typdelim = typeStruct->typdelim;
> !     *typelem = typeStruct->typelem;
> !     *typalign = typeStruct->typalign;
> !     switch (which_func)
> !     {
> !         case IOFunc_input:
> !             *proc = typeStruct->typinput;
> !             break;
> !         case IOFunc_output:
> !             *proc = typeStruct->typoutput;
> !             break;
> !         case IOFunc_receive:
> !             *proc = typeStruct->typreceive;
> !             break;
> !         case IOFunc_send:
> !             *proc = typeStruct->typsend;
> !             break;
>       }
> !     ReleaseSysCache(typeTuple);
>   }
>
>   /*
>    * Fetch array element at pointer, converted correctly to a Datum
>    */
> --- 2439,2628 ----
>   }
>
>
> ! /*-----------------------------------------------------------------------------
> !  * array-array bool operators:
> !  *        Given two arrays, iterate comparison operators
> !  *        over the array. Uses logic similar to text comparison
> !  *        functions, except element-by-element instead of
> !  *        character-by-character.
> !  *----------------------------------------------------------------------------
> !  */
> ! Datum
> ! array_ne(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
> ! }
>
> ! Datum
> ! array_lt(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
> ! }
> !
> ! Datum
> ! array_gt(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
> ! }
> !
> ! Datum
> ! array_le(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
> ! }
> !
> ! Datum
> ! array_ge(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
> ! }
> !
> ! Datum
> ! btarraycmp(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_INT32(array_cmp(fcinfo));
> ! }
> !
> ! /*
> !  * array_cmp()
> !  * Internal comparison function for arrays.
> !  *
> !  * Returns -1, 0 or 1
> !  */
> ! static int
> ! array_cmp(FunctionCallInfo fcinfo)
> ! {
> !     ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
> !     ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
> !     FmgrInfo   *ac_fmgr_info = fcinfo->flinfo;
> !     Datum        opresult;
> !     int            result = 0;
> !     Oid            element_type = InvalidOid;
> !     int            typlen;
> !     bool        typbyval;
> !     char        typdelim;
> !     Oid            typelem;
> !     char        typalign;
> !     Oid            typiofunc;
> !     Datum       *dvalues1;
> !     int            nelems1;
> !     Datum       *dvalues2;
> !     int            nelems2;
> !     int            min_nelems;
> !     int            i;
> !     typedef struct
> !     {
> !         Oid                element_type;
> !         int                typlen;
> !         bool            typbyval;
> !         char            typdelim;
> !         Oid                typelem;
> !         Oid                typiofunc;
> !         char            typalign;
> !         FmgrInfo        eqproc;
> !         FmgrInfo        ordproc;
> !     } ac_extra;
> !     ac_extra *my_extra;
> !
> !     element_type = ARR_ELEMTYPE(array1);
> !
> !     /*
> !      * We arrange to look up the element type operator function only once
> !      * per series of calls, assuming the element type and opname don't
> !      * change underneath us.
> !      */
> !     my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         ac_fmgr_info->fn_extra = MemoryContextAlloc(ac_fmgr_info->fn_mcxt,
> !                                                          sizeof(ac_extra));
> !         my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         Oid        eqfuncid = equality_oper_funcid(element_type);
> !         Oid        ordfuncid = ordering_oper_funcid(element_type);
> !
> !         fmgr_info_cxt(eqfuncid, &my_extra->eqproc, ac_fmgr_info->fn_mcxt);
> !         fmgr_info_cxt(ordfuncid, &my_extra->ordproc, ac_fmgr_info->fn_mcxt);
> !
> !         if (my_extra->eqproc.fn_nargs != 2)
> !             elog(ERROR, "Equality operator does not take 2 arguments: %u",
> !                                                                  eqfuncid);
> !         if (my_extra->ordproc.fn_nargs != 2)
> !             elog(ERROR, "Ordering operator does not take 2 arguments: %u",
> !                                                                  ordfuncid);
> !
> !         get_type_metadata(element_type, IOFunc_output,
> !                           &typlen, &typbyval, &typdelim,
> !                           &typelem, &typiofunc, &typalign);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = InvalidOid;
> !         my_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typalign = my_extra->typalign;
> !     }
> !
> !     /* extract a C array of arg array datums */
> !     deconstruct_array(array1, element_type, typlen, typbyval, typalign,
> !                                                     &dvalues1, &nelems1);
> !
> !     deconstruct_array(array2, element_type, typlen, typbyval, typalign,
> !                                                     &dvalues2, &nelems2);
> !
> !     min_nelems = Min(nelems1, nelems2);
> !     for (i = 0; i < min_nelems; i++)
> !     {
> !         /* are they equal */
> !         opresult = FunctionCall2(&my_extra->eqproc,
> !                                  dvalues1[i], dvalues2[i]);
> !
> !         if (!DatumGetBool(opresult))
> !         {
> !             /* nope, see if arg1 is less than arg2 */
> !             opresult = FunctionCall2(&my_extra->ordproc,
> !                                      dvalues1[i], dvalues2[i]);
> !             if (DatumGetBool(opresult))
> !             {
> !                 /* arg1 is less than arg2 */
> !                 result = -1;
> !                 break;
> !             }
> !             else
> !             {
> !                 /* arg1 is greater than arg2 */
> !                 result = 1;
> !                 break;
> !             }
> !         }
>       }
> !
> !     if ((result == 0) && (nelems1 != nelems2))
> !         result = (nelems1 < nelems2) ? -1 : 1;
> !
> !     /* Avoid leaking memory when handed toasted input. */
> !     PG_FREE_IF_COPY(array1, 0);
> !     PG_FREE_IF_COPY(array2, 1);
> !
> !     return result;
>   }
>
> +
> + /***************************************************************************/
> + /******************|          Support  Routines              |*****************/
> + /***************************************************************************/
> +
>   /*
>    * Fetch array element at pointer, converted correctly to a Datum
>    */
> ***************
> *** 2423,2428 ****
> --- 2926,2943 ----
>           if (tgt_elem_type == InvalidOid)
>               elog(ERROR, "Target type is not an array");
>
> +         /*
> +          * We don't deal with domain constraints yet, so bail out.
> +          * This isn't currently a problem, because we also don't
> +          * support arrays of domain type elements either. But in the
> +          * future we might. At that point consideration should be given
> +          * to removing the check below and adding a domain constraints
> +          * check to the coercion.
> +          */
> +         if (getBaseType(tgt_elem_type) != tgt_elem_type)
> +             elog(ERROR, "array coercion to domain type elements not " \
> +                         "currently supported");
> +
>           if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
>                                      COERCION_EXPLICIT, &funcId))
>           {
> ***************
> *** 2439,2448 ****
>       }
>
>       /*
> !      * If it's binary-compatible, return the array unmodified.
>        */
>       if (my_extra->coerce_finfo.fn_oid == InvalidOid)
> !         PG_RETURN_ARRAYTYPE_P(src);
>
>       /*
>        * Use array_map to apply the function to each array element.
> --- 2954,2969 ----
>       }
>
>       /*
> !      * If it's binary-compatible, modify the element type in the array header,
> !      * but otherwise leave the array as we received it.
>        */
>       if (my_extra->coerce_finfo.fn_oid == InvalidOid)
> !     {
> !         ArrayType  *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
> !
> !         ARR_ELEMTYPE(result) = my_extra->desttype;
> !         PG_RETURN_ARRAYTYPE_P(result);
> !     }
>
>       /*
>        * Use array_map to apply the function to each array element.
> ***************
> *** 2453,2456 ****
> --- 2974,3092 ----
>       locfcinfo.arg[0] = PointerGetDatum(src);
>
>       return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
> + }
> +
> + /*
> +  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
> +  *
> +  *    astate is working state (NULL on first call)
> +  *    rcontext is where to keep working state
> +  */
> + ArrayBuildState *
> + accumArrayResult(ArrayBuildState *astate,
> +                  Datum dvalue, bool disnull,
> +                  Oid element_type,
> +                  MemoryContext rcontext)
> + {
> +     MemoryContext arr_context,
> +                   oldcontext;
> +
> +     if (astate == NULL)
> +     {
> +         /* First time through --- initialize */
> +
> +         /* Make a temporary context to hold all the junk */
> +         arr_context = AllocSetContextCreate(rcontext,
> +                                             "accumArrayResult",
> +                                             ALLOCSET_DEFAULT_MINSIZE,
> +                                             ALLOCSET_DEFAULT_INITSIZE,
> +                                             ALLOCSET_DEFAULT_MAXSIZE);
> +         oldcontext = MemoryContextSwitchTo(arr_context);
> +         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
> +         astate->mcontext = arr_context;
> +         astate->dvalues = (Datum *)
> +             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
> +         astate->nelems = 0;
> +         astate->element_type = element_type;
> +         get_typlenbyvalalign(element_type,
> +                              &astate->typlen,
> +                              &astate->typbyval,
> +                              &astate->typalign);
> +     }
> +     else
> +     {
> +         oldcontext = MemoryContextSwitchTo(astate->mcontext);
> +         Assert(astate->element_type == element_type);
> +         /* enlarge dvalues[] if needed */
> +         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
> +             astate->dvalues = (Datum *)
> +                 repalloc(astate->dvalues,
> +                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
> +     }
> +
> +     if (disnull)
> +         elog(ERROR, "NULL elements not allowed in Arrays");
> +
> +     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
> +     astate->dvalues[astate->nelems++] =
> +         datumCopy(dvalue, astate->typbyval, astate->typlen);
> +
> +     MemoryContextSwitchTo(oldcontext);
> +
> +     return astate;
> + }
> +
> + /*
> +  * makeArrayResult - produce final result of accumArrayResult
> +  *
> +  *    astate is working state (not NULL)
> +  *    rcontext is where to construct result
> +  */
> + Datum
> + makeArrayResult(ArrayBuildState *astate,
> +                 MemoryContext rcontext)
> + {
> +     int            dims[1];
> +     int            lbs[1];
> +
> +     dims[0] = astate->nelems;
> +     lbs[0] = 1;
> +
> +     return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
> + }
> +
> + /*
> +  * makeMdArrayResult - produce md final result of accumArrayResult
> +  *
> +  *    astate is working state (not NULL)
> +  *    rcontext is where to construct result
> +  */
> + Datum
> + makeMdArrayResult(ArrayBuildState *astate,
> +                 int ndims,
> +                 int *dims,
> +                 int *lbs,
> +                 MemoryContext rcontext)
> + {
> +     ArrayType  *result;
> +     MemoryContext oldcontext;
> +
> +     /* Build the final array result in rcontext */
> +     oldcontext = MemoryContextSwitchTo(rcontext);
> +
> +     result = construct_md_array(astate->dvalues,
> +                                 ndims,
> +                                 dims,
> +                                 lbs,
> +                                 astate->element_type,
> +                                 astate->typlen,
> +                                 astate->typbyval,
> +                                 astate->typalign);
> +
> +     MemoryContextSwitchTo(oldcontext);
> +
> +     /* Clean up all the junk */
> +     MemoryContextDelete(astate->mcontext);
> +
> +     return PointerGetDatum(result);
>   }
> Index: src/backend/utils/adt/varlena.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/varlena.c,v
> retrieving revision 1.98
> diff -c -r1.98 varlena.c
> *** src/backend/utils/adt/varlena.c    15 May 2003 15:50:19 -0000    1.98
> --- src/backend/utils/adt/varlena.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 19,29 ****
> --- 19,32 ----
>   #include "mb/pg_wchar.h"
>   #include "miscadmin.h"
>   #include "access/tuptoaster.h"
> + #include "catalog/pg_type.h"
>   #include "lib/stringinfo.h"
>   #include "libpq/crypt.h"
>   #include "libpq/pqformat.h"
> + #include "utils/array.h"
>   #include "utils/builtins.h"
>   #include "utils/pg_locale.h"
> + #include "utils/lsyscache.h"
>
>
>   typedef struct varlena unknown;
> ***************
> *** 1983,1990 ****
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else
> ! /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>
> --- 1986,1992 ----
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else                    /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>
> ***************
> *** 2004,2011 ****
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else
> ! /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>       else if ((start_posn != 0) && (end_posn == 0))
> --- 2006,2012 ----
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else                    /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>       else if ((start_posn != 0) && (end_posn == 0))
> ***************
> *** 2026,2031 ****
> --- 2027,2217 ----
>           result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn -
fldsep_len,false); 
>           PG_RETURN_TEXT_P(result_text);
>       }
> + }
> +
> + /*
> +  * text_to_array
> +  * parse input string
> +  * return text array of elements
> +  * based on provided field separator
> +  */
> + Datum
> + text_to_array(PG_FUNCTION_ARGS)
> + {
> +     text       *inputstring = PG_GETARG_TEXT_P(0);
> +     int            inputstring_len = TEXTLEN(inputstring);
> +     text       *fldsep = PG_GETARG_TEXT_P(1);
> +     int            fldsep_len = TEXTLEN(fldsep);
> +     int            fldnum;
> +     int            start_posn = 0;
> +     int            end_posn = 0;
> +     text       *result_text = NULL;
> +     ArrayBuildState *astate = NULL;
> +     MemoryContext oldcontext = CurrentMemoryContext;
> +
> +     /* return NULL for empty input string */
> +     if (inputstring_len < 1)
> +         PG_RETURN_NULL();
> +
> +     /* empty field separator
> +      * return one element, 1D, array using the input string */
> +     if (fldsep_len < 1)
> +         PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
> +                               CStringGetDatum(inputstring), 1));
> +
> +     /* start with end position holding the initial start position */
> +     end_posn = 0;
> +     for (fldnum=1;;fldnum++)    /* field number is 1 based */
> +     {
> +         Datum    dvalue;
> +         bool    disnull = false;
> +
> +         start_posn = end_posn;
> +         end_posn = text_position(PointerGetDatum(inputstring),
> +                                  PointerGetDatum(fldsep),
> +                                  fldnum);
> +
> +         if ((start_posn == 0) && (end_posn == 0))    /* fldsep not found */
> +         {
> +             if (fldnum == 1)
> +             {
> +                 /* first element
> +                  * return one element, 1D, array using the input string */
> +                 PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
> +                                       CStringGetDatum(inputstring), 1));
> +             }
> +             else
> +             {
> +                 /* otherwise create array and exit */
> +                 PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
> +             }
> +         }
> +         else if ((start_posn != 0) && (end_posn == 0))
> +         {
> +             /* last field requested */
> +             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
> +         }
> +         else if ((start_posn == 0) && (end_posn != 0))
> +         {
> +             /* first field requested */
> +             result_text = LEFT(inputstring, fldsep);
> +         }
> +         else
> +         {
> +             /* prior to last field requested */
> +             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn -
start_posn- fldsep_len, false); 
> +         }
> +
> +         /* stash away current value */
> +         dvalue = PointerGetDatum(result_text);
> +         astate = accumArrayResult(astate, dvalue,
> +                                   disnull, TEXTOID, oldcontext);
> +
> +     }
> +
> +     /* never reached -- keep compiler quiet */
> +     PG_RETURN_NULL();
> + }
> +
> + /*
> +  * array_to_text
> +  * concatenate Cstring representation of input array elements
> +  * using provided field separator
> +  */
> + Datum
> + array_to_text(PG_FUNCTION_ARGS)
> + {
> +     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
> +     char       *fldsep = PG_TEXTARG_GET_STR(1);
> +     int            nitems, *dims, ndims;
> +     char       *p;
> +     Oid            element_type;
> +     int            typlen;
> +     bool        typbyval;
> +     char        typdelim;
> +     Oid            typoutput,
> +                 typelem;
> +     FmgrInfo    outputproc;
> +     char        typalign;
> +     StringInfo    result_str = makeStringInfo();
> +     int            i;
> +     ArrayMetaState *my_extra;
> +
> +     p = ARR_DATA_PTR(v);
> +     ndims = ARR_NDIM(v);
> +     dims = ARR_DIMS(v);
> +     nitems = ArrayGetNItems(ndims, dims);
> +
> +     /* if there are no elements, return an empty string */
> +     if (nitems == 0)
> +         PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
> +
> +     element_type = ARR_ELEMTYPE(v);
> +
> +     /*
> +      * We arrange to look up info about element type, including its output
> +      * conversion proc only once per series of calls, assuming the element
> +      * type doesn't change underneath us.
> +      */
> +     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> +     if (my_extra == NULL)
> +     {
> +         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> +                                                      sizeof(ArrayMetaState));
> +         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> +         my_extra->element_type = InvalidOid;
> +     }
> +
> +     if (my_extra->element_type != element_type)
> +     {
> +         /* Get info about element type, including its output conversion proc */
> +         get_type_metadata(element_type, IOFunc_output,
> +                             &typlen, &typbyval, &typdelim,
> +                             &typelem, &typoutput, &typalign);
> +         fmgr_info(typoutput, &outputproc);
> +
> +         my_extra->element_type = element_type;
> +         my_extra->typlen = typlen;
> +         my_extra->typbyval = typbyval;
> +         my_extra->typdelim = typdelim;
> +         my_extra->typelem = typelem;
> +         my_extra->typiofunc = typoutput;
> +         my_extra->typalign = typalign;
> +         my_extra->proc = outputproc;
> +     }
> +     else
> +     {
> +         typlen = my_extra->typlen;
> +         typbyval = my_extra->typbyval;
> +         typdelim = my_extra->typdelim;
> +         typelem = my_extra->typelem;
> +         typoutput = my_extra->typiofunc;
> +         typalign = my_extra->typalign;
> +         outputproc = my_extra->proc;
> +     }
> +
> +     for (i = 0; i < nitems; i++)
> +     {
> +         Datum        itemvalue;
> +         char       *value;
> +
> +         itemvalue = fetch_att(p, typbyval, typlen);
> +
> +         value = DatumGetCString(FunctionCall3(&outputproc,
> +                                               itemvalue,
> +                                               ObjectIdGetDatum(typelem),
> +                                               Int32GetDatum(-1)));
> +
> +         if (i > 0)
> +             appendStringInfo(result_str, "%s%s", fldsep, value);
> +         else
> +             appendStringInfo(result_str, "%s", value);
> +
> +         p = att_addlength(p, typlen, PointerGetDatum(p));
> +         p = (char *) att_align(p, typalign);
> +     }
> +
> +     PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
>   }
>
>   #define HEXBASE 16
> Index: src/backend/utils/cache/lsyscache.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v
> retrieving revision 1.95
> diff -c -r1.95 lsyscache.c
> *** src/backend/utils/cache/lsyscache.c    26 May 2003 00:11:27 -0000    1.95
> --- src/backend/utils/cache/lsyscache.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 625,630 ****
> --- 625,664 ----
>   }
>
>   /*
> +  * get_func_argtypes
> +  *        Given procedure id, return the function's argument types.
> +  *        Also pass back the number of arguments.
> +  */
> + Oid *
> + get_func_argtypes(Oid funcid, int *nargs)
> + {
> +     HeapTuple        tp;
> +     Form_pg_proc    procstruct;
> +     Oid               *result = NULL;
> +     int                i;
> +
> +     tp = SearchSysCache(PROCOID,
> +                         ObjectIdGetDatum(funcid),
> +                         0, 0, 0);
> +     if (!HeapTupleIsValid(tp))
> +         elog(ERROR, "Function OID %u does not exist", funcid);
> +
> +     procstruct = (Form_pg_proc) GETSTRUCT(tp);
> +     *nargs = (int) procstruct->pronargs;
> +
> +     if (*nargs > 0)
> +     {
> +         result = (Oid *) palloc(*nargs * sizeof(Oid));
> +
> +         for (i = 0; i < *nargs; i++)
> +             result[i] = procstruct->proargtypes[i];
> +     }
> +
> +     ReleaseSysCache(tp);
> +     return result;
> + }
> +
> + /*
>    * get_func_retset
>    *        Given procedure id, return the function's proretset flag.
>    */
> ***************
> *** 994,999 ****
> --- 1028,1083 ----
>       *typbyval = typtup->typbyval;
>       *typalign = typtup->typalign;
>       ReleaseSysCache(tp);
> + }
> +
> + /*
> +  * get_type_metadata
> +  *
> +  *        A six-fer:    given the type OID, return typlen, typbyval, typalign,
> +  *                    typdelim, typelem, IO function Oid. The IO function
> +  *                    returned is controlled by IOFuncSelector
> +  */
> + void
> + get_type_metadata(Oid element_type,
> +                     IOFuncSelector which_func,
> +                     int *typlen,
> +                     bool *typbyval,
> +                     char *typdelim,
> +                     Oid *typelem,
> +                     Oid *proc,
> +                     char *typalign)
> + {
> +     HeapTuple    typeTuple;
> +     Form_pg_type typeStruct;
> +
> +     typeTuple = SearchSysCache(TYPEOID,
> +                                ObjectIdGetDatum(element_type),
> +                                0, 0, 0);
> +     if (!HeapTupleIsValid(typeTuple))
> +         elog(ERROR, "cache lookup failed for type %u", element_type);
> +     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
> +
> +     *typlen = typeStruct->typlen;
> +     *typbyval = typeStruct->typbyval;
> +     *typdelim = typeStruct->typdelim;
> +     *typelem = typeStruct->typelem;
> +     *typalign = typeStruct->typalign;
> +     switch (which_func)
> +     {
> +         case IOFunc_input:
> +             *proc = typeStruct->typinput;
> +             break;
> +         case IOFunc_output:
> +             *proc = typeStruct->typoutput;
> +             break;
> +         case IOFunc_receive:
> +             *proc = typeStruct->typreceive;
> +             break;
> +         case IOFunc_send:
> +             *proc = typeStruct->typsend;
> +             break;
> +     }
> +     ReleaseSysCache(typeTuple);
>   }
>
>   #ifdef NOT_USED
> Index: src/backend/utils/fmgr/fmgr.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/fmgr/fmgr.c,v
> retrieving revision 1.68
> diff -c -r1.68 fmgr.c
> *** src/backend/utils/fmgr/fmgr.c    8 Apr 2003 23:20:02 -0000    1.68
> --- src/backend/utils/fmgr/fmgr.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 1673,1675 ****
> --- 1673,1701 ----
>
>       return exprType((Node *) nth(argnum, args));
>   }
> +
> + /*
> +  * Get the OID of the function or operator
> +  *
> +  * Returns InvalidOid if information is not available
> +  */
> + Oid
> + get_fn_expr_functype(FunctionCallInfo fcinfo)
> + {
> +     Node   *expr;
> +
> +     /*
> +      * can't return anything useful if we have no FmgrInfo or if
> +      * its fn_expr node has not been initialized
> +      */
> +     if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
> +         return InvalidOid;
> +
> +     expr = fcinfo->flinfo->fn_expr;
> +     if (IsA(expr, FuncExpr))
> +         return ((FuncExpr *) expr)->funcid;
> +     else if (IsA(expr, OpExpr))
> +         return ((OpExpr *) expr)->opno;
> +     else
> +         return InvalidOid;
> + }
> Index: src/include/fmgr.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/fmgr.h,v
> retrieving revision 1.27
> diff -c -r1.27 fmgr.h
> *** src/include/fmgr.h    8 Apr 2003 23:20:04 -0000    1.27
> --- src/include/fmgr.h    16 Jun 2003 23:45:51 -0000
> ***************
> *** 18,23 ****
> --- 18,24 ----
>   #ifndef FMGR_H
>   #define FMGR_H
>
> + #include "nodes/nodes.h"
>
>   /*
>    * All functions that can be called directly by fmgr must have this signature.
> ***************
> *** 372,385 ****
>                    Datum arg6, Datum arg7, Datum arg8,
>                    Datum arg9);
>
> -
>   /*
>    * Routines in fmgr.c
>    */
>   extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
> ! extern Oid    fmgr_internal_function(const char *proname);
> ! extern Oid    get_fn_expr_rettype(FunctionCallInfo fcinfo);
> ! extern Oid    get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
>
>   /*
>    * Routines in dfmgr.c
> --- 373,386 ----
>                    Datum arg6, Datum arg7, Datum arg8,
>                    Datum arg9);
>
>   /*
>    * Routines in fmgr.c
>    */
>   extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
> ! extern Oid fmgr_internal_function(const char *proname);
> ! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo);
> ! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
> ! extern Oid get_fn_expr_functype(FunctionCallInfo fcinfo);
>
>   /*
>    * Routines in dfmgr.c
> Index: src/include/catalog/pg_amop.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amop.h,v
> retrieving revision 1.49
> diff -c -r1.49 pg_amop.h
> *** src/include/catalog/pg_amop.h    26 May 2003 00:11:27 -0000    1.49
> --- src/include/catalog/pg_amop.h    16 Jun 2003 23:45:51 -0000
> ***************
> *** 418,423 ****
> --- 418,432 ----
>   DATA(insert (    2098 4 f 2335 ));
>   DATA(insert (    2098 5 f 2336 ));
>
> + /*
> +  *    btree array_ops
> +  */
> +
> + DATA(insert (     397 1 f 1072 ));
> + DATA(insert (     397 2 f 1074 ));
> + DATA(insert (     397 3 f 1070 ));
> + DATA(insert (     397 4 f 1075 ));
> + DATA(insert (     397 5 f 1073 ));
>
>   /*
>    *    hash index _ops
> Index: src/include/catalog/pg_amproc.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amproc.h,v
> retrieving revision 1.37
> diff -c -r1.37 pg_amproc.h
> *** src/include/catalog/pg_amproc.h    26 May 2003 00:11:27 -0000    1.37
> --- src/include/catalog/pg_amproc.h    16 Jun 2003 23:45:51 -0000
> ***************
> *** 78,83 ****
> --- 78,84 ----
>
>
>   /* btree */
> + DATA(insert (     397 1  398 ));
>   DATA(insert (     421 1    357 ));
>   DATA(insert (     423 1 1596 ));
>   DATA(insert (     424 1 1693 ));
> Index: src/include/catalog/pg_opclass.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_opclass.h,v
> retrieving revision 1.50
> diff -c -r1.50 pg_opclass.h
> *** src/include/catalog/pg_opclass.h    28 May 2003 16:04:00 -0000    1.50
> --- src/include/catalog/pg_opclass.h    16 Jun 2003 23:45:51 -0000
> ***************
> *** 87,92 ****
> --- 87,94 ----
>    */
>
>   DATA(insert OID =  421 (    403        abstime_ops        PGNSP PGUID  702 t 0 ));
> + DATA(insert OID =  397 (    403        array_ops        PGNSP PGUID 2277 t 0 ));
> + #define ARRAY_BTREE_OPS_OID 397
>   DATA(insert OID =  422 (    402        bigbox_ops        PGNSP PGUID  603 f 0 ));
>   DATA(insert OID =  423 (    403        bit_ops            PGNSP PGUID 1560 t 0 ));
>   DATA(insert OID =  424 (    403        bool_ops        PGNSP PGUID   16 t 0 ));
> Index: src/include/catalog/pg_operator.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_operator.h,v
> retrieving revision 1.114
> diff -c -r1.114 pg_operator.h
> *** src/include/catalog/pg_operator.h    26 May 2003 00:11:27 -0000    1.114
> --- src/include/catalog/pg_operator.h    16 Jun 2003 23:45:51 -0000
> ***************
> *** 116,125 ****
>   DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
>   DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 
>
> ! DATA(insert OID = 329 (  "="       PGNSP PGUID b f 2277 2277 16   329 0 0 0    0 0 array_eq     eqsel eqjoinsel ));
> ! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277   0 0 0 0    0 0 array_append   -       -
));
> ! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277   0 0 0 0    0 0 array_prepend  -       -
));
> ! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277   0 0 0 0    0 0 array_cat      -       -
));
>
>   DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
>   DATA(insert OID = 353 (  "="       PGNSP PGUID b t    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
> --- 116,130 ----
>   DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
>   DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 
>
> ! DATA(insert OID = 1070 (  "="       PGNSP PGUID b f 2277 2277 16 1070 1071  1072 1072 1072 1073 array_eq eqsel
eqjoinsel)); 
> ! DATA(insert OID = 1071 (  "<>"       PGNSP PGUID b f 2277 2277 16 1071 1070  0 0 0 0 array_ne neqsel neqjoinsel ));
> ! DATA(insert OID = 1072 (  "<"       PGNSP PGUID b f 2277 2277 16 1073 1075  0 0 0 0 array_lt scalarltsel
scalarltjoinsel)); 
> ! DATA(insert OID = 1073 (  ">"       PGNSP PGUID b f 2277 2277 16 1072 1074  0 0 0 0 array_gt scalargtsel
scalargtjoinsel)); 
> ! DATA(insert OID = 1074 (  "<="       PGNSP PGUID b f 2277 2277 16 1075 1073  0 0 0 0 array_le scalarltsel
scalarltjoinsel)); 
> ! DATA(insert OID = 1075 (  ">="       PGNSP PGUID b f 2277 2277 16 1074 1072  0 0 0 0 array_ge scalargtsel
scalargtjoinsel)); 
> ! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277 0 0  0 0 0 0 array_append   -       -     ));
> ! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277 0 0  0 0 0 0 array_prepend  -       -     ));
> ! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277 0 0  0 0 0 0 array_cat      -       -     ));
>
>   DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
>   DATA(insert OID = 353 (  "="       PGNSP PGUID b t    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
> ***************
> *** 425,430 ****
> --- 430,436 ----
>   DATA(insert OID = 966 (  "+"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - ));
>   DATA(insert OID = 967 (  "-"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclremove - - ));
>   DATA(insert OID = 968 (  "~"       PGNSP PGUID b f 1034 1033     16 0 0 0 0 0 0 aclcontains - - ));
> + DATA(insert OID = 974 (  "="       PGNSP PGUID b f 1033 1033     16 0 0 0 0 0 0 aclitemeq eqsel eqjoinsel ));
>
>   /* additional geometric operators - thomas 1997-07-09 */
>   DATA(insert OID =  969 (  "@@"       PGNSP PGUID l f    0  601    600    0  0 0 0 0 0 lseg_center - - ));
> Index: src/include/catalog/pg_proc.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v
> retrieving revision 1.303
> diff -c -r1.303 pg_proc.h
> *** src/include/catalog/pg_proc.h    11 Jun 2003 09:23:55 -0000    1.303
> --- src/include/catalog/pg_proc.h    16 Jun 2003 23:45:51 -0000
> ***************
> *** 758,763 ****
> --- 758,765 ----
>   DESCR("btree less-equal-greater");
>   DATA(insert OID = 360 (  bttextcmp           PGNSP PGUID 12 f f t f i 2 23 "25 25"    bttextcmp - _null_ ));
>   DESCR("btree less-equal-greater");
> + DATA(insert OID = 398 (  btarraycmp           PGNSP PGUID 12 f f t f i 2 23 "2277 2277"    btarraycmp - _null_ ));
> + DESCR("btree less-equal-greater");
>
>   DATA(insert OID = 361 (  lseg_distance       PGNSP PGUID 12 f f t f i 2 701 "601 601"  lseg_distance - _null_ ));
>   DESCR("distance between");
> ***************
> *** 984,997 ****
>   DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
>   DESCR("greater-than-or-equal");
>
> - DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
> - DESCR("array equal");
> -
>   DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
>   DESCR("current user name");
>   DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
>   DESCR("session user name");
>
>   DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
>   DESCR("array dimensions");
>   DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
> --- 986,1008 ----
>   DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
>   DESCR("greater-than-or-equal");
>
>   DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
>   DESCR("current user name");
>   DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
>   DESCR("session user name");
>
> + DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
> + DESCR("array equal");
> + DATA(insert OID = 390 (  array_ne           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ne - _null_ ));
> + DESCR("array not equal");
> + DATA(insert OID = 391 (  array_lt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_lt - _null_ ));
> + DESCR("array less than");
> + DATA(insert OID = 392 (  array_gt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_gt - _null_ ));
> + DESCR("array greater than");
> + DATA(insert OID = 393 (  array_le           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_le - _null_ ));
> + DESCR("array less than or equal");
> + DATA(insert OID = 396 (  array_ge           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ge - _null_ ));
> + DESCR("array greater than or equal");
>   DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
>   DESCR("array dimensions");
>   DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
> ***************
> *** 1002,1023 ****
>   DESCR("array lower dimension");
>   DATA(insert OID = 2092 (  array_upper       PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ ));
>   DESCR("array upper dimension");
> - DATA(insert OID = 377 (  singleton_array  PGNSP PGUID 12 f f t f i 1 2277 "2283" singleton_array - _null_ ));
> - DESCR("create array from single element");
>   DATA(insert OID = 378 (  array_append       PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ ));
>   DESCR("append element onto end of array");
>   DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
>   DESCR("prepend element onto front of array");
> - DATA(insert OID = 380 (  array_accum       PGNSP PGUID 12 f f f f i 2 2277 "2277 2283" array_accum - _null_ ));
> - DESCR("push element onto end of array, creating array if needed");
> - DATA(insert OID = 381 (  array_assign       PGNSP PGUID 12 f f t f i 3 2277 "2277 23 2283" array_assign - _null_
));
> - DESCR("assign specific array element");
> - DATA(insert OID = 382 (  array_subscript  PGNSP PGUID 12 f f t f i 2 2283 "2277 23" array_subscript - _null_ ));
> - DESCR("return specific array element");
>   DATA(insert OID = 383 (  array_cat           PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ ));
>   DESCR("concatenate two arrays");
>   DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
>   DESCR("coerce array type to another array type");
>
>   DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
>   DESCR("I/O");
> --- 1013,1030 ----
>   DESCR("array lower dimension");
>   DATA(insert OID = 2092 (  array_upper       PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ ));
>   DESCR("array upper dimension");
>   DATA(insert OID = 378 (  array_append       PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ ));
>   DESCR("append element onto end of array");
>   DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
>   DESCR("prepend element onto front of array");
>   DATA(insert OID = 383 (  array_cat           PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ ));
>   DESCR("concatenate two arrays");
>   DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
>   DESCR("coerce array type to another array type");
> + DATA(insert OID = 394 (  string_to_array   PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ ));
> + DESCR("split delimited text into text[]");
> + DATA(insert OID = 395 (  array_to_string   PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ ));
> + DESCR("concatenate array elements, using delimiter, into text");
>
>   DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
>   DESCR("I/O");
> ***************
> *** 1318,1323 ****
> --- 1325,1332 ----
>   DESCR("remove ACL item");
>   DATA(insert OID = 1037 (  aclcontains       PGNSP PGUID 12 f f t f s 2 16 "1034 1033"    aclcontains - _null_ ));
>   DESCR("does ACL contain item?");
> + DATA(insert OID = 1062 (  aclitemeq           PGNSP PGUID 12 f f t f s 2 16 "1033 1033"    aclitem_eq - _null_ ));
> + DESCR("equality operator for ACL items");
>   DATA(insert OID = 1365 (  makeaclitem       PGNSP PGUID 12 f f t f s 5 1033 "23 23 23 25 16"    makeaclitem -
_null_)); 
>   DESCR("make ACL item");
>   DATA(insert OID = 1038 (  seteval           PGNSP PGUID 12 f f t t v 1 23 "26"  seteval - _null_ ));
> Index: src/include/nodes/primnodes.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v
> retrieving revision 1.83
> diff -c -r1.83 primnodes.h
> *** src/include/nodes/primnodes.h    6 Jun 2003 15:04:03 -0000    1.83
> --- src/include/nodes/primnodes.h    16 Jun 2003 23:45:51 -0000
> ***************
> *** 226,231 ****
> --- 226,232 ----
>       Index        agglevelsup;    /* > 0 if agg belongs to outer query */
>       bool        aggstar;        /* TRUE if argument was really '*' */
>       bool        aggdistinct;    /* TRUE if it's agg(DISTINCT ...) */
> +     List       *args;            /* arguments to the aggregate */
>   } Aggref;
>
>   /* ----------------
> ***************
> *** 358,372 ****
>   /* ----------------
>    * SubLink
>    *
> !  * A SubLink represents a subselect appearing in an expression, and in some
> !  * cases also the combining operator(s) just above it.    The subLinkType
> !  * indicates the form of the expression represented:
>    *    EXISTS_SUBLINK        EXISTS(SELECT ...)
>    *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
>    *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
>    *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
>    *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
>    *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
>    * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
>    * same length as the subselect's targetlist.  MULTIEXPR will *always* have
>    * a list with more than one entry; if the subselect has just one target
> --- 359,377 ----
>   /* ----------------
>    * SubLink
>    *
> !  * A SubLink represents a subselect, or an expression, appearing in an
> !  * expression, and in some cases also the combining operator(s) just above
> !  * it.    The subLinkType indicates the form of the expression represented:
>    *    EXISTS_SUBLINK        EXISTS(SELECT ...)
>    *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
>    *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
>    *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
>    *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
>    *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
> +  * If an expression is used in place of the subselect, it is transformed
> +  * into a simple "(SELECT expr)" in gram.y. This is to allow arrays to be
> +  * used as if they were the result of a single column subselect. If the
> +  * expression is scalar, it is treated as a one element array.
>    * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
>    * same length as the subselect's targetlist.  MULTIEXPR will *always* have
>    * a list with more than one entry; if the subselect has just one target
> ***************
> *** 415,420 ****
> --- 420,427 ----
>       SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
>       bool        useOr;            /* TRUE to combine column results with
>                                    * "OR" not "AND" */
> +     bool        isExpr;            /* TRUE if the subselect is really derived
> +                                  * from a single expression */
>       List       *lefthand;        /* list of outer-query expressions on the
>                                    * left */
>       List       *operName;        /* originally specified operator name */
> ***************
> *** 456,461 ****
> --- 463,477 ----
>       SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
>       bool        useOr;            /* TRUE to combine column results with
>                                    * "OR" not "AND" */
> +     bool        isExpr;            /* TRUE if the subselect is really derived
> +                                  * from a single expression */
> +     /* runtime cache for single array expressions */
> +     Oid            exprtype;        /* array and element type, and other info
> +                                  * needed deconstruct the array */
> +     Oid            elemtype;
> +     int16        elmlen;
> +     bool        elmbyval;
> +     char        elmalign;
>       /* The combining operators, transformed to executable expressions: */
>       List       *exprs;            /* list of OpExpr expression trees */
>       List       *paramIds;        /* IDs of Params embedded in the above */
> Index: src/include/optimizer/clauses.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/optimizer/clauses.h,v
> retrieving revision 1.63
> diff -c -r1.63 clauses.h
> *** src/include/optimizer/clauses.h    28 May 2003 16:04:02 -0000    1.63
> --- src/include/optimizer/clauses.h    16 Jun 2003 23:45:51 -0000
> ***************
> *** 28,33 ****
> --- 28,36 ----
>   extern Node *get_leftop(Expr *clause);
>   extern Node *get_rightop(Expr *clause);
>
> + extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
> +                                     CoercionForm funcformat, List *funcargs);
> +
>   extern bool not_clause(Node *clause);
>   extern Expr *make_notclause(Expr *notclause);
>   extern Expr *get_notclausearg(Expr *notclause);
> Index: src/include/parser/parse_oper.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_oper.h,v
> retrieving revision 1.25
> diff -c -r1.25 parse_oper.h
> *** src/include/parser/parse_oper.h    29 Apr 2003 22:13:11 -0000    1.25
> --- src/include/parser/parse_oper.h    16 Jun 2003 23:45:51 -0000
> ***************
> *** 44,49 ****
> --- 44,50 ----
>   /* Convenience routines for common calls on the above */
>   extern Oid    compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError);
>   extern Oid    equality_oper_funcid(Oid argtype);
> + extern Oid  ordering_oper_funcid(Oid argtype);
>   extern Oid    ordering_oper_opid(Oid argtype);
>
>   /* Extract operator OID or underlying-function OID from an Operator tuple */
> Index: src/include/utils/acl.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/acl.h,v
> retrieving revision 1.52
> diff -c -r1.52 acl.h
> *** src/include/utils/acl.h    11 Jun 2003 09:23:55 -0000    1.52
> --- src/include/utils/acl.h    16 Jun 2003 23:41:46 -0000
> ***************
> *** 192,197 ****
> --- 192,198 ----
>   extern Datum aclremove(PG_FUNCTION_ARGS);
>   extern Datum aclcontains(PG_FUNCTION_ARGS);
>   extern Datum makeaclitem(PG_FUNCTION_ARGS);
> + extern Datum aclitem_eq(PG_FUNCTION_ARGS);
>
>   /*
>    * prototypes for functions in aclchk.c
> Index: src/include/utils/array.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v
> retrieving revision 1.38
> diff -c -r1.38 array.h
> *** src/include/utils/array.h    8 May 2003 22:19:57 -0000    1.38
> --- src/include/utils/array.h    16 Jun 2003 23:45:51 -0000
> ***************
> *** 32,37 ****
> --- 32,68 ----
>       Oid            elemtype;        /* element type OID */
>   } ArrayType;
>
> + typedef struct ArrayBuildState
> + {
> +     MemoryContext mcontext;        /* where all the temp stuff is kept */
> +     Datum       *dvalues;        /* array of accumulated Datums */
> +     /*
> +      * The allocated size of dvalues[] is always a multiple of
> +      * ARRAY_ELEMS_CHUNKSIZE
> +      */
> + #define ARRAY_ELEMS_CHUNKSIZE    64
> +     int            nelems;            /* number of valid Datums in dvalues[] */
> +     Oid            element_type;    /* data type of the Datums */
> +     int16        typlen;            /* needed info about datatype */
> +     bool        typbyval;
> +     char        typalign;
> + } ArrayBuildState;
> +
> + /*
> +  * structure to cache type metadata needed for array manipulation
> +  */
> + typedef struct ArrayMetaState
> + {
> +     Oid                element_type;
> +     int                typlen;
> +     bool            typbyval;
> +     char            typdelim;
> +     Oid                typelem;
> +     Oid                typiofunc;
> +     char            typalign;
> +     FmgrInfo        proc;
> + } ArrayMetaState;
> +
>   /*
>    * fmgr macros for array objects
>    */
> ***************
> *** 86,96 ****
>   extern Datum array_send(PG_FUNCTION_ARGS);
>   extern Datum array_length_coerce(PG_FUNCTION_ARGS);
>   extern Datum array_eq(PG_FUNCTION_ARGS);
>   extern Datum array_dims(PG_FUNCTION_ARGS);
>   extern Datum array_lower(PG_FUNCTION_ARGS);
>   extern Datum array_upper(PG_FUNCTION_ARGS);
> - extern Datum array_assign(PG_FUNCTION_ARGS);
> - extern Datum array_subscript(PG_FUNCTION_ARGS);
>   extern Datum array_type_coerce(PG_FUNCTION_ARGS);
>
>   extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
> --- 117,131 ----
>   extern Datum array_send(PG_FUNCTION_ARGS);
>   extern Datum array_length_coerce(PG_FUNCTION_ARGS);
>   extern Datum array_eq(PG_FUNCTION_ARGS);
> + extern Datum array_ne(PG_FUNCTION_ARGS);
> + extern Datum array_lt(PG_FUNCTION_ARGS);
> + extern Datum array_gt(PG_FUNCTION_ARGS);
> + extern Datum array_le(PG_FUNCTION_ARGS);
> + extern Datum array_ge(PG_FUNCTION_ARGS);
> + extern Datum btarraycmp(PG_FUNCTION_ARGS);
>   extern Datum array_dims(PG_FUNCTION_ARGS);
>   extern Datum array_lower(PG_FUNCTION_ARGS);
>   extern Datum array_upper(PG_FUNCTION_ARGS);
>   extern Datum array_type_coerce(PG_FUNCTION_ARGS);
>
>   extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
> ***************
> *** 124,130 ****
>                     Oid elmtype,
>                     int elmlen, bool elmbyval, char elmalign,
>                     Datum **elemsp, int *nelemsp);
> !
>
>   /*
>    * prototypes for functions defined in arrayutils.c
> --- 159,172 ----
>                     Oid elmtype,
>                     int elmlen, bool elmbyval, char elmalign,
>                     Datum **elemsp, int *nelemsp);
> ! extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
> !                                          Datum dvalue, bool disnull,
> !                                          Oid element_type,
> !                                          MemoryContext rcontext);
> ! extern Datum makeArrayResult(ArrayBuildState *astate,
> !                              MemoryContext rcontext);
> ! extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
> !                                int *dims, int *lbs, MemoryContext rcontext);
>
>   /*
>    * prototypes for functions defined in arrayutils.c
> ***************
> *** 141,152 ****
>   /*
>    * prototypes for functions defined in array_userfuncs.c
>    */
> - extern Datum singleton_array(PG_FUNCTION_ARGS);
>   extern Datum array_push(PG_FUNCTION_ARGS);
> - extern Datum array_accum(PG_FUNCTION_ARGS);
>   extern Datum array_cat(PG_FUNCTION_ARGS);
>
> ! extern ArrayType *create_singleton_array(Oid element_type,
>                                            Datum element,
>                                            int ndims);
>
> --- 183,193 ----
>   /*
>    * prototypes for functions defined in array_userfuncs.c
>    */
>   extern Datum array_push(PG_FUNCTION_ARGS);
>   extern Datum array_cat(PG_FUNCTION_ARGS);
>
> ! extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
> !                                          Oid element_type,
>                                            Datum element,
>                                            int ndims);
>
> Index: src/include/utils/builtins.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/builtins.h,v
> retrieving revision 1.219
> diff -c -r1.219 builtins.h
> *** src/include/utils/builtins.h    26 May 2003 00:11:28 -0000    1.219
> --- src/include/utils/builtins.h    16 Jun 2003 23:45:51 -0000
> ***************
> *** 530,535 ****
> --- 530,537 ----
>                         List **namelist);
>   extern Datum replace_text(PG_FUNCTION_ARGS);
>   extern Datum split_text(PG_FUNCTION_ARGS);
> + extern Datum text_to_array(PG_FUNCTION_ARGS);
> + extern Datum array_to_text(PG_FUNCTION_ARGS);
>   extern Datum to_hex32(PG_FUNCTION_ARGS);
>   extern Datum to_hex64(PG_FUNCTION_ARGS);
>   extern Datum md5_text(PG_FUNCTION_ARGS);
> Index: src/include/utils/lsyscache.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v
> retrieving revision 1.70
> diff -c -r1.70 lsyscache.h
> *** src/include/utils/lsyscache.h    26 May 2003 00:11:28 -0000    1.70
> --- src/include/utils/lsyscache.h    16 Jun 2003 23:45:51 -0000
> ***************
> *** 15,20 ****
> --- 15,29 ----
>
>   #include "access/htup.h"
>
> + /* I/O function selector for system_cache_lookup */
> + typedef enum IOFuncSelector
> + {
> +     IOFunc_input,
> +     IOFunc_output,
> +     IOFunc_receive,
> +     IOFunc_send
> + } IOFuncSelector;
> +
>   extern bool op_in_opclass(Oid opno, Oid opclass);
>   extern bool op_requires_recheck(Oid opno, Oid opclass);
>   extern Oid    get_opclass_member(Oid opclass, int16 strategy);
> ***************
> *** 39,44 ****
> --- 48,54 ----
>   extern RegProcedure get_oprjoin(Oid opno);
>   extern char *get_func_name(Oid funcid);
>   extern Oid    get_func_rettype(Oid funcid);
> + extern Oid *get_func_argtypes(Oid funcid, int *nargs);
>   extern bool get_func_retset(Oid funcid);
>   extern bool func_strict(Oid funcid);
>   extern char func_volatile(Oid funcid);
> ***************
> *** 54,59 ****
> --- 64,77 ----
>   extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
>   extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
>                        char *typalign);
> + extern void get_type_metadata(Oid element_type,
> +                                 IOFuncSelector which_func,
> +                                 int *typlen,
> +                                 bool *typbyval,
> +                                 char *typdelim,
> +                                 Oid *typelem,
> +                                 Oid *proc,
> +                                 char *typalign);
>   extern char get_typstorage(Oid typid);
>   extern int32 get_typtypmod(Oid typid);
>   extern Node *get_typdefault(Oid typid);
> Index: src/interfaces/ecpg/preproc/preproc.y
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/preproc.y,v
> retrieving revision 1.232
> diff -c -r1.232 preproc.y
> *** src/interfaces/ecpg/preproc/preproc.y    16 Jun 2003 16:58:11 -0000    1.232
> --- src/interfaces/ecpg/preproc/preproc.y    16 Jun 2003 23:45:51 -0000
> ***************
> *** 4592,4598 ****
>                   $3.type_enum != ECPGt_char &&
>                       $3.type_enum != ECPGt_unsigned_char &&
>                   atoi(this->type->type_index) >= 0)
> !                 mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
>
>               types = this;
>           }
> --- 4592,4598 ----
>                   $3.type_enum != ECPGt_char &&
>                       $3.type_enum != ECPGt_unsigned_char &&
>                   atoi(this->type->type_index) >= 0)
> !                 mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
>
>               types = this;
>           }
> ***************
> *** 5424,5430 ****
>                       $5.type_enum != ECPGt_char &&
>                       $5.type_enum != ECPGt_unsigned_char &&
>                       atoi(this->type->type_index) >= 0)
> !                     mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
>
>                   types = this;
>               }
> --- 5424,5430 ----
>                       $5.type_enum != ECPGt_char &&
>                       $5.type_enum != ECPGt_unsigned_char &&
>                       atoi(this->type->type_index) >= 0)
> !                     mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
>
>                   types = this;
>               }
> ***************
> *** 5491,5497 ****
>
>                       default:
>                           if (atoi(length) >= 0)
> !                             mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data
types");
>
>                           if (atoi(dimension) < 0)
>                               type = ECPGmake_simple_type($5.type_enum, make_str("1"));
> --- 5491,5497 ----
>
>                       default:
>                           if (atoi(length) >= 0)
> !                             mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data
types");
>
>                           if (atoi(dimension) < 0)
>                               type = ECPGmake_simple_type($5.type_enum, make_str("1"));
> Index: src/interfaces/ecpg/preproc/type.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/type.c,v
> retrieving revision 1.51
> diff -c -r1.51 type.c
> *** src/interfaces/ecpg/preproc/type.c    29 May 2003 13:59:26 -0000    1.51
> --- src/interfaces/ecpg/preproc/type.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 493,499 ****
>                   switch (type->u.element->type)
>                   {
>                       case ECPGt_array:
> !                         yyerror("internal error, found multi-dimensional array\n");
>                           break;
>                       case ECPGt_struct:
>                       case ECPGt_union:
> --- 493,499 ----
>                   switch (type->u.element->type)
>                   {
>                       case ECPGt_array:
> !                         yyerror("internal error, found multidimensional array\n");
>                           break;
>                       case ECPGt_struct:
>                       case ECPGt_union:
> Index: src/interfaces/ecpg/preproc/variable.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/variable.c,v
> retrieving revision 1.21
> diff -c -r1.21 variable.c
> *** src/interfaces/ecpg/preproc/variable.c    11 Jun 2003 06:39:13 -0000    1.21
> --- src/interfaces/ecpg/preproc/variable.c    16 Jun 2003 23:45:51 -0000
> ***************
> *** 436,442 ****
>       if (atoi(type_index) >= 0)
>       {
>           if (atoi(*length) >= 0)
> !             mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
>           *length = type_index;
>       }
> --- 436,442 ----
>       if (atoi(type_index) >= 0)
>       {
>           if (atoi(*length) >= 0)
> !             mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
>           *length = type_index;
>       }
> ***************
> *** 444,450 ****
>       if (atoi(type_dimension) >= 0)
>       {
>           if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
> !             mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
>           if (atoi(*dimension) >= 0)
>               *length = *dimension;
> --- 444,450 ----
>       if (atoi(type_dimension) >= 0)
>       {
>           if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
> !             mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
>           if (atoi(*dimension) >= 0)
>               *length = *dimension;
> ***************
> *** 463,472 ****
>           mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");
>
>       if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
> !         mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
>       if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
> !         mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
>       switch (type_enum)
>       {
> --- 463,472 ----
>           mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");
>
>       if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
> !         mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
>       if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
> !         mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
>       switch (type_enum)
>       {
> ***************
> *** 480,486 ****
>               }
>
>               if (atoi(*length) >= 0)
> !                 mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for structures");
>
>               break;
>           case ECPGt_varchar:
> --- 480,486 ----
>               }
>
>               if (atoi(*length) >= 0)
> !                 mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for structures");
>
>               break;
>           case ECPGt_varchar:
> ***************
> *** 525,531 ****
>               }
>
>               if (atoi(*length) >= 0)
> !                 mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for simple data types");
>
>               break;
>       }
> --- 525,531 ----
>               }
>
>               if (atoi(*length) >= 0)
> !                 mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for simple data types");
>
>               break;
>       }
> Index: src/test/regress/expected/arrays.out
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/test/regress/expected/arrays.out,v
> retrieving revision 1.11
> diff -c -r1.11 arrays.out
> *** src/test/regress/expected/arrays.out    8 Apr 2003 23:20:04 -0000    1.11
> --- src/test/regress/expected/arrays.out    16 Jun 2003 23:45:51 -0000
> ***************
> *** 178,196 ****
>   (1 row)
>
>   -- functions
> ! SELECT singleton_array(42) AS "{42}";
> !  {42}
> ! ------
> !  {42}
> ! (1 row)
> !
> ! SELECT array_append(singleton_array(42), 6) AS "{42,6}";
>    {42,6}
>   --------
>    {42,6}
>   (1 row)
>
> ! SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
>    {6,42}
>   --------
>    {6,42}
> --- 178,190 ----
>   (1 row)
>
>   -- functions
> ! SELECT array_append(array[42], 6) AS "{42,6}";
>    {42,6}
>   --------
>    {42,6}
>   (1 row)
>
> ! SELECT array_prepend(6, array[42]) AS "{6,42}";
>    {6,42}
>   --------
>    {6,42}
> ***************
> *** 212,235 ****
>    {{3,4},{5,6},{1,2}}
>   ---------------------
>    {{3,4},{5,6},{1,2}}
> - (1 row)
> -
> - SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
> -  1.2
> - -----
> -  1.2
> - (1 row)
> -
> - SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
> -  {1.1,9.99,1.3}
> - ----------------
> -  {1.1,9.99,1.3}
> - (1 row)
> -
> - SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
> -  9.99
> - ------
> -  9.99
>   (1 row)
>
>   -- operators
> --- 206,211 ----
> Index: src/test/regress/sql/arrays.sql
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/test/regress/sql/arrays.sql,v
> retrieving revision 1.10
> diff -c -r1.10 arrays.sql
> *** src/test/regress/sql/arrays.sql    8 Apr 2003 23:20:04 -0000    1.10
> --- src/test/regress/sql/arrays.sql    16 Jun 2003 23:45:51 -0000
> ***************
> *** 130,144 ****
>   SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
>
>   -- functions
> ! SELECT singleton_array(42) AS "{42}";
> ! SELECT array_append(singleton_array(42), 6) AS "{42,6}";
> ! SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
>   SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}";
>   SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
>   SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
> - SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
> - SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
> - SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
>
>   -- operators
>   SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
> --- 130,140 ----
>   SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
>
>   -- functions
> ! SELECT array_append(array[42], 6) AS "{42,6}";
> ! SELECT array_prepend(6, array[42]) AS "{6,42}";
>   SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}";
>   SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
>   SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
>
>   -- operators
>   SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Re: array support patch phase 1 patch

From
Joe Conway
Date:
Bruce Momjian wrote:
> Your patch has been added to the PostgreSQL unapplied patches list at:
[...snip...]
>>Joe Conway wrote:
>>>The attached patch addresses Peter's concerns, subject to our agreements
>>>above. I.e, the changes are:
>>
>>The previous patch was no longer applying cleanly, so here is an update.
>>Applies and compiles clean on cvs tip, passes all regression tests.
>>Please apply.

Here's another update against cvs tip. The previous version now has a
duplicate oid conflict -- fixed.

Joe
Index: doc/src/sgml/array.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
retrieving revision 1.25
diff -c -r1.25 array.sgml
*** doc/src/sgml/array.sgml    13 Mar 2003 01:30:26 -0000    1.25
--- doc/src/sgml/array.sgml    24 Jun 2003 02:24:06 -0000
***************
*** 60,73 ****
  </programlisting>
   </para>

   <note>
    <para>
!    A limitation of the present array implementation is that individual
!    elements of an array cannot be SQL null values.  The entire array can be set
!    to null, but you can't have an array with some elements null and some
!    not.  Fixing this is on the to-do list.
    </para>
   </note>
   </sect2>

   <sect2>
--- 60,133 ----
  </programlisting>
   </para>

+  <para>
+   A limitation of the present array implementation is that individual
+   elements of an array cannot be SQL null values.  The entire array can be set
+   to null, but you can't have an array with some elements null and some
+   not.
+  </para>
+  <para>
+   This can lead to surprising results. For example, the result of the
+   previous two inserts looks like this:
+ <programlisting>
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |      schedule
+ -------+---------------------------+--------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting},{""}}
+  Carol | {20000,25000,25000,25000} | {{talk},{meeting}}
+ (2 rows)
+ </programlisting>
+   Because the <literal>[2][2]</literal> element of
+   <structfield>schedule</structfield> is missing in each of the
+   <command>INSERT</command> statements, the <literal>[1][2]</literal>
+   element is discarded.
+  </para>
+
+  <note>
+   <para>
+    Fixing this is on the to-do list.
+   </para>
+  </note>
+
+  <para>
+   The <command>ARRAY</command> expression syntax may also be used:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Bill',
+     ARRAY[10000, 10000, 10000, 10000],
+     ARRAY[['meeting', 'lunch'], ['','']]);
+
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting', '']]);
+ SELECT * FROM sal_emp;
+  name  |      pay_by_quarter       |           schedule
+ -------+---------------------------+-------------------------------
+  Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}}
+  Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}}
+ (2 rows)
+ </programlisting>
+   Note that with this syntax, multidimensional arrays must have matching
+   extents for each dimension. This eliminates the missing-array-elements
+   problem above. For example:
+ <programlisting>
+ INSERT INTO sal_emp
+     VALUES ('Carol',
+     ARRAY[20000, 25000, 25000, 25000],
+     ARRAY[['talk', 'consult'], ['meeting']]);
+ ERROR:  Multidimensional arrays must have array expressions with matching dimensions
+ </programlisting>
+   Also notice that string literals are single quoted instead of double quoted.
+  </para>
+
   <note>
    <para>
!    The examples in the rest of this section are based on the
!    <command>ARRAY</command> expression syntax <command>INSERT</command>s.
    </para>
   </note>
+
   </sect2>

   <sect2>
***************
*** 132,142 ****
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the
!   form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified.
   </para>

   <para>
--- 192,221 ----
  </programlisting>

    with the same result.  An array subscripting operation is always taken to
!   represent an array slice if any of the subscripts are written in the form
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified; another example follows:
! <programlisting>
! SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
!          schedule
! ---------------------------
!  {{meeting,lunch},{"",""}}
! (1 row)
! </programlisting>
!  </para>
!
!  <para>
!   Additionally, we can also access a single arbitrary array element of
!   a one-dimensional array with the <function>array_subscript</function>
!   function:
! <programlisting>
! SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
!  array_subscript
! -----------------
!            10000
! (1 row)
! </programlisting>
   </para>

   <para>
***************
*** 147,153 ****
      WHERE name = 'Carol';
  </programlisting>

!   or updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
--- 226,248 ----
      WHERE name = 'Carol';
  </programlisting>

!   or using the <command>ARRAY</command> expression syntax:
!
! <programlisting>
! UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
!     WHERE name = 'Carol';
! </programlisting>
!
!   <note>
!    <para>
!     Anywhere you can use the <quote>curly braces</quote> array syntax,
!     you can also use the <command>ARRAY</command> expression syntax. The
!     remainder of this section will illustrate only one or the other, but
!     not both.
!    </para>
!   </note>
!
!   An array may also be updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
***************
*** 160,165 ****
--- 255,268 ----
  UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
      WHERE name = 'Carol';
  </programlisting>
+
+   A one-dimensional array may also be updated with the
+   <function>array_assign</function> function:
+
+ <programlisting>
+ UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000)
+     WHERE name = 'Bill';
+ </programListing>
   </para>

   <para>
***************
*** 179,184 ****
--- 282,369 ----
   </para>

   <para>
+   An array can also be enlarged by using the concatenation operator,
+   <command>||</command>.
+ <programlisting>
+ SELECT ARRAY[1,2] || ARRAY[3,4];
+    ?column?
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
+       ?column?
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ (1 row)
+ </programlisting>
+
+   The concatenation operator allows a single element to be pushed on to the
+   beginning or end of a one-dimensional array. It also allows two
+   <replaceable>N</>-dimensional arrays, or an <replaceable>N</>-dimensional
+   and an <replaceable>N+1</>-dimensional array. In the former case, the two
+   <replaceable>N</>-dimension arrays become outer elements of an
+   <replaceable>N+1</>-dimensional array. In the latter, the
+   <replaceable>N</>-dimensional array is added as either the first or last
+   outer element of the <replaceable>N+1</>-dimensional array.
+
+   The array is extended in the direction of the push. Hence, by pushing
+   onto the beginning of an array with a one-based subscript, a zero-based
+   subscript array is created:
+
+ <programlisting>
+ SELECT array_dims(t.f) FROM (SELECT 1 || ARRAY[2,3] AS f) AS t;
+  array_dims
+ ------------
+  [0:2]
+ (1 row)
+ </programlisting>
+  </para>
+
+  <para>
+   An array can also be enlarged by using the functions
+   <function>array_prepend</function>, <function>array_append</function>,
+   or <function>array_cat</function>. The first two only support one-dimensional
+   arrays, but <function>array_cat</function> supports multidimensional arrays.
+
+   Note that the concatenation operator discussed above is preferred over
+   direct use of these functions. In fact, the functions are primarily for use
+   in implementing the concatenation operator. However, they may be directly
+   useful in the creation of user-defined aggregates. Some examples:
+
+ <programlisting>
+ SELECT array_prepend(1, ARRAY[2,3]);
+  array_prepend
+ ---------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_append(ARRAY[1,2], 3);
+  array_append
+ --------------
+  {1,2,3}
+ (1 row)
+
+ SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
+    array_cat
+ ---------------
+  {{1,2},{3,4}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
+       array_cat
+ ---------------------
+  {{1,2},{3,4},{5,6}}
+ (1 row)
+
+ SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
+       array_cat
+ ---------------------
+  {{5,6},{1,2},{3,4}}
+ </programlisting>
+  </para>
+
+  <para>
    The syntax for <command>CREATE TABLE</command> allows fixed-length
    arrays to be defined:

***************
*** 194,199 ****
--- 379,394 ----
   </para>

   <para>
+   An alternative syntax for one-dimensional arrays may be used.
+   <structfield>pay_by_quarter</structfield> could have been defined as:
+ <programlisting>
+     pay_by_quarter  integer ARRAY[4],
+ </programlisting>
+   This syntax may <emphasis>only</emphasis> be used with the integer
+   constant to denote the array size.
+  </para>
+
+  <para>
    Actually, the current implementation does not enforce the declared
    number of dimensions either.  Arrays of a particular element type are
    all considered to be of the same type, regardless of size or number
***************
*** 300,305 ****
--- 495,566 ----
     is not ignored, however: after skipping leading whitespace, everything
     up to the next right brace or delimiter is taken as the item value.
    </para>
+
+   <para>
+    As illustrated earlier in this chapter, arrays may also be represented
+    using the <command>ARRAY</command> expression syntax. This representation
+    of an array value consists of items that are interpreted according to the
+    I/O conversion rules for the array's element type, plus decoration that
+    indicates the array structure. The decoration consists of the keyword
+    <command>ARRAY</command> and square brackets (<literal>[</> and
+    <literal>]</>) around the array values, plus delimiter characters between
+    adjacent items. The delimiter character is always a comma (<literal>,</>).
+    When representing multidimensional arrays, the keyword
+    <command>ARRAY</command> is only necessary for the outer level. For example,
+    <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as:
+ <programlisting>
+ SELECT ARRAY[['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   or it also could be written as:
+ <programlisting>
+ SELECT ARRAY[ARRAY['hello world', 'happy birthday']];
+                array
+ ------------------------------------
+  {{"hello world","happy birthday"}}
+ (1 row)
+ </programlisting>
+   </para>
+
+   <para>
+    A final method to represent an array, is through an
+    <command>ARRAY</command> sub-select expression. For example:
+ <programlisting>
+ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
+                           ?column?
+ -------------------------------------------------------------
+  {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
+ (1 row)
+ </programlisting>
+   The sub-select may <emphasis>only</emphasis> return a single column. The
+   resulting one-dimensional array will have an element for each row in the
+   sub-select result, with an element type matching that of the sub-select's
+   target column.
+   </para>
+
+   <para>
+    Arrays may be cast from one type to another in similar fashion to other
+    data types:
+
+ <programlisting>
+ SELECT ARRAY[1,2,3]::oid[];
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+
+ SELECT CAST(ARRAY[1,2,3] AS float8[]);
+   array
+ ---------
+  {1,2,3}
+ (1 row)
+ </programlisting>
+
+   </para>
+
   </sect2>

   <sect2>
***************
*** 316,321 ****
--- 577,590 ----
     Alternatively, you can use backslash-escaping to protect all data characters
     that would otherwise be taken as array syntax or ignorable white space.
    </para>
+
+  <note>
+   <para>
+    The discussion in the preceding paragraph with respect to double quoting does
+    not pertain to the <command>ARRAY</command> expression syntax. In that case,
+    each element is quoted exactly as any other literal value of the element type.
+   </para>
+  </note>

    <para>
     The array output routine will put double quotes around element values
Index: doc/src/sgml/func.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v
retrieving revision 1.154
diff -c -r1.154 func.sgml
*** doc/src/sgml/func.sgml    5 May 2003 15:08:49 -0000    1.154
--- doc/src/sgml/func.sgml    24 Jun 2003 02:24:06 -0000
***************
*** 6962,6967 ****
--- 6962,7164 ----

    </sect1>

+  <sect1 id="functions-array">
+   <title>Array Functions</title>
+
+   <para>
+    <xref linkend="array-operators-table"> shows the operators
+    available for the <type>array</type> types.
+   </para>
+
+     <table id="array-operators-table">
+      <title><type>array</type> Operators</title>
+      <tgroup cols="4">
+       <thead>
+        <row>
+     <entry>Operator</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry> <literal>=</literal> </entry>
+     <entry>equals</entry>
+     <entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry>
+     <entry><literal>t</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-array concatenation</entry>
+     <entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>element-to-array concatenation</entry>
+     <entry><literal>3 || ARRAY[4,5,6]</literal></entry>
+     <entry><literal>{3,4,5,6}</literal></entry>
+        </row>
+        <row>
+     <entry> <literal>||</literal> </entry>
+     <entry>array-to-element concatenation</entry>
+     <entry><literal>ARRAY[4,5,6] || 7</literal></entry>
+     <entry><literal>{4,5,6,7}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+
+   <para>
+    <xref linkend="array-functions-table"> shows the functions
+    available for use with array types. See <xref linkend="arrays">
+    for more discussion and examples for the use of these functions.
+   </para>
+
+     <table id="array-functions-table">
+      <title><type>array</type> Functions</title>
+      <tgroup cols="5">
+       <thead>
+        <row>
+     <entry>Function</entry>
+     <entry>Return Type</entry>
+     <entry>Description</entry>
+     <entry>Example</entry>
+     <entry>Result</entry>
+        </row>
+       </thead>
+       <tbody>
+        <row>
+     <entry>
+      <literal>
+       <function>array_append</function>
+       (<type>anyarray</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the end of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_append(ARRAY[1,2], 3)</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_cat</function>
+       (<type>anyarray</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      concatenate two arrays, returning <literal>NULL</literal>
+      for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry>
+     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_dims</function>
+       (<type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      returns a text representation of array dimension lower and upper bounds,
+      generating an ERROR for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry>
+     <entry><literal>[1:2][1:3]</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_lower</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns lower bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry>
+     <entry><literal>0</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_prepend</function>
+       (<type>anyelement</type>, <type>anyarray</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the beginning of an array, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry>
+     <entry><literal>{1,2,3}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_to_string</function>
+       (<type>anyarray</type>, <type>text</type>)
+      </literal>
+     </entry>
+     <entry><type>text</type></entry>
+     <entry>
+      concatenates array elements using provided delimiter, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_to_string(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry>
+     <entry><literal>1.10~^~2.20~^~3.30</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_upper</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>integer</type></entry>
+     <entry>
+      returns upper bound of the requested array dimension, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry>
+     <entry><literal>4</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>string_to_array</function>
+       (<type>text</type>, <type>text</type>)
+      </literal>
+     </entry>
+     <entry><type>text[]</type></entry>
+     <entry>
+      splits string into array elements using provided delimiter, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>string_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry>
+     <entry><literal>{1.1,2.2,3.3}</literal></entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </table>
+   </sect1>

   <sect1 id="functions-aggregate">
    <title>Aggregate Functions</title>
Index: src/backend/catalog/pg_aggregate.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_aggregate.c,v
retrieving revision 1.56
diff -c -r1.56 pg_aggregate.c
*** src/backend/catalog/pg_aggregate.c    18 Sep 2002 21:35:20 -0000    1.56
--- src/backend/catalog/pg_aggregate.c    24 Jun 2003 02:24:06 -0000
***************
*** 50,59 ****
      Oid            finalfn = InvalidOid;    /* can be omitted */
      Oid            finaltype;
      Oid            fnArgs[FUNC_MAX_ARGS];
!     int            nargs;
      Oid            procOid;
      TupleDesc    tupDesc;
      int            i;
      ObjectAddress myself,
                  referenced;

--- 50,65 ----
      Oid            finalfn = InvalidOid;    /* can be omitted */
      Oid            finaltype;
      Oid            fnArgs[FUNC_MAX_ARGS];
!     int            nargs_transfn;
!     int            nargs_finalfn;
      Oid            procOid;
      TupleDesc    tupDesc;
      int            i;
+     Oid            rettype;
+     Oid           *true_oid_array_transfn;
+     Oid           *true_oid_array_finalfn;
+     bool        retset;
+     FuncDetailCode fdresult;
      ObjectAddress myself,
                  referenced;

***************
*** 68,91 ****
      MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
      fnArgs[0] = aggTransType;
      if (aggBaseType == ANYOID)
!         nargs = 1;
      else
      {
          fnArgs[1] = aggBaseType;
!         nargs = 2;
      }
!     transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
      if (!OidIsValid(transfn))
!         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
      tup = SearchSysCache(PROCOID,
                           ObjectIdGetDatum(transfn),
                           0, 0, 0);
      if (!HeapTupleIsValid(tup))
!         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
      proc = (Form_pg_proc) GETSTRUCT(tup);
-     if (proc->prorettype != aggTransType)
-         elog(ERROR, "return type of transition function %s is not %s",
-          NameListToString(aggtransfnName), format_type_be(aggTransType));

      /*
       * If the transfn is strict and the initval is NULL, make sure input
--- 74,122 ----
      MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
      fnArgs[0] = aggTransType;
      if (aggBaseType == ANYOID)
!         nargs_transfn = 1;
      else
      {
          fnArgs[1] = aggBaseType;
!         nargs_transfn = 2;
      }
!
!     /*
!      * func_get_detail looks up the function in the catalogs, does
!      * disambiguation for polymorphic functions, handles inheritance, and
!      * returns the funcid and type and set or singleton status of the
!      * function's return value.  it also returns the true argument types
!      * to the function.
!      */
!     fdresult = func_get_detail(aggtransfnName, NIL, nargs_transfn, fnArgs,
!                                &transfn, &rettype, &retset,
!                                &true_oid_array_transfn);
!
!     /* only valid case is a normal function */
!     if (fdresult != FUNCDETAIL_NORMAL)
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
      if (!OidIsValid(transfn))
!         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
!
!     /*
!      * enforce consistency with ANYARRAY and ANYELEMENT argument
!      * and return types, possibly modifying return type along the way
!      */
!     rettype = enforce_generic_type_consistency(fnArgs, true_oid_array_transfn,
!                                                        nargs_transfn, rettype);
!
!     if (rettype != aggTransType)
!         elog(ERROR, "return type of transition function %s is not %s",
!          NameListToString(aggtransfnName), format_type_be(aggTransType));
!
      tup = SearchSysCache(PROCOID,
                           ObjectIdGetDatum(transfn),
                           0, 0, 0);
      if (!HeapTupleIsValid(tup))
!         func_error("AggregateCreate", aggtransfnName,
!                         nargs_transfn, fnArgs, NULL);
      proc = (Form_pg_proc) GETSTRUCT(tup);

      /*
       * If the transfn is strict and the initval is NULL, make sure input
***************
*** 105,121 ****
      {
          MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
          fnArgs[0] = aggTransType;
!         finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
!         if (!OidIsValid(finalfn))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!         tup = SearchSysCache(PROCOID,
!                              ObjectIdGetDatum(finalfn),
!                              0, 0, 0);
!         if (!HeapTupleIsValid(tup))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!         proc = (Form_pg_proc) GETSTRUCT(tup);
!         finaltype = proc->prorettype;
!         ReleaseSysCache(tup);
      }
      else
      {
--- 136,161 ----
      {
          MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
          fnArgs[0] = aggTransType;
!         nargs_finalfn = 1;
!
!         fdresult = func_get_detail(aggfinalfnName, NIL, 1, fnArgs,
!                                    &finalfn, &rettype, &retset,
!                                    &true_oid_array_finalfn);
!
!         /* only valid case is a normal function */
!         if (fdresult != FUNCDETAIL_NORMAL)
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!
!         if (!OidIsValid(finalfn))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!
!         /*
!          * enforce consistency with ANYARRAY and ANYELEMENT argument
!          * and return types, possibly modifying return type along the way
!          */
!         finaltype = enforce_generic_type_consistency(fnArgs,
!                                                      true_oid_array_finalfn,
!                                                      nargs_finalfn, rettype);
      }
      else
      {
***************
*** 125,130 ****
--- 165,191 ----
          finaltype = aggTransType;
      }
      Assert(OidIsValid(finaltype));
+
+     /*
+      * special disallowed cases:
+      * 1)    if finaltype is polymorphic, basetype cannot be ANY
+      * 2)    if finaltype is polymorphic, both args to transfn must be
+      *        polymorphic
+      */
+     if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
+     {
+         if (aggBaseType == ANYOID)
+             elog(ERROR, "aggregate with base type ANY must have a " \
+                         "non-polymorphic return type");
+
+         if (nargs_transfn > 1 && (
+             (true_oid_array_transfn[0] != ANYARRAYOID &&
+              true_oid_array_transfn[0] != ANYELEMENTOID) ||
+             (true_oid_array_transfn[1] != ANYARRAYOID &&
+              true_oid_array_transfn[1] != ANYELEMENTOID)))
+             elog(ERROR, "aggregate with polymorphic return type requires " \
+                         "state function with both arguments polymorphic");
+     }

      /*
       * Everything looks okay.  Try to create the pg_proc entry for the
Index: src/backend/commands/aggregatecmds.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/aggregatecmds.c,v
retrieving revision 1.5
diff -c -r1.5 aggregatecmds.c
*** src/backend/commands/aggregatecmds.c    4 Sep 2002 20:31:14 -0000    1.5
--- src/backend/commands/aggregatecmds.c    24 Jun 2003 02:24:06 -0000
***************
*** 119,125 ****
          baseTypeId = typenameTypeId(baseType);

      transTypeId = typenameTypeId(transType);
!     if (get_typtype(transTypeId) == 'p')
          elog(ERROR, "Aggregate transition datatype cannot be %s",
               format_type_be(transTypeId));

--- 119,127 ----
          baseTypeId = typenameTypeId(baseType);

      transTypeId = typenameTypeId(transType);
!     if (get_typtype(transTypeId) == 'p' &&
!         transTypeId != ANYARRAYOID &&
!         transTypeId != ANYELEMENTOID)
          elog(ERROR, "Aggregate transition datatype cannot be %s",
               format_type_be(transTypeId));

Index: src/backend/executor/execQual.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/execQual.c,v
retrieving revision 1.130
diff -c -r1.130 execQual.c
*** src/backend/executor/execQual.c    28 May 2003 22:32:49 -0000    1.130
--- src/backend/executor/execQual.c    24 Jun 2003 02:24:06 -0000
***************
*** 1528,1544 ****
              {
                  /* Check other sub-arrays are compatible */
                  if (elem_ndims != ARR_NDIM(array))
!                     elog(ERROR, "Multiple dimension arrays must have array "
                           "expressions with matching number of dimensions");

                  if (memcmp(elem_dims, ARR_DIMS(array),
                             elem_ndims * sizeof(int)) != 0)
!                     elog(ERROR, "Multiple dimension arrays must have array "
                           "expressions with matching dimensions");

                  if (memcmp(elem_lbs, ARR_LBOUND(array),
                             elem_ndims * sizeof(int)) != 0)
!                     elog(ERROR, "Multiple dimension arrays must have array "
                           "expressions with matching dimensions");
              }

--- 1528,1544 ----
              {
                  /* Check other sub-arrays are compatible */
                  if (elem_ndims != ARR_NDIM(array))
!                     elog(ERROR, "Multidimensional arrays must have array "
                           "expressions with matching number of dimensions");

                  if (memcmp(elem_dims, ARR_DIMS(array),
                             elem_ndims * sizeof(int)) != 0)
!                     elog(ERROR, "Multidimensional arrays must have array "
                           "expressions with matching dimensions");

                  if (memcmp(elem_lbs, ARR_LBOUND(array),
                             elem_ndims * sizeof(int)) != 0)
!                     elog(ERROR, "Multidimensional arrays must have array "
                           "expressions with matching dimensions");
              }

Index: src/backend/executor/nodeAgg.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeAgg.c,v
retrieving revision 1.107
diff -c -r1.107 nodeAgg.c
*** src/backend/executor/nodeAgg.c    22 Jun 2003 22:04:54 -0000    1.107
--- src/backend/executor/nodeAgg.c    24 Jun 2003 02:24:06 -0000
***************
*** 58,63 ****
--- 58,64 ----
  #include "executor/executor.h"
  #include "executor/nodeAgg.h"
  #include "miscadmin.h"
+ #include "nodes/makefuncs.h"
  #include "optimizer/clauses.h"
  #include "parser/parse_coerce.h"
  #include "parser/parse_expr.h"
***************
*** 212,218 ****
  static void agg_fill_hash_table(AggState *aggstate);
  static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
  static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
!

  /*
   * Initialize all aggregates for a new group of input values.
--- 213,219 ----
  static void agg_fill_hash_table(AggState *aggstate);
  static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
  static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
! static Oid resolve_type(Oid type_to_resolve, Oid context_type);

  /*
   * Initialize all aggregates for a new group of input values.
***************
*** 351,364 ****
      fcinfo.context = NULL;
      fcinfo.resultinfo = NULL;
      fcinfo.isnull = false;
-
      fcinfo.flinfo = &peraggstate->transfn;
      fcinfo.nargs = 2;
      fcinfo.arg[0] = pergroupstate->transValue;
      fcinfo.argnull[0] = pergroupstate->transValueIsNull;
      fcinfo.arg[1] = newVal;
      fcinfo.argnull[1] = isNull;
-
      newVal = FunctionCallInvoke(&fcinfo);

      /*
--- 352,363 ----
***************
*** 1187,1193 ****
--- 1186,1206 ----
          AclResult    aclresult;
          Oid            transfn_oid,
                      finalfn_oid;
+         FuncExpr   *transfnexpr,
+                    *finalfnexpr;
          Datum        textInitVal;
+         List       *fargs;
+         Oid            agg_rt_type;
+         Oid           *transfn_arg_types;
+         List       *transfn_args = NIL;
+         int            transfn_nargs;
+         Oid            transfn_ret_type;
+         Oid           *finalfn_arg_types = NULL;
+         List       *finalfn_args = NIL;
+         Oid            finalfn_ret_type = InvalidOid;
+         int            finalfn_nargs = 0;
+         Node       *arg0;
+         Node       *arg1;
          int            i;

          /* Planner should have assigned aggregate to correct level */
***************
*** 1238,1243 ****
--- 1251,1416 ----
                          &peraggstate->transtypeLen,
                          &peraggstate->transtypeByVal);

+         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
+         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
+
+         /* get the runtime aggregate argument type */
+         fargs = aggref->args;
+         agg_rt_type = exprType((Node *) nth(0, fargs));
+
+         /* get the transition function argument and return types */
+         transfn_ret_type = get_func_rettype(transfn_oid);
+         transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs);
+
+         /* resolve any polymorphic types */
+         if (transfn_nargs == 2)
+         /* base type was not ANY */
+         {
+             if (transfn_arg_types[1] == ANYARRAYOID ||
+                 transfn_arg_types[1] == ANYELEMENTOID)
+                 transfn_arg_types[1] = agg_rt_type;
+
+             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+                                                         agg_rt_type);
+
+             /*
+              * Build arg list to use on the transfn FuncExpr node. We really
+              * only care that the node type is correct so that the transfn
+              * can discover the actual argument types at runtime using
+              * get_fn_expr_argtype()
+              */
+             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
+                                                             -1, COERCE_DONTCARE);
+             arg1 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[1],
+                                                             -1, COERCE_DONTCARE);
+             transfn_args = makeList2(arg0, arg1);
+
+             /*
+              * the state transition function always returns the same type
+              * as its first argument
+              */
+             if (transfn_ret_type == ANYARRAYOID ||
+                 transfn_ret_type == ANYELEMENTOID)
+                 transfn_ret_type = transfn_arg_types[0];
+         }
+         else if (transfn_nargs == 1)
+         /*
+          * base type was ANY, therefore the aggregate return type should
+          * be non-polymorphic
+          */
+         {
+             Oid    finaltype = get_func_rettype(aggref->aggfnoid);
+
+             /*
+              * this should have been prevented in AggregateCreate,
+              * but check anyway
+              */
+             if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
+                 elog(ERROR, "aggregate with base type ANY must have a " \
+                             "non-polymorphic return type");
+
+             /* see if we have a final function */
+             if (OidIsValid(finalfn_oid))
+             {
+                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
+                 if (finalfn_nargs != 1)
+                     elog(ERROR, "final function takes unexpected number " \
+                                 "of arguments: %d", finalfn_nargs);
+
+                 /*
+                  * final function argument is always the same as the state
+                  * function return type
+                  */
+                 if (finalfn_arg_types[0] != ANYARRAYOID &&
+                     finalfn_arg_types[0] != ANYELEMENTOID)
+                 {
+                     /* if it is not ambiguous, use it */
+                     transfn_ret_type = finalfn_arg_types[0];
+                 }
+                 else
+                 {
+                     /* if it is ambiguous, try to derive it */
+                     finalfn_ret_type = finaltype;
+                     finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0],
+                                                             finalfn_ret_type);
+                     transfn_ret_type = finalfn_arg_types[0];
+                 }
+             }
+             else
+                 transfn_ret_type = finaltype;
+
+             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+                                                         transfn_ret_type);
+
+             /*
+              * Build arg list to use on the transfn FuncExpr node. We really
+              * only care that the node type is correct so that the transfn
+              * can discover the actual argument types at runtime using
+              * get_fn_expr_argtype()
+              */
+             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
+                                                             -1, COERCE_DONTCARE);
+             transfn_args = makeList1(arg0);
+         }
+         else
+             elog(ERROR, "state transition function takes unexpected number " \
+                         "of arguments: %d", transfn_nargs);
+
+         if (OidIsValid(finalfn_oid))
+         {
+             /* get the final function argument and return types */
+             if (finalfn_ret_type == InvalidOid)
+                 finalfn_ret_type = get_func_rettype(finalfn_oid);
+
+             if (!finalfn_arg_types)
+             {
+                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
+                 if (finalfn_nargs != 1)
+                     elog(ERROR, "final function takes unexpected number " \
+                                 "of arguments: %d", finalfn_nargs);
+             }
+
+             /*
+              * final function argument is always the same as the state
+              * function return type, which by now should have been resolved
+              */
+             if (finalfn_arg_types[0] == ANYARRAYOID ||
+                 finalfn_arg_types[0] == ANYELEMENTOID)
+                 finalfn_arg_types[0] = transfn_ret_type;
+
+             /*
+              * Build arg list to use on the finalfn FuncExpr node. We really
+              * only care that the node type is correct so that the finalfn
+              * can discover the actual argument type at runtime using
+              * get_fn_expr_argtype()
+              */
+             arg0 = (Node *) makeRelabelType((Expr *) NULL, finalfn_arg_types[0],
+                                                             -1, COERCE_DONTCARE);
+             finalfn_args = makeList1(arg0);
+
+             finalfn_ret_type = resolve_type(finalfn_ret_type,
+                                                 finalfn_arg_types[0]);
+         }
+
+         fmgr_info(transfn_oid, &peraggstate->transfn);
+         transfnexpr = (FuncExpr *) make_funcclause(transfn_oid,
+                           transfn_ret_type,
+                           false,            /* cannot be a set */
+                           COERCE_DONTCARE,    /* to match any user expr */
+                           transfn_args);
+         peraggstate->transfn.fn_expr = (Node *) transfnexpr;
+
+         if (OidIsValid(finalfn_oid))
+         {
+             fmgr_info(finalfn_oid, &peraggstate->finalfn);
+             finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid,
+                           finalfn_ret_type,
+                           false,            /* cannot be a set */
+                           COERCE_DONTCARE,    /* to match any user expr */
+                           finalfn_args);
+             peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
+         }
+
          /*
           * initval is potentially null, so don't try to access it as a
           * struct field. Must do it the hard way with SysCacheGetAttr.
***************
*** 1250,1263 ****
              peraggstate->initValue = (Datum) 0;
          else
              peraggstate->initValue = GetAggInitVal(textInitVal,
!                                                    aggform->aggtranstype);
!
!         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
!         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
!
!         fmgr_info(transfn_oid, &peraggstate->transfn);
!         if (OidIsValid(finalfn_oid))
!             fmgr_info(finalfn_oid, &peraggstate->finalfn);

          /*
           * If the transfn is strict and the initval is NULL, make sure
--- 1423,1429 ----
              peraggstate->initValue = (Datum) 0;
          else
              peraggstate->initValue = GetAggInitVal(textInitVal,
!                                                    transfn_arg_types[0]);

          /*
           * If the transfn is strict and the initval is NULL, make sure
***************
*** 1468,1471 ****
--- 1634,1670 ----
      elog(ERROR, "Aggregate function %u called as normal function",
           fcinfo->flinfo->fn_oid);
      return (Datum) 0;            /* keep compiler quiet */
+ }
+
+ static Oid
+ resolve_type(Oid type_to_resolve, Oid context_type)
+ {
+     Oid        resolved_type;
+
+     if (context_type == ANYARRAYOID || context_type == ANYELEMENTOID)
+         resolved_type = type_to_resolve;
+     else if (type_to_resolve == ANYARRAYOID)
+     /* any array */
+     {
+         Oid        context_type_arraytype = get_array_type(context_type);
+
+         if (context_type_arraytype != InvalidOid)
+             resolved_type = context_type_arraytype;
+         else
+             resolved_type = context_type;
+     }
+     else if (type_to_resolve == ANYELEMENTOID)
+     /* any element */
+     {
+         Oid        context_type_elemtype = get_element_type(context_type);
+
+         if (context_type_elemtype != InvalidOid)
+             resolved_type = context_type_elemtype;
+         else
+             resolved_type = context_type;
+     }
+     else
+         resolved_type = type_to_resolve;
+
+     return resolved_type;
  }
Index: src/backend/executor/nodeSubplan.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v
retrieving revision 1.47
diff -c -r1.47 nodeSubplan.c
*** src/backend/executor/nodeSubplan.c    22 Jun 2003 22:04:54 -0000    1.47
--- src/backend/executor/nodeSubplan.c    24 Jun 2003 02:24:06 -0000
***************
*** 28,50 ****
  #include "utils/datum.h"
  #include "utils/lsyscache.h"

-
- typedef struct ArrayBuildState
- {
-     MemoryContext mcontext;        /* where all the temp stuff is kept */
-     Datum       *dvalues;        /* array of accumulated Datums */
-     /*
-      * The allocated size of dvalues[] is always a multiple of
-      * ARRAY_ELEMS_CHUNKSIZE
-      */
- #define ARRAY_ELEMS_CHUNKSIZE    64
-     int            nelems;            /* number of valid Datums in dvalues[] */
-     Oid            element_type;    /* data type of the Datums */
-     int16        typlen;            /* needed info about datatype */
-     bool        typbyval;
-     char        typalign;
- } ArrayBuildState;
-
  static Datum ExecHashSubPlan(SubPlanState *node,
                               ExprContext *econtext,
                               bool *isNull);
--- 28,33 ----
***************
*** 54,66 ****
  static void buildSubPlanHash(SubPlanState *node);
  static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
  static bool tupleAllNulls(HeapTuple tuple);
- static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
-                                          Datum dvalue, bool disnull,
-                                          Oid element_type,
-                                          MemoryContext rcontext);
- static Datum makeArrayResult(ArrayBuildState *astate,
-                              MemoryContext rcontext);
-

  /* ----------------------------------------------------------------
   *        ExecSubPlan
--- 37,42 ----
***************
*** 224,229 ****
--- 200,206 ----
      PlanState  *planstate = node->planstate;
      SubLinkType subLinkType = subplan->subLinkType;
      bool        useOr = subplan->useOr;
+     bool        isExpr = subplan->isExpr;
      MemoryContext oldcontext;
      TupleTableSlot *slot;
      Datum        result;
***************
*** 294,299 ****
--- 271,281 ----
          bool        rownull = false;
          int            col = 1;
          List       *plst;
+         int            numelems;
+         int            elemnum;
+         Datum        dvalue;
+         Datum       *dvalues = NULL;
+         bool        disnull;

          if (subLinkType == EXISTS_SUBLINK)
          {
***************
*** 331,339 ****

          if (subLinkType == ARRAY_SUBLINK)
          {
-             Datum    dvalue;
-             bool    disnull;
-
              found = true;
              /* stash away current value */
              dvalue = heap_getattr(tup, 1, tdesc, &disnull);
--- 313,318 ----
***************
*** 351,448 ****
          found = true;

          /*
!          * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
!          * operators for columns of tuple.
           */
!         plst = subplan->paramIds;
!         foreach(lst, node->exprs)
          {
!             ExprState  *exprstate = (ExprState *) lfirst(lst);
!             int            paramid = lfirsti(plst);
!             ParamExecData *prmdata;
!             Datum        expresult;
!             bool        expnull;
!
!             /*
!              * Load up the Param representing this column of the sub-select.
!              */
!             prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
!             Assert(prmdata->execPlan == NULL);
!             prmdata->value = heap_getattr(tup, col, tdesc,
!                                           &(prmdata->isnull));

!             /*
!              * Now we can eval the combining operator for this column.
!              */
!             expresult = ExecEvalExprSwitchContext(exprstate, econtext,
!                                                   &expnull, NULL);
!
!             /*
!              * Combine the result into the row result as appropriate.
!              */
!             if (col == 1)
              {
!                 rowresult = expresult;
!                 rownull = expnull;
              }
!             else if (useOr)
              {
!                 /* combine within row per OR semantics */
!                 if (expnull)
!                     rownull = true;
!                 else if (DatumGetBool(expresult))
                  {
!                     rowresult = BoolGetDatum(true);
!                     rownull = false;
!                     break;        /* needn't look at any more columns */
                  }
              }
              else
              {
!                 /* combine within row per AND semantics */
!                 if (expnull)
!                     rownull = true;
!                 else if (!DatumGetBool(expresult))
!                 {
!                     rowresult = BoolGetDatum(false);
!                     rownull = false;
!                     break;        /* needn't look at any more columns */
!                 }
              }

-             plst = lnext(plst);
-             col++;
          }

!         if (subLinkType == ANY_SUBLINK)
          {
!             /* combine across rows per OR semantics */
!             if (rownull)
!                 *isNull = true;
!             else if (DatumGetBool(rowresult))
              {
!                 result = BoolGetDatum(true);
!                 *isNull = false;
!                 break;            /* needn't look at any more rows */
              }
!         }
!         else if (subLinkType == ALL_SUBLINK)
!         {
!             /* combine across rows per AND semantics */
!             if (rownull)
!                 *isNull = true;
!             else if (!DatumGetBool(rowresult))
              {
!                 result = BoolGetDatum(false);
!                 *isNull = false;
!                 break;            /* needn't look at any more rows */
              }
-         }
-         else
-         {
-             /* must be MULTIEXPR_SUBLINK */
-             result = rowresult;
-             *isNull = rownull;
          }
      }

--- 330,492 ----
          found = true;

          /*
!          * When isExpr is true, we have either a scalar expression or an
!          * array. In the former case, this is no different than the !isExpr
!          * case. In the latter case, iterate over the elements as if they
!          * were from multiple input tuples.
           */
!         if (!isExpr)
!             numelems = 1;
!         else
          {
!             Oid        expr_typeid = tdesc->attrs[0]->atttypid;

!             if (expr_typeid != subplan->exprtype)
              {
!                 subplan->exprtype = expr_typeid;
!                 subplan->elemtype = get_element_type(expr_typeid);
!
!                 if (subplan->elemtype != InvalidOid)
!                     get_typlenbyvalalign(subplan->elemtype,
!                                          &subplan->elmlen,
!                                          &subplan->elmbyval,
!                                          &subplan->elmalign);
              }
!
!             /* get current value */
!             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
!
!             /* XXX this will need work if/when arrays support NULL elements */
!             if (!disnull)
              {
!                 if (subplan->elemtype != InvalidOid)
!                 {
!                     ArrayType   *v = DatumGetArrayTypeP(dvalue);
!
!                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
!                                       subplan->elmbyval, subplan->elmalign,
!                                         &dvalues, &numelems);
!                 }
!                 else
                  {
!                     numelems = 1;
!                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                     dvalues[0] = dvalue;
                  }
              }
              else
              {
!                 numelems = 1;
!                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                 dvalues[0] = (Datum) 0;
              }

          }

!         for (elemnum = 0; elemnum < numelems; elemnum++)
          {
!             /*
!              * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
!              * operators for columns of tuple.
!              */
!             col = 1;
!             plst = subplan->paramIds;
!             foreach(lst, node->exprs)
              {
!                 ExprState  *exprstate = (ExprState *) lfirst(lst);
!                 int            paramid = lfirsti(plst);
!                 ParamExecData *prmdata;
!                 Datum        expresult;
!                 bool        expnull;
!
!                 /*
!                  * Load up the Param representing this column of the sub-select.
!                  */
!                 prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
!                 Assert(prmdata->execPlan == NULL);
!
!                 if (!isExpr)
!                     prmdata->value = heap_getattr(tup, col, tdesc,
!                                                   &(prmdata->isnull));
!                 else
!                 {
!                     prmdata->value = dvalues[elemnum];
!                     prmdata->isnull = disnull;
!                 }
!
!                 /*
!                  * Now we can eval the combining operator for this column.
!                  */
!                 expresult = ExecEvalExprSwitchContext(exprstate, econtext,
!                                                       &expnull, NULL);
!
!                 /*
!                  * Combine the result into the row result as appropriate.
!                  */
!                 if (col == 1)
!                 {
!                     rowresult = expresult;
!                     rownull = expnull;
!                 }
!                 else if (useOr)
!                 {
!                     /* combine within row per OR semantics */
!                     if (expnull)
!                         rownull = true;
!                     else if (DatumGetBool(expresult))
!                     {
!                         rowresult = BoolGetDatum(true);
!                         rownull = false;
!                         break;        /* needn't look at any more columns */
!                     }
!                 }
!                 else
!                 {
!                     /* combine within row per AND semantics */
!                     if (expnull)
!                         rownull = true;
!                     else if (!DatumGetBool(expresult))
!                     {
!                         rowresult = BoolGetDatum(false);
!                         rownull = false;
!                         break;        /* needn't look at any more columns */
!                     }
!                 }
!
!                 plst = lnext(plst);
!                 col++;
              }
!
!             if (subLinkType == ANY_SUBLINK)
              {
!                 /* combine across rows per OR semantics */
!                 if (rownull)
!                     *isNull = true;
!                 else if (DatumGetBool(rowresult))
!                 {
!                     result = BoolGetDatum(true);
!                     *isNull = false;
!                     break;            /* needn't look at any more rows */
!                 }
!             }
!             else if (subLinkType == ALL_SUBLINK)
!             {
!                 /* combine across rows per AND semantics */
!                 if (rownull)
!                     *isNull = true;
!                 else if (!DatumGetBool(rowresult))
!                 {
!                     result = BoolGetDatum(false);
!                     *isNull = false;
!                     break;            /* needn't look at any more rows */
!                 }
!             }
!             else
!             {
!                 /* must be MULTIEXPR_SUBLINK */
!                 result = rowresult;
!                 *isNull = rownull;
              }
          }
      }

***************
*** 480,485 ****
--- 524,530 ----
  buildSubPlanHash(SubPlanState *node)
  {
      SubPlan       *subplan = (SubPlan *) node->xprstate.expr;
+     bool        isExpr = subplan->isExpr;
      PlanState  *planstate = node->planstate;
      int            ncols = length(node->exprs);
      ExprContext *innerecontext = node->innerecontext;
***************
*** 487,492 ****
--- 532,538 ----
      MemoryContext oldcontext;
      int            nbuckets;
      TupleTableSlot *slot;
+     TupleTableSlot *arrslot = NULL;

      Assert(subplan->subLinkType == ANY_SUBLINK);
      Assert(!subplan->useOr);
***************
*** 566,608 ****
      {
          HeapTuple    tup = slot->val;
          TupleDesc    tdesc = slot->ttc_tupleDescriptor;
!         int            col = 1;
          List       *plst;
          bool        isnew;

          /*
!          * Load up the Params representing the raw sub-select outputs,
!          * then form the projection tuple to store in the hashtable.
           */
!         foreach(plst, subplan->paramIds)
          {
!             int            paramid = lfirsti(plst);
!             ParamExecData *prmdata;

!             prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
!             Assert(prmdata->execPlan == NULL);
!             prmdata->value = heap_getattr(tup, col, tdesc,
!                                           &(prmdata->isnull));
!             col++;
!         }
!         slot = ExecProject(node->projRight, NULL);
!         tup = slot->val;

-         /*
-          * If result contains any nulls, store separately or not at all.
-          * (Since we know the projection tuple has no junk columns, we
-          * can just look at the overall hasnull info bit, instead of
-          * groveling through the columns.)
-          */
-         if (HeapTupleNoNulls(tup))
-         {
-             (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
-             node->havehashrows = true;
          }
!         else if (node->hashnulls)
          {
!             (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
!             node->havenullrows = true;
          }

          /*
--- 612,750 ----
      {
          HeapTuple    tup = slot->val;
          TupleDesc    tdesc = slot->ttc_tupleDescriptor;
!         TupleDesc    arrtdesc = NULL;
          List       *plst;
          bool        isnew;
+         int            numelems;
+         int            elemnum;
+         Datum        dvalue;
+         Datum       *dvalues = NULL;
+         bool        disnull;

          /*
!          * When isExpr is true, we have either a scalar expression or an
!          * array. In the former case, this is no different than the !isExpr
!          * case. In the latter case, iterate over the elements as if they
!          * were from multiple input tuples.
           */
!         if (!isExpr)
!             numelems = 1;
!         else
          {
!             Oid        expr_typeid = tdesc->attrs[0]->atttypid;

!             if (expr_typeid != subplan->exprtype)
!             {
!                 subplan->exprtype = expr_typeid;
!                 subplan->elemtype = get_element_type(expr_typeid);
!
!                 if (subplan->elemtype != InvalidOid)
!                     get_typlenbyvalalign(subplan->elemtype,
!                                          &subplan->elmlen,
!                                          &subplan->elmbyval,
!                                          &subplan->elmalign);
!             }
!
!             /* get current value */
!             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
!
!             if (subplan->elemtype != InvalidOid)
!             {
!                 TupleTable    tupleTable;
!                 ArrayType   *v = DatumGetArrayTypeP(dvalue);
!
!                 arrtdesc = CreateTemplateTupleDesc(1, false);
!                 TupleDescInitEntry(arrtdesc, 1, "elem", subplan->elemtype,
!                                                             -1, 0, false);
!
!                 tupleTable = ExecCreateTupleTable(1);
!                 arrslot = ExecAllocTableSlot(tupleTable);
!                 ExecSetSlotDescriptor(arrslot, arrtdesc, true);
!
!                 /* XXX this will need work if/when arrays support NULL elements */
!                 if (!disnull)
!                 {
!                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
!                                       subplan->elmbyval, subplan->elmalign,
!                                         &dvalues, &numelems);
!                 }
!                 else
!                 {
!                     numelems = 1;
!                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                     dvalues[0] = (Datum) 0;
!                 }
!             }
!             else
!             {
!                 numelems = 1;
!                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                 dvalues[0] = dvalue;
!             }

          }
!
!         for (elemnum = 0; elemnum < numelems; elemnum++)
          {
!             int    col = 1;
!
!             if (!isExpr || subplan->elemtype == InvalidOid)
!             {
!                 /*
!                  * Load up the Params representing the raw sub-select outputs,
!                  * then form the projection tuple to store in the hashtable.
!                  */
!                 foreach(plst, subplan->paramIds)
!                 {
!                     int            paramid = lfirsti(plst);
!                     ParamExecData *prmdata;
!
!                     prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
!                     Assert(prmdata->execPlan == NULL);
!
!                     prmdata->value = heap_getattr(tup, col, tdesc,
!                                                   &(prmdata->isnull));
!
!                     col++;
!                 }
!                 slot = ExecProject(node->projRight, NULL);
!                 tup = slot->val;
!             }
!             else
!             {
!                 /*
!                  * For array type expressions, we need to build up our own
!                  * tuple and slot
!                  */
!                 char        nullflag;
!
!                 nullflag = disnull ? 'n' : ' ';
!                 tup = heap_formtuple(arrtdesc, &dvalues[elemnum], &nullflag);
!                 arrslot = ExecStoreTuple(tup, arrslot, InvalidBuffer, true);
!             }
!
!             /*
!              * If result contains any nulls, store separately or not at all.
!              * (Since we know the projection tuple has no junk columns, we
!              * can just look at the overall hasnull info bit, instead of
!              * groveling through the columns.)
!              */
!             if (HeapTupleNoNulls(tup))
!             {
!                 if (!isExpr)
!                     (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
!                 else
!                     (void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew);
!                 node->havehashrows = true;
!             }
!             else if (node->hashnulls)
!             {
!                 if (!isExpr)
!                     (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
!                 else
!                     (void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew);
!                 node->havenullrows = true;
!             }
          }

          /*
***************
*** 619,624 ****
--- 761,768 ----
       * have the potential for a double free attempt.
       */
      ExecClearTuple(node->projRight->pi_slot);
+     if (arrslot)
+         ExecClearTuple(arrslot);

      MemoryContextSwitchTo(oldcontext);
  }
***************
*** 1098,1199 ****
          prm->execPlan = node;
          parent->chgParam = bms_add_member(parent->chgParam, paramid);
      }
- }
-
- /*
-  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
-  *
-  *    astate is working state (NULL on first call)
-  *    rcontext is where to keep working state
-  */
- static ArrayBuildState *
- accumArrayResult(ArrayBuildState *astate,
-                  Datum dvalue, bool disnull,
-                  Oid element_type,
-                  MemoryContext rcontext)
- {
-     MemoryContext arr_context,
-                   oldcontext;
-
-     if (astate == NULL)
-     {
-         /* First time through --- initialize */
-
-         /* Make a temporary context to hold all the junk */
-         arr_context = AllocSetContextCreate(rcontext,
-                                             "ARRAY_SUBLINK Result",
-                                             ALLOCSET_DEFAULT_MINSIZE,
-                                             ALLOCSET_DEFAULT_INITSIZE,
-                                             ALLOCSET_DEFAULT_MAXSIZE);
-         oldcontext = MemoryContextSwitchTo(arr_context);
-         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
-         astate->mcontext = arr_context;
-         astate->dvalues = (Datum *)
-             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
-         astate->nelems = 0;
-         astate->element_type = element_type;
-         get_typlenbyvalalign(element_type,
-                              &astate->typlen,
-                              &astate->typbyval,
-                              &astate->typalign);
-     }
-     else
-     {
-         oldcontext = MemoryContextSwitchTo(astate->mcontext);
-         Assert(astate->element_type == element_type);
-         /* enlarge dvalues[] if needed */
-         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
-             astate->dvalues = (Datum *)
-                 repalloc(astate->dvalues,
-                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
-     }
-
-     if (disnull)
-         elog(ERROR, "NULL elements not allowed in Arrays");
-
-     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
-     astate->dvalues[astate->nelems++] =
-         datumCopy(dvalue, astate->typbyval, astate->typlen);
-
-     MemoryContextSwitchTo(oldcontext);
-
-     return astate;
- }
-
- /*
-  * makeArrayResult - produce final result of ARRAY_SUBLINK
-  *
-  *    astate is working state (not NULL)
-  *    rcontext is where to construct result
-  */
- static Datum
- makeArrayResult(ArrayBuildState *astate,
-                 MemoryContext rcontext)
- {
-     ArrayType  *result;
-     int            dims[1];
-     int            lbs[1];
-     MemoryContext oldcontext;
-
-     /* Build the final array result in rcontext */
-     oldcontext = MemoryContextSwitchTo(rcontext);
-
-     dims[0] = astate->nelems;
-     lbs[0] = 1;
-
-     result = construct_md_array(astate->dvalues,
-                                 1,
-                                 dims,
-                                 lbs,
-                                 astate->element_type,
-                                 astate->typlen,
-                                 astate->typbyval,
-                                 astate->typalign);
-
-     MemoryContextSwitchTo(oldcontext);
-
-     /* Clean up all the junk */
-     MemoryContextDelete(astate->mcontext);
-
-     return PointerGetDatum(result);
  }
--- 1242,1245 ----
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.252
diff -c -r1.252 copyfuncs.c
*** src/backend/nodes/copyfuncs.c    6 Jun 2003 15:04:02 -0000    1.252
--- src/backend/nodes/copyfuncs.c    24 Jun 2003 02:24:06 -0000
***************
*** 728,733 ****
--- 728,734 ----
      COPY_SCALAR_FIELD(agglevelsup);
      COPY_SCALAR_FIELD(aggstar);
      COPY_SCALAR_FIELD(aggdistinct);
+     COPY_NODE_FIELD(args);

      return newnode;
  }
***************
*** 826,831 ****
--- 827,833 ----

      COPY_SCALAR_FIELD(subLinkType);
      COPY_SCALAR_FIELD(useOr);
+     COPY_SCALAR_FIELD(isExpr);
      COPY_NODE_FIELD(lefthand);
      COPY_NODE_FIELD(operName);
      COPY_OIDLIST_FIELD(operOids);
***************
*** 844,849 ****
--- 846,857 ----

      COPY_SCALAR_FIELD(subLinkType);
      COPY_SCALAR_FIELD(useOr);
+     COPY_SCALAR_FIELD(isExpr);
+     COPY_SCALAR_FIELD(exprtype);
+     COPY_SCALAR_FIELD(elemtype);
+     COPY_SCALAR_FIELD(elmlen);
+     COPY_SCALAR_FIELD(elmbyval);
+     COPY_SCALAR_FIELD(elmalign);
      COPY_NODE_FIELD(exprs);
      COPY_INTLIST_FIELD(paramIds);
      COPY_NODE_FIELD(plan);
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.195
diff -c -r1.195 equalfuncs.c
*** src/backend/nodes/equalfuncs.c    6 Jun 2003 15:04:02 -0000    1.195
--- src/backend/nodes/equalfuncs.c    24 Jun 2003 02:24:06 -0000
***************
*** 205,210 ****
--- 205,211 ----
      COMPARE_SCALAR_FIELD(agglevelsup);
      COMPARE_SCALAR_FIELD(aggstar);
      COMPARE_SCALAR_FIELD(aggdistinct);
+     COMPARE_NODE_FIELD(args);

      return true;
  }
***************
*** 301,306 ****
--- 302,308 ----
  {
      COMPARE_SCALAR_FIELD(subLinkType);
      COMPARE_SCALAR_FIELD(useOr);
+     COMPARE_SCALAR_FIELD(isExpr);
      COMPARE_NODE_FIELD(lefthand);
      COMPARE_NODE_FIELD(operName);
      COMPARE_OIDLIST_FIELD(operOids);
***************
*** 314,319 ****
--- 316,327 ----
  {
      COMPARE_SCALAR_FIELD(subLinkType);
      COMPARE_SCALAR_FIELD(useOr);
+     COMPARE_SCALAR_FIELD(isExpr);
+     COMPARE_SCALAR_FIELD(exprtype);
+     COMPARE_SCALAR_FIELD(elemtype);
+     COMPARE_SCALAR_FIELD(elmlen);
+     COMPARE_SCALAR_FIELD(elmbyval);
+     COMPARE_SCALAR_FIELD(elmalign);
      COMPARE_NODE_FIELD(exprs);
      COMPARE_INTLIST_FIELD(paramIds);
      /* should compare plans, but have to settle for comparing plan IDs */
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v
retrieving revision 1.208
diff -c -r1.208 outfuncs.c
*** src/backend/nodes/outfuncs.c    15 Jun 2003 22:51:45 -0000    1.208
--- src/backend/nodes/outfuncs.c    24 Jun 2003 02:24:06 -0000
***************
*** 616,621 ****
--- 616,622 ----
      WRITE_UINT_FIELD(agglevelsup);
      WRITE_BOOL_FIELD(aggstar);
      WRITE_BOOL_FIELD(aggdistinct);
+     WRITE_NODE_FIELD(args);
  }

  static void
***************
*** 701,706 ****
--- 702,708 ----

      WRITE_ENUM_FIELD(subLinkType, SubLinkType);
      WRITE_BOOL_FIELD(useOr);
+     WRITE_BOOL_FIELD(isExpr);
      WRITE_NODE_FIELD(lefthand);
      WRITE_NODE_FIELD(operName);
      WRITE_OIDLIST_FIELD(operOids);
***************
*** 714,719 ****
--- 716,727 ----

      WRITE_ENUM_FIELD(subLinkType, SubLinkType);
      WRITE_BOOL_FIELD(useOr);
+     WRITE_BOOL_FIELD(isExpr);
+     WRITE_OID_FIELD(exprtype);
+     WRITE_OID_FIELD(elemtype);
+     WRITE_INT_FIELD(elmlen);
+     WRITE_BOOL_FIELD(elmbyval);
+     WRITE_CHAR_FIELD(elmalign);
      WRITE_NODE_FIELD(exprs);
      WRITE_INTLIST_FIELD(paramIds);
      WRITE_NODE_FIELD(plan);
Index: src/backend/nodes/readfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v
retrieving revision 1.154
diff -c -r1.154 readfuncs.c
*** src/backend/nodes/readfuncs.c    6 Jun 2003 15:04:02 -0000    1.154
--- src/backend/nodes/readfuncs.c    24 Jun 2003 02:24:06 -0000
***************
*** 416,421 ****
--- 416,422 ----
      READ_UINT_FIELD(agglevelsup);
      READ_BOOL_FIELD(aggstar);
      READ_BOOL_FIELD(aggdistinct);
+     READ_NODE_FIELD(args);

      READ_DONE();
  }
***************
*** 545,550 ****
--- 546,552 ----

      READ_ENUM_FIELD(subLinkType, SubLinkType);
      READ_BOOL_FIELD(useOr);
+     READ_BOOL_FIELD(isExpr);
      READ_NODE_FIELD(lefthand);
      READ_NODE_FIELD(operName);
      READ_OIDLIST_FIELD(operOids);
Index: src/backend/optimizer/plan/subselect.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/plan/subselect.c,v
retrieving revision 1.76
diff -c -r1.76 subselect.c
*** src/backend/optimizer/plan/subselect.c    6 Jun 2003 15:04:02 -0000    1.76
--- src/backend/optimizer/plan/subselect.c    24 Jun 2003 02:24:06 -0000
***************
*** 83,89 ****

  static List *convert_sublink_opers(List *lefthand, List *operOids,
                                     List *targetlist, int rtindex,
!                                    List **righthandIds);
  static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
  static Node *replace_correlation_vars_mutator(Node *node, void *context);
  static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
--- 83,89 ----

  static List *convert_sublink_opers(List *lefthand, List *operOids,
                                     List *targetlist, int rtindex,
!                                    bool isExpr, List **righthandIds);
  static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
  static Node *replace_correlation_vars_mutator(Node *node, void *context);
  static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
***************
*** 299,304 ****
--- 299,310 ----
       */
      node->subLinkType = slink->subLinkType;
      node->useOr = slink->useOr;
+     node->isExpr = slink->isExpr;
+     node->exprtype = InvalidOid;
+     node->elemtype = InvalidOid;
+     node->elmlen = 0;
+     node->elmbyval = false;
+     node->elmalign = '\0';
      node->exprs = NIL;
      node->paramIds = NIL;
      node->useHashTable = false;
***************
*** 374,380 ****
          exprs = convert_sublink_opers(lefthand,
                                        slink->operOids,
                                        plan->targetlist,
!                                       0,
                                        &node->paramIds);
          node->setParam = listCopy(node->paramIds);
          PlannerInitPlan = lappend(PlannerInitPlan, node);
--- 380,386 ----
          exprs = convert_sublink_opers(lefthand,
                                        slink->operOids,
                                        plan->targetlist,
!                                       0, node->isExpr,
                                        &node->paramIds);
          node->setParam = listCopy(node->paramIds);
          PlannerInitPlan = lappend(PlannerInitPlan, node);
***************
*** 457,463 ****
          node->exprs = convert_sublink_opers(lefthand,
                                              slink->operOids,
                                              plan->targetlist,
!                                             0,
                                              &node->paramIds);

          /*
--- 463,469 ----
          node->exprs = convert_sublink_opers(lefthand,
                                              slink->operOids,
                                              plan->targetlist,
!                                             0, node->isExpr,
                                              &node->paramIds);

          /*
***************
*** 499,505 ****
  static List *
  convert_sublink_opers(List *lefthand, List *operOids,
                        List *targetlist, int rtindex,
!                       List **righthandIds)
  {
      List       *result = NIL;
      List       *lst;
--- 505,511 ----
  static List *
  convert_sublink_opers(List *lefthand, List *operOids,
                        List *targetlist, int rtindex,
!                       bool isExpr, List **righthandIds)
  {
      List       *result = NIL;
      List       *lst;
***************
*** 554,566 ****
           * are not expecting to have to resolve unknown Params, so
           * it's okay to pass a null pstate.)
           */
!         result = lappend(result,
!                          make_op_expr(NULL,
!                                       tup,
!                                       leftop,
!                                       rightop,
!                                       exprType(leftop),
!                                       te->resdom->restype));

          ReleaseSysCache(tup);

--- 560,597 ----
           * are not expecting to have to resolve unknown Params, so
           * it's okay to pass a null pstate.)
           */
!         if (!isExpr)
!         {
!             result = lappend(result,
!                              make_op_expr(NULL,
!                                           tup,
!                                           leftop,
!                                           rightop,
!                                           exprType(leftop),
!                                           te->resdom->restype));
!         }
!         else
!         {
!             Oid        exprtype = te->resdom->restype;
!             Oid        elemtype = get_element_type(exprtype);
!
!             if (elemtype != InvalidOid)
!                 result = lappend(result,
!                                  make_op_expr(NULL,
!                                               tup,
!                                               leftop,
!                                               rightop,
!                                               exprType(leftop),
!                                               elemtype));
!             else
!                 result = lappend(result,
!                                  make_op_expr(NULL,
!                                               tup,
!                                               leftop,
!                                               rightop,
!                                               exprType(leftop),
!                                               exprtype));
!         }

          ReleaseSysCache(tup);

***************
*** 671,683 ****
      /*
       * The sublink type must be "= ANY" --- that is, an IN operator.
       * (We require the operator name to be unqualified, which may be
!      * overly paranoid, or may not be.)
       */
      if (sublink->subLinkType != ANY_SUBLINK)
          return NULL;
      if (length(sublink->operName) != 1 ||
          strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
          return NULL;
      /*
       * The sub-select must not refer to any Vars of the parent query.
       * (Vars of higher levels should be okay, though.)
--- 702,718 ----
      /*
       * The sublink type must be "= ANY" --- that is, an IN operator.
       * (We require the operator name to be unqualified, which may be
!      * overly paranoid, or may not be.) It must not be an Expression
!      * sublink.
       */
      if (sublink->subLinkType != ANY_SUBLINK)
          return NULL;
      if (length(sublink->operName) != 1 ||
          strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
          return NULL;
+     if (sublink->isExpr)
+         return NULL;
+
      /*
       * The sub-select must not refer to any Vars of the parent query.
       * (Vars of higher levels should be okay, though.)
***************
*** 730,736 ****
      exprs = convert_sublink_opers(sublink->lefthand,
                                    sublink->operOids,
                                    subselect->targetList,
!                                   rtindex,
                                    &ininfo->sub_targetlist);
      return (Node *) make_ands_explicit(exprs);
  }
--- 765,771 ----
      exprs = convert_sublink_opers(sublink->lefthand,
                                    sublink->operOids,
                                    subselect->targetList,
!                                   rtindex, sublink->isExpr,
                                    &ininfo->sub_targetlist);
      return (Node *) make_ands_explicit(exprs);
  }
Index: src/backend/optimizer/util/clauses.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/util/clauses.c,v
retrieving revision 1.139
diff -c -r1.139 clauses.c
*** src/backend/optimizer/util/clauses.c    6 Jun 2003 15:04:02 -0000    1.139
--- src/backend/optimizer/util/clauses.c    24 Jun 2003 02:24:06 -0000
***************
*** 133,138 ****
--- 133,160 ----
  }

  /*****************************************************************************
+  *              FUNCTION clause functions
+  *****************************************************************************/
+
+ /*
+  * make_funcclause
+  *        Creates a function clause given its function info and argument list.
+  */
+ Expr *
+ make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
+                             CoercionForm funcformat, List *funcargs)
+ {
+     FuncExpr   *expr = makeNode(FuncExpr);
+
+     expr->funcid = funcid;
+     expr->funcresulttype = funcresulttype;
+     expr->funcretset = funcretset;
+     expr->funcformat = funcformat;
+     expr->args = funcargs;
+     return (Expr *) expr;
+ }
+
+ /*****************************************************************************
   *        NOT clause functions
   *****************************************************************************/

Index: src/backend/parser/gram.y
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/gram.y,v
retrieving revision 2.417
diff -c -r2.417 gram.y
*** src/backend/parser/gram.y    17 Jun 2003 23:12:36 -0000    2.417
--- src/backend/parser/gram.y    24 Jun 2003 02:24:06 -0000
***************
*** 5490,5495 ****
--- 5490,5496 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ANY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = makeList1(makeString("="));
                      n->subselect = $3;
***************
*** 5500,5505 ****
--- 5501,5507 ----
                      /* Make an IN node */
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ANY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = makeList1(makeString("="));
                      n->subselect = $4;
***************
*** 5511,5516 ****
--- 5513,5519 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = $3;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = $2;
                      n->subselect = $4;
***************
*** 5521,5526 ****
--- 5524,5530 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = MULTIEXPR_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = $2;
                      n->subselect = $3;
***************
*** 5904,5909 ****
--- 5908,5914 ----
                      {
                              SubLink *n = (SubLink *)$3;
                              n->subLinkType = ANY_SUBLINK;
+                             n->isExpr = false;
                              n->lefthand = makeList1($1);
                              n->operName = makeList1(makeString("="));
                              $$ = (Node *)n;
***************
*** 5931,5936 ****
--- 5936,5942 ----
                      {
                          /* Make an IN node */
                          SubLink *n = (SubLink *)$4;
+                         n->isExpr = false;
                          n->subLinkType = ANY_SUBLINK;
                          n->lefthand = makeList1($1);
                          n->operName = makeList1(makeString("="));
***************
*** 5957,5967 ****
--- 5963,6000 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = $3;
+                     n->isExpr = false;
                      n->lefthand = makeList1($1);
                      n->operName = $2;
                      n->subselect = $4;
                      $$ = (Node *)n;
                  }
+             | a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op
+                 {
+                     SubLink *n = makeNode(SubLink);
+                     SelectStmt *s = makeNode(SelectStmt);
+                     ResTarget *r = makeNode(ResTarget);
+
+                     r->name = NULL;
+                     r->indirection = NIL;
+                     r->val = (Node *)$5;
+
+                     s->distinctClause = NIL;
+                     s->targetList = makeList1(r);
+                     s->into = NULL;
+                     s->intoColNames = NIL;
+                     s->fromClause = NIL;
+                     s->whereClause = NULL;
+                     s->groupClause = NIL;
+                     s->havingClause = NULL;
+
+                     n->subLinkType = $3;
+                     n->isExpr = true;
+                     n->lefthand = makeList1($1);
+                     n->operName = $2;
+                     n->subselect = (Node *) s;
+                     $$ = (Node *)n;
+                 }
              | UNIQUE select_with_parens %prec Op
                  {
                      /* Not sure how to get rid of the parentheses
***************
*** 6538,6543 ****
--- 6571,6577 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = EXPR_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $1;
***************
*** 6547,6552 ****
--- 6581,6587 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = EXISTS_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $2;
***************
*** 6556,6561 ****
--- 6591,6597 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ARRAY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $2;
***************
*** 6730,6735 ****
--- 6766,6772 ----
  in_expr:    select_with_parens
                  {
                      SubLink *n = makeNode(SubLink);
+                     n->isExpr = false;
                      n->subselect = $1;
                      /* other fields will be filled later */
                      $$ = (Node *)n;
Index: src/backend/parser/parse_coerce.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v
retrieving revision 2.97
diff -c -r2.97 parse_coerce.c
*** src/backend/parser/parse_coerce.c    26 May 2003 00:11:27 -0000    2.97
--- src/backend/parser/parse_coerce.c    24 Jun 2003 02:24:06 -0000
***************
*** 859,865 ****
      /* Get the element type based on the array type, if we have one */
      if (OidIsValid(array_typeid))
      {
!         array_typelem = get_element_type(array_typeid);
          if (!OidIsValid(array_typelem))
              elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
                   format_type_be(array_typeid));
--- 859,869 ----
      /* Get the element type based on the array type, if we have one */
      if (OidIsValid(array_typeid))
      {
!         if (array_typeid != ANYARRAYOID)
!             array_typelem = get_element_type(array_typeid);
!         else
!             array_typelem = ANYELEMENTOID;
!
          if (!OidIsValid(array_typelem))
              elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
                   format_type_be(array_typeid));
***************
*** 919,925 ****
      {
          if (!OidIsValid(array_typeid))
          {
!             array_typeid = get_array_type(elem_typeid);
              if (!OidIsValid(array_typeid))
                  elog(ERROR, "Cannot find array type for datatype %s",
                       format_type_be(elem_typeid));
--- 923,933 ----
      {
          if (!OidIsValid(array_typeid))
          {
!             if (elem_typeid != ANYELEMENTOID)
!                 array_typeid = get_array_type(elem_typeid);
!             else
!                 array_typeid = ANYARRAYOID;
!
              if (!OidIsValid(array_typeid))
                  elog(ERROR, "Cannot find array type for datatype %s",
                       format_type_be(elem_typeid));
***************
*** 1169,1174 ****
--- 1177,1187 ----
      /* Somewhat-fast path for domain -> base type case */
      if (srctype == targettype)
          return true;
+
+     /* Last of the fast-paths: check for matching polymorphic arrays */
+     if (targettype == ANYARRAYOID)
+         if (get_element_type(srctype) != InvalidOid)
+             return true;

      /* Else look in pg_cast */
      tuple = SearchSysCache(CASTSOURCETARGET,
Index: src/backend/parser/parse_expr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_expr.c,v
retrieving revision 1.148
diff -c -r1.148 parse_expr.c
*** src/backend/parser/parse_expr.c    29 Apr 2003 22:13:10 -0000    1.148
--- src/backend/parser/parse_expr.c    24 Jun 2003 02:24:06 -0000
***************
*** 436,441 ****
--- 436,442 ----
                      sublink->operName = NIL;
                      sublink->operOids = NIL;
                      sublink->useOr = FALSE;
+                     sublink->isExpr = FALSE;
                  }
                  else if (sublink->subLinkType == EXPR_SUBLINK ||
                           sublink->subLinkType == ARRAY_SUBLINK)
***************
*** 463,468 ****
--- 464,470 ----
                      sublink->operName = NIL;
                      sublink->operOids = NIL;
                      sublink->useOr = FALSE;
+                     sublink->isExpr = FALSE;
                  }
                  else
                  {
***************
*** 538,547 ****
                           * here, because make_subplan() will insert type
                           * coercion calls if needed.
                           */
!                         optup = oper(op,
!                                      exprType(lexpr),
!                                      exprType((Node *) tent->expr),
!                                      false);
                          opform = (Form_pg_operator) GETSTRUCT(optup);

                          if (opform->oprresult != BOOLOID)
--- 540,569 ----
                           * here, because make_subplan() will insert type
                           * coercion calls if needed.
                           */
!                         if (!sublink->isExpr)
!                         {
!                             optup = oper(op,
!                                          exprType(lexpr),
!                                          exprType((Node *) tent->expr),
!                                          false);
!                         }
!                         else
!                         {
!                             Oid        exprtype = exprType((Node *) tent->expr);
!                             Oid        elemtype = get_element_type(exprtype);
!
!                             if (elemtype != InvalidOid)
!                                 optup = oper(op,
!                                              exprType(lexpr),
!                                              elemtype,
!                                              false);
!                             else
!                                 optup = oper(op,
!                                              exprType(lexpr),
!                                              exprtype,
!                                              false);
!                         }
!
                          opform = (Form_pg_operator) GETSTRUCT(optup);

                          if (opform->oprresult != BOOLOID)
***************
*** 743,749 ****
                          ArrayExpr  *e = (ArrayExpr *) lfirst(element);

                          if (!IsA(e, ArrayExpr))
!                             elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions");
                          if (ndims == 0)
                              ndims = e->ndims;
                          else if (e->ndims != ndims)
--- 765,771 ----
                          ArrayExpr  *e = (ArrayExpr *) lfirst(element);

                          if (!IsA(e, ArrayExpr))
!                             elog(ERROR, "Multidimensional ARRAY[] must be built from nested array expressions");
                          if (ndims == 0)
                              ndims = e->ndims;
                          else if (e->ndims != ndims)
Index: src/backend/parser/parse_func.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_func.c,v
retrieving revision 1.149
diff -c -r1.149 parse_func.c
*** src/backend/parser/parse_func.c    6 Jun 2003 15:04:02 -0000    1.149
--- src/backend/parser/parse_func.c    24 Jun 2003 02:24:06 -0000
***************
*** 336,341 ****
--- 336,342 ----
          aggref->target = lfirst(fargs);
          aggref->aggstar = agg_star;
          aggref->aggdistinct = agg_distinct;
+         aggref->args = fargs;

          /* parse_agg.c does additional aggregate-specific processing */
          transformAggregateCall(pstate, aggref);
Index: src/backend/parser/parse_oper.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_oper.c,v
retrieving revision 1.64
diff -c -r1.64 parse_oper.c
*** src/backend/parser/parse_oper.c    26 May 2003 00:11:27 -0000    1.64
--- src/backend/parser/parse_oper.c    24 Jun 2003 02:24:06 -0000
***************
*** 137,142 ****
--- 137,169 ----
  equality_oper(Oid argtype, bool noError)
  {
      Operator    optup;
+     Oid            elem_type = get_element_type(argtype);
+
+     if (OidIsValid(elem_type))
+     {
+         bool    found = false;
+         /*
+          * If the datatype is an array, look for an "=" operator for the
+          * element datatype.  We require it to be an exact or binary-compatible
+          * match, since most callers are not prepared to cope with adding any
+          * run-time type coercion steps.
+          */
+         optup = equality_oper(elem_type, true);
+         if (optup != NULL)
+         {
+             found = true;
+             ReleaseSysCache(optup);
+         }
+
+         if (!found)
+         {
+             if (!noError)
+                 elog(ERROR, "Unable to identify an equality operator for " \
+                             "array type's element type %s",
+                              format_type_be(elem_type));
+             return NULL;
+         }
+     }

      /*
       * Look for an "=" operator for the datatype.  We require it to be
***************
*** 175,180 ****
--- 202,234 ----
  ordering_oper(Oid argtype, bool noError)
  {
      Operator    optup;
+     Oid            elem_type = get_element_type(argtype);
+
+     if (OidIsValid(elem_type))
+     {
+         bool    found = false;
+         /*
+          * If the datatype is an array, find the array element type's equality
+          * operator, and use its lsortop (it *must* be mergejoinable).  We use
+          * this definition because for sorting and grouping purposes, it's
+          * important that the equality and ordering operators are consistent.
+          */
+         optup = ordering_oper(elem_type, true);
+         if (optup != NULL)
+         {
+             found = true;
+             ReleaseSysCache(optup);
+         }
+
+         if (!found)
+         {
+             if (!noError)
+                 elog(ERROR, "Unable to identify an ordering operator for " \
+                             "array type's element type %s",
+                              format_type_be(elem_type));
+             return NULL;
+         }
+     }

      /*
       * Find the type's equality operator, and use its lsortop (it *must*
***************
*** 215,220 ****
--- 269,289 ----
      Oid            result;

      optup = equality_oper(argtype, false);
+     result = oprfuncid(optup);
+     ReleaseSysCache(optup);
+     return result;
+ }
+
+ /*
+  * ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper())
+  */
+ Oid
+ ordering_oper_funcid(Oid argtype)
+ {
+     Operator    optup;
+     Oid            result;
+
+     optup = ordering_oper(argtype, false);
      result = oprfuncid(optup);
      ReleaseSysCache(optup);
      return result;
Index: src/backend/utils/adt/acl.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/acl.c,v
retrieving revision 1.88
diff -c -r1.88 acl.c
*** src/backend/utils/adt/acl.c    11 Jun 2003 09:23:55 -0000    1.88
--- src/backend/utils/adt/acl.c    24 Jun 2003 02:24:06 -0000
***************
*** 427,432 ****
--- 427,441 ----
          a1->ai_grantor == a2->ai_grantor;
  }

+ /*
+  * user-facing version of aclitemeq() for use as the
+  * aclitem equality operator
+  */
+ Datum
+ aclitem_eq(PG_FUNCTION_ARGS)
+ {
+     PG_RETURN_BOOL(aclitemeq(PG_GETARG_ACLITEM_P(0), PG_GETARG_ACLITEM_P(1)));
+ }

  /*
   * acldefault()  --- create an ACL describing default access permissions
Index: src/backend/utils/adt/array_userfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/array_userfuncs.c,v
retrieving revision 1.1
diff -c -r1.1 array_userfuncs.c
*** src/backend/utils/adt/array_userfuncs.c    8 Apr 2003 23:20:02 -0000    1.1
--- src/backend/utils/adt/array_userfuncs.c    24 Jun 2003 02:24:06 -0000
***************
*** 18,52 ****
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"

-
- /*-----------------------------------------------------------------------------
-  * singleton_array :
-  *        Form a multi-dimensional array given one starting element.
-  *
-  * - first argument is the datum with which to build the array
-  * - second argument is the number of dimensions the array should have;
-  *     defaults to 1 if no second argument is provided
-  *----------------------------------------------------------------------------
-  */
- Datum
- singleton_array(PG_FUNCTION_ARGS)
- {
-     Oid            elem_type = get_fn_expr_argtype(fcinfo, 0);
-     int            ndims;
-
-     if (elem_type == InvalidOid)
-         elog(ERROR, "Cannot determine input datatype");
-
-     if (PG_NARGS() == 2)
-         ndims = PG_GETARG_INT32(1);
-     else
-         ndims = 1;
-
-     PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
-                                                  PG_GETARG_DATUM(0),
-                                                  ndims));
- }
-
  /*-----------------------------------------------------------------------------
   * array_push :
   *        push an element onto either end of a one-dimensional array
--- 18,23 ----
***************
*** 70,75 ****
--- 41,47 ----
      Oid            arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
      Oid            arg0_elemid;
      Oid            arg1_elemid;
+     ArrayMetaState *my_extra;

      if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
          elog(ERROR, "array_push: cannot determine input data types");
***************
*** 95,122 ****
          PG_RETURN_NULL();        /* keep compiler quiet */
      }

!     /* Sanity check: do we have a one-dimensional array */
!     if (ARR_NDIM(v) != 1)
!         elog(ERROR, "Arrays greater than one-dimension are not supported");
!
!     lb = ARR_LBOUND(v);
!     dimv = ARR_DIMS(v);
!     if (arg0_elemid != InvalidOid)
      {
!         /* append newelem */
!         int    ub = dimv[0] + lb[0] - 1;
!         indx = ub + 1;
      }
      else
      {
!         /* prepend newelem */
!         indx = lb[0] - 1;
      }

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

!     result = array_set(v, 1, &indx, newelem, -1,
!                        typlen, typbyval, typalign, &isNull);

      PG_RETURN_ARRAYTYPE_P(result);
  }
--- 67,127 ----
          PG_RETURN_NULL();        /* keep compiler quiet */
      }

!     if (ARR_NDIM(v) == 1)
      {
!         lb = ARR_LBOUND(v);
!         dimv = ARR_DIMS(v);
!
!         if (arg0_elemid != InvalidOid)
!         {
!             /* append newelem */
!             int    ub = dimv[0] + lb[0] - 1;
!             indx = ub + 1;
!         }
!         else
!         {
!             /* prepend newelem */
!             indx = lb[0] - 1;
!         }
      }
+     else if (ARR_NDIM(v) == 0)
+         indx = 1;
      else
+         elog(ERROR, "only empty and one-dimensional arrays are supported");
+
+
+     /*
+      * We arrange to look up info about element type only once per series
+      * of calls, assuming the element type doesn't change underneath us.
+      */
+     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+     if (my_extra == NULL)
      {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
      }

!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }
!
!     result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval,
!                                                  typalign, &isNull);

      PG_RETURN_ARRAYTYPE_P(result);
  }
***************
*** 145,157 ****

      /*
       * We must have one of the following combinations of inputs:
!      * 1) two arrays with ndims1 == ndims2
!      * 2) ndims1 == ndims2 - 1
!      * 3) ndims1 == ndims2 + 1
       */
      ndims1 = ARR_NDIM(v1);
      ndims2 = ARR_NDIM(v2);

      if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
          elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
                      "%d dimensions", ndims1, ndims2);
--- 150,177 ----

      /*
       * We must have one of the following combinations of inputs:
!      * 1) one empty array, and one non-empty array
!      * 2) both arrays empty
!      * 3) two arrays with ndims1 == ndims2
!      * 4) ndims1 == ndims2 - 1
!      * 5) ndims1 == ndims2 + 1
       */
      ndims1 = ARR_NDIM(v1);
      ndims2 = ARR_NDIM(v2);

+     /*
+      * short circuit - if one input array is empty, and the other is not,
+      * we return the non-empty one as the result
+      *
+      * if both are empty, return the first one
+      */
+     if (ndims1 == 0 && ndims2 > 0)
+         PG_RETURN_ARRAYTYPE_P(v2);
+
+     if (ndims2 == 0)
+         PG_RETURN_ARRAYTYPE_P(v1);
+
+     /* the rest fall into combo 2, 3, or 4 */
      if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
          elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
                      "%d dimensions", ndims1, ndims2);
***************
*** 266,412 ****
      PG_RETURN_ARRAYTYPE_P(result);
  }

- /*----------------------------------------------------------------------------
-  * array_accum :
-  *        accumulator to build a 1-D array from input values -- this can be used
-  *        to create custom aggregates.
-  *
-  * This function is not marked strict, so we have to be careful about nulls.
-  *----------------------------------------------------------------------------
-  */
- Datum
- array_accum(PG_FUNCTION_ARGS)
- {
-     /* return NULL if both arguments are NULL */
-     if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
-         PG_RETURN_NULL();
-
-     /* create a new 1-D array from the new element if the array is NULL */
-     if (PG_ARGISNULL(0))
-     {
-         Oid            tgt_type = get_fn_expr_rettype(fcinfo);
-         Oid            tgt_elem_type;
-
-         if (tgt_type == InvalidOid)
-             elog(ERROR, "Cannot determine target array type");
-         tgt_elem_type = get_element_type(tgt_type);
-         if (tgt_elem_type == InvalidOid)
-             elog(ERROR, "Target type is not an array");
-
-         PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
-                                                      PG_GETARG_DATUM(1),
-                                                      1));
-     }
-
-     /* return the array if the new element is NULL */
-     if (PG_ARGISNULL(1))
-         PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0));
-
-     /*
-      * Otherwise this is equivalent to array_push.  We hack the call a little
-      * so that array_push can see the fn_expr information.
-      */
-     return array_push(fcinfo);
- }
-
- /*-----------------------------------------------------------------------------
-  * array_assign :
-  *        assign an element of an array to a new value and return the
-  *        redefined array
-  *----------------------------------------------------------------------------
-  */
- Datum
- array_assign(PG_FUNCTION_ARGS)
- {
-     ArrayType  *v;
-     int            idx_to_chg;
-     Datum        newelem;
-     int           *dimv,
-                *lb, ub;
-     ArrayType  *result;
-     bool        isNull;
-     Oid            element_type;
-     int16        typlen;
-     bool        typbyval;
-     char        typalign;
-
-     v = PG_GETARG_ARRAYTYPE_P(0);
-     idx_to_chg = PG_GETARG_INT32(1);
-     newelem = PG_GETARG_DATUM(2);
-
-     /* Sanity check: do we have a one-dimensional array */
-     if (ARR_NDIM(v) != 1)
-         elog(ERROR, "Arrays greater than one-dimension are not supported");
-
-     lb = ARR_LBOUND(v);
-     dimv = ARR_DIMS(v);
-     ub = dimv[0] + lb[0] - 1;
-     if (idx_to_chg < lb[0] || idx_to_chg > ub)
-         elog(ERROR, "Cannot alter nonexistent array element: %d", idx_to_chg);
-
-     element_type = ARR_ELEMTYPE(v);
-     /* Sanity check: do we have a non-zero element type */
-     if (element_type == 0)
-         elog(ERROR, "Invalid array element type: %u", element_type);
-
-     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
-
-     result = array_set(v, 1, &idx_to_chg, newelem, -1,
-                        typlen, typbyval, typalign, &isNull);
-
-     PG_RETURN_ARRAYTYPE_P(result);
- }
-
- /*-----------------------------------------------------------------------------
-  * array_subscript :
-  *        return specific element of an array
-  *----------------------------------------------------------------------------
-  */
- Datum
- array_subscript(PG_FUNCTION_ARGS)
- {
-     ArrayType  *v;
-     int            idx;
-     int           *dimv,
-                *lb, ub;
-     Datum        result;
-     bool        isNull;
-     Oid            element_type;
-     int16        typlen;
-     bool        typbyval;
-     char        typalign;
-
-     v = PG_GETARG_ARRAYTYPE_P(0);
-     idx = PG_GETARG_INT32(1);
-
-     /* Sanity check: do we have a one-dimensional array */
-     if (ARR_NDIM(v) != 1)
-         elog(ERROR, "Arrays greater than one-dimension are not supported");
-
-     lb = ARR_LBOUND(v);
-     dimv = ARR_DIMS(v);
-     ub = dimv[0] + lb[0] - 1;
-     if (idx < lb[0] || idx > ub)
-         elog(ERROR, "Cannot return nonexistent array element: %d", idx);
-
-     element_type = ARR_ELEMTYPE(v);
-     /* Sanity check: do we have a non-zero element type */
-     if (element_type == 0)
-         elog(ERROR, "Invalid array element type: %u", element_type);
-
-     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
-
-     result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);
-
-     PG_RETURN_DATUM(result);
- }

  /*
!  * actually does the work for singleton_array(), and array_accum() if it is
!  * given a null input array.
   */
  ArrayType *
! create_singleton_array(Oid element_type, Datum element, int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
--- 286,300 ----
      PG_RETURN_ARRAYTYPE_P(result);
  }


  /*
!  * used by text_to_array() in varlena.c
   */
  ArrayType *
! create_singleton_array(FunctionCallInfo fcinfo,
!                        Oid element_type,
!                        Datum element,
!                        int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
***************
*** 415,420 ****
--- 303,309 ----
      int        dims[MAXDIM];
      int        lbs[MAXDIM];
      int        i;
+     ArrayMetaState *my_extra;

      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);
***************
*** 429,435 ****
          lbs[i] = 1;
      }

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      return construct_md_array(dvalues, ndims, dims, lbs, element_type,
                                typlen, typbyval, typalign);
--- 318,352 ----
          lbs[i] = 1;
      }

!     /*
!      * We arrange to look up info about element type only once per series
!      * of calls, assuming the element type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type */
!         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }

      return construct_md_array(dvalues, ndims, dims, lbs, element_type,
                                typlen, typbyval, typalign);
Index: src/backend/utils/adt/arrayfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v
retrieving revision 1.89
diff -c -r1.89 arrayfuncs.c
*** src/backend/utils/adt/arrayfuncs.c    9 May 2003 23:01:45 -0000    1.89
--- src/backend/utils/adt/arrayfuncs.c    24 Jun 2003 02:24:06 -0000
***************
*** 21,28 ****
--- 21,30 ----
  #include "catalog/pg_type.h"
  #include "libpq/pqformat.h"
  #include "parser/parse_coerce.h"
+ #include "parser/parse_oper.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
+ #include "utils/datum.h"
  #include "utils/memutils.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
***************
*** 70,85 ****

  #define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)

- /* I/O function selector for system_cache_lookup */
- typedef enum IOFuncSelector
- {
-     IOFunc_input,
-     IOFunc_output,
-     IOFunc_receive,
-     IOFunc_send
- } IOFuncSelector;
-
-
  static int    ArrayCount(char *str, int *dim, char typdelim);
  static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
               FmgrInfo *inputproc, Oid typelem, int32 typmod,
--- 72,77 ----
***************
*** 93,102 ****
  static void CopyArrayEls(char *p, Datum *values, int nitems,
               int typlen, bool typbyval, char typalign,
               bool freedata);
- static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
-                                 int *typlen, bool *typbyval,
-                                 char *typdelim, Oid *typelem,
-                                 Oid *proc, char *typalign);
  static Datum ArrayCast(char *value, bool byval, int len);
  static int ArrayCastAndSet(Datum src,
                  int typlen, bool typbyval, char typalign,
--- 85,90 ----
***************
*** 119,125 ****
                     char *destPtr,
                     int *st, int *endp, char *srcPtr,
                     int typlen, bool typbyval, char typalign);
!

  /*---------------------------------------------------------------------
   * array_in :
--- 107,113 ----
                     char *destPtr,
                     int *st, int *endp, char *srcPtr,
                     int typlen, bool typbyval, char typalign);
! static int array_cmp(FunctionCallInfo fcinfo);

  /*---------------------------------------------------------------------
   * array_in :
***************
*** 154,165 ****
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;

!     /* Get info about element type, including its input conversion proc */
!     system_cache_lookup(element_type, IOFunc_input,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typinput, &typalign);
!     fmgr_info(typinput, &inputproc);

      /* Make a modifiable copy of the input */
      /* XXX why are we allocating an extra 2 bytes here? */
--- 142,190 ----
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;
+     ArrayMetaState *my_extra;

!     /*
!      * We arrange to look up info about element type, including its input
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its input conversion proc */
!         get_type_metadata(element_type, IOFunc_input,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typinput, &typalign);
!         fmgr_info(typinput, &inputproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typinput;
!         my_extra->typalign = typalign;
!         my_extra->proc = inputproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typinput = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         inputproc = my_extra->proc;
!     }

      /* Make a modifiable copy of the input */
      /* XXX why are we allocating an extra 2 bytes here? */
***************
*** 636,647 ****
                  indx[MAXDIM];
      int            ndim,
                 *dim;

      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_output,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typoutput, &typalign);
!     fmgr_info(typoutput, &outputproc);

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
--- 661,711 ----
                  indx[MAXDIM];
      int            ndim,
                 *dim;
+     ArrayMetaState *my_extra;

      element_type = ARR_ELEMTYPE(v);
!
!     /*
!      * We arrange to look up info about element type, including its input
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its output conversion proc */
!         get_type_metadata(element_type, IOFunc_output,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typoutput, &typalign);
!         fmgr_info(typoutput, &outputproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typoutput;
!         my_extra->typalign = typalign;
!         my_extra->proc = outputproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typoutput = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         outputproc = my_extra->proc;
!     }

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
***************
*** 800,805 ****
--- 864,870 ----
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;
+     ArrayMetaState *my_extra;

      /* Get the array header information */
      ndim = pq_getmsgint(buf, 4);
***************
*** 831,844 ****
          PG_RETURN_ARRAYTYPE_P(retval);
      }

!     /* Get info about element type, including its receive conversion proc */
!     system_cache_lookup(element_type, IOFunc_receive,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typreceive, &typalign);
!     if (!OidIsValid(typreceive))
!         elog(ERROR, "No binary input function available for type %s",
!              format_type_be(element_type));
!     fmgr_info(typreceive, &receiveproc);

      dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
                                typlen, typbyval, typalign,
--- 896,945 ----
          PG_RETURN_ARRAYTYPE_P(retval);
      }

!     /*
!      * We arrange to look up info about element type, including its receive
!      * conversion proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its receive conversion proc */
!         get_type_metadata(element_type, IOFunc_receive,
!                             &typlen, &typbyval, &typdelim,
!                             &typelem, &typreceive, &typalign);
!         if (!OidIsValid(typreceive))
!             elog(ERROR, "No binary input function available for type %s",
!                  format_type_be(element_type));
!         fmgr_info(typreceive, &receiveproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typreceive;
!         my_extra->typalign = typalign;
!         my_extra->proc = receiveproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typreceive = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         receiveproc = my_extra->proc;
!     }

      dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
                                typlen, typbyval, typalign,
***************
*** 976,990 ****
      int            ndim,
                 *dim;
      StringInfoData buf;

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
!                         &typdelim, &typelem, &typsend, &typalign);
!     if (!OidIsValid(typsend))
!         elog(ERROR, "No binary output function available for type %s",
!              format_type_be(element_type));
!     fmgr_info(typsend, &sendproc);

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
--- 1077,1130 ----
      int            ndim,
                 *dim;
      StringInfoData buf;
+     ArrayMetaState *my_extra;

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!
!     /*
!      * We arrange to look up info about element type, including its send
!      * proc only once per series of calls, assuming the element
!      * type doesn't change underneath us.
!      */
!     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         /* Get info about element type, including its send proc */
!         get_type_metadata(element_type, IOFunc_send, &typlen, &typbyval,
!                             &typdelim, &typelem, &typsend, &typalign);
!         if (!OidIsValid(typsend))
!             elog(ERROR, "No binary output function available for type %s",
!                  format_type_be(element_type));
!         fmgr_info(typsend, &sendproc);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = typsend;
!         my_extra->typalign = typalign;
!         my_extra->proc = sendproc;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typdelim = my_extra->typdelim;
!         typelem = my_extra->typelem;
!         typsend = my_extra->typiofunc;
!         typalign = my_extra->typalign;
!         sendproc = my_extra->proc;
!     }

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
***************
*** 1476,1481 ****
--- 1616,1641 ----
      array = DatumGetArrayTypeP(PointerGetDatum(array));

      ndim = ARR_NDIM(array);
+
+     /*
+      * if number of dims is zero, i.e. an empty array, create an array
+      * with nSubscripts dimensions, and set the lower bounds to the supplied
+      * subscripts
+      */
+     if (ndim == 0)
+     {
+         Oid        elmtype = ARR_ELEMTYPE(array);
+
+         for (i = 0; i < nSubscripts; i++)
+         {
+             dim[i] = 1;
+             lb[i] = indx[i];
+         }
+
+         return construct_md_array(&dataValue, nSubscripts, dim, lb, elmtype,
+                                                 elmlen, elmbyval, elmalign);
+     }
+
      if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
          elog(ERROR, "Invalid array subscripts");

***************
*** 1632,1637 ****
--- 1792,1822 ----
      /* note: we assume srcArray contains no toasted elements */

      ndim = ARR_NDIM(array);
+
+     /*
+      * if number of dims is zero, i.e. an empty array, create an array
+      * with nSubscripts dimensions, and set the upper and lower bounds
+      * to the supplied subscripts
+      */
+     if (ndim == 0)
+     {
+         Datum  *dvalues;
+         int        nelems;
+         Oid        elmtype = ARR_ELEMTYPE(array);
+
+         deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
+                                                         &dvalues, &nelems);
+
+         for (i = 0; i < nSubscripts; i++)
+         {
+             dim[i] = 1 + upperIndx[i] - lowerIndx[i];
+             lb[i] = lowerIndx[i];
+         }
+
+         return construct_md_array(dvalues, nSubscripts, dim, lb, elmtype,
+                                                  elmlen, elmbyval, elmalign);
+     }
+
      if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
          elog(ERROR, "Invalid array subscripts");

***************
*** 1811,1816 ****
--- 1996,2008 ----
      Oid            typelem;
      Oid            proc;
      char       *s;
+     typedef struct {
+         ArrayMetaState *inp_extra;
+         ArrayMetaState *ret_extra;
+     } am_extra;
+     am_extra  *my_extra;
+     ArrayMetaState *inp_extra;
+     ArrayMetaState *ret_extra;

      /* Get input array */
      if (fcinfo->nargs < 1)
***************
*** 1829,1839 ****
      if (nitems <= 0)
          PG_RETURN_ARRAYTYPE_P(v);

!     /* Lookup source and result types. Unneeded variables are reused. */
!     system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
!                         &typdelim, &typelem, &proc, &inp_typalign);
!     system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
!                         &typdelim, &typelem, &proc, &typalign);

      /* Allocate temporary array for new values */
      values = (Datum *) palloc(nitems * sizeof(Datum));
--- 2021,2101 ----
      if (nitems <= 0)
          PG_RETURN_ARRAYTYPE_P(v);

!     /*
!      * We arrange to look up info about input and return element types only
!      * once per series of calls, assuming the element type doesn't change
!      * underneath us.
!      */
!     my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
!     if (my_extra == NULL)
!     {
!         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(am_extra));
!         my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
!
!         my_extra->inp_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         inp_extra = my_extra->inp_extra;
!         inp_extra->element_type = InvalidOid;
!
!         my_extra->ret_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
!                                                      sizeof(ArrayMetaState));
!         ret_extra = my_extra->ret_extra;
!         ret_extra->element_type = InvalidOid;
!     }
!     else
!     {
!         inp_extra = my_extra->inp_extra;
!         ret_extra = my_extra->ret_extra;
!     }
!
!     if (inp_extra->element_type != inpType)
!     {
!         /* Lookup source and result types. Unneeded variables are reused. */
!         get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
!                             &typdelim, &typelem, &proc, &inp_typalign);
!
!         inp_extra->element_type = inpType;
!         inp_extra->typlen = inp_typlen;
!         inp_extra->typbyval = inp_typbyval;
!         inp_extra->typdelim = typdelim;
!         inp_extra->typelem = typelem;
!         inp_extra->typiofunc = proc;
!         inp_extra->typalign = inp_typalign;
!     }
!     else
!     {
!         inp_typlen = inp_extra->typlen;
!         inp_typbyval = inp_extra->typbyval;
!         typdelim = inp_extra->typdelim;
!         typelem = inp_extra->typelem;
!         proc = inp_extra->typiofunc;
!         inp_typalign = inp_extra->typalign;
!     }
!
!     if (ret_extra->element_type != retType)
!     {
!         /* Lookup source and result types. Unneeded variables are reused. */
!         get_type_metadata(retType, IOFunc_input, &typlen, &typbyval,
!                             &typdelim, &typelem, &proc, &typalign);
!
!         ret_extra->element_type = retType;
!         ret_extra->typlen = typlen;
!         ret_extra->typbyval = typbyval;
!         ret_extra->typdelim = typdelim;
!         ret_extra->typelem = typelem;
!         ret_extra->typiofunc = proc;
!         ret_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = ret_extra->typlen;
!         typbyval = ret_extra->typbyval;
!         typdelim = ret_extra->typdelim;
!         typelem = ret_extra->typelem;
!         proc = ret_extra->typiofunc;
!         typalign = ret_extra->typalign;
!     }

      /* Allocate temporary array for new values */
      values = (Datum *) palloc(nitems * sizeof(Datum));
***************
*** 2049,2056 ****
   *          compares two arrays for equality
   * result :
   *          returns true if the arrays are equal, false otherwise.
-  *
-  * XXX bitwise equality is pretty bogus ...
   *-----------------------------------------------------------------------------
   */
  Datum
--- 2311,2316 ----
***************
*** 2058,2069 ****
  {
      ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
      ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
      bool        result = true;

!     if (ARR_SIZE(array1) != ARR_SIZE(array2))
!         result = false;
!     else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
          result = false;

      /* Avoid leaking memory when handed toasted input. */
      PG_FREE_IF_COPY(array1, 0);
--- 2318,2435 ----
  {
      ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
      ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
+     char       *p1 = (char *) ARR_DATA_PTR(array1);
+     char       *p2 = (char *) ARR_DATA_PTR(array2);
+     int            ndims1 = ARR_NDIM(array1);
+     int            ndims2 = ARR_NDIM(array2);
+     int           *dims1 = ARR_DIMS(array1);
+     int           *dims2 = ARR_DIMS(array2);
+     int            nitems1 = ArrayGetNItems(ndims1, dims1);
+     int            nitems2 = ArrayGetNItems(ndims2, dims2);
+     Oid            element_type = ARR_ELEMTYPE(array1);
+     FmgrInfo   *ae_fmgr_info = fcinfo->flinfo;
      bool        result = true;
+     int            typlen;
+     bool        typbyval;
+     char        typdelim;
+     Oid            typelem;
+     char        typalign;
+     Oid            typiofunc;
+     int            i;
+     ArrayMetaState *my_extra;
+     FunctionCallInfoData locfcinfo;

!     /* fast path if the arrays do not have the same number of elements */
!     if (nitems1 != nitems2)
          result = false;
+     else
+     {
+         /*
+          * We arrange to look up the equality function only once per series of
+          * calls, assuming the element type doesn't change underneath us.
+          */
+         my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
+         if (my_extra == NULL)
+         {
+             ae_fmgr_info->fn_extra = MemoryContextAlloc(ae_fmgr_info->fn_mcxt,
+                                                      sizeof(ArrayMetaState));
+             my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
+             my_extra->element_type = InvalidOid;
+         }
+
+         if (my_extra->element_type != element_type)
+         {
+             Oid        opfuncid = equality_oper_funcid(element_type);
+
+             if (OidIsValid(opfuncid))
+                 fmgr_info_cxt(opfuncid, &my_extra->proc, ae_fmgr_info->fn_mcxt);
+             else
+                 elog(ERROR,
+                      "array_eq: cannot find equality operator for type: %u",
+                      element_type);
+
+             get_type_metadata(element_type, IOFunc_output,
+                               &typlen, &typbyval, &typdelim,
+                               &typelem, &typiofunc, &typalign);
+
+             my_extra->element_type = element_type;
+             my_extra->typlen = typlen;
+             my_extra->typbyval = typbyval;
+             my_extra->typdelim = typdelim;
+             my_extra->typelem = typelem;
+             my_extra->typiofunc = typiofunc;
+             my_extra->typalign = typalign;
+         }
+         else
+         {
+             typlen = my_extra->typlen;
+             typbyval = my_extra->typbyval;
+             typdelim = my_extra->typdelim;
+             typelem = my_extra->typelem;
+             typiofunc = my_extra->typiofunc;
+             typalign = my_extra->typalign;
+         }
+
+         /*
+          * apply the operator to each pair of array elements.
+          */
+         MemSet(&locfcinfo, 0, sizeof(locfcinfo));
+         locfcinfo.flinfo = &my_extra->proc;
+         locfcinfo.nargs = 2;
+
+         /* Loop over source data */
+         for (i = 0; i < nitems1; i++)
+         {
+             Datum    elt1;
+             Datum    elt2;
+             bool    oprresult;
+
+             /* Get element pair */
+             elt1 = fetch_att(p1, typbyval, typlen);
+             elt2 = fetch_att(p2, typbyval, typlen);
+
+             p1 = att_addlength(p1, typlen, PointerGetDatum(p1));
+             p1 = (char *) att_align(p1, typalign);
+
+             p2 = att_addlength(p2, typlen, PointerGetDatum(p2));
+             p2 = (char *) att_align(p2, typalign);
+
+             /*
+              * Apply the operator to the element pair
+              */
+             locfcinfo.arg[0] = elt1;
+             locfcinfo.arg[1] = elt2;
+             locfcinfo.argnull[0] = false;
+             locfcinfo.argnull[1] = false;
+             locfcinfo.isnull = false;
+             oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+             if (!oprresult)
+             {
+                 result = false;
+                 break;
+             }
+         }
+     }

      /* Avoid leaking memory when handed toasted input. */
      PG_FREE_IF_COPY(array1, 0);
***************
*** 2073,2125 ****
  }


! /***************************************************************************/
! /******************|          Support  Routines              |*****************/
! /***************************************************************************/

! static void
! system_cache_lookup(Oid element_type,
!                     IOFuncSelector which_func,
!                     int *typlen,
!                     bool *typbyval,
!                     char *typdelim,
!                     Oid *typelem,
!                     Oid *proc,
!                     char *typalign)
! {
!     HeapTuple    typeTuple;
!     Form_pg_type typeStruct;
!
!     typeTuple = SearchSysCache(TYPEOID,
!                                ObjectIdGetDatum(element_type),
!                                0, 0, 0);
!     if (!HeapTupleIsValid(typeTuple))
!         elog(ERROR, "cache lookup failed for type %u", element_type);
!     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
!
!     *typlen = typeStruct->typlen;
!     *typbyval = typeStruct->typbyval;
!     *typdelim = typeStruct->typdelim;
!     *typelem = typeStruct->typelem;
!     *typalign = typeStruct->typalign;
!     switch (which_func)
!     {
!         case IOFunc_input:
!             *proc = typeStruct->typinput;
!             break;
!         case IOFunc_output:
!             *proc = typeStruct->typoutput;
!             break;
!         case IOFunc_receive:
!             *proc = typeStruct->typreceive;
!             break;
!         case IOFunc_send:
!             *proc = typeStruct->typsend;
!             break;
      }
!     ReleaseSysCache(typeTuple);
  }

  /*
   * Fetch array element at pointer, converted correctly to a Datum
   */
--- 2439,2628 ----
  }


! /*-----------------------------------------------------------------------------
!  * array-array bool operators:
!  *        Given two arrays, iterate comparison operators
!  *        over the array. Uses logic similar to text comparison
!  *        functions, except element-by-element instead of
!  *        character-by-character.
!  *----------------------------------------------------------------------------
!  */
! Datum
! array_ne(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
! }

! Datum
! array_lt(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
! }
!
! Datum
! array_gt(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
! }
!
! Datum
! array_le(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
! }
!
! Datum
! array_ge(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
! }
!
! Datum
! btarraycmp(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_INT32(array_cmp(fcinfo));
! }
!
! /*
!  * array_cmp()
!  * Internal comparison function for arrays.
!  *
!  * Returns -1, 0 or 1
!  */
! static int
! array_cmp(FunctionCallInfo fcinfo)
! {
!     ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
!     ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
!     FmgrInfo   *ac_fmgr_info = fcinfo->flinfo;
!     Datum        opresult;
!     int            result = 0;
!     Oid            element_type = InvalidOid;
!     int            typlen;
!     bool        typbyval;
!     char        typdelim;
!     Oid            typelem;
!     char        typalign;
!     Oid            typiofunc;
!     Datum       *dvalues1;
!     int            nelems1;
!     Datum       *dvalues2;
!     int            nelems2;
!     int            min_nelems;
!     int            i;
!     typedef struct
!     {
!         Oid                element_type;
!         int                typlen;
!         bool            typbyval;
!         char            typdelim;
!         Oid                typelem;
!         Oid                typiofunc;
!         char            typalign;
!         FmgrInfo        eqproc;
!         FmgrInfo        ordproc;
!     } ac_extra;
!     ac_extra *my_extra;
!
!     element_type = ARR_ELEMTYPE(array1);
!
!     /*
!      * We arrange to look up the element type operator function only once
!      * per series of calls, assuming the element type and opname don't
!      * change underneath us.
!      */
!     my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
!     if (my_extra == NULL)
!     {
!         ac_fmgr_info->fn_extra = MemoryContextAlloc(ac_fmgr_info->fn_mcxt,
!                                                          sizeof(ac_extra));
!         my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
!         my_extra->element_type = InvalidOid;
!     }
!
!     if (my_extra->element_type != element_type)
!     {
!         Oid        eqfuncid = equality_oper_funcid(element_type);
!         Oid        ordfuncid = ordering_oper_funcid(element_type);
!
!         fmgr_info_cxt(eqfuncid, &my_extra->eqproc, ac_fmgr_info->fn_mcxt);
!         fmgr_info_cxt(ordfuncid, &my_extra->ordproc, ac_fmgr_info->fn_mcxt);
!
!         if (my_extra->eqproc.fn_nargs != 2)
!             elog(ERROR, "Equality operator does not take 2 arguments: %u",
!                                                                  eqfuncid);
!         if (my_extra->ordproc.fn_nargs != 2)
!             elog(ERROR, "Ordering operator does not take 2 arguments: %u",
!                                                                  ordfuncid);
!
!         get_type_metadata(element_type, IOFunc_output,
!                           &typlen, &typbyval, &typdelim,
!                           &typelem, &typiofunc, &typalign);
!
!         my_extra->element_type = element_type;
!         my_extra->typlen = typlen;
!         my_extra->typbyval = typbyval;
!         my_extra->typdelim = typdelim;
!         my_extra->typelem = typelem;
!         my_extra->typiofunc = InvalidOid;
!         my_extra->typalign = typalign;
!     }
!     else
!     {
!         typlen = my_extra->typlen;
!         typbyval = my_extra->typbyval;
!         typalign = my_extra->typalign;
!     }
!
!     /* extract a C array of arg array datums */
!     deconstruct_array(array1, element_type, typlen, typbyval, typalign,
!                                                     &dvalues1, &nelems1);
!
!     deconstruct_array(array2, element_type, typlen, typbyval, typalign,
!                                                     &dvalues2, &nelems2);
!
!     min_nelems = Min(nelems1, nelems2);
!     for (i = 0; i < min_nelems; i++)
!     {
!         /* are they equal */
!         opresult = FunctionCall2(&my_extra->eqproc,
!                                  dvalues1[i], dvalues2[i]);
!
!         if (!DatumGetBool(opresult))
!         {
!             /* nope, see if arg1 is less than arg2 */
!             opresult = FunctionCall2(&my_extra->ordproc,
!                                      dvalues1[i], dvalues2[i]);
!             if (DatumGetBool(opresult))
!             {
!                 /* arg1 is less than arg2 */
!                 result = -1;
!                 break;
!             }
!             else
!             {
!                 /* arg1 is greater than arg2 */
!                 result = 1;
!                 break;
!             }
!         }
      }
!
!     if ((result == 0) && (nelems1 != nelems2))
!         result = (nelems1 < nelems2) ? -1 : 1;
!
!     /* Avoid leaking memory when handed toasted input. */
!     PG_FREE_IF_COPY(array1, 0);
!     PG_FREE_IF_COPY(array2, 1);
!
!     return result;
  }

+
+ /***************************************************************************/
+ /******************|          Support  Routines              |*****************/
+ /***************************************************************************/
+
  /*
   * Fetch array element at pointer, converted correctly to a Datum
   */
***************
*** 2423,2428 ****
--- 2926,2943 ----
          if (tgt_elem_type == InvalidOid)
              elog(ERROR, "Target type is not an array");

+         /*
+          * We don't deal with domain constraints yet, so bail out.
+          * This isn't currently a problem, because we also don't
+          * support arrays of domain type elements either. But in the
+          * future we might. At that point consideration should be given
+          * to removing the check below and adding a domain constraints
+          * check to the coercion.
+          */
+         if (getBaseType(tgt_elem_type) != tgt_elem_type)
+             elog(ERROR, "array coercion to domain type elements not " \
+                         "currently supported");
+
          if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
                                     COERCION_EXPLICIT, &funcId))
          {
***************
*** 2439,2448 ****
      }

      /*
!      * If it's binary-compatible, return the array unmodified.
       */
      if (my_extra->coerce_finfo.fn_oid == InvalidOid)
!         PG_RETURN_ARRAYTYPE_P(src);

      /*
       * Use array_map to apply the function to each array element.
--- 2954,2969 ----
      }

      /*
!      * If it's binary-compatible, modify the element type in the array header,
!      * but otherwise leave the array as we received it.
       */
      if (my_extra->coerce_finfo.fn_oid == InvalidOid)
!     {
!         ArrayType  *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
!
!         ARR_ELEMTYPE(result) = my_extra->desttype;
!         PG_RETURN_ARRAYTYPE_P(result);
!     }

      /*
       * Use array_map to apply the function to each array element.
***************
*** 2453,2456 ****
--- 2974,3092 ----
      locfcinfo.arg[0] = PointerGetDatum(src);

      return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
+ }
+
+ /*
+  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
+  *
+  *    astate is working state (NULL on first call)
+  *    rcontext is where to keep working state
+  */
+ ArrayBuildState *
+ accumArrayResult(ArrayBuildState *astate,
+                  Datum dvalue, bool disnull,
+                  Oid element_type,
+                  MemoryContext rcontext)
+ {
+     MemoryContext arr_context,
+                   oldcontext;
+
+     if (astate == NULL)
+     {
+         /* First time through --- initialize */
+
+         /* Make a temporary context to hold all the junk */
+         arr_context = AllocSetContextCreate(rcontext,
+                                             "accumArrayResult",
+                                             ALLOCSET_DEFAULT_MINSIZE,
+                                             ALLOCSET_DEFAULT_INITSIZE,
+                                             ALLOCSET_DEFAULT_MAXSIZE);
+         oldcontext = MemoryContextSwitchTo(arr_context);
+         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
+         astate->mcontext = arr_context;
+         astate->dvalues = (Datum *)
+             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
+         astate->nelems = 0;
+         astate->element_type = element_type;
+         get_typlenbyvalalign(element_type,
+                              &astate->typlen,
+                              &astate->typbyval,
+                              &astate->typalign);
+     }
+     else
+     {
+         oldcontext = MemoryContextSwitchTo(astate->mcontext);
+         Assert(astate->element_type == element_type);
+         /* enlarge dvalues[] if needed */
+         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
+             astate->dvalues = (Datum *)
+                 repalloc(astate->dvalues,
+                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
+     }
+
+     if (disnull)
+         elog(ERROR, "NULL elements not allowed in Arrays");
+
+     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
+     astate->dvalues[astate->nelems++] =
+         datumCopy(dvalue, astate->typbyval, astate->typlen);
+
+     MemoryContextSwitchTo(oldcontext);
+
+     return astate;
+ }
+
+ /*
+  * makeArrayResult - produce final result of accumArrayResult
+  *
+  *    astate is working state (not NULL)
+  *    rcontext is where to construct result
+  */
+ Datum
+ makeArrayResult(ArrayBuildState *astate,
+                 MemoryContext rcontext)
+ {
+     int            dims[1];
+     int            lbs[1];
+
+     dims[0] = astate->nelems;
+     lbs[0] = 1;
+
+     return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
+ }
+
+ /*
+  * makeMdArrayResult - produce md final result of accumArrayResult
+  *
+  *    astate is working state (not NULL)
+  *    rcontext is where to construct result
+  */
+ Datum
+ makeMdArrayResult(ArrayBuildState *astate,
+                 int ndims,
+                 int *dims,
+                 int *lbs,
+                 MemoryContext rcontext)
+ {
+     ArrayType  *result;
+     MemoryContext oldcontext;
+
+     /* Build the final array result in rcontext */
+     oldcontext = MemoryContextSwitchTo(rcontext);
+
+     result = construct_md_array(astate->dvalues,
+                                 ndims,
+                                 dims,
+                                 lbs,
+                                 astate->element_type,
+                                 astate->typlen,
+                                 astate->typbyval,
+                                 astate->typalign);
+
+     MemoryContextSwitchTo(oldcontext);
+
+     /* Clean up all the junk */
+     MemoryContextDelete(astate->mcontext);
+
+     return PointerGetDatum(result);
  }
Index: src/backend/utils/adt/varlena.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/varlena.c,v
retrieving revision 1.98
diff -c -r1.98 varlena.c
*** src/backend/utils/adt/varlena.c    15 May 2003 15:50:19 -0000    1.98
--- src/backend/utils/adt/varlena.c    24 Jun 2003 02:24:06 -0000
***************
*** 19,29 ****
--- 19,32 ----
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
  #include "access/tuptoaster.h"
+ #include "catalog/pg_type.h"
  #include "lib/stringinfo.h"
  #include "libpq/crypt.h"
  #include "libpq/pqformat.h"
+ #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/pg_locale.h"
+ #include "utils/lsyscache.h"


  typedef struct varlena unknown;
***************
*** 1983,1990 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

--- 1986,1992 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

***************
*** 2004,2011 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
--- 2006,2012 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
***************
*** 2026,2031 ****
--- 2027,2217 ----
          result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn -
fldsep_len,false); 
          PG_RETURN_TEXT_P(result_text);
      }
+ }
+
+ /*
+  * text_to_array
+  * parse input string
+  * return text array of elements
+  * based on provided field separator
+  */
+ Datum
+ text_to_array(PG_FUNCTION_ARGS)
+ {
+     text       *inputstring = PG_GETARG_TEXT_P(0);
+     int            inputstring_len = TEXTLEN(inputstring);
+     text       *fldsep = PG_GETARG_TEXT_P(1);
+     int            fldsep_len = TEXTLEN(fldsep);
+     int            fldnum;
+     int            start_posn = 0;
+     int            end_posn = 0;
+     text       *result_text = NULL;
+     ArrayBuildState *astate = NULL;
+     MemoryContext oldcontext = CurrentMemoryContext;
+
+     /* return NULL for empty input string */
+     if (inputstring_len < 1)
+         PG_RETURN_NULL();
+
+     /* empty field separator
+      * return one element, 1D, array using the input string */
+     if (fldsep_len < 1)
+         PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+                               CStringGetDatum(inputstring), 1));
+
+     /* start with end position holding the initial start position */
+     end_posn = 0;
+     for (fldnum=1;;fldnum++)    /* field number is 1 based */
+     {
+         Datum    dvalue;
+         bool    disnull = false;
+
+         start_posn = end_posn;
+         end_posn = text_position(PointerGetDatum(inputstring),
+                                  PointerGetDatum(fldsep),
+                                  fldnum);
+
+         if ((start_posn == 0) && (end_posn == 0))    /* fldsep not found */
+         {
+             if (fldnum == 1)
+             {
+                 /* first element
+                  * return one element, 1D, array using the input string */
+                 PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+                                       CStringGetDatum(inputstring), 1));
+             }
+             else
+             {
+                 /* otherwise create array and exit */
+                 PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
+             }
+         }
+         else if ((start_posn != 0) && (end_posn == 0))
+         {
+             /* last field requested */
+             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
+         }
+         else if ((start_posn == 0) && (end_posn != 0))
+         {
+             /* first field requested */
+             result_text = LEFT(inputstring, fldsep);
+         }
+         else
+         {
+             /* prior to last field requested */
+             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn
-fldsep_len, false); 
+         }
+
+         /* stash away current value */
+         dvalue = PointerGetDatum(result_text);
+         astate = accumArrayResult(astate, dvalue,
+                                   disnull, TEXTOID, oldcontext);
+
+     }
+
+     /* never reached -- keep compiler quiet */
+     PG_RETURN_NULL();
+ }
+
+ /*
+  * array_to_text
+  * concatenate Cstring representation of input array elements
+  * using provided field separator
+  */
+ Datum
+ array_to_text(PG_FUNCTION_ARGS)
+ {
+     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
+     char       *fldsep = PG_TEXTARG_GET_STR(1);
+     int            nitems, *dims, ndims;
+     char       *p;
+     Oid            element_type;
+     int            typlen;
+     bool        typbyval;
+     char        typdelim;
+     Oid            typoutput,
+                 typelem;
+     FmgrInfo    outputproc;
+     char        typalign;
+     StringInfo    result_str = makeStringInfo();
+     int            i;
+     ArrayMetaState *my_extra;
+
+     p = ARR_DATA_PTR(v);
+     ndims = ARR_NDIM(v);
+     dims = ARR_DIMS(v);
+     nitems = ArrayGetNItems(ndims, dims);
+
+     /* if there are no elements, return an empty string */
+     if (nitems == 0)
+         PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
+
+     element_type = ARR_ELEMTYPE(v);
+
+     /*
+      * We arrange to look up info about element type, including its output
+      * conversion proc only once per series of calls, assuming the element
+      * type doesn't change underneath us.
+      */
+     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+     if (my_extra == NULL)
+     {
+         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+                                                      sizeof(ArrayMetaState));
+         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+         my_extra->element_type = InvalidOid;
+     }
+
+     if (my_extra->element_type != element_type)
+     {
+         /* Get info about element type, including its output conversion proc */
+         get_type_metadata(element_type, IOFunc_output,
+                             &typlen, &typbyval, &typdelim,
+                             &typelem, &typoutput, &typalign);
+         fmgr_info(typoutput, &outputproc);
+
+         my_extra->element_type = element_type;
+         my_extra->typlen = typlen;
+         my_extra->typbyval = typbyval;
+         my_extra->typdelim = typdelim;
+         my_extra->typelem = typelem;
+         my_extra->typiofunc = typoutput;
+         my_extra->typalign = typalign;
+         my_extra->proc = outputproc;
+     }
+     else
+     {
+         typlen = my_extra->typlen;
+         typbyval = my_extra->typbyval;
+         typdelim = my_extra->typdelim;
+         typelem = my_extra->typelem;
+         typoutput = my_extra->typiofunc;
+         typalign = my_extra->typalign;
+         outputproc = my_extra->proc;
+     }
+
+     for (i = 0; i < nitems; i++)
+     {
+         Datum        itemvalue;
+         char       *value;
+
+         itemvalue = fetch_att(p, typbyval, typlen);
+
+         value = DatumGetCString(FunctionCall3(&outputproc,
+                                               itemvalue,
+                                               ObjectIdGetDatum(typelem),
+                                               Int32GetDatum(-1)));
+
+         if (i > 0)
+             appendStringInfo(result_str, "%s%s", fldsep, value);
+         else
+             appendStringInfo(result_str, "%s", value);
+
+         p = att_addlength(p, typlen, PointerGetDatum(p));
+         p = (char *) att_align(p, typalign);
+     }
+
+     PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
  }

  #define HEXBASE 16
Index: src/backend/utils/cache/lsyscache.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v
retrieving revision 1.96
diff -c -r1.96 lsyscache.c
*** src/backend/utils/cache/lsyscache.c    22 Jun 2003 22:04:54 -0000    1.96
--- src/backend/utils/cache/lsyscache.c    24 Jun 2003 02:24:06 -0000
***************
*** 719,724 ****
--- 719,758 ----
  }

  /*
+  * get_func_argtypes
+  *        Given procedure id, return the function's argument types.
+  *        Also pass back the number of arguments.
+  */
+ Oid *
+ get_func_argtypes(Oid funcid, int *nargs)
+ {
+     HeapTuple        tp;
+     Form_pg_proc    procstruct;
+     Oid               *result = NULL;
+     int                i;
+
+     tp = SearchSysCache(PROCOID,
+                         ObjectIdGetDatum(funcid),
+                         0, 0, 0);
+     if (!HeapTupleIsValid(tp))
+         elog(ERROR, "Function OID %u does not exist", funcid);
+
+     procstruct = (Form_pg_proc) GETSTRUCT(tp);
+     *nargs = (int) procstruct->pronargs;
+
+     if (*nargs > 0)
+     {
+         result = (Oid *) palloc(*nargs * sizeof(Oid));
+
+         for (i = 0; i < *nargs; i++)
+             result[i] = procstruct->proargtypes[i];
+     }
+
+     ReleaseSysCache(tp);
+     return result;
+ }
+
+ /*
   * get_func_retset
   *        Given procedure id, return the function's proretset flag.
   */
***************
*** 1088,1093 ****
--- 1122,1177 ----
      *typbyval = typtup->typbyval;
      *typalign = typtup->typalign;
      ReleaseSysCache(tp);
+ }
+
+ /*
+  * get_type_metadata
+  *
+  *        A six-fer:    given the type OID, return typlen, typbyval, typalign,
+  *                    typdelim, typelem, IO function Oid. The IO function
+  *                    returned is controlled by IOFuncSelector
+  */
+ void
+ get_type_metadata(Oid element_type,
+                     IOFuncSelector which_func,
+                     int *typlen,
+                     bool *typbyval,
+                     char *typdelim,
+                     Oid *typelem,
+                     Oid *proc,
+                     char *typalign)
+ {
+     HeapTuple    typeTuple;
+     Form_pg_type typeStruct;
+
+     typeTuple = SearchSysCache(TYPEOID,
+                                ObjectIdGetDatum(element_type),
+                                0, 0, 0);
+     if (!HeapTupleIsValid(typeTuple))
+         elog(ERROR, "cache lookup failed for type %u", element_type);
+     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+
+     *typlen = typeStruct->typlen;
+     *typbyval = typeStruct->typbyval;
+     *typdelim = typeStruct->typdelim;
+     *typelem = typeStruct->typelem;
+     *typalign = typeStruct->typalign;
+     switch (which_func)
+     {
+         case IOFunc_input:
+             *proc = typeStruct->typinput;
+             break;
+         case IOFunc_output:
+             *proc = typeStruct->typoutput;
+             break;
+         case IOFunc_receive:
+             *proc = typeStruct->typreceive;
+             break;
+         case IOFunc_send:
+             *proc = typeStruct->typsend;
+             break;
+     }
+     ReleaseSysCache(typeTuple);
  }

  #ifdef NOT_USED
Index: src/backend/utils/fmgr/fmgr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/fmgr/fmgr.c,v
retrieving revision 1.68
diff -c -r1.68 fmgr.c
*** src/backend/utils/fmgr/fmgr.c    8 Apr 2003 23:20:02 -0000    1.68
--- src/backend/utils/fmgr/fmgr.c    24 Jun 2003 02:24:06 -0000
***************
*** 1673,1675 ****
--- 1673,1701 ----

      return exprType((Node *) nth(argnum, args));
  }
+
+ /*
+  * Get the OID of the function or operator
+  *
+  * Returns InvalidOid if information is not available
+  */
+ Oid
+ get_fn_expr_functype(FunctionCallInfo fcinfo)
+ {
+     Node   *expr;
+
+     /*
+      * can't return anything useful if we have no FmgrInfo or if
+      * its fn_expr node has not been initialized
+      */
+     if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
+         return InvalidOid;
+
+     expr = fcinfo->flinfo->fn_expr;
+     if (IsA(expr, FuncExpr))
+         return ((FuncExpr *) expr)->funcid;
+     else if (IsA(expr, OpExpr))
+         return ((OpExpr *) expr)->opno;
+     else
+         return InvalidOid;
+ }
Index: src/include/fmgr.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/fmgr.h,v
retrieving revision 1.27
diff -c -r1.27 fmgr.h
*** src/include/fmgr.h    8 Apr 2003 23:20:04 -0000    1.27
--- src/include/fmgr.h    24 Jun 2003 02:24:06 -0000
***************
*** 18,23 ****
--- 18,24 ----
  #ifndef FMGR_H
  #define FMGR_H

+ #include "nodes/nodes.h"

  /*
   * All functions that can be called directly by fmgr must have this signature.
***************
*** 372,385 ****
                   Datum arg6, Datum arg7, Datum arg8,
                   Datum arg9);

-
  /*
   * Routines in fmgr.c
   */
  extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
! extern Oid    fmgr_internal_function(const char *proname);
! extern Oid    get_fn_expr_rettype(FunctionCallInfo fcinfo);
! extern Oid    get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);

  /*
   * Routines in dfmgr.c
--- 373,386 ----
                   Datum arg6, Datum arg7, Datum arg8,
                   Datum arg9);

  /*
   * Routines in fmgr.c
   */
  extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
! extern Oid fmgr_internal_function(const char *proname);
! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo);
! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
! extern Oid get_fn_expr_functype(FunctionCallInfo fcinfo);

  /*
   * Routines in dfmgr.c
Index: src/include/catalog/pg_amop.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amop.h,v
retrieving revision 1.50
diff -c -r1.50 pg_amop.h
*** src/include/catalog/pg_amop.h    22 Jun 2003 22:04:55 -0000    1.50
--- src/include/catalog/pg_amop.h    24 Jun 2003 02:24:06 -0000
***************
*** 418,423 ****
--- 418,432 ----
  DATA(insert (    2098 4 f 2335 ));
  DATA(insert (    2098 5 f 2336 ));

+ /*
+  *    btree array_ops
+  */
+
+ DATA(insert (     397 1 f 1072 ));
+ DATA(insert (     397 2 f 1074 ));
+ DATA(insert (     397 3 f 1070 ));
+ DATA(insert (     397 4 f 1075 ));
+ DATA(insert (     397 5 f 1073 ));

  /*
   *    hash index _ops
Index: src/include/catalog/pg_amproc.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amproc.h,v
retrieving revision 1.38
diff -c -r1.38 pg_amproc.h
*** src/include/catalog/pg_amproc.h    22 Jun 2003 22:04:55 -0000    1.38
--- src/include/catalog/pg_amproc.h    24 Jun 2003 02:35:24 -0000
***************
*** 78,83 ****
--- 78,84 ----


  /* btree */
+ DATA(insert (     397 1  382 ));
  DATA(insert (     421 1    357 ));
  DATA(insert (     423 1 1596 ));
  DATA(insert (     424 1 1693 ));
Index: src/include/catalog/pg_opclass.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_opclass.h,v
retrieving revision 1.51
diff -c -r1.51 pg_opclass.h
*** src/include/catalog/pg_opclass.h    22 Jun 2003 22:04:55 -0000    1.51
--- src/include/catalog/pg_opclass.h    24 Jun 2003 02:24:06 -0000
***************
*** 87,92 ****
--- 87,94 ----
   */

  DATA(insert OID =  421 (    403        abstime_ops        PGNSP PGUID  702 t 0 ));
+ DATA(insert OID =  397 (    403        array_ops        PGNSP PGUID 2277 t 0 ));
+ #define ARRAY_BTREE_OPS_OID 397
  DATA(insert OID =  422 (    402        bigbox_ops        PGNSP PGUID  603 f 0 ));
  DATA(insert OID =  423 (    403        bit_ops            PGNSP PGUID 1560 t 0 ));
  DATA(insert OID =  424 (    403        bool_ops        PGNSP PGUID   16 t 0 ));
Index: src/include/catalog/pg_operator.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_operator.h,v
retrieving revision 1.115
diff -c -r1.115 pg_operator.h
*** src/include/catalog/pg_operator.h    22 Jun 2003 22:04:55 -0000    1.115
--- src/include/catalog/pg_operator.h    24 Jun 2003 02:24:06 -0000
***************
*** 116,125 ****
  DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
  DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 

! DATA(insert OID = 329 (  "="       PGNSP PGUID b f 2277 2277 16   329 0 0 0    0 0 array_eq     eqsel eqjoinsel ));
! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277   0 0 0 0    0 0 array_append   -       -     ));
! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277   0 0 0 0    0 0 array_prepend  -       -     ));
! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277   0 0 0 0    0 0 array_cat      -       -     ));

  DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
  DATA(insert OID = 353 (  "="       PGNSP PGUID b f    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
--- 116,130 ----
  DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
  DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 

! DATA(insert OID = 1070 (  "="       PGNSP PGUID b f 2277 2277 16 1070 1071  1072 1072 1072 1073 array_eq eqsel
eqjoinsel)); 
! DATA(insert OID = 1071 (  "<>"       PGNSP PGUID b f 2277 2277 16 1071 1070  0 0 0 0 array_ne neqsel neqjoinsel ));
! DATA(insert OID = 1072 (  "<"       PGNSP PGUID b f 2277 2277 16 1073 1075  0 0 0 0 array_lt scalarltsel
scalarltjoinsel)); 
! DATA(insert OID = 1073 (  ">"       PGNSP PGUID b f 2277 2277 16 1072 1074  0 0 0 0 array_gt scalargtsel
scalargtjoinsel)); 
! DATA(insert OID = 1074 (  "<="       PGNSP PGUID b f 2277 2277 16 1075 1073  0 0 0 0 array_le scalarltsel
scalarltjoinsel)); 
! DATA(insert OID = 1075 (  ">="       PGNSP PGUID b f 2277 2277 16 1074 1072  0 0 0 0 array_ge scalargtsel
scalargtjoinsel)); 
! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277 0 0  0 0 0 0 array_append   -       -     ));
! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277 0 0  0 0 0 0 array_prepend  -       -     ));
! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277 0 0  0 0 0 0 array_cat      -       -     ));

  DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
  DATA(insert OID = 353 (  "="       PGNSP PGUID b f    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
***************
*** 425,430 ****
--- 430,436 ----
  DATA(insert OID = 966 (  "+"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - ));
  DATA(insert OID = 967 (  "-"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclremove - - ));
  DATA(insert OID = 968 (  "~"       PGNSP PGUID b f 1034 1033     16 0 0 0 0 0 0 aclcontains - - ));
+ DATA(insert OID = 974 (  "="       PGNSP PGUID b f 1033 1033     16 0 0 0 0 0 0 aclitemeq eqsel eqjoinsel ));

  /* additional geometric operators - thomas 1997-07-09 */
  DATA(insert OID =  969 (  "@@"       PGNSP PGUID l f    0  601    600    0  0 0 0 0 0 lseg_center - - ));
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v
retrieving revision 1.304
diff -c -r1.304 pg_proc.h
*** src/include/catalog/pg_proc.h    22 Jun 2003 22:04:55 -0000    1.304
--- src/include/catalog/pg_proc.h    24 Jun 2003 02:35:46 -0000
***************
*** 758,763 ****
--- 758,765 ----
  DESCR("btree less-equal-greater");
  DATA(insert OID = 360 (  bttextcmp           PGNSP PGUID 12 f f t f i 2 23 "25 25"    bttextcmp - _null_ ));
  DESCR("btree less-equal-greater");
+ DATA(insert OID = 382 (  btarraycmp           PGNSP PGUID 12 f f t f i 2 23 "2277 2277"    btarraycmp - _null_ ));
+ DESCR("btree less-equal-greater");

  DATA(insert OID = 361 (  lseg_distance       PGNSP PGUID 12 f f t f i 2 701 "601 601"  lseg_distance - _null_ ));
  DESCR("distance between");
***************
*** 988,1001 ****
  DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
  DESCR("greater-than-or-equal");

- DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
- DESCR("array equal");
-
  DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
  DESCR("current user name");
  DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
  DESCR("session user name");

  DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
  DESCR("array dimensions");
  DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
--- 990,1012 ----
  DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
  DESCR("greater-than-or-equal");

  DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
  DESCR("current user name");
  DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
  DESCR("session user name");

+ DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
+ DESCR("array equal");
+ DATA(insert OID = 390 (  array_ne           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ne - _null_ ));
+ DESCR("array not equal");
+ DATA(insert OID = 391 (  array_lt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_lt - _null_ ));
+ DESCR("array less than");
+ DATA(insert OID = 392 (  array_gt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_gt - _null_ ));
+ DESCR("array greater than");
+ DATA(insert OID = 393 (  array_le           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_le - _null_ ));
+ DESCR("array less than or equal");
+ DATA(insert OID = 396 (  array_ge           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ge - _null_ ));
+ DESCR("array greater than or equal");
  DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
  DESCR("array dimensions");
  DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
***************
*** 1006,1027 ****
  DESCR("array lower dimension");
  DATA(insert OID = 2092 (  array_upper       PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ ));
  DESCR("array upper dimension");
- DATA(insert OID = 377 (  singleton_array  PGNSP PGUID 12 f f t f i 1 2277 "2283" singleton_array - _null_ ));
- DESCR("create array from single element");
  DATA(insert OID = 378 (  array_append       PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ ));
  DESCR("append element onto end of array");
  DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
  DESCR("prepend element onto front of array");
- DATA(insert OID = 380 (  array_accum       PGNSP PGUID 12 f f f f i 2 2277 "2277 2283" array_accum - _null_ ));
- DESCR("push element onto end of array, creating array if needed");
- DATA(insert OID = 381 (  array_assign       PGNSP PGUID 12 f f t f i 3 2277 "2277 23 2283" array_assign - _null_ ));
- DESCR("assign specific array element");
- DATA(insert OID = 382 (  array_subscript  PGNSP PGUID 12 f f t f i 2 2283 "2277 23" array_subscript - _null_ ));
- DESCR("return specific array element");
  DATA(insert OID = 383 (  array_cat           PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ ));
  DESCR("concatenate two arrays");
  DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
  DESCR("coerce array type to another array type");

  DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
  DESCR("I/O");
--- 1017,1034 ----
  DESCR("array lower dimension");
  DATA(insert OID = 2092 (  array_upper       PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ ));
  DESCR("array upper dimension");
  DATA(insert OID = 378 (  array_append       PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ ));
  DESCR("append element onto end of array");
  DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
  DESCR("prepend element onto front of array");
  DATA(insert OID = 383 (  array_cat           PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ ));
  DESCR("concatenate two arrays");
  DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
  DESCR("coerce array type to another array type");
+ DATA(insert OID = 394 (  string_to_array   PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ ));
+ DESCR("split delimited text into text[]");
+ DATA(insert OID = 395 (  array_to_string   PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ ));
+ DESCR("concatenate array elements, using delimiter, into text");

  DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
  DESCR("I/O");
***************
*** 1322,1327 ****
--- 1329,1336 ----
  DESCR("remove ACL item");
  DATA(insert OID = 1037 (  aclcontains       PGNSP PGUID 12 f f t f s 2 16 "1034 1033"    aclcontains - _null_ ));
  DESCR("does ACL contain item?");
+ DATA(insert OID = 1062 (  aclitemeq           PGNSP PGUID 12 f f t f s 2 16 "1033 1033"    aclitem_eq - _null_ ));
+ DESCR("equality operator for ACL items");
  DATA(insert OID = 1365 (  makeaclitem       PGNSP PGUID 12 f f t f s 5 1033 "23 23 23 25 16"    makeaclitem - _null_
));
  DESCR("make ACL item");
  DATA(insert OID = 1038 (  seteval           PGNSP PGUID 12 f f t t v 1 23 "26"  seteval - _null_ ));
Index: src/include/nodes/primnodes.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v
retrieving revision 1.83
diff -c -r1.83 primnodes.h
*** src/include/nodes/primnodes.h    6 Jun 2003 15:04:03 -0000    1.83
--- src/include/nodes/primnodes.h    24 Jun 2003 02:24:06 -0000
***************
*** 226,231 ****
--- 226,232 ----
      Index        agglevelsup;    /* > 0 if agg belongs to outer query */
      bool        aggstar;        /* TRUE if argument was really '*' */
      bool        aggdistinct;    /* TRUE if it's agg(DISTINCT ...) */
+     List       *args;            /* arguments to the aggregate */
  } Aggref;

  /* ----------------
***************
*** 358,372 ****
  /* ----------------
   * SubLink
   *
!  * A SubLink represents a subselect appearing in an expression, and in some
!  * cases also the combining operator(s) just above it.    The subLinkType
!  * indicates the form of the expression represented:
   *    EXISTS_SUBLINK        EXISTS(SELECT ...)
   *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
   *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
   *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
   *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
   *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
   * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
   * same length as the subselect's targetlist.  MULTIEXPR will *always* have
   * a list with more than one entry; if the subselect has just one target
--- 359,377 ----
  /* ----------------
   * SubLink
   *
!  * A SubLink represents a subselect, or an expression, appearing in an
!  * expression, and in some cases also the combining operator(s) just above
!  * it.    The subLinkType indicates the form of the expression represented:
   *    EXISTS_SUBLINK        EXISTS(SELECT ...)
   *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
   *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
   *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
   *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
   *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
+  * If an expression is used in place of the subselect, it is transformed
+  * into a simple "(SELECT expr)" in gram.y. This is to allow arrays to be
+  * used as if they were the result of a single column subselect. If the
+  * expression is scalar, it is treated as a one element array.
   * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
   * same length as the subselect's targetlist.  MULTIEXPR will *always* have
   * a list with more than one entry; if the subselect has just one target
***************
*** 415,420 ****
--- 420,427 ----
      SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
      bool        useOr;            /* TRUE to combine column results with
                                   * "OR" not "AND" */
+     bool        isExpr;            /* TRUE if the subselect is really derived
+                                  * from a single expression */
      List       *lefthand;        /* list of outer-query expressions on the
                                   * left */
      List       *operName;        /* originally specified operator name */
***************
*** 456,461 ****
--- 463,477 ----
      SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
      bool        useOr;            /* TRUE to combine column results with
                                   * "OR" not "AND" */
+     bool        isExpr;            /* TRUE if the subselect is really derived
+                                  * from a single expression */
+     /* runtime cache for single array expressions */
+     Oid            exprtype;        /* array and element type, and other info
+                                  * needed deconstruct the array */
+     Oid            elemtype;
+     int16        elmlen;
+     bool        elmbyval;
+     char        elmalign;
      /* The combining operators, transformed to executable expressions: */
      List       *exprs;            /* list of OpExpr expression trees */
      List       *paramIds;        /* IDs of Params embedded in the above */
Index: src/include/optimizer/clauses.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/optimizer/clauses.h,v
retrieving revision 1.63
diff -c -r1.63 clauses.h
*** src/include/optimizer/clauses.h    28 May 2003 16:04:02 -0000    1.63
--- src/include/optimizer/clauses.h    24 Jun 2003 02:24:06 -0000
***************
*** 28,33 ****
--- 28,36 ----
  extern Node *get_leftop(Expr *clause);
  extern Node *get_rightop(Expr *clause);

+ extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
+                                     CoercionForm funcformat, List *funcargs);
+
  extern bool not_clause(Node *clause);
  extern Expr *make_notclause(Expr *notclause);
  extern Expr *get_notclausearg(Expr *notclause);
Index: src/include/parser/parse_oper.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_oper.h,v
retrieving revision 1.25
diff -c -r1.25 parse_oper.h
*** src/include/parser/parse_oper.h    29 Apr 2003 22:13:11 -0000    1.25
--- src/include/parser/parse_oper.h    24 Jun 2003 02:24:06 -0000
***************
*** 44,49 ****
--- 44,50 ----
  /* Convenience routines for common calls on the above */
  extern Oid    compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError);
  extern Oid    equality_oper_funcid(Oid argtype);
+ extern Oid  ordering_oper_funcid(Oid argtype);
  extern Oid    ordering_oper_opid(Oid argtype);

  /* Extract operator OID or underlying-function OID from an Operator tuple */
Index: src/include/utils/acl.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/acl.h,v
retrieving revision 1.52
diff -c -r1.52 acl.h
*** src/include/utils/acl.h    11 Jun 2003 09:23:55 -0000    1.52
--- src/include/utils/acl.h    24 Jun 2003 02:24:06 -0000
***************
*** 192,197 ****
--- 192,198 ----
  extern Datum aclremove(PG_FUNCTION_ARGS);
  extern Datum aclcontains(PG_FUNCTION_ARGS);
  extern Datum makeaclitem(PG_FUNCTION_ARGS);
+ extern Datum aclitem_eq(PG_FUNCTION_ARGS);

  /*
   * prototypes for functions in aclchk.c
Index: src/include/utils/array.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v
retrieving revision 1.38
diff -c -r1.38 array.h
*** src/include/utils/array.h    8 May 2003 22:19:57 -0000    1.38
--- src/include/utils/array.h    24 Jun 2003 02:24:06 -0000
***************
*** 32,37 ****
--- 32,68 ----
      Oid            elemtype;        /* element type OID */
  } ArrayType;

+ typedef struct ArrayBuildState
+ {
+     MemoryContext mcontext;        /* where all the temp stuff is kept */
+     Datum       *dvalues;        /* array of accumulated Datums */
+     /*
+      * The allocated size of dvalues[] is always a multiple of
+      * ARRAY_ELEMS_CHUNKSIZE
+      */
+ #define ARRAY_ELEMS_CHUNKSIZE    64
+     int            nelems;            /* number of valid Datums in dvalues[] */
+     Oid            element_type;    /* data type of the Datums */
+     int16        typlen;            /* needed info about datatype */
+     bool        typbyval;
+     char        typalign;
+ } ArrayBuildState;
+
+ /*
+  * structure to cache type metadata needed for array manipulation
+  */
+ typedef struct ArrayMetaState
+ {
+     Oid                element_type;
+     int                typlen;
+     bool            typbyval;
+     char            typdelim;
+     Oid                typelem;
+     Oid                typiofunc;
+     char            typalign;
+     FmgrInfo        proc;
+ } ArrayMetaState;
+
  /*
   * fmgr macros for array objects
   */
***************
*** 86,96 ****
  extern Datum array_send(PG_FUNCTION_ARGS);
  extern Datum array_length_coerce(PG_FUNCTION_ARGS);
  extern Datum array_eq(PG_FUNCTION_ARGS);
  extern Datum array_dims(PG_FUNCTION_ARGS);
  extern Datum array_lower(PG_FUNCTION_ARGS);
  extern Datum array_upper(PG_FUNCTION_ARGS);
- extern Datum array_assign(PG_FUNCTION_ARGS);
- extern Datum array_subscript(PG_FUNCTION_ARGS);
  extern Datum array_type_coerce(PG_FUNCTION_ARGS);

  extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
--- 117,131 ----
  extern Datum array_send(PG_FUNCTION_ARGS);
  extern Datum array_length_coerce(PG_FUNCTION_ARGS);
  extern Datum array_eq(PG_FUNCTION_ARGS);
+ extern Datum array_ne(PG_FUNCTION_ARGS);
+ extern Datum array_lt(PG_FUNCTION_ARGS);
+ extern Datum array_gt(PG_FUNCTION_ARGS);
+ extern Datum array_le(PG_FUNCTION_ARGS);
+ extern Datum array_ge(PG_FUNCTION_ARGS);
+ extern Datum btarraycmp(PG_FUNCTION_ARGS);
  extern Datum array_dims(PG_FUNCTION_ARGS);
  extern Datum array_lower(PG_FUNCTION_ARGS);
  extern Datum array_upper(PG_FUNCTION_ARGS);
  extern Datum array_type_coerce(PG_FUNCTION_ARGS);

  extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
***************
*** 124,130 ****
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
!

  /*
   * prototypes for functions defined in arrayutils.c
--- 159,172 ----
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
! extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
!                                          Datum dvalue, bool disnull,
!                                          Oid element_type,
!                                          MemoryContext rcontext);
! extern Datum makeArrayResult(ArrayBuildState *astate,
!                              MemoryContext rcontext);
! extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
!                                int *dims, int *lbs, MemoryContext rcontext);

  /*
   * prototypes for functions defined in arrayutils.c
***************
*** 141,152 ****
  /*
   * prototypes for functions defined in array_userfuncs.c
   */
- extern Datum singleton_array(PG_FUNCTION_ARGS);
  extern Datum array_push(PG_FUNCTION_ARGS);
- extern Datum array_accum(PG_FUNCTION_ARGS);
  extern Datum array_cat(PG_FUNCTION_ARGS);

! extern ArrayType *create_singleton_array(Oid element_type,
                                           Datum element,
                                           int ndims);

--- 183,193 ----
  /*
   * prototypes for functions defined in array_userfuncs.c
   */
  extern Datum array_push(PG_FUNCTION_ARGS);
  extern Datum array_cat(PG_FUNCTION_ARGS);

! extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
!                                          Oid element_type,
                                           Datum element,
                                           int ndims);

Index: src/include/utils/builtins.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/builtins.h,v
retrieving revision 1.219
diff -c -r1.219 builtins.h
*** src/include/utils/builtins.h    26 May 2003 00:11:28 -0000    1.219
--- src/include/utils/builtins.h    24 Jun 2003 02:24:06 -0000
***************
*** 530,535 ****
--- 530,537 ----
                        List **namelist);
  extern Datum replace_text(PG_FUNCTION_ARGS);
  extern Datum split_text(PG_FUNCTION_ARGS);
+ extern Datum text_to_array(PG_FUNCTION_ARGS);
+ extern Datum array_to_text(PG_FUNCTION_ARGS);
  extern Datum to_hex32(PG_FUNCTION_ARGS);
  extern Datum to_hex64(PG_FUNCTION_ARGS);
  extern Datum md5_text(PG_FUNCTION_ARGS);
Index: src/include/utils/lsyscache.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v
retrieving revision 1.71
diff -c -r1.71 lsyscache.h
*** src/include/utils/lsyscache.h    22 Jun 2003 22:04:55 -0000    1.71
--- src/include/utils/lsyscache.h    24 Jun 2003 02:24:06 -0000
***************
*** 15,20 ****
--- 15,29 ----

  #include "access/htup.h"

+ /* I/O function selector for system_cache_lookup */
+ typedef enum IOFuncSelector
+ {
+     IOFunc_input,
+     IOFunc_output,
+     IOFunc_receive,
+     IOFunc_send
+ } IOFuncSelector;
+
  extern bool op_in_opclass(Oid opno, Oid opclass);
  extern bool op_requires_recheck(Oid opno, Oid opclass);
  extern Oid    get_opclass_member(Oid opclass, int16 strategy);
***************
*** 41,46 ****
--- 50,56 ----
  extern RegProcedure get_oprjoin(Oid opno);
  extern char *get_func_name(Oid funcid);
  extern Oid    get_func_rettype(Oid funcid);
+ extern Oid *get_func_argtypes(Oid funcid, int *nargs);
  extern bool get_func_retset(Oid funcid);
  extern bool func_strict(Oid funcid);
  extern char func_volatile(Oid funcid);
***************
*** 56,61 ****
--- 66,79 ----
  extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
  extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
                       char *typalign);
+ extern void get_type_metadata(Oid element_type,
+                                 IOFuncSelector which_func,
+                                 int *typlen,
+                                 bool *typbyval,
+                                 char *typdelim,
+                                 Oid *typelem,
+                                 Oid *proc,
+                                 char *typalign);
  extern char get_typstorage(Oid typid);
  extern int32 get_typtypmod(Oid typid);
  extern Node *get_typdefault(Oid typid);
Index: src/interfaces/ecpg/preproc/preproc.y
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/preproc.y,v
retrieving revision 1.236
diff -c -r1.236 preproc.y
*** src/interfaces/ecpg/preproc/preproc.y    20 Jun 2003 13:36:34 -0000    1.236
--- src/interfaces/ecpg/preproc/preproc.y    24 Jun 2003 02:24:06 -0000
***************
*** 4595,4601 ****
                  $3.type_enum != ECPGt_char &&
                      $3.type_enum != ECPGt_unsigned_char &&
                  atoi(this->type->type_index) >= 0)
!                 mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");

              types = this;
          }
--- 4595,4601 ----
                  $3.type_enum != ECPGt_char &&
                      $3.type_enum != ECPGt_unsigned_char &&
                  atoi(this->type->type_index) >= 0)
!                 mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");

              types = this;
          }
***************
*** 5415,5421 ****
                      $5.type_enum != ECPGt_char &&
                      $5.type_enum != ECPGt_unsigned_char &&
                      atoi(this->type->type_index) >= 0)
!                     mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");

                  types = this;
              }
--- 5415,5421 ----
                      $5.type_enum != ECPGt_char &&
                      $5.type_enum != ECPGt_unsigned_char &&
                      atoi(this->type->type_index) >= 0)
!                     mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");

                  types = this;
              }
***************
*** 5482,5488 ****

                      default:
                          if (atoi(length) >= 0)
!                             mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data
types");

                          if (atoi(dimension) < 0)
                              type = ECPGmake_simple_type($5.type_enum, make_str("1"));
--- 5482,5488 ----

                      default:
                          if (atoi(length) >= 0)
!                             mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data
types");

                          if (atoi(dimension) < 0)
                              type = ECPGmake_simple_type($5.type_enum, make_str("1"));
Index: src/interfaces/ecpg/preproc/type.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/type.c,v
retrieving revision 1.52
diff -c -r1.52 type.c
*** src/interfaces/ecpg/preproc/type.c    20 Jun 2003 12:00:59 -0000    1.52
--- src/interfaces/ecpg/preproc/type.c    24 Jun 2003 02:24:06 -0000
***************
*** 504,510 ****
                  switch (type->u.element->type)
                  {
                      case ECPGt_array:
!                         yyerror("internal error, found multi-dimensional array\n");
                          break;
                      case ECPGt_struct:
                      case ECPGt_union:
--- 504,510 ----
                  switch (type->u.element->type)
                  {
                      case ECPGt_array:
!                         yyerror("internal error, found multidimensional array\n");
                          break;
                      case ECPGt_struct:
                      case ECPGt_union:
Index: src/interfaces/ecpg/preproc/variable.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/variable.c,v
retrieving revision 1.21
diff -c -r1.21 variable.c
*** src/interfaces/ecpg/preproc/variable.c    11 Jun 2003 06:39:13 -0000    1.21
--- src/interfaces/ecpg/preproc/variable.c    24 Jun 2003 02:24:06 -0000
***************
*** 436,442 ****
      if (atoi(type_index) >= 0)
      {
          if (atoi(*length) >= 0)
!             mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");

          *length = type_index;
      }
--- 436,442 ----
      if (atoi(type_index) >= 0)
      {
          if (atoi(*length) >= 0)
!             mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");

          *length = type_index;
      }
***************
*** 444,450 ****
      if (atoi(type_dimension) >= 0)
      {
          if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
!             mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");

          if (atoi(*dimension) >= 0)
              *length = *dimension;
--- 444,450 ----
      if (atoi(type_dimension) >= 0)
      {
          if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
!             mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");

          if (atoi(*dimension) >= 0)
              *length = *dimension;
***************
*** 463,472 ****
          mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");

      if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
!         mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");

      if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
!         mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");

      switch (type_enum)
      {
--- 463,472 ----
          mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");

      if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
!         mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");

      if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
!         mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");

      switch (type_enum)
      {
***************
*** 480,486 ****
              }

              if (atoi(*length) >= 0)
!                 mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for structures");

              break;
          case ECPGt_varchar:
--- 480,486 ----
              }

              if (atoi(*length) >= 0)
!                 mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for structures");

              break;
          case ECPGt_varchar:
***************
*** 525,531 ****
              }

              if (atoi(*length) >= 0)
!                 mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for simple data types");

              break;
      }
--- 525,531 ----
              }

              if (atoi(*length) >= 0)
!                 mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for simple data types");

              break;
      }
Index: src/test/regress/expected/arrays.out
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/test/regress/expected/arrays.out,v
retrieving revision 1.11
diff -c -r1.11 arrays.out
*** src/test/regress/expected/arrays.out    8 Apr 2003 23:20:04 -0000    1.11
--- src/test/regress/expected/arrays.out    24 Jun 2003 02:24:06 -0000
***************
*** 178,196 ****
  (1 row)

  -- functions
! SELECT singleton_array(42) AS "{42}";
!  {42}
! ------
!  {42}
! (1 row)
!
! SELECT array_append(singleton_array(42), 6) AS "{42,6}";
   {42,6}
  --------
   {42,6}
  (1 row)

! SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
   {6,42}
  --------
   {6,42}
--- 178,190 ----
  (1 row)

  -- functions
! SELECT array_append(array[42], 6) AS "{42,6}";
   {42,6}
  --------
   {42,6}
  (1 row)

! SELECT array_prepend(6, array[42]) AS "{6,42}";
   {6,42}
  --------
   {6,42}
***************
*** 212,235 ****
   {{3,4},{5,6},{1,2}}
  ---------------------
   {{3,4},{5,6},{1,2}}
- (1 row)
-
- SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
-  1.2
- -----
-  1.2
- (1 row)
-
- SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
-  {1.1,9.99,1.3}
- ----------------
-  {1.1,9.99,1.3}
- (1 row)
-
- SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
-  9.99
- ------
-  9.99
  (1 row)

  -- operators
--- 206,211 ----
Index: src/test/regress/sql/arrays.sql
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/test/regress/sql/arrays.sql,v
retrieving revision 1.10
diff -c -r1.10 arrays.sql
*** src/test/regress/sql/arrays.sql    8 Apr 2003 23:20:04 -0000    1.10
--- src/test/regress/sql/arrays.sql    24 Jun 2003 02:24:06 -0000
***************
*** 130,144 ****
  SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";

  -- functions
! SELECT singleton_array(42) AS "{42}";
! SELECT array_append(singleton_array(42), 6) AS "{42,6}";
! SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
  SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}";
  SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
  SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
- SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
- SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
- SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;

  -- operators
  SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
--- 130,140 ----
  SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";

  -- functions
! SELECT array_append(array[42], 6) AS "{42,6}";
! SELECT array_prepend(6, array[42]) AS "{6,42}";
  SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}";
  SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
  SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";

  -- operators
  SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];

Re: array support patch phase 1 patch

From
Bruce Momjian
Date:
Your patch has been added to the PostgreSQL unapplied patches list at:

    http://momjian.postgresql.org/cgi-bin/pgpatches

I will try to apply it within the next 48 hours.

---------------------------------------------------------------------------


Joe Conway wrote:
> Bruce Momjian wrote:
> > Your patch has been added to the PostgreSQL unapplied patches list at:
> [...snip...]
> >>Joe Conway wrote:
> >>>The attached patch addresses Peter's concerns, subject to our agreements
> >>>above. I.e, the changes are:
> >>
> >>The previous patch was no longer applying cleanly, so here is an update.
> >>Applies and compiles clean on cvs tip, passes all regression tests.
> >>Please apply.
>
> Here's another update against cvs tip. The previous version now has a
> duplicate oid conflict -- fixed.
>
> Joe

> Index: doc/src/sgml/array.sgml
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
> retrieving revision 1.25
> diff -c -r1.25 array.sgml
> *** doc/src/sgml/array.sgml    13 Mar 2003 01:30:26 -0000    1.25
> --- doc/src/sgml/array.sgml    24 Jun 2003 02:24:06 -0000
> ***************
> *** 60,73 ****
>   </programlisting>
>    </para>
>
>    <note>
>     <para>
> !    A limitation of the present array implementation is that individual
> !    elements of an array cannot be SQL null values.  The entire array can be set
> !    to null, but you can't have an array with some elements null and some
> !    not.  Fixing this is on the to-do list.
>     </para>
>    </note>
>    </sect2>
>
>    <sect2>
> --- 60,133 ----
>   </programlisting>
>    </para>
>
> +  <para>
> +   A limitation of the present array implementation is that individual
> +   elements of an array cannot be SQL null values.  The entire array can be set
> +   to null, but you can't have an array with some elements null and some
> +   not.
> +  </para>
> +  <para>
> +   This can lead to surprising results. For example, the result of the
> +   previous two inserts looks like this:
> + <programlisting>
> + SELECT * FROM sal_emp;
> +  name  |      pay_by_quarter       |      schedule
> + -------+---------------------------+--------------------
> +  Bill  | {10000,10000,10000,10000} | {{meeting},{""}}
> +  Carol | {20000,25000,25000,25000} | {{talk},{meeting}}
> + (2 rows)
> + </programlisting>
> +   Because the <literal>[2][2]</literal> element of
> +   <structfield>schedule</structfield> is missing in each of the
> +   <command>INSERT</command> statements, the <literal>[1][2]</literal>
> +   element is discarded.
> +  </para>
> +
> +  <note>
> +   <para>
> +    Fixing this is on the to-do list.
> +   </para>
> +  </note>
> +
> +  <para>
> +   The <command>ARRAY</command> expression syntax may also be used:
> + <programlisting>
> + INSERT INTO sal_emp
> +     VALUES ('Bill',
> +     ARRAY[10000, 10000, 10000, 10000],
> +     ARRAY[['meeting', 'lunch'], ['','']]);
> +
> + INSERT INTO sal_emp
> +     VALUES ('Carol',
> +     ARRAY[20000, 25000, 25000, 25000],
> +     ARRAY[['talk', 'consult'], ['meeting', '']]);
> + SELECT * FROM sal_emp;
> +  name  |      pay_by_quarter       |           schedule
> + -------+---------------------------+-------------------------------
> +  Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}}
> +  Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}}
> + (2 rows)
> + </programlisting>
> +   Note that with this syntax, multidimensional arrays must have matching
> +   extents for each dimension. This eliminates the missing-array-elements
> +   problem above. For example:
> + <programlisting>
> + INSERT INTO sal_emp
> +     VALUES ('Carol',
> +     ARRAY[20000, 25000, 25000, 25000],
> +     ARRAY[['talk', 'consult'], ['meeting']]);
> + ERROR:  Multidimensional arrays must have array expressions with matching dimensions
> + </programlisting>
> +   Also notice that string literals are single quoted instead of double quoted.
> +  </para>
> +
>    <note>
>     <para>
> !    The examples in the rest of this section are based on the
> !    <command>ARRAY</command> expression syntax <command>INSERT</command>s.
>     </para>
>    </note>
> +
>    </sect2>
>
>    <sect2>
> ***************
> *** 132,142 ****
>   </programlisting>
>
>     with the same result.  An array subscripting operation is always taken to
> !   represent an array slice if any of the subscripts are written in the
> !   form
>     <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
>     A lower bound of 1 is assumed for any subscript where only one value
> !   is specified.
>    </para>
>
>    <para>
> --- 192,221 ----
>   </programlisting>
>
>     with the same result.  An array subscripting operation is always taken to
> !   represent an array slice if any of the subscripts are written in the form
>     <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
>     A lower bound of 1 is assumed for any subscript where only one value
> !   is specified; another example follows:
> ! <programlisting>
> ! SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
> !          schedule
> ! ---------------------------
> !  {{meeting,lunch},{"",""}}
> ! (1 row)
> ! </programlisting>
> !  </para>
> !
> !  <para>
> !   Additionally, we can also access a single arbitrary array element of
> !   a one-dimensional array with the <function>array_subscript</function>
> !   function:
> ! <programlisting>
> ! SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
> !  array_subscript
> ! -----------------
> !            10000
> ! (1 row)
> ! </programlisting>
>    </para>
>
>    <para>
> ***************
> *** 147,153 ****
>       WHERE name = 'Carol';
>   </programlisting>
>
> !   or updated at a single element:
>
>   <programlisting>
>   UPDATE sal_emp SET pay_by_quarter[4] = 15000
> --- 226,248 ----
>       WHERE name = 'Carol';
>   </programlisting>
>
> !   or using the <command>ARRAY</command> expression syntax:
> !
> ! <programlisting>
> ! UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
> !     WHERE name = 'Carol';
> ! </programlisting>
> !
> !   <note>
> !    <para>
> !     Anywhere you can use the <quote>curly braces</quote> array syntax,
> !     you can also use the <command>ARRAY</command> expression syntax. The
> !     remainder of this section will illustrate only one or the other, but
> !     not both.
> !    </para>
> !   </note>
> !
> !   An array may also be updated at a single element:
>
>   <programlisting>
>   UPDATE sal_emp SET pay_by_quarter[4] = 15000
> ***************
> *** 160,165 ****
> --- 255,268 ----
>   UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
>       WHERE name = 'Carol';
>   </programlisting>
> +
> +   A one-dimensional array may also be updated with the
> +   <function>array_assign</function> function:
> +
> + <programlisting>
> + UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000)
> +     WHERE name = 'Bill';
> + </programListing>
>    </para>
>
>    <para>
> ***************
> *** 179,184 ****
> --- 282,369 ----
>    </para>
>
>    <para>
> +   An array can also be enlarged by using the concatenation operator,
> +   <command>||</command>.
> + <programlisting>
> + SELECT ARRAY[1,2] || ARRAY[3,4];
> +    ?column?
> + ---------------
> +  {{1,2},{3,4}}
> + (1 row)
> +
> + SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
> +       ?column?
> + ---------------------
> +  {{5,6},{1,2},{3,4}}
> + (1 row)
> + </programlisting>
> +
> +   The concatenation operator allows a single element to be pushed on to the
> +   beginning or end of a one-dimensional array. It also allows two
> +   <replaceable>N</>-dimensional arrays, or an <replaceable>N</>-dimensional
> +   and an <replaceable>N+1</>-dimensional array. In the former case, the two
> +   <replaceable>N</>-dimension arrays become outer elements of an
> +   <replaceable>N+1</>-dimensional array. In the latter, the
> +   <replaceable>N</>-dimensional array is added as either the first or last
> +   outer element of the <replaceable>N+1</>-dimensional array.
> +
> +   The array is extended in the direction of the push. Hence, by pushing
> +   onto the beginning of an array with a one-based subscript, a zero-based
> +   subscript array is created:
> +
> + <programlisting>
> + SELECT array_dims(t.f) FROM (SELECT 1 || ARRAY[2,3] AS f) AS t;
> +  array_dims
> + ------------
> +  [0:2]
> + (1 row)
> + </programlisting>
> +  </para>
> +
> +  <para>
> +   An array can also be enlarged by using the functions
> +   <function>array_prepend</function>, <function>array_append</function>,
> +   or <function>array_cat</function>. The first two only support one-dimensional
> +   arrays, but <function>array_cat</function> supports multidimensional arrays.
> +
> +   Note that the concatenation operator discussed above is preferred over
> +   direct use of these functions. In fact, the functions are primarily for use
> +   in implementing the concatenation operator. However, they may be directly
> +   useful in the creation of user-defined aggregates. Some examples:
> +
> + <programlisting>
> + SELECT array_prepend(1, ARRAY[2,3]);
> +  array_prepend
> + ---------------
> +  {1,2,3}
> + (1 row)
> +
> + SELECT array_append(ARRAY[1,2], 3);
> +  array_append
> + --------------
> +  {1,2,3}
> + (1 row)
> +
> + SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
> +    array_cat
> + ---------------
> +  {{1,2},{3,4}}
> + (1 row)
> +
> + SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
> +       array_cat
> + ---------------------
> +  {{1,2},{3,4},{5,6}}
> + (1 row)
> +
> + SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
> +       array_cat
> + ---------------------
> +  {{5,6},{1,2},{3,4}}
> + </programlisting>
> +  </para>
> +
> +  <para>
>     The syntax for <command>CREATE TABLE</command> allows fixed-length
>     arrays to be defined:
>
> ***************
> *** 194,199 ****
> --- 379,394 ----
>    </para>
>
>    <para>
> +   An alternative syntax for one-dimensional arrays may be used.
> +   <structfield>pay_by_quarter</structfield> could have been defined as:
> + <programlisting>
> +     pay_by_quarter  integer ARRAY[4],
> + </programlisting>
> +   This syntax may <emphasis>only</emphasis> be used with the integer
> +   constant to denote the array size.
> +  </para>
> +
> +  <para>
>     Actually, the current implementation does not enforce the declared
>     number of dimensions either.  Arrays of a particular element type are
>     all considered to be of the same type, regardless of size or number
> ***************
> *** 300,305 ****
> --- 495,566 ----
>      is not ignored, however: after skipping leading whitespace, everything
>      up to the next right brace or delimiter is taken as the item value.
>     </para>
> +
> +   <para>
> +    As illustrated earlier in this chapter, arrays may also be represented
> +    using the <command>ARRAY</command> expression syntax. This representation
> +    of an array value consists of items that are interpreted according to the
> +    I/O conversion rules for the array's element type, plus decoration that
> +    indicates the array structure. The decoration consists of the keyword
> +    <command>ARRAY</command> and square brackets (<literal>[</> and
> +    <literal>]</>) around the array values, plus delimiter characters between
> +    adjacent items. The delimiter character is always a comma (<literal>,</>).
> +    When representing multidimensional arrays, the keyword
> +    <command>ARRAY</command> is only necessary for the outer level. For example,
> +    <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as:
> + <programlisting>
> + SELECT ARRAY[['hello world', 'happy birthday']];
> +                array
> + ------------------------------------
> +  {{"hello world","happy birthday"}}
> + (1 row)
> + </programlisting>
> +   or it also could be written as:
> + <programlisting>
> + SELECT ARRAY[ARRAY['hello world', 'happy birthday']];
> +                array
> + ------------------------------------
> +  {{"hello world","happy birthday"}}
> + (1 row)
> + </programlisting>
> +   </para>
> +
> +   <para>
> +    A final method to represent an array, is through an
> +    <command>ARRAY</command> sub-select expression. For example:
> + <programlisting>
> + SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
> +                           ?column?
> + -------------------------------------------------------------
> +  {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
> + (1 row)
> + </programlisting>
> +   The sub-select may <emphasis>only</emphasis> return a single column. The
> +   resulting one-dimensional array will have an element for each row in the
> +   sub-select result, with an element type matching that of the sub-select's
> +   target column.
> +   </para>
> +
> +   <para>
> +    Arrays may be cast from one type to another in similar fashion to other
> +    data types:
> +
> + <programlisting>
> + SELECT ARRAY[1,2,3]::oid[];
> +   array
> + ---------
> +  {1,2,3}
> + (1 row)
> +
> + SELECT CAST(ARRAY[1,2,3] AS float8[]);
> +   array
> + ---------
> +  {1,2,3}
> + (1 row)
> + </programlisting>
> +
> +   </para>
> +
>    </sect2>
>
>    <sect2>
> ***************
> *** 316,321 ****
> --- 577,590 ----
>      Alternatively, you can use backslash-escaping to protect all data characters
>      that would otherwise be taken as array syntax or ignorable white space.
>     </para>
> +
> +  <note>
> +   <para>
> +    The discussion in the preceding paragraph with respect to double quoting does
> +    not pertain to the <command>ARRAY</command> expression syntax. In that case,
> +    each element is quoted exactly as any other literal value of the element type.
> +   </para>
> +  </note>
>
>     <para>
>      The array output routine will put double quotes around element values
> Index: doc/src/sgml/func.sgml
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v
> retrieving revision 1.154
> diff -c -r1.154 func.sgml
> *** doc/src/sgml/func.sgml    5 May 2003 15:08:49 -0000    1.154
> --- doc/src/sgml/func.sgml    24 Jun 2003 02:24:06 -0000
> ***************
> *** 6962,6967 ****
> --- 6962,7164 ----
>
>     </sect1>
>
> +  <sect1 id="functions-array">
> +   <title>Array Functions</title>
> +
> +   <para>
> +    <xref linkend="array-operators-table"> shows the operators
> +    available for the <type>array</type> types.
> +   </para>
> +
> +     <table id="array-operators-table">
> +      <title><type>array</type> Operators</title>
> +      <tgroup cols="4">
> +       <thead>
> +        <row>
> +     <entry>Operator</entry>
> +     <entry>Description</entry>
> +     <entry>Example</entry>
> +     <entry>Result</entry>
> +        </row>
> +       </thead>
> +       <tbody>
> +        <row>
> +     <entry> <literal>=</literal> </entry>
> +     <entry>equals</entry>
> +     <entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry>
> +     <entry><literal>t</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>array-to-array concatenation</entry>
> +     <entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry>
> +     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>array-to-array concatenation</entry>
> +     <entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry>
> +     <entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>element-to-array concatenation</entry>
> +     <entry><literal>3 || ARRAY[4,5,6]</literal></entry>
> +     <entry><literal>{3,4,5,6}</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>array-to-element concatenation</entry>
> +     <entry><literal>ARRAY[4,5,6] || 7</literal></entry>
> +     <entry><literal>{4,5,6,7}</literal></entry>
> +        </row>
> +       </tbody>
> +      </tgroup>
> +     </table>
> +
> +   <para>
> +    <xref linkend="array-functions-table"> shows the functions
> +    available for use with array types. See <xref linkend="arrays">
> +    for more discussion and examples for the use of these functions.
> +   </para>
> +
> +     <table id="array-functions-table">
> +      <title><type>array</type> Functions</title>
> +      <tgroup cols="5">
> +       <thead>
> +        <row>
> +     <entry>Function</entry>
> +     <entry>Return Type</entry>
> +     <entry>Description</entry>
> +     <entry>Example</entry>
> +     <entry>Result</entry>
> +        </row>
> +       </thead>
> +       <tbody>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_append</function>
> +       (<type>anyarray</type>, <type>anyelement</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>anyarray</type></entry>
> +     <entry>
> +      append an element to the end of an array, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_append(ARRAY[1,2], 3)</literal></entry>
> +     <entry><literal>{1,2,3}</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_cat</function>
> +       (<type>anyarray</type>, <type>anyarray</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>anyarray</type></entry>
> +     <entry>
> +      concatenate two arrays, returning <literal>NULL</literal>
> +      for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry>
> +     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_dims</function>
> +       (<type>anyarray</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>text</type></entry>
> +     <entry>
> +      returns a text representation of array dimension lower and upper bounds,
> +      generating an ERROR for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry>
> +     <entry><literal>[1:2][1:3]</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_lower</function>
> +       (<type>anyarray</type>, <type>integer</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>integer</type></entry>
> +     <entry>
> +      returns lower bound of the requested array dimension, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry>
> +     <entry><literal>0</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_prepend</function>
> +       (<type>anyelement</type>, <type>anyarray</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>anyarray</type></entry>
> +     <entry>
> +      append an element to the beginning of an array, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry>
> +     <entry><literal>{1,2,3}</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_to_string</function>
> +       (<type>anyarray</type>, <type>text</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>text</type></entry>
> +     <entry>
> +      concatenates array elements using provided delimiter, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_to_string(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry>
> +     <entry><literal>1.10~^~2.20~^~3.30</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_upper</function>
> +       (<type>anyarray</type>, <type>integer</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>integer</type></entry>
> +     <entry>
> +      returns upper bound of the requested array dimension, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry>
> +     <entry><literal>4</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>string_to_array</function>
> +       (<type>text</type>, <type>text</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>text[]</type></entry>
> +     <entry>
> +      splits string into array elements using provided delimiter, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>string_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry>
> +     <entry><literal>{1.1,2.2,3.3}</literal></entry>
> +        </row>
> +       </tbody>
> +      </tgroup>
> +     </table>
> +   </sect1>
>
>    <sect1 id="functions-aggregate">
>     <title>Aggregate Functions</title>
> Index: src/backend/catalog/pg_aggregate.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_aggregate.c,v
> retrieving revision 1.56
> diff -c -r1.56 pg_aggregate.c
> *** src/backend/catalog/pg_aggregate.c    18 Sep 2002 21:35:20 -0000    1.56
> --- src/backend/catalog/pg_aggregate.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 50,59 ****
>       Oid            finalfn = InvalidOid;    /* can be omitted */
>       Oid            finaltype;
>       Oid            fnArgs[FUNC_MAX_ARGS];
> !     int            nargs;
>       Oid            procOid;
>       TupleDesc    tupDesc;
>       int            i;
>       ObjectAddress myself,
>                   referenced;
>
> --- 50,65 ----
>       Oid            finalfn = InvalidOid;    /* can be omitted */
>       Oid            finaltype;
>       Oid            fnArgs[FUNC_MAX_ARGS];
> !     int            nargs_transfn;
> !     int            nargs_finalfn;
>       Oid            procOid;
>       TupleDesc    tupDesc;
>       int            i;
> +     Oid            rettype;
> +     Oid           *true_oid_array_transfn;
> +     Oid           *true_oid_array_finalfn;
> +     bool        retset;
> +     FuncDetailCode fdresult;
>       ObjectAddress myself,
>                   referenced;
>
> ***************
> *** 68,91 ****
>       MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>       fnArgs[0] = aggTransType;
>       if (aggBaseType == ANYOID)
> !         nargs = 1;
>       else
>       {
>           fnArgs[1] = aggBaseType;
> !         nargs = 2;
>       }
> !     transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
>       if (!OidIsValid(transfn))
> !         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
>       tup = SearchSysCache(PROCOID,
>                            ObjectIdGetDatum(transfn),
>                            0, 0, 0);
>       if (!HeapTupleIsValid(tup))
> !         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
>       proc = (Form_pg_proc) GETSTRUCT(tup);
> -     if (proc->prorettype != aggTransType)
> -         elog(ERROR, "return type of transition function %s is not %s",
> -          NameListToString(aggtransfnName), format_type_be(aggTransType));
>
>       /*
>        * If the transfn is strict and the initval is NULL, make sure input
> --- 74,122 ----
>       MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>       fnArgs[0] = aggTransType;
>       if (aggBaseType == ANYOID)
> !         nargs_transfn = 1;
>       else
>       {
>           fnArgs[1] = aggBaseType;
> !         nargs_transfn = 2;
>       }
> !
> !     /*
> !      * func_get_detail looks up the function in the catalogs, does
> !      * disambiguation for polymorphic functions, handles inheritance, and
> !      * returns the funcid and type and set or singleton status of the
> !      * function's return value.  it also returns the true argument types
> !      * to the function.
> !      */
> !     fdresult = func_get_detail(aggtransfnName, NIL, nargs_transfn, fnArgs,
> !                                &transfn, &rettype, &retset,
> !                                &true_oid_array_transfn);
> !
> !     /* only valid case is a normal function */
> !     if (fdresult != FUNCDETAIL_NORMAL)
> !         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
> !
>       if (!OidIsValid(transfn))
> !         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
> !
> !     /*
> !      * enforce consistency with ANYARRAY and ANYELEMENT argument
> !      * and return types, possibly modifying return type along the way
> !      */
> !     rettype = enforce_generic_type_consistency(fnArgs, true_oid_array_transfn,
> !                                                        nargs_transfn, rettype);
> !
> !     if (rettype != aggTransType)
> !         elog(ERROR, "return type of transition function %s is not %s",
> !          NameListToString(aggtransfnName), format_type_be(aggTransType));
> !
>       tup = SearchSysCache(PROCOID,
>                            ObjectIdGetDatum(transfn),
>                            0, 0, 0);
>       if (!HeapTupleIsValid(tup))
> !         func_error("AggregateCreate", aggtransfnName,
> !                         nargs_transfn, fnArgs, NULL);
>       proc = (Form_pg_proc) GETSTRUCT(tup);
>
>       /*
>        * If the transfn is strict and the initval is NULL, make sure input
> ***************
> *** 105,121 ****
>       {
>           MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>           fnArgs[0] = aggTransType;
> !         finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
> !         if (!OidIsValid(finalfn))
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !         tup = SearchSysCache(PROCOID,
> !                              ObjectIdGetDatum(finalfn),
> !                              0, 0, 0);
> !         if (!HeapTupleIsValid(tup))
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !         proc = (Form_pg_proc) GETSTRUCT(tup);
> !         finaltype = proc->prorettype;
> !         ReleaseSysCache(tup);
>       }
>       else
>       {
> --- 136,161 ----
>       {
>           MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>           fnArgs[0] = aggTransType;
> !         nargs_finalfn = 1;
> !
> !         fdresult = func_get_detail(aggfinalfnName, NIL, 1, fnArgs,
> !                                    &finalfn, &rettype, &retset,
> !                                    &true_oid_array_finalfn);
> !
> !         /* only valid case is a normal function */
> !         if (fdresult != FUNCDETAIL_NORMAL)
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !
> !         if (!OidIsValid(finalfn))
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !
> !         /*
> !          * enforce consistency with ANYARRAY and ANYELEMENT argument
> !          * and return types, possibly modifying return type along the way
> !          */
> !         finaltype = enforce_generic_type_consistency(fnArgs,
> !                                                      true_oid_array_finalfn,
> !                                                      nargs_finalfn, rettype);
>       }
>       else
>       {
> ***************
> *** 125,130 ****
> --- 165,191 ----
>           finaltype = aggTransType;
>       }
>       Assert(OidIsValid(finaltype));
> +
> +     /*
> +      * special disallowed cases:
> +      * 1)    if finaltype is polymorphic, basetype cannot be ANY
> +      * 2)    if finaltype is polymorphic, both args to transfn must be
> +      *        polymorphic
> +      */
> +     if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
> +     {
> +         if (aggBaseType == ANYOID)
> +             elog(ERROR, "aggregate with base type ANY must have a " \
> +                         "non-polymorphic return type");
> +
> +         if (nargs_transfn > 1 && (
> +             (true_oid_array_transfn[0] != ANYARRAYOID &&
> +              true_oid_array_transfn[0] != ANYELEMENTOID) ||
> +             (true_oid_array_transfn[1] != ANYARRAYOID &&
> +              true_oid_array_transfn[1] != ANYELEMENTOID)))
> +             elog(ERROR, "aggregate with polymorphic return type requires " \
> +                         "state function with both arguments polymorphic");
> +     }
>
>       /*
>        * Everything looks okay.  Try to create the pg_proc entry for the
> Index: src/backend/commands/aggregatecmds.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/aggregatecmds.c,v
> retrieving revision 1.5
> diff -c -r1.5 aggregatecmds.c
> *** src/backend/commands/aggregatecmds.c    4 Sep 2002 20:31:14 -0000    1.5
> --- src/backend/commands/aggregatecmds.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 119,125 ****
>           baseTypeId = typenameTypeId(baseType);
>
>       transTypeId = typenameTypeId(transType);
> !     if (get_typtype(transTypeId) == 'p')
>           elog(ERROR, "Aggregate transition datatype cannot be %s",
>                format_type_be(transTypeId));
>
> --- 119,127 ----
>           baseTypeId = typenameTypeId(baseType);
>
>       transTypeId = typenameTypeId(transType);
> !     if (get_typtype(transTypeId) == 'p' &&
> !         transTypeId != ANYARRAYOID &&
> !         transTypeId != ANYELEMENTOID)
>           elog(ERROR, "Aggregate transition datatype cannot be %s",
>                format_type_be(transTypeId));
>
> Index: src/backend/executor/execQual.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/execQual.c,v
> retrieving revision 1.130
> diff -c -r1.130 execQual.c
> *** src/backend/executor/execQual.c    28 May 2003 22:32:49 -0000    1.130
> --- src/backend/executor/execQual.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 1528,1544 ****
>               {
>                   /* Check other sub-arrays are compatible */
>                   if (elem_ndims != ARR_NDIM(array))
> !                     elog(ERROR, "Multiple dimension arrays must have array "
>                            "expressions with matching number of dimensions");
>
>                   if (memcmp(elem_dims, ARR_DIMS(array),
>                              elem_ndims * sizeof(int)) != 0)
> !                     elog(ERROR, "Multiple dimension arrays must have array "
>                            "expressions with matching dimensions");
>
>                   if (memcmp(elem_lbs, ARR_LBOUND(array),
>                              elem_ndims * sizeof(int)) != 0)
> !                     elog(ERROR, "Multiple dimension arrays must have array "
>                            "expressions with matching dimensions");
>               }
>
> --- 1528,1544 ----
>               {
>                   /* Check other sub-arrays are compatible */
>                   if (elem_ndims != ARR_NDIM(array))
> !                     elog(ERROR, "Multidimensional arrays must have array "
>                            "expressions with matching number of dimensions");
>
>                   if (memcmp(elem_dims, ARR_DIMS(array),
>                              elem_ndims * sizeof(int)) != 0)
> !                     elog(ERROR, "Multidimensional arrays must have array "
>                            "expressions with matching dimensions");
>
>                   if (memcmp(elem_lbs, ARR_LBOUND(array),
>                              elem_ndims * sizeof(int)) != 0)
> !                     elog(ERROR, "Multidimensional arrays must have array "
>                            "expressions with matching dimensions");
>               }
>
> Index: src/backend/executor/nodeAgg.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeAgg.c,v
> retrieving revision 1.107
> diff -c -r1.107 nodeAgg.c
> *** src/backend/executor/nodeAgg.c    22 Jun 2003 22:04:54 -0000    1.107
> --- src/backend/executor/nodeAgg.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 58,63 ****
> --- 58,64 ----
>   #include "executor/executor.h"
>   #include "executor/nodeAgg.h"
>   #include "miscadmin.h"
> + #include "nodes/makefuncs.h"
>   #include "optimizer/clauses.h"
>   #include "parser/parse_coerce.h"
>   #include "parser/parse_expr.h"
> ***************
> *** 212,218 ****
>   static void agg_fill_hash_table(AggState *aggstate);
>   static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
>   static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
> !
>
>   /*
>    * Initialize all aggregates for a new group of input values.
> --- 213,219 ----
>   static void agg_fill_hash_table(AggState *aggstate);
>   static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
>   static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
> ! static Oid resolve_type(Oid type_to_resolve, Oid context_type);
>
>   /*
>    * Initialize all aggregates for a new group of input values.
> ***************
> *** 351,364 ****
>       fcinfo.context = NULL;
>       fcinfo.resultinfo = NULL;
>       fcinfo.isnull = false;
> -
>       fcinfo.flinfo = &peraggstate->transfn;
>       fcinfo.nargs = 2;
>       fcinfo.arg[0] = pergroupstate->transValue;
>       fcinfo.argnull[0] = pergroupstate->transValueIsNull;
>       fcinfo.arg[1] = newVal;
>       fcinfo.argnull[1] = isNull;
> -
>       newVal = FunctionCallInvoke(&fcinfo);
>
>       /*
> --- 352,363 ----
> ***************
> *** 1187,1193 ****
> --- 1186,1206 ----
>           AclResult    aclresult;
>           Oid            transfn_oid,
>                       finalfn_oid;
> +         FuncExpr   *transfnexpr,
> +                    *finalfnexpr;
>           Datum        textInitVal;
> +         List       *fargs;
> +         Oid            agg_rt_type;
> +         Oid           *transfn_arg_types;
> +         List       *transfn_args = NIL;
> +         int            transfn_nargs;
> +         Oid            transfn_ret_type;
> +         Oid           *finalfn_arg_types = NULL;
> +         List       *finalfn_args = NIL;
> +         Oid            finalfn_ret_type = InvalidOid;
> +         int            finalfn_nargs = 0;
> +         Node       *arg0;
> +         Node       *arg1;
>           int            i;
>
>           /* Planner should have assigned aggregate to correct level */
> ***************
> *** 1238,1243 ****
> --- 1251,1416 ----
>                           &peraggstate->transtypeLen,
>                           &peraggstate->transtypeByVal);
>
> +         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
> +         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
> +
> +         /* get the runtime aggregate argument type */
> +         fargs = aggref->args;
> +         agg_rt_type = exprType((Node *) nth(0, fargs));
> +
> +         /* get the transition function argument and return types */
> +         transfn_ret_type = get_func_rettype(transfn_oid);
> +         transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs);
> +
> +         /* resolve any polymorphic types */
> +         if (transfn_nargs == 2)
> +         /* base type was not ANY */
> +         {
> +             if (transfn_arg_types[1] == ANYARRAYOID ||
> +                 transfn_arg_types[1] == ANYELEMENTOID)
> +                 transfn_arg_types[1] = agg_rt_type;
> +
> +             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
> +                                                         agg_rt_type);
> +
> +             /*
> +              * Build arg list to use on the transfn FuncExpr node. We really
> +              * only care that the node type is correct so that the transfn
> +              * can discover the actual argument types at runtime using
> +              * get_fn_expr_argtype()
> +              */
> +             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
> +                                                             -1, COERCE_DONTCARE);
> +             arg1 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[1],
> +                                                             -1, COERCE_DONTCARE);
> +             transfn_args = makeList2(arg0, arg1);
> +
> +             /*
> +              * the state transition function always returns the same type
> +              * as its first argument
> +              */
> +             if (transfn_ret_type == ANYARRAYOID ||
> +                 transfn_ret_type == ANYELEMENTOID)
> +                 transfn_ret_type = transfn_arg_types[0];
> +         }
> +         else if (transfn_nargs == 1)
> +         /*
> +          * base type was ANY, therefore the aggregate return type should
> +          * be non-polymorphic
> +          */
> +         {
> +             Oid    finaltype = get_func_rettype(aggref->aggfnoid);
> +
> +             /*
> +              * this should have been prevented in AggregateCreate,
> +              * but check anyway
> +              */
> +             if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
> +                 elog(ERROR, "aggregate with base type ANY must have a " \
> +                             "non-polymorphic return type");
> +
> +             /* see if we have a final function */
> +             if (OidIsValid(finalfn_oid))
> +             {
> +                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
> +                 if (finalfn_nargs != 1)
> +                     elog(ERROR, "final function takes unexpected number " \
> +                                 "of arguments: %d", finalfn_nargs);
> +
> +                 /*
> +                  * final function argument is always the same as the state
> +                  * function return type
> +                  */
> +                 if (finalfn_arg_types[0] != ANYARRAYOID &&
> +                     finalfn_arg_types[0] != ANYELEMENTOID)
> +                 {
> +                     /* if it is not ambiguous, use it */
> +                     transfn_ret_type = finalfn_arg_types[0];
> +                 }
> +                 else
> +                 {
> +                     /* if it is ambiguous, try to derive it */
> +                     finalfn_ret_type = finaltype;
> +                     finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0],
> +                                                             finalfn_ret_type);
> +                     transfn_ret_type = finalfn_arg_types[0];
> +                 }
> +             }
> +             else
> +                 transfn_ret_type = finaltype;
> +
> +             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
> +                                                         transfn_ret_type);
> +
> +             /*
> +              * Build arg list to use on the transfn FuncExpr node. We really
> +              * only care that the node type is correct so that the transfn
> +              * can discover the actual argument types at runtime using
> +              * get_fn_expr_argtype()
> +              */
> +             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
> +                                                             -1, COERCE_DONTCARE);
> +             transfn_args = makeList1(arg0);
> +         }
> +         else
> +             elog(ERROR, "state transition function takes unexpected number " \
> +                         "of arguments: %d", transfn_nargs);
> +
> +         if (OidIsValid(finalfn_oid))
> +         {
> +             /* get the final function argument and return types */
> +             if (finalfn_ret_type == InvalidOid)
> +                 finalfn_ret_type = get_func_rettype(finalfn_oid);
> +
> +             if (!finalfn_arg_types)
> +             {
> +                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
> +                 if (finalfn_nargs != 1)
> +                     elog(ERROR, "final function takes unexpected number " \
> +                                 "of arguments: %d", finalfn_nargs);
> +             }
> +
> +             /*
> +              * final function argument is always the same as the state
> +              * function return type, which by now should have been resolved
> +              */
> +             if (finalfn_arg_types[0] == ANYARRAYOID ||
> +                 finalfn_arg_types[0] == ANYELEMENTOID)
> +                 finalfn_arg_types[0] = transfn_ret_type;
> +
> +             /*
> +              * Build arg list to use on the finalfn FuncExpr node. We really
> +              * only care that the node type is correct so that the finalfn
> +              * can discover the actual argument type at runtime using
> +              * get_fn_expr_argtype()
> +              */
> +             arg0 = (Node *) makeRelabelType((Expr *) NULL, finalfn_arg_types[0],
> +                                                             -1, COERCE_DONTCARE);
> +             finalfn_args = makeList1(arg0);
> +
> +             finalfn_ret_type = resolve_type(finalfn_ret_type,
> +                                                 finalfn_arg_types[0]);
> +         }
> +
> +         fmgr_info(transfn_oid, &peraggstate->transfn);
> +         transfnexpr = (FuncExpr *) make_funcclause(transfn_oid,
> +                           transfn_ret_type,
> +                           false,            /* cannot be a set */
> +                           COERCE_DONTCARE,    /* to match any user expr */
> +                           transfn_args);
> +         peraggstate->transfn.fn_expr = (Node *) transfnexpr;
> +
> +         if (OidIsValid(finalfn_oid))
> +         {
> +             fmgr_info(finalfn_oid, &peraggstate->finalfn);
> +             finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid,
> +                           finalfn_ret_type,
> +                           false,            /* cannot be a set */
> +                           COERCE_DONTCARE,    /* to match any user expr */
> +                           finalfn_args);
> +             peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
> +         }
> +
>           /*
>            * initval is potentially null, so don't try to access it as a
>            * struct field. Must do it the hard way with SysCacheGetAttr.
> ***************
> *** 1250,1263 ****
>               peraggstate->initValue = (Datum) 0;
>           else
>               peraggstate->initValue = GetAggInitVal(textInitVal,
> !                                                    aggform->aggtranstype);
> !
> !         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
> !         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
> !
> !         fmgr_info(transfn_oid, &peraggstate->transfn);
> !         if (OidIsValid(finalfn_oid))
> !             fmgr_info(finalfn_oid, &peraggstate->finalfn);
>
>           /*
>            * If the transfn is strict and the initval is NULL, make sure
> --- 1423,1429 ----
>               peraggstate->initValue = (Datum) 0;
>           else
>               peraggstate->initValue = GetAggInitVal(textInitVal,
> !                                                    transfn_arg_types[0]);
>
>           /*
>            * If the transfn is strict and the initval is NULL, make sure
> ***************
> *** 1468,1471 ****
> --- 1634,1670 ----
>       elog(ERROR, "Aggregate function %u called as normal function",
>            fcinfo->flinfo->fn_oid);
>       return (Datum) 0;            /* keep compiler quiet */
> + }
> +
> + static Oid
> + resolve_type(Oid type_to_resolve, Oid context_type)
> + {
> +     Oid        resolved_type;
> +
> +     if (context_type == ANYARRAYOID || context_type == ANYELEMENTOID)
> +         resolved_type = type_to_resolve;
> +     else if (type_to_resolve == ANYARRAYOID)
> +     /* any array */
> +     {
> +         Oid        context_type_arraytype = get_array_type(context_type);
> +
> +         if (context_type_arraytype != InvalidOid)
> +             resolved_type = context_type_arraytype;
> +         else
> +             resolved_type = context_type;
> +     }
> +     else if (type_to_resolve == ANYELEMENTOID)
> +     /* any element */
> +     {
> +         Oid        context_type_elemtype = get_element_type(context_type);
> +
> +         if (context_type_elemtype != InvalidOid)
> +             resolved_type = context_type_elemtype;
> +         else
> +             resolved_type = context_type;
> +     }
> +     else
> +         resolved_type = type_to_resolve;
> +
> +     return resolved_type;
>   }
> Index: src/backend/executor/nodeSubplan.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v
> retrieving revision 1.47
> diff -c -r1.47 nodeSubplan.c
> *** src/backend/executor/nodeSubplan.c    22 Jun 2003 22:04:54 -0000    1.47
> --- src/backend/executor/nodeSubplan.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 28,50 ****
>   #include "utils/datum.h"
>   #include "utils/lsyscache.h"
>
> -
> - typedef struct ArrayBuildState
> - {
> -     MemoryContext mcontext;        /* where all the temp stuff is kept */
> -     Datum       *dvalues;        /* array of accumulated Datums */
> -     /*
> -      * The allocated size of dvalues[] is always a multiple of
> -      * ARRAY_ELEMS_CHUNKSIZE
> -      */
> - #define ARRAY_ELEMS_CHUNKSIZE    64
> -     int            nelems;            /* number of valid Datums in dvalues[] */
> -     Oid            element_type;    /* data type of the Datums */
> -     int16        typlen;            /* needed info about datatype */
> -     bool        typbyval;
> -     char        typalign;
> - } ArrayBuildState;
> -
>   static Datum ExecHashSubPlan(SubPlanState *node,
>                                ExprContext *econtext,
>                                bool *isNull);
> --- 28,33 ----
> ***************
> *** 54,66 ****
>   static void buildSubPlanHash(SubPlanState *node);
>   static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
>   static bool tupleAllNulls(HeapTuple tuple);
> - static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
> -                                          Datum dvalue, bool disnull,
> -                                          Oid element_type,
> -                                          MemoryContext rcontext);
> - static Datum makeArrayResult(ArrayBuildState *astate,
> -                              MemoryContext rcontext);
> -
>
>   /* ----------------------------------------------------------------
>    *        ExecSubPlan
> --- 37,42 ----
> ***************
> *** 224,229 ****
> --- 200,206 ----
>       PlanState  *planstate = node->planstate;
>       SubLinkType subLinkType = subplan->subLinkType;
>       bool        useOr = subplan->useOr;
> +     bool        isExpr = subplan->isExpr;
>       MemoryContext oldcontext;
>       TupleTableSlot *slot;
>       Datum        result;
> ***************
> *** 294,299 ****
> --- 271,281 ----
>           bool        rownull = false;
>           int            col = 1;
>           List       *plst;
> +         int            numelems;
> +         int            elemnum;
> +         Datum        dvalue;
> +         Datum       *dvalues = NULL;
> +         bool        disnull;
>
>           if (subLinkType == EXISTS_SUBLINK)
>           {
> ***************
> *** 331,339 ****
>
>           if (subLinkType == ARRAY_SUBLINK)
>           {
> -             Datum    dvalue;
> -             bool    disnull;
> -
>               found = true;
>               /* stash away current value */
>               dvalue = heap_getattr(tup, 1, tdesc, &disnull);
> --- 313,318 ----
> ***************
> *** 351,448 ****
>           found = true;
>
>           /*
> !          * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
> !          * operators for columns of tuple.
>            */
> !         plst = subplan->paramIds;
> !         foreach(lst, node->exprs)
>           {
> !             ExprState  *exprstate = (ExprState *) lfirst(lst);
> !             int            paramid = lfirsti(plst);
> !             ParamExecData *prmdata;
> !             Datum        expresult;
> !             bool        expnull;
> !
> !             /*
> !              * Load up the Param representing this column of the sub-select.
> !              */
> !             prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
> !             Assert(prmdata->execPlan == NULL);
> !             prmdata->value = heap_getattr(tup, col, tdesc,
> !                                           &(prmdata->isnull));
>
> !             /*
> !              * Now we can eval the combining operator for this column.
> !              */
> !             expresult = ExecEvalExprSwitchContext(exprstate, econtext,
> !                                                   &expnull, NULL);
> !
> !             /*
> !              * Combine the result into the row result as appropriate.
> !              */
> !             if (col == 1)
>               {
> !                 rowresult = expresult;
> !                 rownull = expnull;
>               }
> !             else if (useOr)
>               {
> !                 /* combine within row per OR semantics */
> !                 if (expnull)
> !                     rownull = true;
> !                 else if (DatumGetBool(expresult))
>                   {
> !                     rowresult = BoolGetDatum(true);
> !                     rownull = false;
> !                     break;        /* needn't look at any more columns */
>                   }
>               }
>               else
>               {
> !                 /* combine within row per AND semantics */
> !                 if (expnull)
> !                     rownull = true;
> !                 else if (!DatumGetBool(expresult))
> !                 {
> !                     rowresult = BoolGetDatum(false);
> !                     rownull = false;
> !                     break;        /* needn't look at any more columns */
> !                 }
>               }
>
> -             plst = lnext(plst);
> -             col++;
>           }
>
> !         if (subLinkType == ANY_SUBLINK)
>           {
> !             /* combine across rows per OR semantics */
> !             if (rownull)
> !                 *isNull = true;
> !             else if (DatumGetBool(rowresult))
>               {
> !                 result = BoolGetDatum(true);
> !                 *isNull = false;
> !                 break;            /* needn't look at any more rows */
>               }
> !         }
> !         else if (subLinkType == ALL_SUBLINK)
> !         {
> !             /* combine across rows per AND semantics */
> !             if (rownull)
> !                 *isNull = true;
> !             else if (!DatumGetBool(rowresult))
>               {
> !                 result = BoolGetDatum(false);
> !                 *isNull = false;
> !                 break;            /* needn't look at any more rows */
>               }
> -         }
> -         else
> -         {
> -             /* must be MULTIEXPR_SUBLINK */
> -             result = rowresult;
> -             *isNull = rownull;
>           }
>       }
>
> --- 330,492 ----
>           found = true;
>
>           /*
> !          * When isExpr is true, we have either a scalar expression or an
> !          * array. In the former case, this is no different than the !isExpr
> !          * case. In the latter case, iterate over the elements as if they
> !          * were from multiple input tuples.
>            */
> !         if (!isExpr)
> !             numelems = 1;
> !         else
>           {
> !             Oid        expr_typeid = tdesc->attrs[0]->atttypid;
>
> !             if (expr_typeid != subplan->exprtype)
>               {
> !                 subplan->exprtype = expr_typeid;
> !                 subplan->elemtype = get_element_type(expr_typeid);
> !
> !                 if (subplan->elemtype != InvalidOid)
> !                     get_typlenbyvalalign(subplan->elemtype,
> !                                          &subplan->elmlen,
> !                                          &subplan->elmbyval,
> !                                          &subplan->elmalign);
>               }
> !
> !             /* get current value */
> !             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
> !
> !             /* XXX this will need work if/when arrays support NULL elements */
> !             if (!disnull)
>               {
> !                 if (subplan->elemtype != InvalidOid)
> !                 {
> !                     ArrayType   *v = DatumGetArrayTypeP(dvalue);
> !
> !                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
> !                                       subplan->elmbyval, subplan->elmalign,
> !                                         &dvalues, &numelems);
> !                 }
> !                 else
>                   {
> !                     numelems = 1;
> !                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                     dvalues[0] = dvalue;
>                   }
>               }
>               else
>               {
> !                 numelems = 1;
> !                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                 dvalues[0] = (Datum) 0;
>               }
>
>           }
>
> !         for (elemnum = 0; elemnum < numelems; elemnum++)
>           {
> !             /*
> !              * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
> !              * operators for columns of tuple.
> !              */
> !             col = 1;
> !             plst = subplan->paramIds;
> !             foreach(lst, node->exprs)
>               {
> !                 ExprState  *exprstate = (ExprState *) lfirst(lst);
> !                 int            paramid = lfirsti(plst);
> !                 ParamExecData *prmdata;
> !                 Datum        expresult;
> !                 bool        expnull;
> !
> !                 /*
> !                  * Load up the Param representing this column of the sub-select.
> !                  */
> !                 prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
> !                 Assert(prmdata->execPlan == NULL);
> !
> !                 if (!isExpr)
> !                     prmdata->value = heap_getattr(tup, col, tdesc,
> !                                                   &(prmdata->isnull));
> !                 else
> !                 {
> !                     prmdata->value = dvalues[elemnum];
> !                     prmdata->isnull = disnull;
> !                 }
> !
> !                 /*
> !                  * Now we can eval the combining operator for this column.
> !                  */
> !                 expresult = ExecEvalExprSwitchContext(exprstate, econtext,
> !                                                       &expnull, NULL);
> !
> !                 /*
> !                  * Combine the result into the row result as appropriate.
> !                  */
> !                 if (col == 1)
> !                 {
> !                     rowresult = expresult;
> !                     rownull = expnull;
> !                 }
> !                 else if (useOr)
> !                 {
> !                     /* combine within row per OR semantics */
> !                     if (expnull)
> !                         rownull = true;
> !                     else if (DatumGetBool(expresult))
> !                     {
> !                         rowresult = BoolGetDatum(true);
> !                         rownull = false;
> !                         break;        /* needn't look at any more columns */
> !                     }
> !                 }
> !                 else
> !                 {
> !                     /* combine within row per AND semantics */
> !                     if (expnull)
> !                         rownull = true;
> !                     else if (!DatumGetBool(expresult))
> !                     {
> !                         rowresult = BoolGetDatum(false);
> !                         rownull = false;
> !                         break;        /* needn't look at any more columns */
> !                     }
> !                 }
> !
> !                 plst = lnext(plst);
> !                 col++;
>               }
> !
> !             if (subLinkType == ANY_SUBLINK)
>               {
> !                 /* combine across rows per OR semantics */
> !                 if (rownull)
> !                     *isNull = true;
> !                 else if (DatumGetBool(rowresult))
> !                 {
> !                     result = BoolGetDatum(true);
> !                     *isNull = false;
> !                     break;            /* needn't look at any more rows */
> !                 }
> !             }
> !             else if (subLinkType == ALL_SUBLINK)
> !             {
> !                 /* combine across rows per AND semantics */
> !                 if (rownull)
> !                     *isNull = true;
> !                 else if (!DatumGetBool(rowresult))
> !                 {
> !                     result = BoolGetDatum(false);
> !                     *isNull = false;
> !                     break;            /* needn't look at any more rows */
> !                 }
> !             }
> !             else
> !             {
> !                 /* must be MULTIEXPR_SUBLINK */
> !                 result = rowresult;
> !                 *isNull = rownull;
>               }
>           }
>       }
>
> ***************
> *** 480,485 ****
> --- 524,530 ----
>   buildSubPlanHash(SubPlanState *node)
>   {
>       SubPlan       *subplan = (SubPlan *) node->xprstate.expr;
> +     bool        isExpr = subplan->isExpr;
>       PlanState  *planstate = node->planstate;
>       int            ncols = length(node->exprs);
>       ExprContext *innerecontext = node->innerecontext;
> ***************
> *** 487,492 ****
> --- 532,538 ----
>       MemoryContext oldcontext;
>       int            nbuckets;
>       TupleTableSlot *slot;
> +     TupleTableSlot *arrslot = NULL;
>
>       Assert(subplan->subLinkType == ANY_SUBLINK);
>       Assert(!subplan->useOr);
> ***************
> *** 566,608 ****
>       {
>           HeapTuple    tup = slot->val;
>           TupleDesc    tdesc = slot->ttc_tupleDescriptor;
> !         int            col = 1;
>           List       *plst;
>           bool        isnew;
>
>           /*
> !          * Load up the Params representing the raw sub-select outputs,
> !          * then form the projection tuple to store in the hashtable.
>            */
> !         foreach(plst, subplan->paramIds)
>           {
> !             int            paramid = lfirsti(plst);
> !             ParamExecData *prmdata;
>
> !             prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
> !             Assert(prmdata->execPlan == NULL);
> !             prmdata->value = heap_getattr(tup, col, tdesc,
> !                                           &(prmdata->isnull));
> !             col++;
> !         }
> !         slot = ExecProject(node->projRight, NULL);
> !         tup = slot->val;
>
> -         /*
> -          * If result contains any nulls, store separately or not at all.
> -          * (Since we know the projection tuple has no junk columns, we
> -          * can just look at the overall hasnull info bit, instead of
> -          * groveling through the columns.)
> -          */
> -         if (HeapTupleNoNulls(tup))
> -         {
> -             (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
> -             node->havehashrows = true;
>           }
> !         else if (node->hashnulls)
>           {
> !             (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
> !             node->havenullrows = true;
>           }
>
>           /*
> --- 612,750 ----
>       {
>           HeapTuple    tup = slot->val;
>           TupleDesc    tdesc = slot->ttc_tupleDescriptor;
> !         TupleDesc    arrtdesc = NULL;
>           List       *plst;
>           bool        isnew;
> +         int            numelems;
> +         int            elemnum;
> +         Datum        dvalue;
> +         Datum       *dvalues = NULL;
> +         bool        disnull;
>
>           /*
> !          * When isExpr is true, we have either a scalar expression or an
> !          * array. In the former case, this is no different than the !isExpr
> !          * case. In the latter case, iterate over the elements as if they
> !          * were from multiple input tuples.
>            */
> !         if (!isExpr)
> !             numelems = 1;
> !         else
>           {
> !             Oid        expr_typeid = tdesc->attrs[0]->atttypid;
>
> !             if (expr_typeid != subplan->exprtype)
> !             {
> !                 subplan->exprtype = expr_typeid;
> !                 subplan->elemtype = get_element_type(expr_typeid);
> !
> !                 if (subplan->elemtype != InvalidOid)
> !                     get_typlenbyvalalign(subplan->elemtype,
> !                                          &subplan->elmlen,
> !                                          &subplan->elmbyval,
> !                                          &subplan->elmalign);
> !             }
> !
> !             /* get current value */
> !             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
> !
> !             if (subplan->elemtype != InvalidOid)
> !             {
> !                 TupleTable    tupleTable;
> !                 ArrayType   *v = DatumGetArrayTypeP(dvalue);
> !
> !                 arrtdesc = CreateTemplateTupleDesc(1, false);
> !                 TupleDescInitEntry(arrtdesc, 1, "elem", subplan->elemtype,
> !                                                             -1, 0, false);
> !
> !                 tupleTable = ExecCreateTupleTable(1);
> !                 arrslot = ExecAllocTableSlot(tupleTable);
> !                 ExecSetSlotDescriptor(arrslot, arrtdesc, true);
> !
> !                 /* XXX this will need work if/when arrays support NULL elements */
> !                 if (!disnull)
> !                 {
> !                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
> !                                       subplan->elmbyval, subplan->elmalign,
> !                                         &dvalues, &numelems);
> !                 }
> !                 else
> !                 {
> !                     numelems = 1;
> !                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                     dvalues[0] = (Datum) 0;
> !                 }
> !             }
> !             else
> !             {
> !                 numelems = 1;
> !                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                 dvalues[0] = dvalue;
> !             }
>
>           }
> !
> !         for (elemnum = 0; elemnum < numelems; elemnum++)
>           {
> !             int    col = 1;
> !
> !             if (!isExpr || subplan->elemtype == InvalidOid)
> !             {
> !                 /*
> !                  * Load up the Params representing the raw sub-select outputs,
> !                  * then form the projection tuple to store in the hashtable.
> !                  */
> !                 foreach(plst, subplan->paramIds)
> !                 {
> !                     int            paramid = lfirsti(plst);
> !                     ParamExecData *prmdata;
> !
> !                     prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
> !                     Assert(prmdata->execPlan == NULL);
> !
> !                     prmdata->value = heap_getattr(tup, col, tdesc,
> !                                                   &(prmdata->isnull));
> !
> !                     col++;
> !                 }
> !                 slot = ExecProject(node->projRight, NULL);
> !                 tup = slot->val;
> !             }
> !             else
> !             {
> !                 /*
> !                  * For array type expressions, we need to build up our own
> !                  * tuple and slot
> !                  */
> !                 char        nullflag;
> !
> !                 nullflag = disnull ? 'n' : ' ';
> !                 tup = heap_formtuple(arrtdesc, &dvalues[elemnum], &nullflag);
> !                 arrslot = ExecStoreTuple(tup, arrslot, InvalidBuffer, true);
> !             }
> !
> !             /*
> !              * If result contains any nulls, store separately or not at all.
> !              * (Since we know the projection tuple has no junk columns, we
> !              * can just look at the overall hasnull info bit, instead of
> !              * groveling through the columns.)
> !              */
> !             if (HeapTupleNoNulls(tup))
> !             {
> !                 if (!isExpr)
> !                     (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
> !                 else
> !                     (void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew);
> !                 node->havehashrows = true;
> !             }
> !             else if (node->hashnulls)
> !             {
> !                 if (!isExpr)
> !                     (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
> !                 else
> !                     (void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew);
> !                 node->havenullrows = true;
> !             }
>           }
>
>           /*
> ***************
> *** 619,624 ****
> --- 761,768 ----
>        * have the potential for a double free attempt.
>        */
>       ExecClearTuple(node->projRight->pi_slot);
> +     if (arrslot)
> +         ExecClearTuple(arrslot);
>
>       MemoryContextSwitchTo(oldcontext);
>   }
> ***************
> *** 1098,1199 ****
>           prm->execPlan = node;
>           parent->chgParam = bms_add_member(parent->chgParam, paramid);
>       }
> - }
> -
> - /*
> -  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
> -  *
> -  *    astate is working state (NULL on first call)
> -  *    rcontext is where to keep working state
> -  */
> - static ArrayBuildState *
> - accumArrayResult(ArrayBuildState *astate,
> -                  Datum dvalue, bool disnull,
> -                  Oid element_type,
> -                  MemoryContext rcontext)
> - {
> -     MemoryContext arr_context,
> -                   oldcontext;
> -
> -     if (astate == NULL)
> -     {
> -         /* First time through --- initialize */
> -
> -         /* Make a temporary context to hold all the junk */
> -         arr_context = AllocSetContextCreate(rcontext,
> -                                             "ARRAY_SUBLINK Result",
> -                                             ALLOCSET_DEFAULT_MINSIZE,
> -                                             ALLOCSET_DEFAULT_INITSIZE,
> -                                             ALLOCSET_DEFAULT_MAXSIZE);
> -         oldcontext = MemoryContextSwitchTo(arr_context);
> -         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
> -         astate->mcontext = arr_context;
> -         astate->dvalues = (Datum *)
> -             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
> -         astate->nelems = 0;
> -         astate->element_type = element_type;
> -         get_typlenbyvalalign(element_type,
> -                              &astate->typlen,
> -                              &astate->typbyval,
> -                              &astate->typalign);
> -     }
> -     else
> -     {
> -         oldcontext = MemoryContextSwitchTo(astate->mcontext);
> -         Assert(astate->element_type == element_type);
> -         /* enlarge dvalues[] if needed */
> -         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
> -             astate->dvalues = (Datum *)
> -                 repalloc(astate->dvalues,
> -                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
> -     }
> -
> -     if (disnull)
> -         elog(ERROR, "NULL elements not allowed in Arrays");
> -
> -     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
> -     astate->dvalues[astate->nelems++] =
> -         datumCopy(dvalue, astate->typbyval, astate->typlen);
> -
> -     MemoryContextSwitchTo(oldcontext);
> -
> -     return astate;
> - }
> -
> - /*
> -  * makeArrayResult - produce final result of ARRAY_SUBLINK
> -  *
> -  *    astate is working state (not NULL)
> -  *    rcontext is where to construct result
> -  */
> - static Datum
> - makeArrayResult(ArrayBuildState *astate,
> -                 MemoryContext rcontext)
> - {
> -     ArrayType  *result;
> -     int            dims[1];
> -     int            lbs[1];
> -     MemoryContext oldcontext;
> -
> -     /* Build the final array result in rcontext */
> -     oldcontext = MemoryContextSwitchTo(rcontext);
> -
> -     dims[0] = astate->nelems;
> -     lbs[0] = 1;
> -
> -     result = construct_md_array(astate->dvalues,
> -                                 1,
> -                                 dims,
> -                                 lbs,
> -                                 astate->element_type,
> -                                 astate->typlen,
> -                                 astate->typbyval,
> -                                 astate->typalign);
> -
> -     MemoryContextSwitchTo(oldcontext);
> -
> -     /* Clean up all the junk */
> -     MemoryContextDelete(astate->mcontext);
> -
> -     return PointerGetDatum(result);
>   }
> --- 1242,1245 ----
> Index: src/backend/nodes/copyfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
> retrieving revision 1.252
> diff -c -r1.252 copyfuncs.c
> *** src/backend/nodes/copyfuncs.c    6 Jun 2003 15:04:02 -0000    1.252
> --- src/backend/nodes/copyfuncs.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 728,733 ****
> --- 728,734 ----
>       COPY_SCALAR_FIELD(agglevelsup);
>       COPY_SCALAR_FIELD(aggstar);
>       COPY_SCALAR_FIELD(aggdistinct);
> +     COPY_NODE_FIELD(args);
>
>       return newnode;
>   }
> ***************
> *** 826,831 ****
> --- 827,833 ----
>
>       COPY_SCALAR_FIELD(subLinkType);
>       COPY_SCALAR_FIELD(useOr);
> +     COPY_SCALAR_FIELD(isExpr);
>       COPY_NODE_FIELD(lefthand);
>       COPY_NODE_FIELD(operName);
>       COPY_OIDLIST_FIELD(operOids);
> ***************
> *** 844,849 ****
> --- 846,857 ----
>
>       COPY_SCALAR_FIELD(subLinkType);
>       COPY_SCALAR_FIELD(useOr);
> +     COPY_SCALAR_FIELD(isExpr);
> +     COPY_SCALAR_FIELD(exprtype);
> +     COPY_SCALAR_FIELD(elemtype);
> +     COPY_SCALAR_FIELD(elmlen);
> +     COPY_SCALAR_FIELD(elmbyval);
> +     COPY_SCALAR_FIELD(elmalign);
>       COPY_NODE_FIELD(exprs);
>       COPY_INTLIST_FIELD(paramIds);
>       COPY_NODE_FIELD(plan);
> Index: src/backend/nodes/equalfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v
> retrieving revision 1.195
> diff -c -r1.195 equalfuncs.c
> *** src/backend/nodes/equalfuncs.c    6 Jun 2003 15:04:02 -0000    1.195
> --- src/backend/nodes/equalfuncs.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 205,210 ****
> --- 205,211 ----
>       COMPARE_SCALAR_FIELD(agglevelsup);
>       COMPARE_SCALAR_FIELD(aggstar);
>       COMPARE_SCALAR_FIELD(aggdistinct);
> +     COMPARE_NODE_FIELD(args);
>
>       return true;
>   }
> ***************
> *** 301,306 ****
> --- 302,308 ----
>   {
>       COMPARE_SCALAR_FIELD(subLinkType);
>       COMPARE_SCALAR_FIELD(useOr);
> +     COMPARE_SCALAR_FIELD(isExpr);
>       COMPARE_NODE_FIELD(lefthand);
>       COMPARE_NODE_FIELD(operName);
>       COMPARE_OIDLIST_FIELD(operOids);
> ***************
> *** 314,319 ****
> --- 316,327 ----
>   {
>       COMPARE_SCALAR_FIELD(subLinkType);
>       COMPARE_SCALAR_FIELD(useOr);
> +     COMPARE_SCALAR_FIELD(isExpr);
> +     COMPARE_SCALAR_FIELD(exprtype);
> +     COMPARE_SCALAR_FIELD(elemtype);
> +     COMPARE_SCALAR_FIELD(elmlen);
> +     COMPARE_SCALAR_FIELD(elmbyval);
> +     COMPARE_SCALAR_FIELD(elmalign);
>       COMPARE_NODE_FIELD(exprs);
>       COMPARE_INTLIST_FIELD(paramIds);
>       /* should compare plans, but have to settle for comparing plan IDs */
> Index: src/backend/nodes/outfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v
> retrieving revision 1.208
> diff -c -r1.208 outfuncs.c
> *** src/backend/nodes/outfuncs.c    15 Jun 2003 22:51:45 -0000    1.208
> --- src/backend/nodes/outfuncs.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 616,621 ****
> --- 616,622 ----
>       WRITE_UINT_FIELD(agglevelsup);
>       WRITE_BOOL_FIELD(aggstar);
>       WRITE_BOOL_FIELD(aggdistinct);
> +     WRITE_NODE_FIELD(args);
>   }
>
>   static void
> ***************
> *** 701,706 ****
> --- 702,708 ----
>
>       WRITE_ENUM_FIELD(subLinkType, SubLinkType);
>       WRITE_BOOL_FIELD(useOr);
> +     WRITE_BOOL_FIELD(isExpr);
>       WRITE_NODE_FIELD(lefthand);
>       WRITE_NODE_FIELD(operName);
>       WRITE_OIDLIST_FIELD(operOids);
> ***************
> *** 714,719 ****
> --- 716,727 ----
>
>       WRITE_ENUM_FIELD(subLinkType, SubLinkType);
>       WRITE_BOOL_FIELD(useOr);
> +     WRITE_BOOL_FIELD(isExpr);
> +     WRITE_OID_FIELD(exprtype);
> +     WRITE_OID_FIELD(elemtype);
> +     WRITE_INT_FIELD(elmlen);
> +     WRITE_BOOL_FIELD(elmbyval);
> +     WRITE_CHAR_FIELD(elmalign);
>       WRITE_NODE_FIELD(exprs);
>       WRITE_INTLIST_FIELD(paramIds);
>       WRITE_NODE_FIELD(plan);
> Index: src/backend/nodes/readfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v
> retrieving revision 1.154
> diff -c -r1.154 readfuncs.c
> *** src/backend/nodes/readfuncs.c    6 Jun 2003 15:04:02 -0000    1.154
> --- src/backend/nodes/readfuncs.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 416,421 ****
> --- 416,422 ----
>       READ_UINT_FIELD(agglevelsup);
>       READ_BOOL_FIELD(aggstar);
>       READ_BOOL_FIELD(aggdistinct);
> +     READ_NODE_FIELD(args);
>
>       READ_DONE();
>   }
> ***************
> *** 545,550 ****
> --- 546,552 ----
>
>       READ_ENUM_FIELD(subLinkType, SubLinkType);
>       READ_BOOL_FIELD(useOr);
> +     READ_BOOL_FIELD(isExpr);
>       READ_NODE_FIELD(lefthand);
>       READ_NODE_FIELD(operName);
>       READ_OIDLIST_FIELD(operOids);
> Index: src/backend/optimizer/plan/subselect.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/plan/subselect.c,v
> retrieving revision 1.76
> diff -c -r1.76 subselect.c
> *** src/backend/optimizer/plan/subselect.c    6 Jun 2003 15:04:02 -0000    1.76
> --- src/backend/optimizer/plan/subselect.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 83,89 ****
>
>   static List *convert_sublink_opers(List *lefthand, List *operOids,
>                                      List *targetlist, int rtindex,
> !                                    List **righthandIds);
>   static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
>   static Node *replace_correlation_vars_mutator(Node *node, void *context);
>   static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
> --- 83,89 ----
>
>   static List *convert_sublink_opers(List *lefthand, List *operOids,
>                                      List *targetlist, int rtindex,
> !                                    bool isExpr, List **righthandIds);
>   static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
>   static Node *replace_correlation_vars_mutator(Node *node, void *context);
>   static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
> ***************
> *** 299,304 ****
> --- 299,310 ----
>        */
>       node->subLinkType = slink->subLinkType;
>       node->useOr = slink->useOr;
> +     node->isExpr = slink->isExpr;
> +     node->exprtype = InvalidOid;
> +     node->elemtype = InvalidOid;
> +     node->elmlen = 0;
> +     node->elmbyval = false;
> +     node->elmalign = '\0';
>       node->exprs = NIL;
>       node->paramIds = NIL;
>       node->useHashTable = false;
> ***************
> *** 374,380 ****
>           exprs = convert_sublink_opers(lefthand,
>                                         slink->operOids,
>                                         plan->targetlist,
> !                                       0,
>                                         &node->paramIds);
>           node->setParam = listCopy(node->paramIds);
>           PlannerInitPlan = lappend(PlannerInitPlan, node);
> --- 380,386 ----
>           exprs = convert_sublink_opers(lefthand,
>                                         slink->operOids,
>                                         plan->targetlist,
> !                                       0, node->isExpr,
>                                         &node->paramIds);
>           node->setParam = listCopy(node->paramIds);
>           PlannerInitPlan = lappend(PlannerInitPlan, node);
> ***************
> *** 457,463 ****
>           node->exprs = convert_sublink_opers(lefthand,
>                                               slink->operOids,
>                                               plan->targetlist,
> !                                             0,
>                                               &node->paramIds);
>
>           /*
> --- 463,469 ----
>           node->exprs = convert_sublink_opers(lefthand,
>                                               slink->operOids,
>                                               plan->targetlist,
> !                                             0, node->isExpr,
>                                               &node->paramIds);
>
>           /*
> ***************
> *** 499,505 ****
>   static List *
>   convert_sublink_opers(List *lefthand, List *operOids,
>                         List *targetlist, int rtindex,
> !                       List **righthandIds)
>   {
>       List       *result = NIL;
>       List       *lst;
> --- 505,511 ----
>   static List *
>   convert_sublink_opers(List *lefthand, List *operOids,
>                         List *targetlist, int rtindex,
> !                       bool isExpr, List **righthandIds)
>   {
>       List       *result = NIL;
>       List       *lst;
> ***************
> *** 554,566 ****
>            * are not expecting to have to resolve unknown Params, so
>            * it's okay to pass a null pstate.)
>            */
> !         result = lappend(result,
> !                          make_op_expr(NULL,
> !                                       tup,
> !                                       leftop,
> !                                       rightop,
> !                                       exprType(leftop),
> !                                       te->resdom->restype));
>
>           ReleaseSysCache(tup);
>
> --- 560,597 ----
>            * are not expecting to have to resolve unknown Params, so
>            * it's okay to pass a null pstate.)
>            */
> !         if (!isExpr)
> !         {
> !             result = lappend(result,
> !                              make_op_expr(NULL,
> !                                           tup,
> !                                           leftop,
> !                                           rightop,
> !                                           exprType(leftop),
> !                                           te->resdom->restype));
> !         }
> !         else
> !         {
> !             Oid        exprtype = te->resdom->restype;
> !             Oid        elemtype = get_element_type(exprtype);
> !
> !             if (elemtype != InvalidOid)
> !                 result = lappend(result,
> !                                  make_op_expr(NULL,
> !                                               tup,
> !                                               leftop,
> !                                               rightop,
> !                                               exprType(leftop),
> !                                               elemtype));
> !             else
> !                 result = lappend(result,
> !                                  make_op_expr(NULL,
> !                                               tup,
> !                                               leftop,
> !                                               rightop,
> !                                               exprType(leftop),
> !                                               exprtype));
> !         }
>
>           ReleaseSysCache(tup);
>
> ***************
> *** 671,683 ****
>       /*
>        * The sublink type must be "= ANY" --- that is, an IN operator.
>        * (We require the operator name to be unqualified, which may be
> !      * overly paranoid, or may not be.)
>        */
>       if (sublink->subLinkType != ANY_SUBLINK)
>           return NULL;
>       if (length(sublink->operName) != 1 ||
>           strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
>           return NULL;
>       /*
>        * The sub-select must not refer to any Vars of the parent query.
>        * (Vars of higher levels should be okay, though.)
> --- 702,718 ----
>       /*
>        * The sublink type must be "= ANY" --- that is, an IN operator.
>        * (We require the operator name to be unqualified, which may be
> !      * overly paranoid, or may not be.) It must not be an Expression
> !      * sublink.
>        */
>       if (sublink->subLinkType != ANY_SUBLINK)
>           return NULL;
>       if (length(sublink->operName) != 1 ||
>           strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
>           return NULL;
> +     if (sublink->isExpr)
> +         return NULL;
> +
>       /*
>        * The sub-select must not refer to any Vars of the parent query.
>        * (Vars of higher levels should be okay, though.)
> ***************
> *** 730,736 ****
>       exprs = convert_sublink_opers(sublink->lefthand,
>                                     sublink->operOids,
>                                     subselect->targetList,
> !                                   rtindex,
>                                     &ininfo->sub_targetlist);
>       return (Node *) make_ands_explicit(exprs);
>   }
> --- 765,771 ----
>       exprs = convert_sublink_opers(sublink->lefthand,
>                                     sublink->operOids,
>                                     subselect->targetList,
> !                                   rtindex, sublink->isExpr,
>                                     &ininfo->sub_targetlist);
>       return (Node *) make_ands_explicit(exprs);
>   }
> Index: src/backend/optimizer/util/clauses.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/util/clauses.c,v
> retrieving revision 1.139
> diff -c -r1.139 clauses.c
> *** src/backend/optimizer/util/clauses.c    6 Jun 2003 15:04:02 -0000    1.139
> --- src/backend/optimizer/util/clauses.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 133,138 ****
> --- 133,160 ----
>   }
>
>   /*****************************************************************************
> +  *              FUNCTION clause functions
> +  *****************************************************************************/
> +
> + /*
> +  * make_funcclause
> +  *        Creates a function clause given its function info and argument list.
> +  */
> + Expr *
> + make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
> +                             CoercionForm funcformat, List *funcargs)
> + {
> +     FuncExpr   *expr = makeNode(FuncExpr);
> +
> +     expr->funcid = funcid;
> +     expr->funcresulttype = funcresulttype;
> +     expr->funcretset = funcretset;
> +     expr->funcformat = funcformat;
> +     expr->args = funcargs;
> +     return (Expr *) expr;
> + }
> +
> + /*****************************************************************************
>    *        NOT clause functions
>    *****************************************************************************/
>
> Index: src/backend/parser/gram.y
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/gram.y,v
> retrieving revision 2.417
> diff -c -r2.417 gram.y
> *** src/backend/parser/gram.y    17 Jun 2003 23:12:36 -0000    2.417
> --- src/backend/parser/gram.y    24 Jun 2003 02:24:06 -0000
> ***************
> *** 5490,5495 ****
> --- 5490,5496 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = ANY_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = makeList1(makeString("="));
>                       n->subselect = $3;
> ***************
> *** 5500,5505 ****
> --- 5501,5507 ----
>                       /* Make an IN node */
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = ANY_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = makeList1(makeString("="));
>                       n->subselect = $4;
> ***************
> *** 5511,5516 ****
> --- 5513,5519 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = $3;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = $2;
>                       n->subselect = $4;
> ***************
> *** 5521,5526 ****
> --- 5524,5530 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = MULTIEXPR_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = $2;
>                       n->subselect = $3;
> ***************
> *** 5904,5909 ****
> --- 5908,5914 ----
>                       {
>                               SubLink *n = (SubLink *)$3;
>                               n->subLinkType = ANY_SUBLINK;
> +                             n->isExpr = false;
>                               n->lefthand = makeList1($1);
>                               n->operName = makeList1(makeString("="));
>                               $$ = (Node *)n;
> ***************
> *** 5931,5936 ****
> --- 5936,5942 ----
>                       {
>                           /* Make an IN node */
>                           SubLink *n = (SubLink *)$4;
> +                         n->isExpr = false;
>                           n->subLinkType = ANY_SUBLINK;
>                           n->lefthand = makeList1($1);
>                           n->operName = makeList1(makeString("="));
> ***************
> *** 5957,5967 ****
> --- 5963,6000 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = $3;
> +                     n->isExpr = false;
>                       n->lefthand = makeList1($1);
>                       n->operName = $2;
>                       n->subselect = $4;
>                       $$ = (Node *)n;
>                   }
> +             | a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op
> +                 {
> +                     SubLink *n = makeNode(SubLink);
> +                     SelectStmt *s = makeNode(SelectStmt);
> +                     ResTarget *r = makeNode(ResTarget);
> +
> +                     r->name = NULL;
> +                     r->indirection = NIL;
> +                     r->val = (Node *)$5;
> +
> +                     s->distinctClause = NIL;
> +                     s->targetList = makeList1(r);
> +                     s->into = NULL;
> +                     s->intoColNames = NIL;
> +                     s->fromClause = NIL;
> +                     s->whereClause = NULL;
> +                     s->groupClause = NIL;
> +                     s->havingClause = NULL;
> +
> +                     n->subLinkType = $3;
> +                     n->isExpr = true;
> +                     n->lefthand = makeList1($1);
> +                     n->operName = $2;
> +                     n->subselect = (Node *) s;
> +                     $$ = (Node *)n;
> +                 }
>               | UNIQUE select_with_parens %prec Op
>                   {
>                       /* Not sure how to get rid of the parentheses
> ***************
> *** 6538,6543 ****
> --- 6571,6577 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = EXPR_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = NIL;
>                       n->operName = NIL;
>                       n->subselect = $1;
> ***************
> *** 6547,6552 ****
> --- 6581,6587 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = EXISTS_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = NIL;
>                       n->operName = NIL;
>                       n->subselect = $2;
> ***************
> *** 6556,6561 ****
> --- 6591,6597 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = ARRAY_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = NIL;
>                       n->operName = NIL;
>                       n->subselect = $2;
> ***************
> *** 6730,6735 ****
> --- 6766,6772 ----
>   in_expr:    select_with_parens
>                   {
>                       SubLink *n = makeNode(SubLink);
> +                     n->isExpr = false;
>                       n->subselect = $1;
>                       /* other fields will be filled later */
>                       $$ = (Node *)n;
> Index: src/backend/parser/parse_coerce.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v
> retrieving revision 2.97
> diff -c -r2.97 parse_coerce.c
> *** src/backend/parser/parse_coerce.c    26 May 2003 00:11:27 -0000    2.97
> --- src/backend/parser/parse_coerce.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 859,865 ****
>       /* Get the element type based on the array type, if we have one */
>       if (OidIsValid(array_typeid))
>       {
> !         array_typelem = get_element_type(array_typeid);
>           if (!OidIsValid(array_typelem))
>               elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
>                    format_type_be(array_typeid));
> --- 859,869 ----
>       /* Get the element type based on the array type, if we have one */
>       if (OidIsValid(array_typeid))
>       {
> !         if (array_typeid != ANYARRAYOID)
> !             array_typelem = get_element_type(array_typeid);
> !         else
> !             array_typelem = ANYELEMENTOID;
> !
>           if (!OidIsValid(array_typelem))
>               elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
>                    format_type_be(array_typeid));
> ***************
> *** 919,925 ****
>       {
>           if (!OidIsValid(array_typeid))
>           {
> !             array_typeid = get_array_type(elem_typeid);
>               if (!OidIsValid(array_typeid))
>                   elog(ERROR, "Cannot find array type for datatype %s",
>                        format_type_be(elem_typeid));
> --- 923,933 ----
>       {
>           if (!OidIsValid(array_typeid))
>           {
> !             if (elem_typeid != ANYELEMENTOID)
> !                 array_typeid = get_array_type(elem_typeid);
> !             else
> !                 array_typeid = ANYARRAYOID;
> !
>               if (!OidIsValid(array_typeid))
>                   elog(ERROR, "Cannot find array type for datatype %s",
>                        format_type_be(elem_typeid));
> ***************
> *** 1169,1174 ****
> --- 1177,1187 ----
>       /* Somewhat-fast path for domain -> base type case */
>       if (srctype == targettype)
>           return true;
> +
> +     /* Last of the fast-paths: check for matching polymorphic arrays */
> +     if (targettype == ANYARRAYOID)
> +         if (get_element_type(srctype) != InvalidOid)
> +             return true;
>
>       /* Else look in pg_cast */
>       tuple = SearchSysCache(CASTSOURCETARGET,
> Index: src/backend/parser/parse_expr.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_expr.c,v
> retrieving revision 1.148
> diff -c -r1.148 parse_expr.c
> *** src/backend/parser/parse_expr.c    29 Apr 2003 22:13:10 -0000    1.148
> --- src/backend/parser/parse_expr.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 436,441 ****
> --- 436,442 ----
>                       sublink->operName = NIL;
>                       sublink->operOids = NIL;
>                       sublink->useOr = FALSE;
> +                     sublink->isExpr = FALSE;
>                   }
>                   else if (sublink->subLinkType == EXPR_SUBLINK ||
>                            sublink->subLinkType == ARRAY_SUBLINK)
> ***************
> *** 463,468 ****
> --- 464,470 ----
>                       sublink->operName = NIL;
>                       sublink->operOids = NIL;
>                       sublink->useOr = FALSE;
> +                     sublink->isExpr = FALSE;
>                   }
>                   else
>                   {
> ***************
> *** 538,547 ****
>                            * here, because make_subplan() will insert type
>                            * coercion calls if needed.
>                            */
> !                         optup = oper(op,
> !                                      exprType(lexpr),
> !                                      exprType((Node *) tent->expr),
> !                                      false);
>                           opform = (Form_pg_operator) GETSTRUCT(optup);
>
>                           if (opform->oprresult != BOOLOID)
> --- 540,569 ----
>                            * here, because make_subplan() will insert type
>                            * coercion calls if needed.
>                            */
> !                         if (!sublink->isExpr)
> !                         {
> !                             optup = oper(op,
> !                                          exprType(lexpr),
> !                                          exprType((Node *) tent->expr),
> !                                          false);
> !                         }
> !                         else
> !                         {
> !                             Oid        exprtype = exprType((Node *) tent->expr);
> !                             Oid        elemtype = get_element_type(exprtype);
> !
> !                             if (elemtype != InvalidOid)
> !                                 optup = oper(op,
> !                                              exprType(lexpr),
> !                                              elemtype,
> !                                              false);
> !                             else
> !                                 optup = oper(op,
> !                                              exprType(lexpr),
> !                                              exprtype,
> !                                              false);
> !                         }
> !
>                           opform = (Form_pg_operator) GETSTRUCT(optup);
>
>                           if (opform->oprresult != BOOLOID)
> ***************
> *** 743,749 ****
>                           ArrayExpr  *e = (ArrayExpr *) lfirst(element);
>
>                           if (!IsA(e, ArrayExpr))
> !                             elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions");
>                           if (ndims == 0)
>                               ndims = e->ndims;
>                           else if (e->ndims != ndims)
> --- 765,771 ----
>                           ArrayExpr  *e = (ArrayExpr *) lfirst(element);
>
>                           if (!IsA(e, ArrayExpr))
> !                             elog(ERROR, "Multidimensional ARRAY[] must be built from nested array expressions");
>                           if (ndims == 0)
>                               ndims = e->ndims;
>                           else if (e->ndims != ndims)
> Index: src/backend/parser/parse_func.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_func.c,v
> retrieving revision 1.149
> diff -c -r1.149 parse_func.c
> *** src/backend/parser/parse_func.c    6 Jun 2003 15:04:02 -0000    1.149
> --- src/backend/parser/parse_func.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 336,341 ****
> --- 336,342 ----
>           aggref->target = lfirst(fargs);
>           aggref->aggstar = agg_star;
>           aggref->aggdistinct = agg_distinct;
> +         aggref->args = fargs;
>
>           /* parse_agg.c does additional aggregate-specific processing */
>           transformAggregateCall(pstate, aggref);
> Index: src/backend/parser/parse_oper.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_oper.c,v
> retrieving revision 1.64
> diff -c -r1.64 parse_oper.c
> *** src/backend/parser/parse_oper.c    26 May 2003 00:11:27 -0000    1.64
> --- src/backend/parser/parse_oper.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 137,142 ****
> --- 137,169 ----
>   equality_oper(Oid argtype, bool noError)
>   {
>       Operator    optup;
> +     Oid            elem_type = get_element_type(argtype);
> +
> +     if (OidIsValid(elem_type))
> +     {
> +         bool    found = false;
> +         /*
> +          * If the datatype is an array, look for an "=" operator for the
> +          * element datatype.  We require it to be an exact or binary-compatible
> +          * match, since most callers are not prepared to cope with adding any
> +          * run-time type coercion steps.
> +          */
> +         optup = equality_oper(elem_type, true);
> +         if (optup != NULL)
> +         {
> +             found = true;
> +             ReleaseSysCache(optup);
> +         }
> +
> +         if (!found)
> +         {
> +             if (!noError)
> +                 elog(ERROR, "Unable to identify an equality operator for " \
> +                             "array type's element type %s",
> +                              format_type_be(elem_type));
> +             return NULL;
> +         }
> +     }
>
>       /*
>        * Look for an "=" operator for the datatype.  We require it to be
> ***************
> *** 175,180 ****
> --- 202,234 ----
>   ordering_oper(Oid argtype, bool noError)
>   {
>       Operator    optup;
> +     Oid            elem_type = get_element_type(argtype);
> +
> +     if (OidIsValid(elem_type))
> +     {
> +         bool    found = false;
> +         /*
> +          * If the datatype is an array, find the array element type's equality
> +          * operator, and use its lsortop (it *must* be mergejoinable).  We use
> +          * this definition because for sorting and grouping purposes, it's
> +          * important that the equality and ordering operators are consistent.
> +          */
> +         optup = ordering_oper(elem_type, true);
> +         if (optup != NULL)
> +         {
> +             found = true;
> +             ReleaseSysCache(optup);
> +         }
> +
> +         if (!found)
> +         {
> +             if (!noError)
> +                 elog(ERROR, "Unable to identify an ordering operator for " \
> +                             "array type's element type %s",
> +                              format_type_be(elem_type));
> +             return NULL;
> +         }
> +     }
>
>       /*
>        * Find the type's equality operator, and use its lsortop (it *must*
> ***************
> *** 215,220 ****
> --- 269,289 ----
>       Oid            result;
>
>       optup = equality_oper(argtype, false);
> +     result = oprfuncid(optup);
> +     ReleaseSysCache(optup);
> +     return result;
> + }
> +
> + /*
> +  * ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper())
> +  */
> + Oid
> + ordering_oper_funcid(Oid argtype)
> + {
> +     Operator    optup;
> +     Oid            result;
> +
> +     optup = ordering_oper(argtype, false);
>       result = oprfuncid(optup);
>       ReleaseSysCache(optup);
>       return result;
> Index: src/backend/utils/adt/acl.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/acl.c,v
> retrieving revision 1.88
> diff -c -r1.88 acl.c
> *** src/backend/utils/adt/acl.c    11 Jun 2003 09:23:55 -0000    1.88
> --- src/backend/utils/adt/acl.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 427,432 ****
> --- 427,441 ----
>           a1->ai_grantor == a2->ai_grantor;
>   }
>
> + /*
> +  * user-facing version of aclitemeq() for use as the
> +  * aclitem equality operator
> +  */
> + Datum
> + aclitem_eq(PG_FUNCTION_ARGS)
> + {
> +     PG_RETURN_BOOL(aclitemeq(PG_GETARG_ACLITEM_P(0), PG_GETARG_ACLITEM_P(1)));
> + }
>
>   /*
>    * acldefault()  --- create an ACL describing default access permissions
> Index: src/backend/utils/adt/array_userfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/array_userfuncs.c,v
> retrieving revision 1.1
> diff -c -r1.1 array_userfuncs.c
> *** src/backend/utils/adt/array_userfuncs.c    8 Apr 2003 23:20:02 -0000    1.1
> --- src/backend/utils/adt/array_userfuncs.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 18,52 ****
>   #include "utils/lsyscache.h"
>   #include "utils/syscache.h"
>
> -
> - /*-----------------------------------------------------------------------------
> -  * singleton_array :
> -  *        Form a multi-dimensional array given one starting element.
> -  *
> -  * - first argument is the datum with which to build the array
> -  * - second argument is the number of dimensions the array should have;
> -  *     defaults to 1 if no second argument is provided
> -  *----------------------------------------------------------------------------
> -  */
> - Datum
> - singleton_array(PG_FUNCTION_ARGS)
> - {
> -     Oid            elem_type = get_fn_expr_argtype(fcinfo, 0);
> -     int            ndims;
> -
> -     if (elem_type == InvalidOid)
> -         elog(ERROR, "Cannot determine input datatype");
> -
> -     if (PG_NARGS() == 2)
> -         ndims = PG_GETARG_INT32(1);
> -     else
> -         ndims = 1;
> -
> -     PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
> -                                                  PG_GETARG_DATUM(0),
> -                                                  ndims));
> - }
> -
>   /*-----------------------------------------------------------------------------
>    * array_push :
>    *        push an element onto either end of a one-dimensional array
> --- 18,23 ----
> ***************
> *** 70,75 ****
> --- 41,47 ----
>       Oid            arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
>       Oid            arg0_elemid;
>       Oid            arg1_elemid;
> +     ArrayMetaState *my_extra;
>
>       if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
>           elog(ERROR, "array_push: cannot determine input data types");
> ***************
> *** 95,122 ****
>           PG_RETURN_NULL();        /* keep compiler quiet */
>       }
>
> !     /* Sanity check: do we have a one-dimensional array */
> !     if (ARR_NDIM(v) != 1)
> !         elog(ERROR, "Arrays greater than one-dimension are not supported");
> !
> !     lb = ARR_LBOUND(v);
> !     dimv = ARR_DIMS(v);
> !     if (arg0_elemid != InvalidOid)
>       {
> !         /* append newelem */
> !         int    ub = dimv[0] + lb[0] - 1;
> !         indx = ub + 1;
>       }
>       else
>       {
> !         /* prepend newelem */
> !         indx = lb[0] - 1;
>       }
>
> !     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
> !     result = array_set(v, 1, &indx, newelem, -1,
> !                        typlen, typbyval, typalign, &isNull);
>
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
> --- 67,127 ----
>           PG_RETURN_NULL();        /* keep compiler quiet */
>       }
>
> !     if (ARR_NDIM(v) == 1)
>       {
> !         lb = ARR_LBOUND(v);
> !         dimv = ARR_DIMS(v);
> !
> !         if (arg0_elemid != InvalidOid)
> !         {
> !             /* append newelem */
> !             int    ub = dimv[0] + lb[0] - 1;
> !             indx = ub + 1;
> !         }
> !         else
> !         {
> !             /* prepend newelem */
> !             indx = lb[0] - 1;
> !         }
>       }
> +     else if (ARR_NDIM(v) == 0)
> +         indx = 1;
>       else
> +         elog(ERROR, "only empty and one-dimensional arrays are supported");
> +
> +
> +     /*
> +      * We arrange to look up info about element type only once per series
> +      * of calls, assuming the element type doesn't change underneath us.
> +      */
> +     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> +     if (my_extra == NULL)
>       {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
>       }
>
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type */
> !         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typalign = my_extra->typalign;
> !     }
> !
> !     result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval,
> !                                                  typalign, &isNull);
>
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
> ***************
> *** 145,157 ****
>
>       /*
>        * We must have one of the following combinations of inputs:
> !      * 1) two arrays with ndims1 == ndims2
> !      * 2) ndims1 == ndims2 - 1
> !      * 3) ndims1 == ndims2 + 1
>        */
>       ndims1 = ARR_NDIM(v1);
>       ndims2 = ARR_NDIM(v2);
>
>       if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
>           elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
>                       "%d dimensions", ndims1, ndims2);
> --- 150,177 ----
>
>       /*
>        * We must have one of the following combinations of inputs:
> !      * 1) one empty array, and one non-empty array
> !      * 2) both arrays empty
> !      * 3) two arrays with ndims1 == ndims2
> !      * 4) ndims1 == ndims2 - 1
> !      * 5) ndims1 == ndims2 + 1
>        */
>       ndims1 = ARR_NDIM(v1);
>       ndims2 = ARR_NDIM(v2);
>
> +     /*
> +      * short circuit - if one input array is empty, and the other is not,
> +      * we return the non-empty one as the result
> +      *
> +      * if both are empty, return the first one
> +      */
> +     if (ndims1 == 0 && ndims2 > 0)
> +         PG_RETURN_ARRAYTYPE_P(v2);
> +
> +     if (ndims2 == 0)
> +         PG_RETURN_ARRAYTYPE_P(v1);
> +
> +     /* the rest fall into combo 2, 3, or 4 */
>       if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
>           elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
>                       "%d dimensions", ndims1, ndims2);
> ***************
> *** 266,412 ****
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
>
> - /*----------------------------------------------------------------------------
> -  * array_accum :
> -  *        accumulator to build a 1-D array from input values -- this can be used
> -  *        to create custom aggregates.
> -  *
> -  * This function is not marked strict, so we have to be careful about nulls.
> -  *----------------------------------------------------------------------------
> -  */
> - Datum
> - array_accum(PG_FUNCTION_ARGS)
> - {
> -     /* return NULL if both arguments are NULL */
> -     if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
> -         PG_RETURN_NULL();
> -
> -     /* create a new 1-D array from the new element if the array is NULL */
> -     if (PG_ARGISNULL(0))
> -     {
> -         Oid            tgt_type = get_fn_expr_rettype(fcinfo);
> -         Oid            tgt_elem_type;
> -
> -         if (tgt_type == InvalidOid)
> -             elog(ERROR, "Cannot determine target array type");
> -         tgt_elem_type = get_element_type(tgt_type);
> -         if (tgt_elem_type == InvalidOid)
> -             elog(ERROR, "Target type is not an array");
> -
> -         PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
> -                                                      PG_GETARG_DATUM(1),
> -                                                      1));
> -     }
> -
> -     /* return the array if the new element is NULL */
> -     if (PG_ARGISNULL(1))
> -         PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0));
> -
> -     /*
> -      * Otherwise this is equivalent to array_push.  We hack the call a little
> -      * so that array_push can see the fn_expr information.
> -      */
> -     return array_push(fcinfo);
> - }
> -
> - /*-----------------------------------------------------------------------------
> -  * array_assign :
> -  *        assign an element of an array to a new value and return the
> -  *        redefined array
> -  *----------------------------------------------------------------------------
> -  */
> - Datum
> - array_assign(PG_FUNCTION_ARGS)
> - {
> -     ArrayType  *v;
> -     int            idx_to_chg;
> -     Datum        newelem;
> -     int           *dimv,
> -                *lb, ub;
> -     ArrayType  *result;
> -     bool        isNull;
> -     Oid            element_type;
> -     int16        typlen;
> -     bool        typbyval;
> -     char        typalign;
> -
> -     v = PG_GETARG_ARRAYTYPE_P(0);
> -     idx_to_chg = PG_GETARG_INT32(1);
> -     newelem = PG_GETARG_DATUM(2);
> -
> -     /* Sanity check: do we have a one-dimensional array */
> -     if (ARR_NDIM(v) != 1)
> -         elog(ERROR, "Arrays greater than one-dimension are not supported");
> -
> -     lb = ARR_LBOUND(v);
> -     dimv = ARR_DIMS(v);
> -     ub = dimv[0] + lb[0] - 1;
> -     if (idx_to_chg < lb[0] || idx_to_chg > ub)
> -         elog(ERROR, "Cannot alter nonexistent array element: %d", idx_to_chg);
> -
> -     element_type = ARR_ELEMTYPE(v);
> -     /* Sanity check: do we have a non-zero element type */
> -     if (element_type == 0)
> -         elog(ERROR, "Invalid array element type: %u", element_type);
> -
> -     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
> -
> -     result = array_set(v, 1, &idx_to_chg, newelem, -1,
> -                        typlen, typbyval, typalign, &isNull);
> -
> -     PG_RETURN_ARRAYTYPE_P(result);
> - }
> -
> - /*-----------------------------------------------------------------------------
> -  * array_subscript :
> -  *        return specific element of an array
> -  *----------------------------------------------------------------------------
> -  */
> - Datum
> - array_subscript(PG_FUNCTION_ARGS)
> - {
> -     ArrayType  *v;
> -     int            idx;
> -     int           *dimv,
> -                *lb, ub;
> -     Datum        result;
> -     bool        isNull;
> -     Oid            element_type;
> -     int16        typlen;
> -     bool        typbyval;
> -     char        typalign;
> -
> -     v = PG_GETARG_ARRAYTYPE_P(0);
> -     idx = PG_GETARG_INT32(1);
> -
> -     /* Sanity check: do we have a one-dimensional array */
> -     if (ARR_NDIM(v) != 1)
> -         elog(ERROR, "Arrays greater than one-dimension are not supported");
> -
> -     lb = ARR_LBOUND(v);
> -     dimv = ARR_DIMS(v);
> -     ub = dimv[0] + lb[0] - 1;
> -     if (idx < lb[0] || idx > ub)
> -         elog(ERROR, "Cannot return nonexistent array element: %d", idx);
> -
> -     element_type = ARR_ELEMTYPE(v);
> -     /* Sanity check: do we have a non-zero element type */
> -     if (element_type == 0)
> -         elog(ERROR, "Invalid array element type: %u", element_type);
> -
> -     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
> -
> -     result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);
> -
> -     PG_RETURN_DATUM(result);
> - }
>
>   /*
> !  * actually does the work for singleton_array(), and array_accum() if it is
> !  * given a null input array.
>    */
>   ArrayType *
> ! create_singleton_array(Oid element_type, Datum element, int ndims)
>   {
>       Datum    dvalues[1];
>       int16    typlen;
> --- 286,300 ----
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
>
>
>   /*
> !  * used by text_to_array() in varlena.c
>    */
>   ArrayType *
> ! create_singleton_array(FunctionCallInfo fcinfo,
> !                        Oid element_type,
> !                        Datum element,
> !                        int ndims)
>   {
>       Datum    dvalues[1];
>       int16    typlen;
> ***************
> *** 415,420 ****
> --- 303,309 ----
>       int        dims[MAXDIM];
>       int        lbs[MAXDIM];
>       int        i;
> +     ArrayMetaState *my_extra;
>
>       if (element_type == 0)
>           elog(ERROR, "Invalid array element type: %u", element_type);
> ***************
> *** 429,435 ****
>           lbs[i] = 1;
>       }
>
> !     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
>       return construct_md_array(dvalues, ndims, dims, lbs, element_type,
>                                 typlen, typbyval, typalign);
> --- 318,352 ----
>           lbs[i] = 1;
>       }
>
> !     /*
> !      * We arrange to look up info about element type only once per series
> !      * of calls, assuming the element type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type */
> !         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typalign = my_extra->typalign;
> !     }
>
>       return construct_md_array(dvalues, ndims, dims, lbs, element_type,
>                                 typlen, typbyval, typalign);
> Index: src/backend/utils/adt/arrayfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v
> retrieving revision 1.89
> diff -c -r1.89 arrayfuncs.c
> *** src/backend/utils/adt/arrayfuncs.c    9 May 2003 23:01:45 -0000    1.89
> --- src/backend/utils/adt/arrayfuncs.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 21,28 ****
> --- 21,30 ----
>   #include "catalog/pg_type.h"
>   #include "libpq/pqformat.h"
>   #include "parser/parse_coerce.h"
> + #include "parser/parse_oper.h"
>   #include "utils/array.h"
>   #include "utils/builtins.h"
> + #include "utils/datum.h"
>   #include "utils/memutils.h"
>   #include "utils/lsyscache.h"
>   #include "utils/syscache.h"
> ***************
> *** 70,85 ****
>
>   #define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)
>
> - /* I/O function selector for system_cache_lookup */
> - typedef enum IOFuncSelector
> - {
> -     IOFunc_input,
> -     IOFunc_output,
> -     IOFunc_receive,
> -     IOFunc_send
> - } IOFuncSelector;
> -
> -
>   static int    ArrayCount(char *str, int *dim, char typdelim);
>   static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
>                FmgrInfo *inputproc, Oid typelem, int32 typmod,
> --- 72,77 ----
> ***************
> *** 93,102 ****
>   static void CopyArrayEls(char *p, Datum *values, int nitems,
>                int typlen, bool typbyval, char typalign,
>                bool freedata);
> - static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
> -                                 int *typlen, bool *typbyval,
> -                                 char *typdelim, Oid *typelem,
> -                                 Oid *proc, char *typalign);
>   static Datum ArrayCast(char *value, bool byval, int len);
>   static int ArrayCastAndSet(Datum src,
>                   int typlen, bool typbyval, char typalign,
> --- 85,90 ----
> ***************
> *** 119,125 ****
>                      char *destPtr,
>                      int *st, int *endp, char *srcPtr,
>                      int typlen, bool typbyval, char typalign);
> !
>
>   /*---------------------------------------------------------------------
>    * array_in :
> --- 107,113 ----
>                      char *destPtr,
>                      int *st, int *endp, char *srcPtr,
>                      int typlen, bool typbyval, char typalign);
> ! static int array_cmp(FunctionCallInfo fcinfo);
>
>   /*---------------------------------------------------------------------
>    * array_in :
> ***************
> *** 154,165 ****
>                   dim[MAXDIM],
>                   lBound[MAXDIM];
>       char        typalign;
>
> !     /* Get info about element type, including its input conversion proc */
> !     system_cache_lookup(element_type, IOFunc_input,
> !                         &typlen, &typbyval, &typdelim,
> !                         &typelem, &typinput, &typalign);
> !     fmgr_info(typinput, &inputproc);
>
>       /* Make a modifiable copy of the input */
>       /* XXX why are we allocating an extra 2 bytes here? */
> --- 142,190 ----
>                   dim[MAXDIM],
>                   lBound[MAXDIM];
>       char        typalign;
> +     ArrayMetaState *my_extra;
>
> !     /*
> !      * We arrange to look up info about element type, including its input
> !      * conversion proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its input conversion proc */
> !         get_type_metadata(element_type, IOFunc_input,
> !                             &typlen, &typbyval, &typdelim,
> !                             &typelem, &typinput, &typalign);
> !         fmgr_info(typinput, &inputproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typinput;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = inputproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typinput = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         inputproc = my_extra->proc;
> !     }
>
>       /* Make a modifiable copy of the input */
>       /* XXX why are we allocating an extra 2 bytes here? */
> ***************
> *** 636,647 ****
>                   indx[MAXDIM];
>       int            ndim,
>                  *dim;
>
>       element_type = ARR_ELEMTYPE(v);
> !     system_cache_lookup(element_type, IOFunc_output,
> !                         &typlen, &typbyval, &typdelim,
> !                         &typelem, &typoutput, &typalign);
> !     fmgr_info(typoutput, &outputproc);
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> --- 661,711 ----
>                   indx[MAXDIM];
>       int            ndim,
>                  *dim;
> +     ArrayMetaState *my_extra;
>
>       element_type = ARR_ELEMTYPE(v);
> !
> !     /*
> !      * We arrange to look up info about element type, including its input
> !      * conversion proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its output conversion proc */
> !         get_type_metadata(element_type, IOFunc_output,
> !                             &typlen, &typbyval, &typdelim,
> !                             &typelem, &typoutput, &typalign);
> !         fmgr_info(typoutput, &outputproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typoutput;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = outputproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typoutput = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         outputproc = my_extra->proc;
> !     }
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> ***************
> *** 800,805 ****
> --- 864,870 ----
>                   dim[MAXDIM],
>                   lBound[MAXDIM];
>       char        typalign;
> +     ArrayMetaState *my_extra;
>
>       /* Get the array header information */
>       ndim = pq_getmsgint(buf, 4);
> ***************
> *** 831,844 ****
>           PG_RETURN_ARRAYTYPE_P(retval);
>       }
>
> !     /* Get info about element type, including its receive conversion proc */
> !     system_cache_lookup(element_type, IOFunc_receive,
> !                         &typlen, &typbyval, &typdelim,
> !                         &typelem, &typreceive, &typalign);
> !     if (!OidIsValid(typreceive))
> !         elog(ERROR, "No binary input function available for type %s",
> !              format_type_be(element_type));
> !     fmgr_info(typreceive, &receiveproc);
>
>       dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
>                                 typlen, typbyval, typalign,
> --- 896,945 ----
>           PG_RETURN_ARRAYTYPE_P(retval);
>       }
>
> !     /*
> !      * We arrange to look up info about element type, including its receive
> !      * conversion proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its receive conversion proc */
> !         get_type_metadata(element_type, IOFunc_receive,
> !                             &typlen, &typbyval, &typdelim,
> !                             &typelem, &typreceive, &typalign);
> !         if (!OidIsValid(typreceive))
> !             elog(ERROR, "No binary input function available for type %s",
> !                  format_type_be(element_type));
> !         fmgr_info(typreceive, &receiveproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typreceive;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = receiveproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typreceive = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         receiveproc = my_extra->proc;
> !     }
>
>       dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
>                                 typlen, typbyval, typalign,
> ***************
> *** 976,990 ****
>       int            ndim,
>                  *dim;
>       StringInfoData buf;
>
>       /* Get information about the element type and the array dimensions */
>       element_type = ARR_ELEMTYPE(v);
> !     system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
> !                         &typdelim, &typelem, &typsend, &typalign);
> !     if (!OidIsValid(typsend))
> !         elog(ERROR, "No binary output function available for type %s",
> !              format_type_be(element_type));
> !     fmgr_info(typsend, &sendproc);
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> --- 1077,1130 ----
>       int            ndim,
>                  *dim;
>       StringInfoData buf;
> +     ArrayMetaState *my_extra;
>
>       /* Get information about the element type and the array dimensions */
>       element_type = ARR_ELEMTYPE(v);
> !
> !     /*
> !      * We arrange to look up info about element type, including its send
> !      * proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its send proc */
> !         get_type_metadata(element_type, IOFunc_send, &typlen, &typbyval,
> !                             &typdelim, &typelem, &typsend, &typalign);
> !         if (!OidIsValid(typsend))
> !             elog(ERROR, "No binary output function available for type %s",
> !                  format_type_be(element_type));
> !         fmgr_info(typsend, &sendproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typsend;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = sendproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typsend = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         sendproc = my_extra->proc;
> !     }
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> ***************
> *** 1476,1481 ****
> --- 1616,1641 ----
>       array = DatumGetArrayTypeP(PointerGetDatum(array));
>
>       ndim = ARR_NDIM(array);
> +
> +     /*
> +      * if number of dims is zero, i.e. an empty array, create an array
> +      * with nSubscripts dimensions, and set the lower bounds to the supplied
> +      * subscripts
> +      */
> +     if (ndim == 0)
> +     {
> +         Oid        elmtype = ARR_ELEMTYPE(array);
> +
> +         for (i = 0; i < nSubscripts; i++)
> +         {
> +             dim[i] = 1;
> +             lb[i] = indx[i];
> +         }
> +
> +         return construct_md_array(&dataValue, nSubscripts, dim, lb, elmtype,
> +                                                 elmlen, elmbyval, elmalign);
> +     }
> +
>       if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
>           elog(ERROR, "Invalid array subscripts");
>
> ***************
> *** 1632,1637 ****
> --- 1792,1822 ----
>       /* note: we assume srcArray contains no toasted elements */
>
>       ndim = ARR_NDIM(array);
> +
> +     /*
> +      * if number of dims is zero, i.e. an empty array, create an array
> +      * with nSubscripts dimensions, and set the upper and lower bounds
> +      * to the supplied subscripts
> +      */
> +     if (ndim == 0)
> +     {
> +         Datum  *dvalues;
> +         int        nelems;
> +         Oid        elmtype = ARR_ELEMTYPE(array);
> +
> +         deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
> +                                                         &dvalues, &nelems);
> +
> +         for (i = 0; i < nSubscripts; i++)
> +         {
> +             dim[i] = 1 + upperIndx[i] - lowerIndx[i];
> +             lb[i] = lowerIndx[i];
> +         }
> +
> +         return construct_md_array(dvalues, nSubscripts, dim, lb, elmtype,
> +                                                  elmlen, elmbyval, elmalign);
> +     }
> +
>       if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
>           elog(ERROR, "Invalid array subscripts");
>
> ***************
> *** 1811,1816 ****
> --- 1996,2008 ----
>       Oid            typelem;
>       Oid            proc;
>       char       *s;
> +     typedef struct {
> +         ArrayMetaState *inp_extra;
> +         ArrayMetaState *ret_extra;
> +     } am_extra;
> +     am_extra  *my_extra;
> +     ArrayMetaState *inp_extra;
> +     ArrayMetaState *ret_extra;
>
>       /* Get input array */
>       if (fcinfo->nargs < 1)
> ***************
> *** 1829,1839 ****
>       if (nitems <= 0)
>           PG_RETURN_ARRAYTYPE_P(v);
>
> !     /* Lookup source and result types. Unneeded variables are reused. */
> !     system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
> !                         &typdelim, &typelem, &proc, &inp_typalign);
> !     system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
> !                         &typdelim, &typelem, &proc, &typalign);
>
>       /* Allocate temporary array for new values */
>       values = (Datum *) palloc(nitems * sizeof(Datum));
> --- 2021,2101 ----
>       if (nitems <= 0)
>           PG_RETURN_ARRAYTYPE_P(v);
>
> !     /*
> !      * We arrange to look up info about input and return element types only
> !      * once per series of calls, assuming the element type doesn't change
> !      * underneath us.
> !      */
> !     my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(am_extra));
> !         my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
> !
> !         my_extra->inp_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         inp_extra = my_extra->inp_extra;
> !         inp_extra->element_type = InvalidOid;
> !
> !         my_extra->ret_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         ret_extra = my_extra->ret_extra;
> !         ret_extra->element_type = InvalidOid;
> !     }
> !     else
> !     {
> !         inp_extra = my_extra->inp_extra;
> !         ret_extra = my_extra->ret_extra;
> !     }
> !
> !     if (inp_extra->element_type != inpType)
> !     {
> !         /* Lookup source and result types. Unneeded variables are reused. */
> !         get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
> !                             &typdelim, &typelem, &proc, &inp_typalign);
> !
> !         inp_extra->element_type = inpType;
> !         inp_extra->typlen = inp_typlen;
> !         inp_extra->typbyval = inp_typbyval;
> !         inp_extra->typdelim = typdelim;
> !         inp_extra->typelem = typelem;
> !         inp_extra->typiofunc = proc;
> !         inp_extra->typalign = inp_typalign;
> !     }
> !     else
> !     {
> !         inp_typlen = inp_extra->typlen;
> !         inp_typbyval = inp_extra->typbyval;
> !         typdelim = inp_extra->typdelim;
> !         typelem = inp_extra->typelem;
> !         proc = inp_extra->typiofunc;
> !         inp_typalign = inp_extra->typalign;
> !     }
> !
> !     if (ret_extra->element_type != retType)
> !     {
> !         /* Lookup source and result types. Unneeded variables are reused. */
> !         get_type_metadata(retType, IOFunc_input, &typlen, &typbyval,
> !                             &typdelim, &typelem, &proc, &typalign);
> !
> !         ret_extra->element_type = retType;
> !         ret_extra->typlen = typlen;
> !         ret_extra->typbyval = typbyval;
> !         ret_extra->typdelim = typdelim;
> !         ret_extra->typelem = typelem;
> !         ret_extra->typiofunc = proc;
> !         ret_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = ret_extra->typlen;
> !         typbyval = ret_extra->typbyval;
> !         typdelim = ret_extra->typdelim;
> !         typelem = ret_extra->typelem;
> !         proc = ret_extra->typiofunc;
> !         typalign = ret_extra->typalign;
> !     }
>
>       /* Allocate temporary array for new values */
>       values = (Datum *) palloc(nitems * sizeof(Datum));
> ***************
> *** 2049,2056 ****
>    *          compares two arrays for equality
>    * result :
>    *          returns true if the arrays are equal, false otherwise.
> -  *
> -  * XXX bitwise equality is pretty bogus ...
>    *-----------------------------------------------------------------------------
>    */
>   Datum
> --- 2311,2316 ----
> ***************
> *** 2058,2069 ****
>   {
>       ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
>       ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
>       bool        result = true;
>
> !     if (ARR_SIZE(array1) != ARR_SIZE(array2))
> !         result = false;
> !     else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
>           result = false;
>
>       /* Avoid leaking memory when handed toasted input. */
>       PG_FREE_IF_COPY(array1, 0);
> --- 2318,2435 ----
>   {
>       ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
>       ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
> +     char       *p1 = (char *) ARR_DATA_PTR(array1);
> +     char       *p2 = (char *) ARR_DATA_PTR(array2);
> +     int            ndims1 = ARR_NDIM(array1);
> +     int            ndims2 = ARR_NDIM(array2);
> +     int           *dims1 = ARR_DIMS(array1);
> +     int           *dims2 = ARR_DIMS(array2);
> +     int            nitems1 = ArrayGetNItems(ndims1, dims1);
> +     int            nitems2 = ArrayGetNItems(ndims2, dims2);
> +     Oid            element_type = ARR_ELEMTYPE(array1);
> +     FmgrInfo   *ae_fmgr_info = fcinfo->flinfo;
>       bool        result = true;
> +     int            typlen;
> +     bool        typbyval;
> +     char        typdelim;
> +     Oid            typelem;
> +     char        typalign;
> +     Oid            typiofunc;
> +     int            i;
> +     ArrayMetaState *my_extra;
> +     FunctionCallInfoData locfcinfo;
>
> !     /* fast path if the arrays do not have the same number of elements */
> !     if (nitems1 != nitems2)
>           result = false;
> +     else
> +     {
> +         /*
> +          * We arrange to look up the equality function only once per series of
> +          * calls, assuming the element type doesn't change underneath us.
> +          */
> +         my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
> +         if (my_extra == NULL)
> +         {
> +             ae_fmgr_info->fn_extra = MemoryContextAlloc(ae_fmgr_info->fn_mcxt,
> +                                                      sizeof(ArrayMetaState));
> +             my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
> +             my_extra->element_type = InvalidOid;
> +         }
> +
> +         if (my_extra->element_type != element_type)
> +         {
> +             Oid        opfuncid = equality_oper_funcid(element_type);
> +
> +             if (OidIsValid(opfuncid))
> +                 fmgr_info_cxt(opfuncid, &my_extra->proc, ae_fmgr_info->fn_mcxt);
> +             else
> +                 elog(ERROR,
> +                      "array_eq: cannot find equality operator for type: %u",
> +                      element_type);
> +
> +             get_type_metadata(element_type, IOFunc_output,
> +                               &typlen, &typbyval, &typdelim,
> +                               &typelem, &typiofunc, &typalign);
> +
> +             my_extra->element_type = element_type;
> +             my_extra->typlen = typlen;
> +             my_extra->typbyval = typbyval;
> +             my_extra->typdelim = typdelim;
> +             my_extra->typelem = typelem;
> +             my_extra->typiofunc = typiofunc;
> +             my_extra->typalign = typalign;
> +         }
> +         else
> +         {
> +             typlen = my_extra->typlen;
> +             typbyval = my_extra->typbyval;
> +             typdelim = my_extra->typdelim;
> +             typelem = my_extra->typelem;
> +             typiofunc = my_extra->typiofunc;
> +             typalign = my_extra->typalign;
> +         }
> +
> +         /*
> +          * apply the operator to each pair of array elements.
> +          */
> +         MemSet(&locfcinfo, 0, sizeof(locfcinfo));
> +         locfcinfo.flinfo = &my_extra->proc;
> +         locfcinfo.nargs = 2;
> +
> +         /* Loop over source data */
> +         for (i = 0; i < nitems1; i++)
> +         {
> +             Datum    elt1;
> +             Datum    elt2;
> +             bool    oprresult;
> +
> +             /* Get element pair */
> +             elt1 = fetch_att(p1, typbyval, typlen);
> +             elt2 = fetch_att(p2, typbyval, typlen);
> +
> +             p1 = att_addlength(p1, typlen, PointerGetDatum(p1));
> +             p1 = (char *) att_align(p1, typalign);
> +
> +             p2 = att_addlength(p2, typlen, PointerGetDatum(p2));
> +             p2 = (char *) att_align(p2, typalign);
> +
> +             /*
> +              * Apply the operator to the element pair
> +              */
> +             locfcinfo.arg[0] = elt1;
> +             locfcinfo.arg[1] = elt2;
> +             locfcinfo.argnull[0] = false;
> +             locfcinfo.argnull[1] = false;
> +             locfcinfo.isnull = false;
> +             oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
> +             if (!oprresult)
> +             {
> +                 result = false;
> +                 break;
> +             }
> +         }
> +     }
>
>       /* Avoid leaking memory when handed toasted input. */
>       PG_FREE_IF_COPY(array1, 0);
> ***************
> *** 2073,2125 ****
>   }
>
>
> ! /***************************************************************************/
> ! /******************|          Support  Routines              |*****************/
> ! /***************************************************************************/
>
> ! static void
> ! system_cache_lookup(Oid element_type,
> !                     IOFuncSelector which_func,
> !                     int *typlen,
> !                     bool *typbyval,
> !                     char *typdelim,
> !                     Oid *typelem,
> !                     Oid *proc,
> !                     char *typalign)
> ! {
> !     HeapTuple    typeTuple;
> !     Form_pg_type typeStruct;
> !
> !     typeTuple = SearchSysCache(TYPEOID,
> !                                ObjectIdGetDatum(element_type),
> !                                0, 0, 0);
> !     if (!HeapTupleIsValid(typeTuple))
> !         elog(ERROR, "cache lookup failed for type %u", element_type);
> !     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
> !
> !     *typlen = typeStruct->typlen;
> !     *typbyval = typeStruct->typbyval;
> !     *typdelim = typeStruct->typdelim;
> !     *typelem = typeStruct->typelem;
> !     *typalign = typeStruct->typalign;
> !     switch (which_func)
> !     {
> !         case IOFunc_input:
> !             *proc = typeStruct->typinput;
> !             break;
> !         case IOFunc_output:
> !             *proc = typeStruct->typoutput;
> !             break;
> !         case IOFunc_receive:
> !             *proc = typeStruct->typreceive;
> !             break;
> !         case IOFunc_send:
> !             *proc = typeStruct->typsend;
> !             break;
>       }
> !     ReleaseSysCache(typeTuple);
>   }
>
>   /*
>    * Fetch array element at pointer, converted correctly to a Datum
>    */
> --- 2439,2628 ----
>   }
>
>
> ! /*-----------------------------------------------------------------------------
> !  * array-array bool operators:
> !  *        Given two arrays, iterate comparison operators
> !  *        over the array. Uses logic similar to text comparison
> !  *        functions, except element-by-element instead of
> !  *        character-by-character.
> !  *----------------------------------------------------------------------------
> !  */
> ! Datum
> ! array_ne(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
> ! }
>
> ! Datum
> ! array_lt(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
> ! }
> !
> ! Datum
> ! array_gt(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
> ! }
> !
> ! Datum
> ! array_le(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
> ! }
> !
> ! Datum
> ! array_ge(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
> ! }
> !
> ! Datum
> ! btarraycmp(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_INT32(array_cmp(fcinfo));
> ! }
> !
> ! /*
> !  * array_cmp()
> !  * Internal comparison function for arrays.
> !  *
> !  * Returns -1, 0 or 1
> !  */
> ! static int
> ! array_cmp(FunctionCallInfo fcinfo)
> ! {
> !     ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
> !     ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
> !     FmgrInfo   *ac_fmgr_info = fcinfo->flinfo;
> !     Datum        opresult;
> !     int            result = 0;
> !     Oid            element_type = InvalidOid;
> !     int            typlen;
> !     bool        typbyval;
> !     char        typdelim;
> !     Oid            typelem;
> !     char        typalign;
> !     Oid            typiofunc;
> !     Datum       *dvalues1;
> !     int            nelems1;
> !     Datum       *dvalues2;
> !     int            nelems2;
> !     int            min_nelems;
> !     int            i;
> !     typedef struct
> !     {
> !         Oid                element_type;
> !         int                typlen;
> !         bool            typbyval;
> !         char            typdelim;
> !         Oid                typelem;
> !         Oid                typiofunc;
> !         char            typalign;
> !         FmgrInfo        eqproc;
> !         FmgrInfo        ordproc;
> !     } ac_extra;
> !     ac_extra *my_extra;
> !
> !     element_type = ARR_ELEMTYPE(array1);
> !
> !     /*
> !      * We arrange to look up the element type operator function only once
> !      * per series of calls, assuming the element type and opname don't
> !      * change underneath us.
> !      */
> !     my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         ac_fmgr_info->fn_extra = MemoryContextAlloc(ac_fmgr_info->fn_mcxt,
> !                                                          sizeof(ac_extra));
> !         my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         Oid        eqfuncid = equality_oper_funcid(element_type);
> !         Oid        ordfuncid = ordering_oper_funcid(element_type);
> !
> !         fmgr_info_cxt(eqfuncid, &my_extra->eqproc, ac_fmgr_info->fn_mcxt);
> !         fmgr_info_cxt(ordfuncid, &my_extra->ordproc, ac_fmgr_info->fn_mcxt);
> !
> !         if (my_extra->eqproc.fn_nargs != 2)
> !             elog(ERROR, "Equality operator does not take 2 arguments: %u",
> !                                                                  eqfuncid);
> !         if (my_extra->ordproc.fn_nargs != 2)
> !             elog(ERROR, "Ordering operator does not take 2 arguments: %u",
> !                                                                  ordfuncid);
> !
> !         get_type_metadata(element_type, IOFunc_output,
> !                           &typlen, &typbyval, &typdelim,
> !                           &typelem, &typiofunc, &typalign);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = InvalidOid;
> !         my_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typalign = my_extra->typalign;
> !     }
> !
> !     /* extract a C array of arg array datums */
> !     deconstruct_array(array1, element_type, typlen, typbyval, typalign,
> !                                                     &dvalues1, &nelems1);
> !
> !     deconstruct_array(array2, element_type, typlen, typbyval, typalign,
> !                                                     &dvalues2, &nelems2);
> !
> !     min_nelems = Min(nelems1, nelems2);
> !     for (i = 0; i < min_nelems; i++)
> !     {
> !         /* are they equal */
> !         opresult = FunctionCall2(&my_extra->eqproc,
> !                                  dvalues1[i], dvalues2[i]);
> !
> !         if (!DatumGetBool(opresult))
> !         {
> !             /* nope, see if arg1 is less than arg2 */
> !             opresult = FunctionCall2(&my_extra->ordproc,
> !                                      dvalues1[i], dvalues2[i]);
> !             if (DatumGetBool(opresult))
> !             {
> !                 /* arg1 is less than arg2 */
> !                 result = -1;
> !                 break;
> !             }
> !             else
> !             {
> !                 /* arg1 is greater than arg2 */
> !                 result = 1;
> !                 break;
> !             }
> !         }
>       }
> !
> !     if ((result == 0) && (nelems1 != nelems2))
> !         result = (nelems1 < nelems2) ? -1 : 1;
> !
> !     /* Avoid leaking memory when handed toasted input. */
> !     PG_FREE_IF_COPY(array1, 0);
> !     PG_FREE_IF_COPY(array2, 1);
> !
> !     return result;
>   }
>
> +
> + /***************************************************************************/
> + /******************|          Support  Routines              |*****************/
> + /***************************************************************************/
> +
>   /*
>    * Fetch array element at pointer, converted correctly to a Datum
>    */
> ***************
> *** 2423,2428 ****
> --- 2926,2943 ----
>           if (tgt_elem_type == InvalidOid)
>               elog(ERROR, "Target type is not an array");
>
> +         /*
> +          * We don't deal with domain constraints yet, so bail out.
> +          * This isn't currently a problem, because we also don't
> +          * support arrays of domain type elements either. But in the
> +          * future we might. At that point consideration should be given
> +          * to removing the check below and adding a domain constraints
> +          * check to the coercion.
> +          */
> +         if (getBaseType(tgt_elem_type) != tgt_elem_type)
> +             elog(ERROR, "array coercion to domain type elements not " \
> +                         "currently supported");
> +
>           if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
>                                      COERCION_EXPLICIT, &funcId))
>           {
> ***************
> *** 2439,2448 ****
>       }
>
>       /*
> !      * If it's binary-compatible, return the array unmodified.
>        */
>       if (my_extra->coerce_finfo.fn_oid == InvalidOid)
> !         PG_RETURN_ARRAYTYPE_P(src);
>
>       /*
>        * Use array_map to apply the function to each array element.
> --- 2954,2969 ----
>       }
>
>       /*
> !      * If it's binary-compatible, modify the element type in the array header,
> !      * but otherwise leave the array as we received it.
>        */
>       if (my_extra->coerce_finfo.fn_oid == InvalidOid)
> !     {
> !         ArrayType  *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
> !
> !         ARR_ELEMTYPE(result) = my_extra->desttype;
> !         PG_RETURN_ARRAYTYPE_P(result);
> !     }
>
>       /*
>        * Use array_map to apply the function to each array element.
> ***************
> *** 2453,2456 ****
> --- 2974,3092 ----
>       locfcinfo.arg[0] = PointerGetDatum(src);
>
>       return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
> + }
> +
> + /*
> +  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
> +  *
> +  *    astate is working state (NULL on first call)
> +  *    rcontext is where to keep working state
> +  */
> + ArrayBuildState *
> + accumArrayResult(ArrayBuildState *astate,
> +                  Datum dvalue, bool disnull,
> +                  Oid element_type,
> +                  MemoryContext rcontext)
> + {
> +     MemoryContext arr_context,
> +                   oldcontext;
> +
> +     if (astate == NULL)
> +     {
> +         /* First time through --- initialize */
> +
> +         /* Make a temporary context to hold all the junk */
> +         arr_context = AllocSetContextCreate(rcontext,
> +                                             "accumArrayResult",
> +                                             ALLOCSET_DEFAULT_MINSIZE,
> +                                             ALLOCSET_DEFAULT_INITSIZE,
> +                                             ALLOCSET_DEFAULT_MAXSIZE);
> +         oldcontext = MemoryContextSwitchTo(arr_context);
> +         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
> +         astate->mcontext = arr_context;
> +         astate->dvalues = (Datum *)
> +             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
> +         astate->nelems = 0;
> +         astate->element_type = element_type;
> +         get_typlenbyvalalign(element_type,
> +                              &astate->typlen,
> +                              &astate->typbyval,
> +                              &astate->typalign);
> +     }
> +     else
> +     {
> +         oldcontext = MemoryContextSwitchTo(astate->mcontext);
> +         Assert(astate->element_type == element_type);
> +         /* enlarge dvalues[] if needed */
> +         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
> +             astate->dvalues = (Datum *)
> +                 repalloc(astate->dvalues,
> +                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
> +     }
> +
> +     if (disnull)
> +         elog(ERROR, "NULL elements not allowed in Arrays");
> +
> +     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
> +     astate->dvalues[astate->nelems++] =
> +         datumCopy(dvalue, astate->typbyval, astate->typlen);
> +
> +     MemoryContextSwitchTo(oldcontext);
> +
> +     return astate;
> + }
> +
> + /*
> +  * makeArrayResult - produce final result of accumArrayResult
> +  *
> +  *    astate is working state (not NULL)
> +  *    rcontext is where to construct result
> +  */
> + Datum
> + makeArrayResult(ArrayBuildState *astate,
> +                 MemoryContext rcontext)
> + {
> +     int            dims[1];
> +     int            lbs[1];
> +
> +     dims[0] = astate->nelems;
> +     lbs[0] = 1;
> +
> +     return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
> + }
> +
> + /*
> +  * makeMdArrayResult - produce md final result of accumArrayResult
> +  *
> +  *    astate is working state (not NULL)
> +  *    rcontext is where to construct result
> +  */
> + Datum
> + makeMdArrayResult(ArrayBuildState *astate,
> +                 int ndims,
> +                 int *dims,
> +                 int *lbs,
> +                 MemoryContext rcontext)
> + {
> +     ArrayType  *result;
> +     MemoryContext oldcontext;
> +
> +     /* Build the final array result in rcontext */
> +     oldcontext = MemoryContextSwitchTo(rcontext);
> +
> +     result = construct_md_array(astate->dvalues,
> +                                 ndims,
> +                                 dims,
> +                                 lbs,
> +                                 astate->element_type,
> +                                 astate->typlen,
> +                                 astate->typbyval,
> +                                 astate->typalign);
> +
> +     MemoryContextSwitchTo(oldcontext);
> +
> +     /* Clean up all the junk */
> +     MemoryContextDelete(astate->mcontext);
> +
> +     return PointerGetDatum(result);
>   }
> Index: src/backend/utils/adt/varlena.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/varlena.c,v
> retrieving revision 1.98
> diff -c -r1.98 varlena.c
> *** src/backend/utils/adt/varlena.c    15 May 2003 15:50:19 -0000    1.98
> --- src/backend/utils/adt/varlena.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 19,29 ****
> --- 19,32 ----
>   #include "mb/pg_wchar.h"
>   #include "miscadmin.h"
>   #include "access/tuptoaster.h"
> + #include "catalog/pg_type.h"
>   #include "lib/stringinfo.h"
>   #include "libpq/crypt.h"
>   #include "libpq/pqformat.h"
> + #include "utils/array.h"
>   #include "utils/builtins.h"
>   #include "utils/pg_locale.h"
> + #include "utils/lsyscache.h"
>
>
>   typedef struct varlena unknown;
> ***************
> *** 1983,1990 ****
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else
> ! /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>
> --- 1986,1992 ----
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else                    /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>
> ***************
> *** 2004,2011 ****
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else
> ! /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>       else if ((start_posn != 0) && (end_posn == 0))
> --- 2006,2012 ----
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else                    /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>       else if ((start_posn != 0) && (end_posn == 0))
> ***************
> *** 2026,2031 ****
> --- 2027,2217 ----
>           result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn -
fldsep_len,false); 
>           PG_RETURN_TEXT_P(result_text);
>       }
> + }
> +
> + /*
> +  * text_to_array
> +  * parse input string
> +  * return text array of elements
> +  * based on provided field separator
> +  */
> + Datum
> + text_to_array(PG_FUNCTION_ARGS)
> + {
> +     text       *inputstring = PG_GETARG_TEXT_P(0);
> +     int            inputstring_len = TEXTLEN(inputstring);
> +     text       *fldsep = PG_GETARG_TEXT_P(1);
> +     int            fldsep_len = TEXTLEN(fldsep);
> +     int            fldnum;
> +     int            start_posn = 0;
> +     int            end_posn = 0;
> +     text       *result_text = NULL;
> +     ArrayBuildState *astate = NULL;
> +     MemoryContext oldcontext = CurrentMemoryContext;
> +
> +     /* return NULL for empty input string */
> +     if (inputstring_len < 1)
> +         PG_RETURN_NULL();
> +
> +     /* empty field separator
> +      * return one element, 1D, array using the input string */
> +     if (fldsep_len < 1)
> +         PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
> +                               CStringGetDatum(inputstring), 1));
> +
> +     /* start with end position holding the initial start position */
> +     end_posn = 0;
> +     for (fldnum=1;;fldnum++)    /* field number is 1 based */
> +     {
> +         Datum    dvalue;
> +         bool    disnull = false;
> +
> +         start_posn = end_posn;
> +         end_posn = text_position(PointerGetDatum(inputstring),
> +                                  PointerGetDatum(fldsep),
> +                                  fldnum);
> +
> +         if ((start_posn == 0) && (end_posn == 0))    /* fldsep not found */
> +         {
> +             if (fldnum == 1)
> +             {
> +                 /* first element
> +                  * return one element, 1D, array using the input string */
> +                 PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
> +                                       CStringGetDatum(inputstring), 1));
> +             }
> +             else
> +             {
> +                 /* otherwise create array and exit */
> +                 PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
> +             }
> +         }
> +         else if ((start_posn != 0) && (end_posn == 0))
> +         {
> +             /* last field requested */
> +             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
> +         }
> +         else if ((start_posn == 0) && (end_posn != 0))
> +         {
> +             /* first field requested */
> +             result_text = LEFT(inputstring, fldsep);
> +         }
> +         else
> +         {
> +             /* prior to last field requested */
> +             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn -
start_posn- fldsep_len, false); 
> +         }
> +
> +         /* stash away current value */
> +         dvalue = PointerGetDatum(result_text);
> +         astate = accumArrayResult(astate, dvalue,
> +                                   disnull, TEXTOID, oldcontext);
> +
> +     }
> +
> +     /* never reached -- keep compiler quiet */
> +     PG_RETURN_NULL();
> + }
> +
> + /*
> +  * array_to_text
> +  * concatenate Cstring representation of input array elements
> +  * using provided field separator
> +  */
> + Datum
> + array_to_text(PG_FUNCTION_ARGS)
> + {
> +     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
> +     char       *fldsep = PG_TEXTARG_GET_STR(1);
> +     int            nitems, *dims, ndims;
> +     char       *p;
> +     Oid            element_type;
> +     int            typlen;
> +     bool        typbyval;
> +     char        typdelim;
> +     Oid            typoutput,
> +                 typelem;
> +     FmgrInfo    outputproc;
> +     char        typalign;
> +     StringInfo    result_str = makeStringInfo();
> +     int            i;
> +     ArrayMetaState *my_extra;
> +
> +     p = ARR_DATA_PTR(v);
> +     ndims = ARR_NDIM(v);
> +     dims = ARR_DIMS(v);
> +     nitems = ArrayGetNItems(ndims, dims);
> +
> +     /* if there are no elements, return an empty string */
> +     if (nitems == 0)
> +         PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
> +
> +     element_type = ARR_ELEMTYPE(v);
> +
> +     /*
> +      * We arrange to look up info about element type, including its output
> +      * conversion proc only once per series of calls, assuming the element
> +      * type doesn't change underneath us.
> +      */
> +     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> +     if (my_extra == NULL)
> +     {
> +         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> +                                                      sizeof(ArrayMetaState));
> +         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> +         my_extra->element_type = InvalidOid;
> +     }
> +
> +     if (my_extra->element_type != element_type)
> +     {
> +         /* Get info about element type, including its output conversion proc */
> +         get_type_metadata(element_type, IOFunc_output,
> +                             &typlen, &typbyval, &typdelim,
> +                             &typelem, &typoutput, &typalign);
> +         fmgr_info(typoutput, &outputproc);
> +
> +         my_extra->element_type = element_type;
> +         my_extra->typlen = typlen;
> +         my_extra->typbyval = typbyval;
> +         my_extra->typdelim = typdelim;
> +         my_extra->typelem = typelem;
> +         my_extra->typiofunc = typoutput;
> +         my_extra->typalign = typalign;
> +         my_extra->proc = outputproc;
> +     }
> +     else
> +     {
> +         typlen = my_extra->typlen;
> +         typbyval = my_extra->typbyval;
> +         typdelim = my_extra->typdelim;
> +         typelem = my_extra->typelem;
> +         typoutput = my_extra->typiofunc;
> +         typalign = my_extra->typalign;
> +         outputproc = my_extra->proc;
> +     }
> +
> +     for (i = 0; i < nitems; i++)
> +     {
> +         Datum        itemvalue;
> +         char       *value;
> +
> +         itemvalue = fetch_att(p, typbyval, typlen);
> +
> +         value = DatumGetCString(FunctionCall3(&outputproc,
> +                                               itemvalue,
> +                                               ObjectIdGetDatum(typelem),
> +                                               Int32GetDatum(-1)));
> +
> +         if (i > 0)
> +             appendStringInfo(result_str, "%s%s", fldsep, value);
> +         else
> +             appendStringInfo(result_str, "%s", value);
> +
> +         p = att_addlength(p, typlen, PointerGetDatum(p));
> +         p = (char *) att_align(p, typalign);
> +     }
> +
> +     PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
>   }
>
>   #define HEXBASE 16
> Index: src/backend/utils/cache/lsyscache.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v
> retrieving revision 1.96
> diff -c -r1.96 lsyscache.c
> *** src/backend/utils/cache/lsyscache.c    22 Jun 2003 22:04:54 -0000    1.96
> --- src/backend/utils/cache/lsyscache.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 719,724 ****
> --- 719,758 ----
>   }
>
>   /*
> +  * get_func_argtypes
> +  *        Given procedure id, return the function's argument types.
> +  *        Also pass back the number of arguments.
> +  */
> + Oid *
> + get_func_argtypes(Oid funcid, int *nargs)
> + {
> +     HeapTuple        tp;
> +     Form_pg_proc    procstruct;
> +     Oid               *result = NULL;
> +     int                i;
> +
> +     tp = SearchSysCache(PROCOID,
> +                         ObjectIdGetDatum(funcid),
> +                         0, 0, 0);
> +     if (!HeapTupleIsValid(tp))
> +         elog(ERROR, "Function OID %u does not exist", funcid);
> +
> +     procstruct = (Form_pg_proc) GETSTRUCT(tp);
> +     *nargs = (int) procstruct->pronargs;
> +
> +     if (*nargs > 0)
> +     {
> +         result = (Oid *) palloc(*nargs * sizeof(Oid));
> +
> +         for (i = 0; i < *nargs; i++)
> +             result[i] = procstruct->proargtypes[i];
> +     }
> +
> +     ReleaseSysCache(tp);
> +     return result;
> + }
> +
> + /*
>    * get_func_retset
>    *        Given procedure id, return the function's proretset flag.
>    */
> ***************
> *** 1088,1093 ****
> --- 1122,1177 ----
>       *typbyval = typtup->typbyval;
>       *typalign = typtup->typalign;
>       ReleaseSysCache(tp);
> + }
> +
> + /*
> +  * get_type_metadata
> +  *
> +  *        A six-fer:    given the type OID, return typlen, typbyval, typalign,
> +  *                    typdelim, typelem, IO function Oid. The IO function
> +  *                    returned is controlled by IOFuncSelector
> +  */
> + void
> + get_type_metadata(Oid element_type,
> +                     IOFuncSelector which_func,
> +                     int *typlen,
> +                     bool *typbyval,
> +                     char *typdelim,
> +                     Oid *typelem,
> +                     Oid *proc,
> +                     char *typalign)
> + {
> +     HeapTuple    typeTuple;
> +     Form_pg_type typeStruct;
> +
> +     typeTuple = SearchSysCache(TYPEOID,
> +                                ObjectIdGetDatum(element_type),
> +                                0, 0, 0);
> +     if (!HeapTupleIsValid(typeTuple))
> +         elog(ERROR, "cache lookup failed for type %u", element_type);
> +     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
> +
> +     *typlen = typeStruct->typlen;
> +     *typbyval = typeStruct->typbyval;
> +     *typdelim = typeStruct->typdelim;
> +     *typelem = typeStruct->typelem;
> +     *typalign = typeStruct->typalign;
> +     switch (which_func)
> +     {
> +         case IOFunc_input:
> +             *proc = typeStruct->typinput;
> +             break;
> +         case IOFunc_output:
> +             *proc = typeStruct->typoutput;
> +             break;
> +         case IOFunc_receive:
> +             *proc = typeStruct->typreceive;
> +             break;
> +         case IOFunc_send:
> +             *proc = typeStruct->typsend;
> +             break;
> +     }
> +     ReleaseSysCache(typeTuple);
>   }
>
>   #ifdef NOT_USED
> Index: src/backend/utils/fmgr/fmgr.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/fmgr/fmgr.c,v
> retrieving revision 1.68
> diff -c -r1.68 fmgr.c
> *** src/backend/utils/fmgr/fmgr.c    8 Apr 2003 23:20:02 -0000    1.68
> --- src/backend/utils/fmgr/fmgr.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 1673,1675 ****
> --- 1673,1701 ----
>
>       return exprType((Node *) nth(argnum, args));
>   }
> +
> + /*
> +  * Get the OID of the function or operator
> +  *
> +  * Returns InvalidOid if information is not available
> +  */
> + Oid
> + get_fn_expr_functype(FunctionCallInfo fcinfo)
> + {
> +     Node   *expr;
> +
> +     /*
> +      * can't return anything useful if we have no FmgrInfo or if
> +      * its fn_expr node has not been initialized
> +      */
> +     if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
> +         return InvalidOid;
> +
> +     expr = fcinfo->flinfo->fn_expr;
> +     if (IsA(expr, FuncExpr))
> +         return ((FuncExpr *) expr)->funcid;
> +     else if (IsA(expr, OpExpr))
> +         return ((OpExpr *) expr)->opno;
> +     else
> +         return InvalidOid;
> + }
> Index: src/include/fmgr.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/fmgr.h,v
> retrieving revision 1.27
> diff -c -r1.27 fmgr.h
> *** src/include/fmgr.h    8 Apr 2003 23:20:04 -0000    1.27
> --- src/include/fmgr.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 18,23 ****
> --- 18,24 ----
>   #ifndef FMGR_H
>   #define FMGR_H
>
> + #include "nodes/nodes.h"
>
>   /*
>    * All functions that can be called directly by fmgr must have this signature.
> ***************
> *** 372,385 ****
>                    Datum arg6, Datum arg7, Datum arg8,
>                    Datum arg9);
>
> -
>   /*
>    * Routines in fmgr.c
>    */
>   extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
> ! extern Oid    fmgr_internal_function(const char *proname);
> ! extern Oid    get_fn_expr_rettype(FunctionCallInfo fcinfo);
> ! extern Oid    get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
>
>   /*
>    * Routines in dfmgr.c
> --- 373,386 ----
>                    Datum arg6, Datum arg7, Datum arg8,
>                    Datum arg9);
>
>   /*
>    * Routines in fmgr.c
>    */
>   extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
> ! extern Oid fmgr_internal_function(const char *proname);
> ! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo);
> ! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
> ! extern Oid get_fn_expr_functype(FunctionCallInfo fcinfo);
>
>   /*
>    * Routines in dfmgr.c
> Index: src/include/catalog/pg_amop.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amop.h,v
> retrieving revision 1.50
> diff -c -r1.50 pg_amop.h
> *** src/include/catalog/pg_amop.h    22 Jun 2003 22:04:55 -0000    1.50
> --- src/include/catalog/pg_amop.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 418,423 ****
> --- 418,432 ----
>   DATA(insert (    2098 4 f 2335 ));
>   DATA(insert (    2098 5 f 2336 ));
>
> + /*
> +  *    btree array_ops
> +  */
> +
> + DATA(insert (     397 1 f 1072 ));
> + DATA(insert (     397 2 f 1074 ));
> + DATA(insert (     397 3 f 1070 ));
> + DATA(insert (     397 4 f 1075 ));
> + DATA(insert (     397 5 f 1073 ));
>
>   /*
>    *    hash index _ops
> Index: src/include/catalog/pg_amproc.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amproc.h,v
> retrieving revision 1.38
> diff -c -r1.38 pg_amproc.h
> *** src/include/catalog/pg_amproc.h    22 Jun 2003 22:04:55 -0000    1.38
> --- src/include/catalog/pg_amproc.h    24 Jun 2003 02:35:24 -0000
> ***************
> *** 78,83 ****
> --- 78,84 ----
>
>
>   /* btree */
> + DATA(insert (     397 1  382 ));
>   DATA(insert (     421 1    357 ));
>   DATA(insert (     423 1 1596 ));
>   DATA(insert (     424 1 1693 ));
> Index: src/include/catalog/pg_opclass.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_opclass.h,v
> retrieving revision 1.51
> diff -c -r1.51 pg_opclass.h
> *** src/include/catalog/pg_opclass.h    22 Jun 2003 22:04:55 -0000    1.51
> --- src/include/catalog/pg_opclass.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 87,92 ****
> --- 87,94 ----
>    */
>
>   DATA(insert OID =  421 (    403        abstime_ops        PGNSP PGUID  702 t 0 ));
> + DATA(insert OID =  397 (    403        array_ops        PGNSP PGUID 2277 t 0 ));
> + #define ARRAY_BTREE_OPS_OID 397
>   DATA(insert OID =  422 (    402        bigbox_ops        PGNSP PGUID  603 f 0 ));
>   DATA(insert OID =  423 (    403        bit_ops            PGNSP PGUID 1560 t 0 ));
>   DATA(insert OID =  424 (    403        bool_ops        PGNSP PGUID   16 t 0 ));
> Index: src/include/catalog/pg_operator.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_operator.h,v
> retrieving revision 1.115
> diff -c -r1.115 pg_operator.h
> *** src/include/catalog/pg_operator.h    22 Jun 2003 22:04:55 -0000    1.115
> --- src/include/catalog/pg_operator.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 116,125 ****
>   DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
>   DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 
>
> ! DATA(insert OID = 329 (  "="       PGNSP PGUID b f 2277 2277 16   329 0 0 0    0 0 array_eq     eqsel eqjoinsel ));
> ! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277   0 0 0 0    0 0 array_append   -       -
));
> ! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277   0 0 0 0    0 0 array_prepend  -       -
));
> ! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277   0 0 0 0    0 0 array_cat      -       -
));
>
>   DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
>   DATA(insert OID = 353 (  "="       PGNSP PGUID b f    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
> --- 116,130 ----
>   DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
>   DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 
>
> ! DATA(insert OID = 1070 (  "="       PGNSP PGUID b f 2277 2277 16 1070 1071  1072 1072 1072 1073 array_eq eqsel
eqjoinsel)); 
> ! DATA(insert OID = 1071 (  "<>"       PGNSP PGUID b f 2277 2277 16 1071 1070  0 0 0 0 array_ne neqsel neqjoinsel ));
> ! DATA(insert OID = 1072 (  "<"       PGNSP PGUID b f 2277 2277 16 1073 1075  0 0 0 0 array_lt scalarltsel
scalarltjoinsel)); 
> ! DATA(insert OID = 1073 (  ">"       PGNSP PGUID b f 2277 2277 16 1072 1074  0 0 0 0 array_gt scalargtsel
scalargtjoinsel)); 
> ! DATA(insert OID = 1074 (  "<="       PGNSP PGUID b f 2277 2277 16 1075 1073  0 0 0 0 array_le scalarltsel
scalarltjoinsel)); 
> ! DATA(insert OID = 1075 (  ">="       PGNSP PGUID b f 2277 2277 16 1074 1072  0 0 0 0 array_ge scalargtsel
scalargtjoinsel)); 
> ! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277 0 0  0 0 0 0 array_append   -       -     ));
> ! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277 0 0  0 0 0 0 array_prepend  -       -     ));
> ! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277 0 0  0 0 0 0 array_cat      -       -     ));
>
>   DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
>   DATA(insert OID = 353 (  "="       PGNSP PGUID b f    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
> ***************
> *** 425,430 ****
> --- 430,436 ----
>   DATA(insert OID = 966 (  "+"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - ));
>   DATA(insert OID = 967 (  "-"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclremove - - ));
>   DATA(insert OID = 968 (  "~"       PGNSP PGUID b f 1034 1033     16 0 0 0 0 0 0 aclcontains - - ));
> + DATA(insert OID = 974 (  "="       PGNSP PGUID b f 1033 1033     16 0 0 0 0 0 0 aclitemeq eqsel eqjoinsel ));
>
>   /* additional geometric operators - thomas 1997-07-09 */
>   DATA(insert OID =  969 (  "@@"       PGNSP PGUID l f    0  601    600    0  0 0 0 0 0 lseg_center - - ));
> Index: src/include/catalog/pg_proc.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v
> retrieving revision 1.304
> diff -c -r1.304 pg_proc.h
> *** src/include/catalog/pg_proc.h    22 Jun 2003 22:04:55 -0000    1.304
> --- src/include/catalog/pg_proc.h    24 Jun 2003 02:35:46 -0000
> ***************
> *** 758,763 ****
> --- 758,765 ----
>   DESCR("btree less-equal-greater");
>   DATA(insert OID = 360 (  bttextcmp           PGNSP PGUID 12 f f t f i 2 23 "25 25"    bttextcmp - _null_ ));
>   DESCR("btree less-equal-greater");
> + DATA(insert OID = 382 (  btarraycmp           PGNSP PGUID 12 f f t f i 2 23 "2277 2277"    btarraycmp - _null_ ));
> + DESCR("btree less-equal-greater");
>
>   DATA(insert OID = 361 (  lseg_distance       PGNSP PGUID 12 f f t f i 2 701 "601 601"  lseg_distance - _null_ ));
>   DESCR("distance between");
> ***************
> *** 988,1001 ****
>   DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
>   DESCR("greater-than-or-equal");
>
> - DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
> - DESCR("array equal");
> -
>   DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
>   DESCR("current user name");
>   DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
>   DESCR("session user name");
>
>   DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
>   DESCR("array dimensions");
>   DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
> --- 990,1012 ----
>   DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
>   DESCR("greater-than-or-equal");
>
>   DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
>   DESCR("current user name");
>   DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
>   DESCR("session user name");
>
> + DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
> + DESCR("array equal");
> + DATA(insert OID = 390 (  array_ne           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ne - _null_ ));
> + DESCR("array not equal");
> + DATA(insert OID = 391 (  array_lt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_lt - _null_ ));
> + DESCR("array less than");
> + DATA(insert OID = 392 (  array_gt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_gt - _null_ ));
> + DESCR("array greater than");
> + DATA(insert OID = 393 (  array_le           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_le - _null_ ));
> + DESCR("array less than or equal");
> + DATA(insert OID = 396 (  array_ge           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ge - _null_ ));
> + DESCR("array greater than or equal");
>   DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
>   DESCR("array dimensions");
>   DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
> ***************
> *** 1006,1027 ****
>   DESCR("array lower dimension");
>   DATA(insert OID = 2092 (  array_upper       PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ ));
>   DESCR("array upper dimension");
> - DATA(insert OID = 377 (  singleton_array  PGNSP PGUID 12 f f t f i 1 2277 "2283" singleton_array - _null_ ));
> - DESCR("create array from single element");
>   DATA(insert OID = 378 (  array_append       PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ ));
>   DESCR("append element onto end of array");
>   DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
>   DESCR("prepend element onto front of array");
> - DATA(insert OID = 380 (  array_accum       PGNSP PGUID 12 f f f f i 2 2277 "2277 2283" array_accum - _null_ ));
> - DESCR("push element onto end of array, creating array if needed");
> - DATA(insert OID = 381 (  array_assign       PGNSP PGUID 12 f f t f i 3 2277 "2277 23 2283" array_assign - _null_
));
> - DESCR("assign specific array element");
> - DATA(insert OID = 382 (  array_subscript  PGNSP PGUID 12 f f t f i 2 2283 "2277 23" array_subscript - _null_ ));
> - DESCR("return specific array element");
>   DATA(insert OID = 383 (  array_cat           PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ ));
>   DESCR("concatenate two arrays");
>   DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
>   DESCR("coerce array type to another array type");
>
>   DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
>   DESCR("I/O");
> --- 1017,1034 ----
>   DESCR("array lower dimension");
>   DATA(insert OID = 2092 (  array_upper       PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ ));
>   DESCR("array upper dimension");
>   DATA(insert OID = 378 (  array_append       PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ ));
>   DESCR("append element onto end of array");
>   DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
>   DESCR("prepend element onto front of array");
>   DATA(insert OID = 383 (  array_cat           PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ ));
>   DESCR("concatenate two arrays");
>   DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
>   DESCR("coerce array type to another array type");
> + DATA(insert OID = 394 (  string_to_array   PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ ));
> + DESCR("split delimited text into text[]");
> + DATA(insert OID = 395 (  array_to_string   PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ ));
> + DESCR("concatenate array elements, using delimiter, into text");
>
>   DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
>   DESCR("I/O");
> ***************
> *** 1322,1327 ****
> --- 1329,1336 ----
>   DESCR("remove ACL item");
>   DATA(insert OID = 1037 (  aclcontains       PGNSP PGUID 12 f f t f s 2 16 "1034 1033"    aclcontains - _null_ ));
>   DESCR("does ACL contain item?");
> + DATA(insert OID = 1062 (  aclitemeq           PGNSP PGUID 12 f f t f s 2 16 "1033 1033"    aclitem_eq - _null_ ));
> + DESCR("equality operator for ACL items");
>   DATA(insert OID = 1365 (  makeaclitem       PGNSP PGUID 12 f f t f s 5 1033 "23 23 23 25 16"    makeaclitem -
_null_)); 
>   DESCR("make ACL item");
>   DATA(insert OID = 1038 (  seteval           PGNSP PGUID 12 f f t t v 1 23 "26"  seteval - _null_ ));
> Index: src/include/nodes/primnodes.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v
> retrieving revision 1.83
> diff -c -r1.83 primnodes.h
> *** src/include/nodes/primnodes.h    6 Jun 2003 15:04:03 -0000    1.83
> --- src/include/nodes/primnodes.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 226,231 ****
> --- 226,232 ----
>       Index        agglevelsup;    /* > 0 if agg belongs to outer query */
>       bool        aggstar;        /* TRUE if argument was really '*' */
>       bool        aggdistinct;    /* TRUE if it's agg(DISTINCT ...) */
> +     List       *args;            /* arguments to the aggregate */
>   } Aggref;
>
>   /* ----------------
> ***************
> *** 358,372 ****
>   /* ----------------
>    * SubLink
>    *
> !  * A SubLink represents a subselect appearing in an expression, and in some
> !  * cases also the combining operator(s) just above it.    The subLinkType
> !  * indicates the form of the expression represented:
>    *    EXISTS_SUBLINK        EXISTS(SELECT ...)
>    *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
>    *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
>    *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
>    *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
>    *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
>    * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
>    * same length as the subselect's targetlist.  MULTIEXPR will *always* have
>    * a list with more than one entry; if the subselect has just one target
> --- 359,377 ----
>   /* ----------------
>    * SubLink
>    *
> !  * A SubLink represents a subselect, or an expression, appearing in an
> !  * expression, and in some cases also the combining operator(s) just above
> !  * it.    The subLinkType indicates the form of the expression represented:
>    *    EXISTS_SUBLINK        EXISTS(SELECT ...)
>    *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
>    *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
>    *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
>    *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
>    *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
> +  * If an expression is used in place of the subselect, it is transformed
> +  * into a simple "(SELECT expr)" in gram.y. This is to allow arrays to be
> +  * used as if they were the result of a single column subselect. If the
> +  * expression is scalar, it is treated as a one element array.
>    * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
>    * same length as the subselect's targetlist.  MULTIEXPR will *always* have
>    * a list with more than one entry; if the subselect has just one target
> ***************
> *** 415,420 ****
> --- 420,427 ----
>       SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
>       bool        useOr;            /* TRUE to combine column results with
>                                    * "OR" not "AND" */
> +     bool        isExpr;            /* TRUE if the subselect is really derived
> +                                  * from a single expression */
>       List       *lefthand;        /* list of outer-query expressions on the
>                                    * left */
>       List       *operName;        /* originally specified operator name */
> ***************
> *** 456,461 ****
> --- 463,477 ----
>       SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
>       bool        useOr;            /* TRUE to combine column results with
>                                    * "OR" not "AND" */
> +     bool        isExpr;            /* TRUE if the subselect is really derived
> +                                  * from a single expression */
> +     /* runtime cache for single array expressions */
> +     Oid            exprtype;        /* array and element type, and other info
> +                                  * needed deconstruct the array */
> +     Oid            elemtype;
> +     int16        elmlen;
> +     bool        elmbyval;
> +     char        elmalign;
>       /* The combining operators, transformed to executable expressions: */
>       List       *exprs;            /* list of OpExpr expression trees */
>       List       *paramIds;        /* IDs of Params embedded in the above */
> Index: src/include/optimizer/clauses.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/optimizer/clauses.h,v
> retrieving revision 1.63
> diff -c -r1.63 clauses.h
> *** src/include/optimizer/clauses.h    28 May 2003 16:04:02 -0000    1.63
> --- src/include/optimizer/clauses.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 28,33 ****
> --- 28,36 ----
>   extern Node *get_leftop(Expr *clause);
>   extern Node *get_rightop(Expr *clause);
>
> + extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
> +                                     CoercionForm funcformat, List *funcargs);
> +
>   extern bool not_clause(Node *clause);
>   extern Expr *make_notclause(Expr *notclause);
>   extern Expr *get_notclausearg(Expr *notclause);
> Index: src/include/parser/parse_oper.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_oper.h,v
> retrieving revision 1.25
> diff -c -r1.25 parse_oper.h
> *** src/include/parser/parse_oper.h    29 Apr 2003 22:13:11 -0000    1.25
> --- src/include/parser/parse_oper.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 44,49 ****
> --- 44,50 ----
>   /* Convenience routines for common calls on the above */
>   extern Oid    compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError);
>   extern Oid    equality_oper_funcid(Oid argtype);
> + extern Oid  ordering_oper_funcid(Oid argtype);
>   extern Oid    ordering_oper_opid(Oid argtype);
>
>   /* Extract operator OID or underlying-function OID from an Operator tuple */
> Index: src/include/utils/acl.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/acl.h,v
> retrieving revision 1.52
> diff -c -r1.52 acl.h
> *** src/include/utils/acl.h    11 Jun 2003 09:23:55 -0000    1.52
> --- src/include/utils/acl.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 192,197 ****
> --- 192,198 ----
>   extern Datum aclremove(PG_FUNCTION_ARGS);
>   extern Datum aclcontains(PG_FUNCTION_ARGS);
>   extern Datum makeaclitem(PG_FUNCTION_ARGS);
> + extern Datum aclitem_eq(PG_FUNCTION_ARGS);
>
>   /*
>    * prototypes for functions in aclchk.c
> Index: src/include/utils/array.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v
> retrieving revision 1.38
> diff -c -r1.38 array.h
> *** src/include/utils/array.h    8 May 2003 22:19:57 -0000    1.38
> --- src/include/utils/array.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 32,37 ****
> --- 32,68 ----
>       Oid            elemtype;        /* element type OID */
>   } ArrayType;
>
> + typedef struct ArrayBuildState
> + {
> +     MemoryContext mcontext;        /* where all the temp stuff is kept */
> +     Datum       *dvalues;        /* array of accumulated Datums */
> +     /*
> +      * The allocated size of dvalues[] is always a multiple of
> +      * ARRAY_ELEMS_CHUNKSIZE
> +      */
> + #define ARRAY_ELEMS_CHUNKSIZE    64
> +     int            nelems;            /* number of valid Datums in dvalues[] */
> +     Oid            element_type;    /* data type of the Datums */
> +     int16        typlen;            /* needed info about datatype */
> +     bool        typbyval;
> +     char        typalign;
> + } ArrayBuildState;
> +
> + /*
> +  * structure to cache type metadata needed for array manipulation
> +  */
> + typedef struct ArrayMetaState
> + {
> +     Oid                element_type;
> +     int                typlen;
> +     bool            typbyval;
> +     char            typdelim;
> +     Oid                typelem;
> +     Oid                typiofunc;
> +     char            typalign;
> +     FmgrInfo        proc;
> + } ArrayMetaState;
> +
>   /*
>    * fmgr macros for array objects
>    */
> ***************
> *** 86,96 ****
>   extern Datum array_send(PG_FUNCTION_ARGS);
>   extern Datum array_length_coerce(PG_FUNCTION_ARGS);
>   extern Datum array_eq(PG_FUNCTION_ARGS);
>   extern Datum array_dims(PG_FUNCTION_ARGS);
>   extern Datum array_lower(PG_FUNCTION_ARGS);
>   extern Datum array_upper(PG_FUNCTION_ARGS);
> - extern Datum array_assign(PG_FUNCTION_ARGS);
> - extern Datum array_subscript(PG_FUNCTION_ARGS);
>   extern Datum array_type_coerce(PG_FUNCTION_ARGS);
>
>   extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
> --- 117,131 ----
>   extern Datum array_send(PG_FUNCTION_ARGS);
>   extern Datum array_length_coerce(PG_FUNCTION_ARGS);
>   extern Datum array_eq(PG_FUNCTION_ARGS);
> + extern Datum array_ne(PG_FUNCTION_ARGS);
> + extern Datum array_lt(PG_FUNCTION_ARGS);
> + extern Datum array_gt(PG_FUNCTION_ARGS);
> + extern Datum array_le(PG_FUNCTION_ARGS);
> + extern Datum array_ge(PG_FUNCTION_ARGS);
> + extern Datum btarraycmp(PG_FUNCTION_ARGS);
>   extern Datum array_dims(PG_FUNCTION_ARGS);
>   extern Datum array_lower(PG_FUNCTION_ARGS);
>   extern Datum array_upper(PG_FUNCTION_ARGS);
>   extern Datum array_type_coerce(PG_FUNCTION_ARGS);
>
>   extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
> ***************
> *** 124,130 ****
>                     Oid elmtype,
>                     int elmlen, bool elmbyval, char elmalign,
>                     Datum **elemsp, int *nelemsp);
> !
>
>   /*
>    * prototypes for functions defined in arrayutils.c
> --- 159,172 ----
>                     Oid elmtype,
>                     int elmlen, bool elmbyval, char elmalign,
>                     Datum **elemsp, int *nelemsp);
> ! extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
> !                                          Datum dvalue, bool disnull,
> !                                          Oid element_type,
> !                                          MemoryContext rcontext);
> ! extern Datum makeArrayResult(ArrayBuildState *astate,
> !                              MemoryContext rcontext);
> ! extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
> !                                int *dims, int *lbs, MemoryContext rcontext);
>
>   /*
>    * prototypes for functions defined in arrayutils.c
> ***************
> *** 141,152 ****
>   /*
>    * prototypes for functions defined in array_userfuncs.c
>    */
> - extern Datum singleton_array(PG_FUNCTION_ARGS);
>   extern Datum array_push(PG_FUNCTION_ARGS);
> - extern Datum array_accum(PG_FUNCTION_ARGS);
>   extern Datum array_cat(PG_FUNCTION_ARGS);
>
> ! extern ArrayType *create_singleton_array(Oid element_type,
>                                            Datum element,
>                                            int ndims);
>
> --- 183,193 ----
>   /*
>    * prototypes for functions defined in array_userfuncs.c
>    */
>   extern Datum array_push(PG_FUNCTION_ARGS);
>   extern Datum array_cat(PG_FUNCTION_ARGS);
>
> ! extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
> !                                          Oid element_type,
>                                            Datum element,
>                                            int ndims);
>
> Index: src/include/utils/builtins.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/builtins.h,v
> retrieving revision 1.219
> diff -c -r1.219 builtins.h
> *** src/include/utils/builtins.h    26 May 2003 00:11:28 -0000    1.219
> --- src/include/utils/builtins.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 530,535 ****
> --- 530,537 ----
>                         List **namelist);
>   extern Datum replace_text(PG_FUNCTION_ARGS);
>   extern Datum split_text(PG_FUNCTION_ARGS);
> + extern Datum text_to_array(PG_FUNCTION_ARGS);
> + extern Datum array_to_text(PG_FUNCTION_ARGS);
>   extern Datum to_hex32(PG_FUNCTION_ARGS);
>   extern Datum to_hex64(PG_FUNCTION_ARGS);
>   extern Datum md5_text(PG_FUNCTION_ARGS);
> Index: src/include/utils/lsyscache.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v
> retrieving revision 1.71
> diff -c -r1.71 lsyscache.h
> *** src/include/utils/lsyscache.h    22 Jun 2003 22:04:55 -0000    1.71
> --- src/include/utils/lsyscache.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 15,20 ****
> --- 15,29 ----
>
>   #include "access/htup.h"
>
> + /* I/O function selector for system_cache_lookup */
> + typedef enum IOFuncSelector
> + {
> +     IOFunc_input,
> +     IOFunc_output,
> +     IOFunc_receive,
> +     IOFunc_send
> + } IOFuncSelector;
> +
>   extern bool op_in_opclass(Oid opno, Oid opclass);
>   extern bool op_requires_recheck(Oid opno, Oid opclass);
>   extern Oid    get_opclass_member(Oid opclass, int16 strategy);
> ***************
> *** 41,46 ****
> --- 50,56 ----
>   extern RegProcedure get_oprjoin(Oid opno);
>   extern char *get_func_name(Oid funcid);
>   extern Oid    get_func_rettype(Oid funcid);
> + extern Oid *get_func_argtypes(Oid funcid, int *nargs);
>   extern bool get_func_retset(Oid funcid);
>   extern bool func_strict(Oid funcid);
>   extern char func_volatile(Oid funcid);
> ***************
> *** 56,61 ****
> --- 66,79 ----
>   extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
>   extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
>                        char *typalign);
> + extern void get_type_metadata(Oid element_type,
> +                                 IOFuncSelector which_func,
> +                                 int *typlen,
> +                                 bool *typbyval,
> +                                 char *typdelim,
> +                                 Oid *typelem,
> +                                 Oid *proc,
> +                                 char *typalign);
>   extern char get_typstorage(Oid typid);
>   extern int32 get_typtypmod(Oid typid);
>   extern Node *get_typdefault(Oid typid);
> Index: src/interfaces/ecpg/preproc/preproc.y
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/preproc.y,v
> retrieving revision 1.236
> diff -c -r1.236 preproc.y
> *** src/interfaces/ecpg/preproc/preproc.y    20 Jun 2003 13:36:34 -0000    1.236
> --- src/interfaces/ecpg/preproc/preproc.y    24 Jun 2003 02:24:06 -0000
> ***************
> *** 4595,4601 ****
>                   $3.type_enum != ECPGt_char &&
>                       $3.type_enum != ECPGt_unsigned_char &&
>                   atoi(this->type->type_index) >= 0)
> !                 mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
>
>               types = this;
>           }
> --- 4595,4601 ----
>                   $3.type_enum != ECPGt_char &&
>                       $3.type_enum != ECPGt_unsigned_char &&
>                   atoi(this->type->type_index) >= 0)
> !                 mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
>
>               types = this;
>           }
> ***************
> *** 5415,5421 ****
>                       $5.type_enum != ECPGt_char &&
>                       $5.type_enum != ECPGt_unsigned_char &&
>                       atoi(this->type->type_index) >= 0)
> !                     mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
>
>                   types = this;
>               }
> --- 5415,5421 ----
>                       $5.type_enum != ECPGt_char &&
>                       $5.type_enum != ECPGt_unsigned_char &&
>                       atoi(this->type->type_index) >= 0)
> !                     mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
>
>                   types = this;
>               }
> ***************
> *** 5482,5488 ****
>
>                       default:
>                           if (atoi(length) >= 0)
> !                             mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data
types");
>
>                           if (atoi(dimension) < 0)
>                               type = ECPGmake_simple_type($5.type_enum, make_str("1"));
> --- 5482,5488 ----
>
>                       default:
>                           if (atoi(length) >= 0)
> !                             mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data
types");
>
>                           if (atoi(dimension) < 0)
>                               type = ECPGmake_simple_type($5.type_enum, make_str("1"));
> Index: src/interfaces/ecpg/preproc/type.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/type.c,v
> retrieving revision 1.52
> diff -c -r1.52 type.c
> *** src/interfaces/ecpg/preproc/type.c    20 Jun 2003 12:00:59 -0000    1.52
> --- src/interfaces/ecpg/preproc/type.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 504,510 ****
>                   switch (type->u.element->type)
>                   {
>                       case ECPGt_array:
> !                         yyerror("internal error, found multi-dimensional array\n");
>                           break;
>                       case ECPGt_struct:
>                       case ECPGt_union:
> --- 504,510 ----
>                   switch (type->u.element->type)
>                   {
>                       case ECPGt_array:
> !                         yyerror("internal error, found multidimensional array\n");
>                           break;
>                       case ECPGt_struct:
>                       case ECPGt_union:
> Index: src/interfaces/ecpg/preproc/variable.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/variable.c,v
> retrieving revision 1.21
> diff -c -r1.21 variable.c
> *** src/interfaces/ecpg/preproc/variable.c    11 Jun 2003 06:39:13 -0000    1.21
> --- src/interfaces/ecpg/preproc/variable.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 436,442 ****
>       if (atoi(type_index) >= 0)
>       {
>           if (atoi(*length) >= 0)
> !             mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
>           *length = type_index;
>       }
> --- 436,442 ----
>       if (atoi(type_index) >= 0)
>       {
>           if (atoi(*length) >= 0)
> !             mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
>           *length = type_index;
>       }
> ***************
> *** 444,450 ****
>       if (atoi(type_dimension) >= 0)
>       {
>           if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
> !             mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
>           if (atoi(*dimension) >= 0)
>               *length = *dimension;
> --- 444,450 ----
>       if (atoi(type_dimension) >= 0)
>       {
>           if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
> !             mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
>           if (atoi(*dimension) >= 0)
>               *length = *dimension;
> ***************
> *** 463,472 ****
>           mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");
>
>       if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
> !         mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
>       if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
> !         mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
>       switch (type_enum)
>       {
> --- 463,472 ----
>           mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");
>
>       if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
> !         mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
>       if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
> !         mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
>       switch (type_enum)
>       {
> ***************
> *** 480,486 ****
>               }
>
>               if (atoi(*length) >= 0)
> !                 mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for structures");
>
>               break;
>           case ECPGt_varchar:
> --- 480,486 ----
>               }
>
>               if (atoi(*length) >= 0)
> !                 mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for structures");
>
>               break;
>           case ECPGt_varchar:
> ***************
> *** 525,531 ****
>               }
>
>               if (atoi(*length) >= 0)
> !                 mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for simple data types");
>
>               break;
>       }
> --- 525,531 ----
>               }
>
>               if (atoi(*length) >= 0)
> !                 mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for simple data types");
>
>               break;
>       }
> Index: src/test/regress/expected/arrays.out
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/test/regress/expected/arrays.out,v
> retrieving revision 1.11
> diff -c -r1.11 arrays.out
> *** src/test/regress/expected/arrays.out    8 Apr 2003 23:20:04 -0000    1.11
> --- src/test/regress/expected/arrays.out    24 Jun 2003 02:24:06 -0000
> ***************
> *** 178,196 ****
>   (1 row)
>
>   -- functions
> ! SELECT singleton_array(42) AS "{42}";
> !  {42}
> ! ------
> !  {42}
> ! (1 row)
> !
> ! SELECT array_append(singleton_array(42), 6) AS "{42,6}";
>    {42,6}
>   --------
>    {42,6}
>   (1 row)
>
> ! SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
>    {6,42}
>   --------
>    {6,42}
> --- 178,190 ----
>   (1 row)
>
>   -- functions
> ! SELECT array_append(array[42], 6) AS "{42,6}";
>    {42,6}
>   --------
>    {42,6}
>   (1 row)
>
> ! SELECT array_prepend(6, array[42]) AS "{6,42}";
>    {6,42}
>   --------
>    {6,42}
> ***************
> *** 212,235 ****
>    {{3,4},{5,6},{1,2}}
>   ---------------------
>    {{3,4},{5,6},{1,2}}
> - (1 row)
> -
> - SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
> -  1.2
> - -----
> -  1.2
> - (1 row)
> -
> - SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
> -  {1.1,9.99,1.3}
> - ----------------
> -  {1.1,9.99,1.3}
> - (1 row)
> -
> - SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
> -  9.99
> - ------
> -  9.99
>   (1 row)
>
>   -- operators
> --- 206,211 ----
> Index: src/test/regress/sql/arrays.sql
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/test/regress/sql/arrays.sql,v
> retrieving revision 1.10
> diff -c -r1.10 arrays.sql
> *** src/test/regress/sql/arrays.sql    8 Apr 2003 23:20:04 -0000    1.10
> --- src/test/regress/sql/arrays.sql    24 Jun 2003 02:24:06 -0000
> ***************
> *** 130,144 ****
>   SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
>
>   -- functions
> ! SELECT singleton_array(42) AS "{42}";
> ! SELECT array_append(singleton_array(42), 6) AS "{42,6}";
> ! SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
>   SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}";
>   SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
>   SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
> - SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
> - SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
> - SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
>
>   -- operators
>   SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
> --- 130,140 ----
>   SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
>
>   -- functions
> ! SELECT array_append(array[42], 6) AS "{42,6}";
> ! SELECT array_prepend(6, array[42]) AS "{6,42}";
>   SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}";
>   SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
>   SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
>
>   -- operators
>   SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Re: array support patch phase 1 patch

From
Bruce Momjian
Date:
Patch applied.  Joe, I need some text about this patch for the release
notes.  It has so much stuff in it I don't know how to describe it.

---------------------------------------------------------------------------

Joe Conway wrote:
> Bruce Momjian wrote:
> > Your patch has been added to the PostgreSQL unapplied patches list at:
> [...snip...]
> >>Joe Conway wrote:
> >>>The attached patch addresses Peter's concerns, subject to our agreements
> >>>above. I.e, the changes are:
> >>
> >>The previous patch was no longer applying cleanly, so here is an update.
> >>Applies and compiles clean on cvs tip, passes all regression tests.
> >>Please apply.
>
> Here's another update against cvs tip. The previous version now has a
> duplicate oid conflict -- fixed.
>
> Joe

> Index: doc/src/sgml/array.sgml
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
> retrieving revision 1.25
> diff -c -r1.25 array.sgml
> *** doc/src/sgml/array.sgml    13 Mar 2003 01:30:26 -0000    1.25
> --- doc/src/sgml/array.sgml    24 Jun 2003 02:24:06 -0000
> ***************
> *** 60,73 ****
>   </programlisting>
>    </para>
>
>    <note>
>     <para>
> !    A limitation of the present array implementation is that individual
> !    elements of an array cannot be SQL null values.  The entire array can be set
> !    to null, but you can't have an array with some elements null and some
> !    not.  Fixing this is on the to-do list.
>     </para>
>    </note>
>    </sect2>
>
>    <sect2>
> --- 60,133 ----
>   </programlisting>
>    </para>
>
> +  <para>
> +   A limitation of the present array implementation is that individual
> +   elements of an array cannot be SQL null values.  The entire array can be set
> +   to null, but you can't have an array with some elements null and some
> +   not.
> +  </para>
> +  <para>
> +   This can lead to surprising results. For example, the result of the
> +   previous two inserts looks like this:
> + <programlisting>
> + SELECT * FROM sal_emp;
> +  name  |      pay_by_quarter       |      schedule
> + -------+---------------------------+--------------------
> +  Bill  | {10000,10000,10000,10000} | {{meeting},{""}}
> +  Carol | {20000,25000,25000,25000} | {{talk},{meeting}}
> + (2 rows)
> + </programlisting>
> +   Because the <literal>[2][2]</literal> element of
> +   <structfield>schedule</structfield> is missing in each of the
> +   <command>INSERT</command> statements, the <literal>[1][2]</literal>
> +   element is discarded.
> +  </para>
> +
> +  <note>
> +   <para>
> +    Fixing this is on the to-do list.
> +   </para>
> +  </note>
> +
> +  <para>
> +   The <command>ARRAY</command> expression syntax may also be used:
> + <programlisting>
> + INSERT INTO sal_emp
> +     VALUES ('Bill',
> +     ARRAY[10000, 10000, 10000, 10000],
> +     ARRAY[['meeting', 'lunch'], ['','']]);
> +
> + INSERT INTO sal_emp
> +     VALUES ('Carol',
> +     ARRAY[20000, 25000, 25000, 25000],
> +     ARRAY[['talk', 'consult'], ['meeting', '']]);
> + SELECT * FROM sal_emp;
> +  name  |      pay_by_quarter       |           schedule
> + -------+---------------------------+-------------------------------
> +  Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}}
> +  Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}}
> + (2 rows)
> + </programlisting>
> +   Note that with this syntax, multidimensional arrays must have matching
> +   extents for each dimension. This eliminates the missing-array-elements
> +   problem above. For example:
> + <programlisting>
> + INSERT INTO sal_emp
> +     VALUES ('Carol',
> +     ARRAY[20000, 25000, 25000, 25000],
> +     ARRAY[['talk', 'consult'], ['meeting']]);
> + ERROR:  Multidimensional arrays must have array expressions with matching dimensions
> + </programlisting>
> +   Also notice that string literals are single quoted instead of double quoted.
> +  </para>
> +
>    <note>
>     <para>
> !    The examples in the rest of this section are based on the
> !    <command>ARRAY</command> expression syntax <command>INSERT</command>s.
>     </para>
>    </note>
> +
>    </sect2>
>
>    <sect2>
> ***************
> *** 132,142 ****
>   </programlisting>
>
>     with the same result.  An array subscripting operation is always taken to
> !   represent an array slice if any of the subscripts are written in the
> !   form
>     <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
>     A lower bound of 1 is assumed for any subscript where only one value
> !   is specified.
>    </para>
>
>    <para>
> --- 192,221 ----
>   </programlisting>
>
>     with the same result.  An array subscripting operation is always taken to
> !   represent an array slice if any of the subscripts are written in the form
>     <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
>     A lower bound of 1 is assumed for any subscript where only one value
> !   is specified; another example follows:
> ! <programlisting>
> ! SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
> !          schedule
> ! ---------------------------
> !  {{meeting,lunch},{"",""}}
> ! (1 row)
> ! </programlisting>
> !  </para>
> !
> !  <para>
> !   Additionally, we can also access a single arbitrary array element of
> !   a one-dimensional array with the <function>array_subscript</function>
> !   function:
> ! <programlisting>
> ! SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
> !  array_subscript
> ! -----------------
> !            10000
> ! (1 row)
> ! </programlisting>
>    </para>
>
>    <para>
> ***************
> *** 147,153 ****
>       WHERE name = 'Carol';
>   </programlisting>
>
> !   or updated at a single element:
>
>   <programlisting>
>   UPDATE sal_emp SET pay_by_quarter[4] = 15000
> --- 226,248 ----
>       WHERE name = 'Carol';
>   </programlisting>
>
> !   or using the <command>ARRAY</command> expression syntax:
> !
> ! <programlisting>
> ! UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
> !     WHERE name = 'Carol';
> ! </programlisting>
> !
> !   <note>
> !    <para>
> !     Anywhere you can use the <quote>curly braces</quote> array syntax,
> !     you can also use the <command>ARRAY</command> expression syntax. The
> !     remainder of this section will illustrate only one or the other, but
> !     not both.
> !    </para>
> !   </note>
> !
> !   An array may also be updated at a single element:
>
>   <programlisting>
>   UPDATE sal_emp SET pay_by_quarter[4] = 15000
> ***************
> *** 160,165 ****
> --- 255,268 ----
>   UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
>       WHERE name = 'Carol';
>   </programlisting>
> +
> +   A one-dimensional array may also be updated with the
> +   <function>array_assign</function> function:
> +
> + <programlisting>
> + UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000)
> +     WHERE name = 'Bill';
> + </programListing>
>    </para>
>
>    <para>
> ***************
> *** 179,184 ****
> --- 282,369 ----
>    </para>
>
>    <para>
> +   An array can also be enlarged by using the concatenation operator,
> +   <command>||</command>.
> + <programlisting>
> + SELECT ARRAY[1,2] || ARRAY[3,4];
> +    ?column?
> + ---------------
> +  {{1,2},{3,4}}
> + (1 row)
> +
> + SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
> +       ?column?
> + ---------------------
> +  {{5,6},{1,2},{3,4}}
> + (1 row)
> + </programlisting>
> +
> +   The concatenation operator allows a single element to be pushed on to the
> +   beginning or end of a one-dimensional array. It also allows two
> +   <replaceable>N</>-dimensional arrays, or an <replaceable>N</>-dimensional
> +   and an <replaceable>N+1</>-dimensional array. In the former case, the two
> +   <replaceable>N</>-dimension arrays become outer elements of an
> +   <replaceable>N+1</>-dimensional array. In the latter, the
> +   <replaceable>N</>-dimensional array is added as either the first or last
> +   outer element of the <replaceable>N+1</>-dimensional array.
> +
> +   The array is extended in the direction of the push. Hence, by pushing
> +   onto the beginning of an array with a one-based subscript, a zero-based
> +   subscript array is created:
> +
> + <programlisting>
> + SELECT array_dims(t.f) FROM (SELECT 1 || ARRAY[2,3] AS f) AS t;
> +  array_dims
> + ------------
> +  [0:2]
> + (1 row)
> + </programlisting>
> +  </para>
> +
> +  <para>
> +   An array can also be enlarged by using the functions
> +   <function>array_prepend</function>, <function>array_append</function>,
> +   or <function>array_cat</function>. The first two only support one-dimensional
> +   arrays, but <function>array_cat</function> supports multidimensional arrays.
> +
> +   Note that the concatenation operator discussed above is preferred over
> +   direct use of these functions. In fact, the functions are primarily for use
> +   in implementing the concatenation operator. However, they may be directly
> +   useful in the creation of user-defined aggregates. Some examples:
> +
> + <programlisting>
> + SELECT array_prepend(1, ARRAY[2,3]);
> +  array_prepend
> + ---------------
> +  {1,2,3}
> + (1 row)
> +
> + SELECT array_append(ARRAY[1,2], 3);
> +  array_append
> + --------------
> +  {1,2,3}
> + (1 row)
> +
> + SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
> +    array_cat
> + ---------------
> +  {{1,2},{3,4}}
> + (1 row)
> +
> + SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
> +       array_cat
> + ---------------------
> +  {{1,2},{3,4},{5,6}}
> + (1 row)
> +
> + SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
> +       array_cat
> + ---------------------
> +  {{5,6},{1,2},{3,4}}
> + </programlisting>
> +  </para>
> +
> +  <para>
>     The syntax for <command>CREATE TABLE</command> allows fixed-length
>     arrays to be defined:
>
> ***************
> *** 194,199 ****
> --- 379,394 ----
>    </para>
>
>    <para>
> +   An alternative syntax for one-dimensional arrays may be used.
> +   <structfield>pay_by_quarter</structfield> could have been defined as:
> + <programlisting>
> +     pay_by_quarter  integer ARRAY[4],
> + </programlisting>
> +   This syntax may <emphasis>only</emphasis> be used with the integer
> +   constant to denote the array size.
> +  </para>
> +
> +  <para>
>     Actually, the current implementation does not enforce the declared
>     number of dimensions either.  Arrays of a particular element type are
>     all considered to be of the same type, regardless of size or number
> ***************
> *** 300,305 ****
> --- 495,566 ----
>      is not ignored, however: after skipping leading whitespace, everything
>      up to the next right brace or delimiter is taken as the item value.
>     </para>
> +
> +   <para>
> +    As illustrated earlier in this chapter, arrays may also be represented
> +    using the <command>ARRAY</command> expression syntax. This representation
> +    of an array value consists of items that are interpreted according to the
> +    I/O conversion rules for the array's element type, plus decoration that
> +    indicates the array structure. The decoration consists of the keyword
> +    <command>ARRAY</command> and square brackets (<literal>[</> and
> +    <literal>]</>) around the array values, plus delimiter characters between
> +    adjacent items. The delimiter character is always a comma (<literal>,</>).
> +    When representing multidimensional arrays, the keyword
> +    <command>ARRAY</command> is only necessary for the outer level. For example,
> +    <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as:
> + <programlisting>
> + SELECT ARRAY[['hello world', 'happy birthday']];
> +                array
> + ------------------------------------
> +  {{"hello world","happy birthday"}}
> + (1 row)
> + </programlisting>
> +   or it also could be written as:
> + <programlisting>
> + SELECT ARRAY[ARRAY['hello world', 'happy birthday']];
> +                array
> + ------------------------------------
> +  {{"hello world","happy birthday"}}
> + (1 row)
> + </programlisting>
> +   </para>
> +
> +   <para>
> +    A final method to represent an array, is through an
> +    <command>ARRAY</command> sub-select expression. For example:
> + <programlisting>
> + SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
> +                           ?column?
> + -------------------------------------------------------------
> +  {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
> + (1 row)
> + </programlisting>
> +   The sub-select may <emphasis>only</emphasis> return a single column. The
> +   resulting one-dimensional array will have an element for each row in the
> +   sub-select result, with an element type matching that of the sub-select's
> +   target column.
> +   </para>
> +
> +   <para>
> +    Arrays may be cast from one type to another in similar fashion to other
> +    data types:
> +
> + <programlisting>
> + SELECT ARRAY[1,2,3]::oid[];
> +   array
> + ---------
> +  {1,2,3}
> + (1 row)
> +
> + SELECT CAST(ARRAY[1,2,3] AS float8[]);
> +   array
> + ---------
> +  {1,2,3}
> + (1 row)
> + </programlisting>
> +
> +   </para>
> +
>    </sect2>
>
>    <sect2>
> ***************
> *** 316,321 ****
> --- 577,590 ----
>      Alternatively, you can use backslash-escaping to protect all data characters
>      that would otherwise be taken as array syntax or ignorable white space.
>     </para>
> +
> +  <note>
> +   <para>
> +    The discussion in the preceding paragraph with respect to double quoting does
> +    not pertain to the <command>ARRAY</command> expression syntax. In that case,
> +    each element is quoted exactly as any other literal value of the element type.
> +   </para>
> +  </note>
>
>     <para>
>      The array output routine will put double quotes around element values
> Index: doc/src/sgml/func.sgml
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v
> retrieving revision 1.154
> diff -c -r1.154 func.sgml
> *** doc/src/sgml/func.sgml    5 May 2003 15:08:49 -0000    1.154
> --- doc/src/sgml/func.sgml    24 Jun 2003 02:24:06 -0000
> ***************
> *** 6962,6967 ****
> --- 6962,7164 ----
>
>     </sect1>
>
> +  <sect1 id="functions-array">
> +   <title>Array Functions</title>
> +
> +   <para>
> +    <xref linkend="array-operators-table"> shows the operators
> +    available for the <type>array</type> types.
> +   </para>
> +
> +     <table id="array-operators-table">
> +      <title><type>array</type> Operators</title>
> +      <tgroup cols="4">
> +       <thead>
> +        <row>
> +     <entry>Operator</entry>
> +     <entry>Description</entry>
> +     <entry>Example</entry>
> +     <entry>Result</entry>
> +        </row>
> +       </thead>
> +       <tbody>
> +        <row>
> +     <entry> <literal>=</literal> </entry>
> +     <entry>equals</entry>
> +     <entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry>
> +     <entry><literal>t</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>array-to-array concatenation</entry>
> +     <entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry>
> +     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>array-to-array concatenation</entry>
> +     <entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry>
> +     <entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>element-to-array concatenation</entry>
> +     <entry><literal>3 || ARRAY[4,5,6]</literal></entry>
> +     <entry><literal>{3,4,5,6}</literal></entry>
> +        </row>
> +        <row>
> +     <entry> <literal>||</literal> </entry>
> +     <entry>array-to-element concatenation</entry>
> +     <entry><literal>ARRAY[4,5,6] || 7</literal></entry>
> +     <entry><literal>{4,5,6,7}</literal></entry>
> +        </row>
> +       </tbody>
> +      </tgroup>
> +     </table>
> +
> +   <para>
> +    <xref linkend="array-functions-table"> shows the functions
> +    available for use with array types. See <xref linkend="arrays">
> +    for more discussion and examples for the use of these functions.
> +   </para>
> +
> +     <table id="array-functions-table">
> +      <title><type>array</type> Functions</title>
> +      <tgroup cols="5">
> +       <thead>
> +        <row>
> +     <entry>Function</entry>
> +     <entry>Return Type</entry>
> +     <entry>Description</entry>
> +     <entry>Example</entry>
> +     <entry>Result</entry>
> +        </row>
> +       </thead>
> +       <tbody>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_append</function>
> +       (<type>anyarray</type>, <type>anyelement</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>anyarray</type></entry>
> +     <entry>
> +      append an element to the end of an array, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_append(ARRAY[1,2], 3)</literal></entry>
> +     <entry><literal>{1,2,3}</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_cat</function>
> +       (<type>anyarray</type>, <type>anyarray</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>anyarray</type></entry>
> +     <entry>
> +      concatenate two arrays, returning <literal>NULL</literal>
> +      for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry>
> +     <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_dims</function>
> +       (<type>anyarray</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>text</type></entry>
> +     <entry>
> +      returns a text representation of array dimension lower and upper bounds,
> +      generating an ERROR for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry>
> +     <entry><literal>[1:2][1:3]</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_lower</function>
> +       (<type>anyarray</type>, <type>integer</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>integer</type></entry>
> +     <entry>
> +      returns lower bound of the requested array dimension, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry>
> +     <entry><literal>0</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_prepend</function>
> +       (<type>anyelement</type>, <type>anyarray</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>anyarray</type></entry>
> +     <entry>
> +      append an element to the beginning of an array, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry>
> +     <entry><literal>{1,2,3}</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_to_string</function>
> +       (<type>anyarray</type>, <type>text</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>text</type></entry>
> +     <entry>
> +      concatenates array elements using provided delimiter, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_to_string(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry>
> +     <entry><literal>1.10~^~2.20~^~3.30</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>array_upper</function>
> +       (<type>anyarray</type>, <type>integer</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>integer</type></entry>
> +     <entry>
> +      returns upper bound of the requested array dimension, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry>
> +     <entry><literal>4</literal></entry>
> +        </row>
> +        <row>
> +     <entry>
> +      <literal>
> +       <function>string_to_array</function>
> +       (<type>text</type>, <type>text</type>)
> +      </literal>
> +     </entry>
> +     <entry><type>text[]</type></entry>
> +     <entry>
> +      splits string into array elements using provided delimiter, returning
> +      <literal>NULL</literal> for <literal>NULL</literal> inputs
> +     </entry>
> +     <entry><literal>string_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry>
> +     <entry><literal>{1.1,2.2,3.3}</literal></entry>
> +        </row>
> +       </tbody>
> +      </tgroup>
> +     </table>
> +   </sect1>
>
>    <sect1 id="functions-aggregate">
>     <title>Aggregate Functions</title>
> Index: src/backend/catalog/pg_aggregate.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_aggregate.c,v
> retrieving revision 1.56
> diff -c -r1.56 pg_aggregate.c
> *** src/backend/catalog/pg_aggregate.c    18 Sep 2002 21:35:20 -0000    1.56
> --- src/backend/catalog/pg_aggregate.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 50,59 ****
>       Oid            finalfn = InvalidOid;    /* can be omitted */
>       Oid            finaltype;
>       Oid            fnArgs[FUNC_MAX_ARGS];
> !     int            nargs;
>       Oid            procOid;
>       TupleDesc    tupDesc;
>       int            i;
>       ObjectAddress myself,
>                   referenced;
>
> --- 50,65 ----
>       Oid            finalfn = InvalidOid;    /* can be omitted */
>       Oid            finaltype;
>       Oid            fnArgs[FUNC_MAX_ARGS];
> !     int            nargs_transfn;
> !     int            nargs_finalfn;
>       Oid            procOid;
>       TupleDesc    tupDesc;
>       int            i;
> +     Oid            rettype;
> +     Oid           *true_oid_array_transfn;
> +     Oid           *true_oid_array_finalfn;
> +     bool        retset;
> +     FuncDetailCode fdresult;
>       ObjectAddress myself,
>                   referenced;
>
> ***************
> *** 68,91 ****
>       MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>       fnArgs[0] = aggTransType;
>       if (aggBaseType == ANYOID)
> !         nargs = 1;
>       else
>       {
>           fnArgs[1] = aggBaseType;
> !         nargs = 2;
>       }
> !     transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
>       if (!OidIsValid(transfn))
> !         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
>       tup = SearchSysCache(PROCOID,
>                            ObjectIdGetDatum(transfn),
>                            0, 0, 0);
>       if (!HeapTupleIsValid(tup))
> !         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
>       proc = (Form_pg_proc) GETSTRUCT(tup);
> -     if (proc->prorettype != aggTransType)
> -         elog(ERROR, "return type of transition function %s is not %s",
> -          NameListToString(aggtransfnName), format_type_be(aggTransType));
>
>       /*
>        * If the transfn is strict and the initval is NULL, make sure input
> --- 74,122 ----
>       MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>       fnArgs[0] = aggTransType;
>       if (aggBaseType == ANYOID)
> !         nargs_transfn = 1;
>       else
>       {
>           fnArgs[1] = aggBaseType;
> !         nargs_transfn = 2;
>       }
> !
> !     /*
> !      * func_get_detail looks up the function in the catalogs, does
> !      * disambiguation for polymorphic functions, handles inheritance, and
> !      * returns the funcid and type and set or singleton status of the
> !      * function's return value.  it also returns the true argument types
> !      * to the function.
> !      */
> !     fdresult = func_get_detail(aggtransfnName, NIL, nargs_transfn, fnArgs,
> !                                &transfn, &rettype, &retset,
> !                                &true_oid_array_transfn);
> !
> !     /* only valid case is a normal function */
> !     if (fdresult != FUNCDETAIL_NORMAL)
> !         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
> !
>       if (!OidIsValid(transfn))
> !         func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
> !
> !     /*
> !      * enforce consistency with ANYARRAY and ANYELEMENT argument
> !      * and return types, possibly modifying return type along the way
> !      */
> !     rettype = enforce_generic_type_consistency(fnArgs, true_oid_array_transfn,
> !                                                        nargs_transfn, rettype);
> !
> !     if (rettype != aggTransType)
> !         elog(ERROR, "return type of transition function %s is not %s",
> !          NameListToString(aggtransfnName), format_type_be(aggTransType));
> !
>       tup = SearchSysCache(PROCOID,
>                            ObjectIdGetDatum(transfn),
>                            0, 0, 0);
>       if (!HeapTupleIsValid(tup))
> !         func_error("AggregateCreate", aggtransfnName,
> !                         nargs_transfn, fnArgs, NULL);
>       proc = (Form_pg_proc) GETSTRUCT(tup);
>
>       /*
>        * If the transfn is strict and the initval is NULL, make sure input
> ***************
> *** 105,121 ****
>       {
>           MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>           fnArgs[0] = aggTransType;
> !         finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
> !         if (!OidIsValid(finalfn))
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !         tup = SearchSysCache(PROCOID,
> !                              ObjectIdGetDatum(finalfn),
> !                              0, 0, 0);
> !         if (!HeapTupleIsValid(tup))
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !         proc = (Form_pg_proc) GETSTRUCT(tup);
> !         finaltype = proc->prorettype;
> !         ReleaseSysCache(tup);
>       }
>       else
>       {
> --- 136,161 ----
>       {
>           MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
>           fnArgs[0] = aggTransType;
> !         nargs_finalfn = 1;
> !
> !         fdresult = func_get_detail(aggfinalfnName, NIL, 1, fnArgs,
> !                                    &finalfn, &rettype, &retset,
> !                                    &true_oid_array_finalfn);
> !
> !         /* only valid case is a normal function */
> !         if (fdresult != FUNCDETAIL_NORMAL)
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !
> !         if (!OidIsValid(finalfn))
>               func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !
> !         /*
> !          * enforce consistency with ANYARRAY and ANYELEMENT argument
> !          * and return types, possibly modifying return type along the way
> !          */
> !         finaltype = enforce_generic_type_consistency(fnArgs,
> !                                                      true_oid_array_finalfn,
> !                                                      nargs_finalfn, rettype);
>       }
>       else
>       {
> ***************
> *** 125,130 ****
> --- 165,191 ----
>           finaltype = aggTransType;
>       }
>       Assert(OidIsValid(finaltype));
> +
> +     /*
> +      * special disallowed cases:
> +      * 1)    if finaltype is polymorphic, basetype cannot be ANY
> +      * 2)    if finaltype is polymorphic, both args to transfn must be
> +      *        polymorphic
> +      */
> +     if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
> +     {
> +         if (aggBaseType == ANYOID)
> +             elog(ERROR, "aggregate with base type ANY must have a " \
> +                         "non-polymorphic return type");
> +
> +         if (nargs_transfn > 1 && (
> +             (true_oid_array_transfn[0] != ANYARRAYOID &&
> +              true_oid_array_transfn[0] != ANYELEMENTOID) ||
> +             (true_oid_array_transfn[1] != ANYARRAYOID &&
> +              true_oid_array_transfn[1] != ANYELEMENTOID)))
> +             elog(ERROR, "aggregate with polymorphic return type requires " \
> +                         "state function with both arguments polymorphic");
> +     }
>
>       /*
>        * Everything looks okay.  Try to create the pg_proc entry for the
> Index: src/backend/commands/aggregatecmds.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/aggregatecmds.c,v
> retrieving revision 1.5
> diff -c -r1.5 aggregatecmds.c
> *** src/backend/commands/aggregatecmds.c    4 Sep 2002 20:31:14 -0000    1.5
> --- src/backend/commands/aggregatecmds.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 119,125 ****
>           baseTypeId = typenameTypeId(baseType);
>
>       transTypeId = typenameTypeId(transType);
> !     if (get_typtype(transTypeId) == 'p')
>           elog(ERROR, "Aggregate transition datatype cannot be %s",
>                format_type_be(transTypeId));
>
> --- 119,127 ----
>           baseTypeId = typenameTypeId(baseType);
>
>       transTypeId = typenameTypeId(transType);
> !     if (get_typtype(transTypeId) == 'p' &&
> !         transTypeId != ANYARRAYOID &&
> !         transTypeId != ANYELEMENTOID)
>           elog(ERROR, "Aggregate transition datatype cannot be %s",
>                format_type_be(transTypeId));
>
> Index: src/backend/executor/execQual.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/execQual.c,v
> retrieving revision 1.130
> diff -c -r1.130 execQual.c
> *** src/backend/executor/execQual.c    28 May 2003 22:32:49 -0000    1.130
> --- src/backend/executor/execQual.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 1528,1544 ****
>               {
>                   /* Check other sub-arrays are compatible */
>                   if (elem_ndims != ARR_NDIM(array))
> !                     elog(ERROR, "Multiple dimension arrays must have array "
>                            "expressions with matching number of dimensions");
>
>                   if (memcmp(elem_dims, ARR_DIMS(array),
>                              elem_ndims * sizeof(int)) != 0)
> !                     elog(ERROR, "Multiple dimension arrays must have array "
>                            "expressions with matching dimensions");
>
>                   if (memcmp(elem_lbs, ARR_LBOUND(array),
>                              elem_ndims * sizeof(int)) != 0)
> !                     elog(ERROR, "Multiple dimension arrays must have array "
>                            "expressions with matching dimensions");
>               }
>
> --- 1528,1544 ----
>               {
>                   /* Check other sub-arrays are compatible */
>                   if (elem_ndims != ARR_NDIM(array))
> !                     elog(ERROR, "Multidimensional arrays must have array "
>                            "expressions with matching number of dimensions");
>
>                   if (memcmp(elem_dims, ARR_DIMS(array),
>                              elem_ndims * sizeof(int)) != 0)
> !                     elog(ERROR, "Multidimensional arrays must have array "
>                            "expressions with matching dimensions");
>
>                   if (memcmp(elem_lbs, ARR_LBOUND(array),
>                              elem_ndims * sizeof(int)) != 0)
> !                     elog(ERROR, "Multidimensional arrays must have array "
>                            "expressions with matching dimensions");
>               }
>
> Index: src/backend/executor/nodeAgg.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeAgg.c,v
> retrieving revision 1.107
> diff -c -r1.107 nodeAgg.c
> *** src/backend/executor/nodeAgg.c    22 Jun 2003 22:04:54 -0000    1.107
> --- src/backend/executor/nodeAgg.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 58,63 ****
> --- 58,64 ----
>   #include "executor/executor.h"
>   #include "executor/nodeAgg.h"
>   #include "miscadmin.h"
> + #include "nodes/makefuncs.h"
>   #include "optimizer/clauses.h"
>   #include "parser/parse_coerce.h"
>   #include "parser/parse_expr.h"
> ***************
> *** 212,218 ****
>   static void agg_fill_hash_table(AggState *aggstate);
>   static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
>   static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
> !
>
>   /*
>    * Initialize all aggregates for a new group of input values.
> --- 213,219 ----
>   static void agg_fill_hash_table(AggState *aggstate);
>   static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
>   static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
> ! static Oid resolve_type(Oid type_to_resolve, Oid context_type);
>
>   /*
>    * Initialize all aggregates for a new group of input values.
> ***************
> *** 351,364 ****
>       fcinfo.context = NULL;
>       fcinfo.resultinfo = NULL;
>       fcinfo.isnull = false;
> -
>       fcinfo.flinfo = &peraggstate->transfn;
>       fcinfo.nargs = 2;
>       fcinfo.arg[0] = pergroupstate->transValue;
>       fcinfo.argnull[0] = pergroupstate->transValueIsNull;
>       fcinfo.arg[1] = newVal;
>       fcinfo.argnull[1] = isNull;
> -
>       newVal = FunctionCallInvoke(&fcinfo);
>
>       /*
> --- 352,363 ----
> ***************
> *** 1187,1193 ****
> --- 1186,1206 ----
>           AclResult    aclresult;
>           Oid            transfn_oid,
>                       finalfn_oid;
> +         FuncExpr   *transfnexpr,
> +                    *finalfnexpr;
>           Datum        textInitVal;
> +         List       *fargs;
> +         Oid            agg_rt_type;
> +         Oid           *transfn_arg_types;
> +         List       *transfn_args = NIL;
> +         int            transfn_nargs;
> +         Oid            transfn_ret_type;
> +         Oid           *finalfn_arg_types = NULL;
> +         List       *finalfn_args = NIL;
> +         Oid            finalfn_ret_type = InvalidOid;
> +         int            finalfn_nargs = 0;
> +         Node       *arg0;
> +         Node       *arg1;
>           int            i;
>
>           /* Planner should have assigned aggregate to correct level */
> ***************
> *** 1238,1243 ****
> --- 1251,1416 ----
>                           &peraggstate->transtypeLen,
>                           &peraggstate->transtypeByVal);
>
> +         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
> +         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
> +
> +         /* get the runtime aggregate argument type */
> +         fargs = aggref->args;
> +         agg_rt_type = exprType((Node *) nth(0, fargs));
> +
> +         /* get the transition function argument and return types */
> +         transfn_ret_type = get_func_rettype(transfn_oid);
> +         transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs);
> +
> +         /* resolve any polymorphic types */
> +         if (transfn_nargs == 2)
> +         /* base type was not ANY */
> +         {
> +             if (transfn_arg_types[1] == ANYARRAYOID ||
> +                 transfn_arg_types[1] == ANYELEMENTOID)
> +                 transfn_arg_types[1] = agg_rt_type;
> +
> +             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
> +                                                         agg_rt_type);
> +
> +             /*
> +              * Build arg list to use on the transfn FuncExpr node. We really
> +              * only care that the node type is correct so that the transfn
> +              * can discover the actual argument types at runtime using
> +              * get_fn_expr_argtype()
> +              */
> +             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
> +                                                             -1, COERCE_DONTCARE);
> +             arg1 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[1],
> +                                                             -1, COERCE_DONTCARE);
> +             transfn_args = makeList2(arg0, arg1);
> +
> +             /*
> +              * the state transition function always returns the same type
> +              * as its first argument
> +              */
> +             if (transfn_ret_type == ANYARRAYOID ||
> +                 transfn_ret_type == ANYELEMENTOID)
> +                 transfn_ret_type = transfn_arg_types[0];
> +         }
> +         else if (transfn_nargs == 1)
> +         /*
> +          * base type was ANY, therefore the aggregate return type should
> +          * be non-polymorphic
> +          */
> +         {
> +             Oid    finaltype = get_func_rettype(aggref->aggfnoid);
> +
> +             /*
> +              * this should have been prevented in AggregateCreate,
> +              * but check anyway
> +              */
> +             if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
> +                 elog(ERROR, "aggregate with base type ANY must have a " \
> +                             "non-polymorphic return type");
> +
> +             /* see if we have a final function */
> +             if (OidIsValid(finalfn_oid))
> +             {
> +                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
> +                 if (finalfn_nargs != 1)
> +                     elog(ERROR, "final function takes unexpected number " \
> +                                 "of arguments: %d", finalfn_nargs);
> +
> +                 /*
> +                  * final function argument is always the same as the state
> +                  * function return type
> +                  */
> +                 if (finalfn_arg_types[0] != ANYARRAYOID &&
> +                     finalfn_arg_types[0] != ANYELEMENTOID)
> +                 {
> +                     /* if it is not ambiguous, use it */
> +                     transfn_ret_type = finalfn_arg_types[0];
> +                 }
> +                 else
> +                 {
> +                     /* if it is ambiguous, try to derive it */
> +                     finalfn_ret_type = finaltype;
> +                     finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0],
> +                                                             finalfn_ret_type);
> +                     transfn_ret_type = finalfn_arg_types[0];
> +                 }
> +             }
> +             else
> +                 transfn_ret_type = finaltype;
> +
> +             transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
> +                                                         transfn_ret_type);
> +
> +             /*
> +              * Build arg list to use on the transfn FuncExpr node. We really
> +              * only care that the node type is correct so that the transfn
> +              * can discover the actual argument types at runtime using
> +              * get_fn_expr_argtype()
> +              */
> +             arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
> +                                                             -1, COERCE_DONTCARE);
> +             transfn_args = makeList1(arg0);
> +         }
> +         else
> +             elog(ERROR, "state transition function takes unexpected number " \
> +                         "of arguments: %d", transfn_nargs);
> +
> +         if (OidIsValid(finalfn_oid))
> +         {
> +             /* get the final function argument and return types */
> +             if (finalfn_ret_type == InvalidOid)
> +                 finalfn_ret_type = get_func_rettype(finalfn_oid);
> +
> +             if (!finalfn_arg_types)
> +             {
> +                 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
> +                 if (finalfn_nargs != 1)
> +                     elog(ERROR, "final function takes unexpected number " \
> +                                 "of arguments: %d", finalfn_nargs);
> +             }
> +
> +             /*
> +              * final function argument is always the same as the state
> +              * function return type, which by now should have been resolved
> +              */
> +             if (finalfn_arg_types[0] == ANYARRAYOID ||
> +                 finalfn_arg_types[0] == ANYELEMENTOID)
> +                 finalfn_arg_types[0] = transfn_ret_type;
> +
> +             /*
> +              * Build arg list to use on the finalfn FuncExpr node. We really
> +              * only care that the node type is correct so that the finalfn
> +              * can discover the actual argument type at runtime using
> +              * get_fn_expr_argtype()
> +              */
> +             arg0 = (Node *) makeRelabelType((Expr *) NULL, finalfn_arg_types[0],
> +                                                             -1, COERCE_DONTCARE);
> +             finalfn_args = makeList1(arg0);
> +
> +             finalfn_ret_type = resolve_type(finalfn_ret_type,
> +                                                 finalfn_arg_types[0]);
> +         }
> +
> +         fmgr_info(transfn_oid, &peraggstate->transfn);
> +         transfnexpr = (FuncExpr *) make_funcclause(transfn_oid,
> +                           transfn_ret_type,
> +                           false,            /* cannot be a set */
> +                           COERCE_DONTCARE,    /* to match any user expr */
> +                           transfn_args);
> +         peraggstate->transfn.fn_expr = (Node *) transfnexpr;
> +
> +         if (OidIsValid(finalfn_oid))
> +         {
> +             fmgr_info(finalfn_oid, &peraggstate->finalfn);
> +             finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid,
> +                           finalfn_ret_type,
> +                           false,            /* cannot be a set */
> +                           COERCE_DONTCARE,    /* to match any user expr */
> +                           finalfn_args);
> +             peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
> +         }
> +
>           /*
>            * initval is potentially null, so don't try to access it as a
>            * struct field. Must do it the hard way with SysCacheGetAttr.
> ***************
> *** 1250,1263 ****
>               peraggstate->initValue = (Datum) 0;
>           else
>               peraggstate->initValue = GetAggInitVal(textInitVal,
> !                                                    aggform->aggtranstype);
> !
> !         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
> !         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
> !
> !         fmgr_info(transfn_oid, &peraggstate->transfn);
> !         if (OidIsValid(finalfn_oid))
> !             fmgr_info(finalfn_oid, &peraggstate->finalfn);
>
>           /*
>            * If the transfn is strict and the initval is NULL, make sure
> --- 1423,1429 ----
>               peraggstate->initValue = (Datum) 0;
>           else
>               peraggstate->initValue = GetAggInitVal(textInitVal,
> !                                                    transfn_arg_types[0]);
>
>           /*
>            * If the transfn is strict and the initval is NULL, make sure
> ***************
> *** 1468,1471 ****
> --- 1634,1670 ----
>       elog(ERROR, "Aggregate function %u called as normal function",
>            fcinfo->flinfo->fn_oid);
>       return (Datum) 0;            /* keep compiler quiet */
> + }
> +
> + static Oid
> + resolve_type(Oid type_to_resolve, Oid context_type)
> + {
> +     Oid        resolved_type;
> +
> +     if (context_type == ANYARRAYOID || context_type == ANYELEMENTOID)
> +         resolved_type = type_to_resolve;
> +     else if (type_to_resolve == ANYARRAYOID)
> +     /* any array */
> +     {
> +         Oid        context_type_arraytype = get_array_type(context_type);
> +
> +         if (context_type_arraytype != InvalidOid)
> +             resolved_type = context_type_arraytype;
> +         else
> +             resolved_type = context_type;
> +     }
> +     else if (type_to_resolve == ANYELEMENTOID)
> +     /* any element */
> +     {
> +         Oid        context_type_elemtype = get_element_type(context_type);
> +
> +         if (context_type_elemtype != InvalidOid)
> +             resolved_type = context_type_elemtype;
> +         else
> +             resolved_type = context_type;
> +     }
> +     else
> +         resolved_type = type_to_resolve;
> +
> +     return resolved_type;
>   }
> Index: src/backend/executor/nodeSubplan.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v
> retrieving revision 1.47
> diff -c -r1.47 nodeSubplan.c
> *** src/backend/executor/nodeSubplan.c    22 Jun 2003 22:04:54 -0000    1.47
> --- src/backend/executor/nodeSubplan.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 28,50 ****
>   #include "utils/datum.h"
>   #include "utils/lsyscache.h"
>
> -
> - typedef struct ArrayBuildState
> - {
> -     MemoryContext mcontext;        /* where all the temp stuff is kept */
> -     Datum       *dvalues;        /* array of accumulated Datums */
> -     /*
> -      * The allocated size of dvalues[] is always a multiple of
> -      * ARRAY_ELEMS_CHUNKSIZE
> -      */
> - #define ARRAY_ELEMS_CHUNKSIZE    64
> -     int            nelems;            /* number of valid Datums in dvalues[] */
> -     Oid            element_type;    /* data type of the Datums */
> -     int16        typlen;            /* needed info about datatype */
> -     bool        typbyval;
> -     char        typalign;
> - } ArrayBuildState;
> -
>   static Datum ExecHashSubPlan(SubPlanState *node,
>                                ExprContext *econtext,
>                                bool *isNull);
> --- 28,33 ----
> ***************
> *** 54,66 ****
>   static void buildSubPlanHash(SubPlanState *node);
>   static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
>   static bool tupleAllNulls(HeapTuple tuple);
> - static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
> -                                          Datum dvalue, bool disnull,
> -                                          Oid element_type,
> -                                          MemoryContext rcontext);
> - static Datum makeArrayResult(ArrayBuildState *astate,
> -                              MemoryContext rcontext);
> -
>
>   /* ----------------------------------------------------------------
>    *        ExecSubPlan
> --- 37,42 ----
> ***************
> *** 224,229 ****
> --- 200,206 ----
>       PlanState  *planstate = node->planstate;
>       SubLinkType subLinkType = subplan->subLinkType;
>       bool        useOr = subplan->useOr;
> +     bool        isExpr = subplan->isExpr;
>       MemoryContext oldcontext;
>       TupleTableSlot *slot;
>       Datum        result;
> ***************
> *** 294,299 ****
> --- 271,281 ----
>           bool        rownull = false;
>           int            col = 1;
>           List       *plst;
> +         int            numelems;
> +         int            elemnum;
> +         Datum        dvalue;
> +         Datum       *dvalues = NULL;
> +         bool        disnull;
>
>           if (subLinkType == EXISTS_SUBLINK)
>           {
> ***************
> *** 331,339 ****
>
>           if (subLinkType == ARRAY_SUBLINK)
>           {
> -             Datum    dvalue;
> -             bool    disnull;
> -
>               found = true;
>               /* stash away current value */
>               dvalue = heap_getattr(tup, 1, tdesc, &disnull);
> --- 313,318 ----
> ***************
> *** 351,448 ****
>           found = true;
>
>           /*
> !          * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
> !          * operators for columns of tuple.
>            */
> !         plst = subplan->paramIds;
> !         foreach(lst, node->exprs)
>           {
> !             ExprState  *exprstate = (ExprState *) lfirst(lst);
> !             int            paramid = lfirsti(plst);
> !             ParamExecData *prmdata;
> !             Datum        expresult;
> !             bool        expnull;
> !
> !             /*
> !              * Load up the Param representing this column of the sub-select.
> !              */
> !             prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
> !             Assert(prmdata->execPlan == NULL);
> !             prmdata->value = heap_getattr(tup, col, tdesc,
> !                                           &(prmdata->isnull));
>
> !             /*
> !              * Now we can eval the combining operator for this column.
> !              */
> !             expresult = ExecEvalExprSwitchContext(exprstate, econtext,
> !                                                   &expnull, NULL);
> !
> !             /*
> !              * Combine the result into the row result as appropriate.
> !              */
> !             if (col == 1)
>               {
> !                 rowresult = expresult;
> !                 rownull = expnull;
>               }
> !             else if (useOr)
>               {
> !                 /* combine within row per OR semantics */
> !                 if (expnull)
> !                     rownull = true;
> !                 else if (DatumGetBool(expresult))
>                   {
> !                     rowresult = BoolGetDatum(true);
> !                     rownull = false;
> !                     break;        /* needn't look at any more columns */
>                   }
>               }
>               else
>               {
> !                 /* combine within row per AND semantics */
> !                 if (expnull)
> !                     rownull = true;
> !                 else if (!DatumGetBool(expresult))
> !                 {
> !                     rowresult = BoolGetDatum(false);
> !                     rownull = false;
> !                     break;        /* needn't look at any more columns */
> !                 }
>               }
>
> -             plst = lnext(plst);
> -             col++;
>           }
>
> !         if (subLinkType == ANY_SUBLINK)
>           {
> !             /* combine across rows per OR semantics */
> !             if (rownull)
> !                 *isNull = true;
> !             else if (DatumGetBool(rowresult))
>               {
> !                 result = BoolGetDatum(true);
> !                 *isNull = false;
> !                 break;            /* needn't look at any more rows */
>               }
> !         }
> !         else if (subLinkType == ALL_SUBLINK)
> !         {
> !             /* combine across rows per AND semantics */
> !             if (rownull)
> !                 *isNull = true;
> !             else if (!DatumGetBool(rowresult))
>               {
> !                 result = BoolGetDatum(false);
> !                 *isNull = false;
> !                 break;            /* needn't look at any more rows */
>               }
> -         }
> -         else
> -         {
> -             /* must be MULTIEXPR_SUBLINK */
> -             result = rowresult;
> -             *isNull = rownull;
>           }
>       }
>
> --- 330,492 ----
>           found = true;
>
>           /*
> !          * When isExpr is true, we have either a scalar expression or an
> !          * array. In the former case, this is no different than the !isExpr
> !          * case. In the latter case, iterate over the elements as if they
> !          * were from multiple input tuples.
>            */
> !         if (!isExpr)
> !             numelems = 1;
> !         else
>           {
> !             Oid        expr_typeid = tdesc->attrs[0]->atttypid;
>
> !             if (expr_typeid != subplan->exprtype)
>               {
> !                 subplan->exprtype = expr_typeid;
> !                 subplan->elemtype = get_element_type(expr_typeid);
> !
> !                 if (subplan->elemtype != InvalidOid)
> !                     get_typlenbyvalalign(subplan->elemtype,
> !                                          &subplan->elmlen,
> !                                          &subplan->elmbyval,
> !                                          &subplan->elmalign);
>               }
> !
> !             /* get current value */
> !             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
> !
> !             /* XXX this will need work if/when arrays support NULL elements */
> !             if (!disnull)
>               {
> !                 if (subplan->elemtype != InvalidOid)
> !                 {
> !                     ArrayType   *v = DatumGetArrayTypeP(dvalue);
> !
> !                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
> !                                       subplan->elmbyval, subplan->elmalign,
> !                                         &dvalues, &numelems);
> !                 }
> !                 else
>                   {
> !                     numelems = 1;
> !                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                     dvalues[0] = dvalue;
>                   }
>               }
>               else
>               {
> !                 numelems = 1;
> !                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                 dvalues[0] = (Datum) 0;
>               }
>
>           }
>
> !         for (elemnum = 0; elemnum < numelems; elemnum++)
>           {
> !             /*
> !              * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
> !              * operators for columns of tuple.
> !              */
> !             col = 1;
> !             plst = subplan->paramIds;
> !             foreach(lst, node->exprs)
>               {
> !                 ExprState  *exprstate = (ExprState *) lfirst(lst);
> !                 int            paramid = lfirsti(plst);
> !                 ParamExecData *prmdata;
> !                 Datum        expresult;
> !                 bool        expnull;
> !
> !                 /*
> !                  * Load up the Param representing this column of the sub-select.
> !                  */
> !                 prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
> !                 Assert(prmdata->execPlan == NULL);
> !
> !                 if (!isExpr)
> !                     prmdata->value = heap_getattr(tup, col, tdesc,
> !                                                   &(prmdata->isnull));
> !                 else
> !                 {
> !                     prmdata->value = dvalues[elemnum];
> !                     prmdata->isnull = disnull;
> !                 }
> !
> !                 /*
> !                  * Now we can eval the combining operator for this column.
> !                  */
> !                 expresult = ExecEvalExprSwitchContext(exprstate, econtext,
> !                                                       &expnull, NULL);
> !
> !                 /*
> !                  * Combine the result into the row result as appropriate.
> !                  */
> !                 if (col == 1)
> !                 {
> !                     rowresult = expresult;
> !                     rownull = expnull;
> !                 }
> !                 else if (useOr)
> !                 {
> !                     /* combine within row per OR semantics */
> !                     if (expnull)
> !                         rownull = true;
> !                     else if (DatumGetBool(expresult))
> !                     {
> !                         rowresult = BoolGetDatum(true);
> !                         rownull = false;
> !                         break;        /* needn't look at any more columns */
> !                     }
> !                 }
> !                 else
> !                 {
> !                     /* combine within row per AND semantics */
> !                     if (expnull)
> !                         rownull = true;
> !                     else if (!DatumGetBool(expresult))
> !                     {
> !                         rowresult = BoolGetDatum(false);
> !                         rownull = false;
> !                         break;        /* needn't look at any more columns */
> !                     }
> !                 }
> !
> !                 plst = lnext(plst);
> !                 col++;
>               }
> !
> !             if (subLinkType == ANY_SUBLINK)
>               {
> !                 /* combine across rows per OR semantics */
> !                 if (rownull)
> !                     *isNull = true;
> !                 else if (DatumGetBool(rowresult))
> !                 {
> !                     result = BoolGetDatum(true);
> !                     *isNull = false;
> !                     break;            /* needn't look at any more rows */
> !                 }
> !             }
> !             else if (subLinkType == ALL_SUBLINK)
> !             {
> !                 /* combine across rows per AND semantics */
> !                 if (rownull)
> !                     *isNull = true;
> !                 else if (!DatumGetBool(rowresult))
> !                 {
> !                     result = BoolGetDatum(false);
> !                     *isNull = false;
> !                     break;            /* needn't look at any more rows */
> !                 }
> !             }
> !             else
> !             {
> !                 /* must be MULTIEXPR_SUBLINK */
> !                 result = rowresult;
> !                 *isNull = rownull;
>               }
>           }
>       }
>
> ***************
> *** 480,485 ****
> --- 524,530 ----
>   buildSubPlanHash(SubPlanState *node)
>   {
>       SubPlan       *subplan = (SubPlan *) node->xprstate.expr;
> +     bool        isExpr = subplan->isExpr;
>       PlanState  *planstate = node->planstate;
>       int            ncols = length(node->exprs);
>       ExprContext *innerecontext = node->innerecontext;
> ***************
> *** 487,492 ****
> --- 532,538 ----
>       MemoryContext oldcontext;
>       int            nbuckets;
>       TupleTableSlot *slot;
> +     TupleTableSlot *arrslot = NULL;
>
>       Assert(subplan->subLinkType == ANY_SUBLINK);
>       Assert(!subplan->useOr);
> ***************
> *** 566,608 ****
>       {
>           HeapTuple    tup = slot->val;
>           TupleDesc    tdesc = slot->ttc_tupleDescriptor;
> !         int            col = 1;
>           List       *plst;
>           bool        isnew;
>
>           /*
> !          * Load up the Params representing the raw sub-select outputs,
> !          * then form the projection tuple to store in the hashtable.
>            */
> !         foreach(plst, subplan->paramIds)
>           {
> !             int            paramid = lfirsti(plst);
> !             ParamExecData *prmdata;
>
> !             prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
> !             Assert(prmdata->execPlan == NULL);
> !             prmdata->value = heap_getattr(tup, col, tdesc,
> !                                           &(prmdata->isnull));
> !             col++;
> !         }
> !         slot = ExecProject(node->projRight, NULL);
> !         tup = slot->val;
>
> -         /*
> -          * If result contains any nulls, store separately or not at all.
> -          * (Since we know the projection tuple has no junk columns, we
> -          * can just look at the overall hasnull info bit, instead of
> -          * groveling through the columns.)
> -          */
> -         if (HeapTupleNoNulls(tup))
> -         {
> -             (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
> -             node->havehashrows = true;
>           }
> !         else if (node->hashnulls)
>           {
> !             (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
> !             node->havenullrows = true;
>           }
>
>           /*
> --- 612,750 ----
>       {
>           HeapTuple    tup = slot->val;
>           TupleDesc    tdesc = slot->ttc_tupleDescriptor;
> !         TupleDesc    arrtdesc = NULL;
>           List       *plst;
>           bool        isnew;
> +         int            numelems;
> +         int            elemnum;
> +         Datum        dvalue;
> +         Datum       *dvalues = NULL;
> +         bool        disnull;
>
>           /*
> !          * When isExpr is true, we have either a scalar expression or an
> !          * array. In the former case, this is no different than the !isExpr
> !          * case. In the latter case, iterate over the elements as if they
> !          * were from multiple input tuples.
>            */
> !         if (!isExpr)
> !             numelems = 1;
> !         else
>           {
> !             Oid        expr_typeid = tdesc->attrs[0]->atttypid;
>
> !             if (expr_typeid != subplan->exprtype)
> !             {
> !                 subplan->exprtype = expr_typeid;
> !                 subplan->elemtype = get_element_type(expr_typeid);
> !
> !                 if (subplan->elemtype != InvalidOid)
> !                     get_typlenbyvalalign(subplan->elemtype,
> !                                          &subplan->elmlen,
> !                                          &subplan->elmbyval,
> !                                          &subplan->elmalign);
> !             }
> !
> !             /* get current value */
> !             dvalue = heap_getattr(tup, 1, tdesc, &disnull);
> !
> !             if (subplan->elemtype != InvalidOid)
> !             {
> !                 TupleTable    tupleTable;
> !                 ArrayType   *v = DatumGetArrayTypeP(dvalue);
> !
> !                 arrtdesc = CreateTemplateTupleDesc(1, false);
> !                 TupleDescInitEntry(arrtdesc, 1, "elem", subplan->elemtype,
> !                                                             -1, 0, false);
> !
> !                 tupleTable = ExecCreateTupleTable(1);
> !                 arrslot = ExecAllocTableSlot(tupleTable);
> !                 ExecSetSlotDescriptor(arrslot, arrtdesc, true);
> !
> !                 /* XXX this will need work if/when arrays support NULL elements */
> !                 if (!disnull)
> !                 {
> !                     deconstruct_array(v, subplan->elemtype, subplan->elmlen,
> !                                       subplan->elmbyval, subplan->elmalign,
> !                                         &dvalues, &numelems);
> !                 }
> !                 else
> !                 {
> !                     numelems = 1;
> !                     dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                     dvalues[0] = (Datum) 0;
> !                 }
> !             }
> !             else
> !             {
> !                 numelems = 1;
> !                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> !                 dvalues[0] = dvalue;
> !             }
>
>           }
> !
> !         for (elemnum = 0; elemnum < numelems; elemnum++)
>           {
> !             int    col = 1;
> !
> !             if (!isExpr || subplan->elemtype == InvalidOid)
> !             {
> !                 /*
> !                  * Load up the Params representing the raw sub-select outputs,
> !                  * then form the projection tuple to store in the hashtable.
> !                  */
> !                 foreach(plst, subplan->paramIds)
> !                 {
> !                     int            paramid = lfirsti(plst);
> !                     ParamExecData *prmdata;
> !
> !                     prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
> !                     Assert(prmdata->execPlan == NULL);
> !
> !                     prmdata->value = heap_getattr(tup, col, tdesc,
> !                                                   &(prmdata->isnull));
> !
> !                     col++;
> !                 }
> !                 slot = ExecProject(node->projRight, NULL);
> !                 tup = slot->val;
> !             }
> !             else
> !             {
> !                 /*
> !                  * For array type expressions, we need to build up our own
> !                  * tuple and slot
> !                  */
> !                 char        nullflag;
> !
> !                 nullflag = disnull ? 'n' : ' ';
> !                 tup = heap_formtuple(arrtdesc, &dvalues[elemnum], &nullflag);
> !                 arrslot = ExecStoreTuple(tup, arrslot, InvalidBuffer, true);
> !             }
> !
> !             /*
> !              * If result contains any nulls, store separately or not at all.
> !              * (Since we know the projection tuple has no junk columns, we
> !              * can just look at the overall hasnull info bit, instead of
> !              * groveling through the columns.)
> !              */
> !             if (HeapTupleNoNulls(tup))
> !             {
> !                 if (!isExpr)
> !                     (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
> !                 else
> !                     (void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew);
> !                 node->havehashrows = true;
> !             }
> !             else if (node->hashnulls)
> !             {
> !                 if (!isExpr)
> !                     (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
> !                 else
> !                     (void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew);
> !                 node->havenullrows = true;
> !             }
>           }
>
>           /*
> ***************
> *** 619,624 ****
> --- 761,768 ----
>        * have the potential for a double free attempt.
>        */
>       ExecClearTuple(node->projRight->pi_slot);
> +     if (arrslot)
> +         ExecClearTuple(arrslot);
>
>       MemoryContextSwitchTo(oldcontext);
>   }
> ***************
> *** 1098,1199 ****
>           prm->execPlan = node;
>           parent->chgParam = bms_add_member(parent->chgParam, paramid);
>       }
> - }
> -
> - /*
> -  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
> -  *
> -  *    astate is working state (NULL on first call)
> -  *    rcontext is where to keep working state
> -  */
> - static ArrayBuildState *
> - accumArrayResult(ArrayBuildState *astate,
> -                  Datum dvalue, bool disnull,
> -                  Oid element_type,
> -                  MemoryContext rcontext)
> - {
> -     MemoryContext arr_context,
> -                   oldcontext;
> -
> -     if (astate == NULL)
> -     {
> -         /* First time through --- initialize */
> -
> -         /* Make a temporary context to hold all the junk */
> -         arr_context = AllocSetContextCreate(rcontext,
> -                                             "ARRAY_SUBLINK Result",
> -                                             ALLOCSET_DEFAULT_MINSIZE,
> -                                             ALLOCSET_DEFAULT_INITSIZE,
> -                                             ALLOCSET_DEFAULT_MAXSIZE);
> -         oldcontext = MemoryContextSwitchTo(arr_context);
> -         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
> -         astate->mcontext = arr_context;
> -         astate->dvalues = (Datum *)
> -             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
> -         astate->nelems = 0;
> -         astate->element_type = element_type;
> -         get_typlenbyvalalign(element_type,
> -                              &astate->typlen,
> -                              &astate->typbyval,
> -                              &astate->typalign);
> -     }
> -     else
> -     {
> -         oldcontext = MemoryContextSwitchTo(astate->mcontext);
> -         Assert(astate->element_type == element_type);
> -         /* enlarge dvalues[] if needed */
> -         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
> -             astate->dvalues = (Datum *)
> -                 repalloc(astate->dvalues,
> -                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
> -     }
> -
> -     if (disnull)
> -         elog(ERROR, "NULL elements not allowed in Arrays");
> -
> -     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
> -     astate->dvalues[astate->nelems++] =
> -         datumCopy(dvalue, astate->typbyval, astate->typlen);
> -
> -     MemoryContextSwitchTo(oldcontext);
> -
> -     return astate;
> - }
> -
> - /*
> -  * makeArrayResult - produce final result of ARRAY_SUBLINK
> -  *
> -  *    astate is working state (not NULL)
> -  *    rcontext is where to construct result
> -  */
> - static Datum
> - makeArrayResult(ArrayBuildState *astate,
> -                 MemoryContext rcontext)
> - {
> -     ArrayType  *result;
> -     int            dims[1];
> -     int            lbs[1];
> -     MemoryContext oldcontext;
> -
> -     /* Build the final array result in rcontext */
> -     oldcontext = MemoryContextSwitchTo(rcontext);
> -
> -     dims[0] = astate->nelems;
> -     lbs[0] = 1;
> -
> -     result = construct_md_array(astate->dvalues,
> -                                 1,
> -                                 dims,
> -                                 lbs,
> -                                 astate->element_type,
> -                                 astate->typlen,
> -                                 astate->typbyval,
> -                                 astate->typalign);
> -
> -     MemoryContextSwitchTo(oldcontext);
> -
> -     /* Clean up all the junk */
> -     MemoryContextDelete(astate->mcontext);
> -
> -     return PointerGetDatum(result);
>   }
> --- 1242,1245 ----
> Index: src/backend/nodes/copyfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
> retrieving revision 1.252
> diff -c -r1.252 copyfuncs.c
> *** src/backend/nodes/copyfuncs.c    6 Jun 2003 15:04:02 -0000    1.252
> --- src/backend/nodes/copyfuncs.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 728,733 ****
> --- 728,734 ----
>       COPY_SCALAR_FIELD(agglevelsup);
>       COPY_SCALAR_FIELD(aggstar);
>       COPY_SCALAR_FIELD(aggdistinct);
> +     COPY_NODE_FIELD(args);
>
>       return newnode;
>   }
> ***************
> *** 826,831 ****
> --- 827,833 ----
>
>       COPY_SCALAR_FIELD(subLinkType);
>       COPY_SCALAR_FIELD(useOr);
> +     COPY_SCALAR_FIELD(isExpr);
>       COPY_NODE_FIELD(lefthand);
>       COPY_NODE_FIELD(operName);
>       COPY_OIDLIST_FIELD(operOids);
> ***************
> *** 844,849 ****
> --- 846,857 ----
>
>       COPY_SCALAR_FIELD(subLinkType);
>       COPY_SCALAR_FIELD(useOr);
> +     COPY_SCALAR_FIELD(isExpr);
> +     COPY_SCALAR_FIELD(exprtype);
> +     COPY_SCALAR_FIELD(elemtype);
> +     COPY_SCALAR_FIELD(elmlen);
> +     COPY_SCALAR_FIELD(elmbyval);
> +     COPY_SCALAR_FIELD(elmalign);
>       COPY_NODE_FIELD(exprs);
>       COPY_INTLIST_FIELD(paramIds);
>       COPY_NODE_FIELD(plan);
> Index: src/backend/nodes/equalfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v
> retrieving revision 1.195
> diff -c -r1.195 equalfuncs.c
> *** src/backend/nodes/equalfuncs.c    6 Jun 2003 15:04:02 -0000    1.195
> --- src/backend/nodes/equalfuncs.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 205,210 ****
> --- 205,211 ----
>       COMPARE_SCALAR_FIELD(agglevelsup);
>       COMPARE_SCALAR_FIELD(aggstar);
>       COMPARE_SCALAR_FIELD(aggdistinct);
> +     COMPARE_NODE_FIELD(args);
>
>       return true;
>   }
> ***************
> *** 301,306 ****
> --- 302,308 ----
>   {
>       COMPARE_SCALAR_FIELD(subLinkType);
>       COMPARE_SCALAR_FIELD(useOr);
> +     COMPARE_SCALAR_FIELD(isExpr);
>       COMPARE_NODE_FIELD(lefthand);
>       COMPARE_NODE_FIELD(operName);
>       COMPARE_OIDLIST_FIELD(operOids);
> ***************
> *** 314,319 ****
> --- 316,327 ----
>   {
>       COMPARE_SCALAR_FIELD(subLinkType);
>       COMPARE_SCALAR_FIELD(useOr);
> +     COMPARE_SCALAR_FIELD(isExpr);
> +     COMPARE_SCALAR_FIELD(exprtype);
> +     COMPARE_SCALAR_FIELD(elemtype);
> +     COMPARE_SCALAR_FIELD(elmlen);
> +     COMPARE_SCALAR_FIELD(elmbyval);
> +     COMPARE_SCALAR_FIELD(elmalign);
>       COMPARE_NODE_FIELD(exprs);
>       COMPARE_INTLIST_FIELD(paramIds);
>       /* should compare plans, but have to settle for comparing plan IDs */
> Index: src/backend/nodes/outfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v
> retrieving revision 1.208
> diff -c -r1.208 outfuncs.c
> *** src/backend/nodes/outfuncs.c    15 Jun 2003 22:51:45 -0000    1.208
> --- src/backend/nodes/outfuncs.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 616,621 ****
> --- 616,622 ----
>       WRITE_UINT_FIELD(agglevelsup);
>       WRITE_BOOL_FIELD(aggstar);
>       WRITE_BOOL_FIELD(aggdistinct);
> +     WRITE_NODE_FIELD(args);
>   }
>
>   static void
> ***************
> *** 701,706 ****
> --- 702,708 ----
>
>       WRITE_ENUM_FIELD(subLinkType, SubLinkType);
>       WRITE_BOOL_FIELD(useOr);
> +     WRITE_BOOL_FIELD(isExpr);
>       WRITE_NODE_FIELD(lefthand);
>       WRITE_NODE_FIELD(operName);
>       WRITE_OIDLIST_FIELD(operOids);
> ***************
> *** 714,719 ****
> --- 716,727 ----
>
>       WRITE_ENUM_FIELD(subLinkType, SubLinkType);
>       WRITE_BOOL_FIELD(useOr);
> +     WRITE_BOOL_FIELD(isExpr);
> +     WRITE_OID_FIELD(exprtype);
> +     WRITE_OID_FIELD(elemtype);
> +     WRITE_INT_FIELD(elmlen);
> +     WRITE_BOOL_FIELD(elmbyval);
> +     WRITE_CHAR_FIELD(elmalign);
>       WRITE_NODE_FIELD(exprs);
>       WRITE_INTLIST_FIELD(paramIds);
>       WRITE_NODE_FIELD(plan);
> Index: src/backend/nodes/readfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v
> retrieving revision 1.154
> diff -c -r1.154 readfuncs.c
> *** src/backend/nodes/readfuncs.c    6 Jun 2003 15:04:02 -0000    1.154
> --- src/backend/nodes/readfuncs.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 416,421 ****
> --- 416,422 ----
>       READ_UINT_FIELD(agglevelsup);
>       READ_BOOL_FIELD(aggstar);
>       READ_BOOL_FIELD(aggdistinct);
> +     READ_NODE_FIELD(args);
>
>       READ_DONE();
>   }
> ***************
> *** 545,550 ****
> --- 546,552 ----
>
>       READ_ENUM_FIELD(subLinkType, SubLinkType);
>       READ_BOOL_FIELD(useOr);
> +     READ_BOOL_FIELD(isExpr);
>       READ_NODE_FIELD(lefthand);
>       READ_NODE_FIELD(operName);
>       READ_OIDLIST_FIELD(operOids);
> Index: src/backend/optimizer/plan/subselect.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/plan/subselect.c,v
> retrieving revision 1.76
> diff -c -r1.76 subselect.c
> *** src/backend/optimizer/plan/subselect.c    6 Jun 2003 15:04:02 -0000    1.76
> --- src/backend/optimizer/plan/subselect.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 83,89 ****
>
>   static List *convert_sublink_opers(List *lefthand, List *operOids,
>                                      List *targetlist, int rtindex,
> !                                    List **righthandIds);
>   static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
>   static Node *replace_correlation_vars_mutator(Node *node, void *context);
>   static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
> --- 83,89 ----
>
>   static List *convert_sublink_opers(List *lefthand, List *operOids,
>                                      List *targetlist, int rtindex,
> !                                    bool isExpr, List **righthandIds);
>   static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
>   static Node *replace_correlation_vars_mutator(Node *node, void *context);
>   static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
> ***************
> *** 299,304 ****
> --- 299,310 ----
>        */
>       node->subLinkType = slink->subLinkType;
>       node->useOr = slink->useOr;
> +     node->isExpr = slink->isExpr;
> +     node->exprtype = InvalidOid;
> +     node->elemtype = InvalidOid;
> +     node->elmlen = 0;
> +     node->elmbyval = false;
> +     node->elmalign = '\0';
>       node->exprs = NIL;
>       node->paramIds = NIL;
>       node->useHashTable = false;
> ***************
> *** 374,380 ****
>           exprs = convert_sublink_opers(lefthand,
>                                         slink->operOids,
>                                         plan->targetlist,
> !                                       0,
>                                         &node->paramIds);
>           node->setParam = listCopy(node->paramIds);
>           PlannerInitPlan = lappend(PlannerInitPlan, node);
> --- 380,386 ----
>           exprs = convert_sublink_opers(lefthand,
>                                         slink->operOids,
>                                         plan->targetlist,
> !                                       0, node->isExpr,
>                                         &node->paramIds);
>           node->setParam = listCopy(node->paramIds);
>           PlannerInitPlan = lappend(PlannerInitPlan, node);
> ***************
> *** 457,463 ****
>           node->exprs = convert_sublink_opers(lefthand,
>                                               slink->operOids,
>                                               plan->targetlist,
> !                                             0,
>                                               &node->paramIds);
>
>           /*
> --- 463,469 ----
>           node->exprs = convert_sublink_opers(lefthand,
>                                               slink->operOids,
>                                               plan->targetlist,
> !                                             0, node->isExpr,
>                                               &node->paramIds);
>
>           /*
> ***************
> *** 499,505 ****
>   static List *
>   convert_sublink_opers(List *lefthand, List *operOids,
>                         List *targetlist, int rtindex,
> !                       List **righthandIds)
>   {
>       List       *result = NIL;
>       List       *lst;
> --- 505,511 ----
>   static List *
>   convert_sublink_opers(List *lefthand, List *operOids,
>                         List *targetlist, int rtindex,
> !                       bool isExpr, List **righthandIds)
>   {
>       List       *result = NIL;
>       List       *lst;
> ***************
> *** 554,566 ****
>            * are not expecting to have to resolve unknown Params, so
>            * it's okay to pass a null pstate.)
>            */
> !         result = lappend(result,
> !                          make_op_expr(NULL,
> !                                       tup,
> !                                       leftop,
> !                                       rightop,
> !                                       exprType(leftop),
> !                                       te->resdom->restype));
>
>           ReleaseSysCache(tup);
>
> --- 560,597 ----
>            * are not expecting to have to resolve unknown Params, so
>            * it's okay to pass a null pstate.)
>            */
> !         if (!isExpr)
> !         {
> !             result = lappend(result,
> !                              make_op_expr(NULL,
> !                                           tup,
> !                                           leftop,
> !                                           rightop,
> !                                           exprType(leftop),
> !                                           te->resdom->restype));
> !         }
> !         else
> !         {
> !             Oid        exprtype = te->resdom->restype;
> !             Oid        elemtype = get_element_type(exprtype);
> !
> !             if (elemtype != InvalidOid)
> !                 result = lappend(result,
> !                                  make_op_expr(NULL,
> !                                               tup,
> !                                               leftop,
> !                                               rightop,
> !                                               exprType(leftop),
> !                                               elemtype));
> !             else
> !                 result = lappend(result,
> !                                  make_op_expr(NULL,
> !                                               tup,
> !                                               leftop,
> !                                               rightop,
> !                                               exprType(leftop),
> !                                               exprtype));
> !         }
>
>           ReleaseSysCache(tup);
>
> ***************
> *** 671,683 ****
>       /*
>        * The sublink type must be "= ANY" --- that is, an IN operator.
>        * (We require the operator name to be unqualified, which may be
> !      * overly paranoid, or may not be.)
>        */
>       if (sublink->subLinkType != ANY_SUBLINK)
>           return NULL;
>       if (length(sublink->operName) != 1 ||
>           strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
>           return NULL;
>       /*
>        * The sub-select must not refer to any Vars of the parent query.
>        * (Vars of higher levels should be okay, though.)
> --- 702,718 ----
>       /*
>        * The sublink type must be "= ANY" --- that is, an IN operator.
>        * (We require the operator name to be unqualified, which may be
> !      * overly paranoid, or may not be.) It must not be an Expression
> !      * sublink.
>        */
>       if (sublink->subLinkType != ANY_SUBLINK)
>           return NULL;
>       if (length(sublink->operName) != 1 ||
>           strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
>           return NULL;
> +     if (sublink->isExpr)
> +         return NULL;
> +
>       /*
>        * The sub-select must not refer to any Vars of the parent query.
>        * (Vars of higher levels should be okay, though.)
> ***************
> *** 730,736 ****
>       exprs = convert_sublink_opers(sublink->lefthand,
>                                     sublink->operOids,
>                                     subselect->targetList,
> !                                   rtindex,
>                                     &ininfo->sub_targetlist);
>       return (Node *) make_ands_explicit(exprs);
>   }
> --- 765,771 ----
>       exprs = convert_sublink_opers(sublink->lefthand,
>                                     sublink->operOids,
>                                     subselect->targetList,
> !                                   rtindex, sublink->isExpr,
>                                     &ininfo->sub_targetlist);
>       return (Node *) make_ands_explicit(exprs);
>   }
> Index: src/backend/optimizer/util/clauses.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/util/clauses.c,v
> retrieving revision 1.139
> diff -c -r1.139 clauses.c
> *** src/backend/optimizer/util/clauses.c    6 Jun 2003 15:04:02 -0000    1.139
> --- src/backend/optimizer/util/clauses.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 133,138 ****
> --- 133,160 ----
>   }
>
>   /*****************************************************************************
> +  *              FUNCTION clause functions
> +  *****************************************************************************/
> +
> + /*
> +  * make_funcclause
> +  *        Creates a function clause given its function info and argument list.
> +  */
> + Expr *
> + make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
> +                             CoercionForm funcformat, List *funcargs)
> + {
> +     FuncExpr   *expr = makeNode(FuncExpr);
> +
> +     expr->funcid = funcid;
> +     expr->funcresulttype = funcresulttype;
> +     expr->funcretset = funcretset;
> +     expr->funcformat = funcformat;
> +     expr->args = funcargs;
> +     return (Expr *) expr;
> + }
> +
> + /*****************************************************************************
>    *        NOT clause functions
>    *****************************************************************************/
>
> Index: src/backend/parser/gram.y
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/gram.y,v
> retrieving revision 2.417
> diff -c -r2.417 gram.y
> *** src/backend/parser/gram.y    17 Jun 2003 23:12:36 -0000    2.417
> --- src/backend/parser/gram.y    24 Jun 2003 02:24:06 -0000
> ***************
> *** 5490,5495 ****
> --- 5490,5496 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = ANY_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = makeList1(makeString("="));
>                       n->subselect = $3;
> ***************
> *** 5500,5505 ****
> --- 5501,5507 ----
>                       /* Make an IN node */
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = ANY_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = makeList1(makeString("="));
>                       n->subselect = $4;
> ***************
> *** 5511,5516 ****
> --- 5513,5519 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = $3;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = $2;
>                       n->subselect = $4;
> ***************
> *** 5521,5526 ****
> --- 5524,5530 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = MULTIEXPR_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = $1;
>                       n->operName = $2;
>                       n->subselect = $3;
> ***************
> *** 5904,5909 ****
> --- 5908,5914 ----
>                       {
>                               SubLink *n = (SubLink *)$3;
>                               n->subLinkType = ANY_SUBLINK;
> +                             n->isExpr = false;
>                               n->lefthand = makeList1($1);
>                               n->operName = makeList1(makeString("="));
>                               $$ = (Node *)n;
> ***************
> *** 5931,5936 ****
> --- 5936,5942 ----
>                       {
>                           /* Make an IN node */
>                           SubLink *n = (SubLink *)$4;
> +                         n->isExpr = false;
>                           n->subLinkType = ANY_SUBLINK;
>                           n->lefthand = makeList1($1);
>                           n->operName = makeList1(makeString("="));
> ***************
> *** 5957,5967 ****
> --- 5963,6000 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = $3;
> +                     n->isExpr = false;
>                       n->lefthand = makeList1($1);
>                       n->operName = $2;
>                       n->subselect = $4;
>                       $$ = (Node *)n;
>                   }
> +             | a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op
> +                 {
> +                     SubLink *n = makeNode(SubLink);
> +                     SelectStmt *s = makeNode(SelectStmt);
> +                     ResTarget *r = makeNode(ResTarget);
> +
> +                     r->name = NULL;
> +                     r->indirection = NIL;
> +                     r->val = (Node *)$5;
> +
> +                     s->distinctClause = NIL;
> +                     s->targetList = makeList1(r);
> +                     s->into = NULL;
> +                     s->intoColNames = NIL;
> +                     s->fromClause = NIL;
> +                     s->whereClause = NULL;
> +                     s->groupClause = NIL;
> +                     s->havingClause = NULL;
> +
> +                     n->subLinkType = $3;
> +                     n->isExpr = true;
> +                     n->lefthand = makeList1($1);
> +                     n->operName = $2;
> +                     n->subselect = (Node *) s;
> +                     $$ = (Node *)n;
> +                 }
>               | UNIQUE select_with_parens %prec Op
>                   {
>                       /* Not sure how to get rid of the parentheses
> ***************
> *** 6538,6543 ****
> --- 6571,6577 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = EXPR_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = NIL;
>                       n->operName = NIL;
>                       n->subselect = $1;
> ***************
> *** 6547,6552 ****
> --- 6581,6587 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = EXISTS_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = NIL;
>                       n->operName = NIL;
>                       n->subselect = $2;
> ***************
> *** 6556,6561 ****
> --- 6591,6597 ----
>                   {
>                       SubLink *n = makeNode(SubLink);
>                       n->subLinkType = ARRAY_SUBLINK;
> +                     n->isExpr = false;
>                       n->lefthand = NIL;
>                       n->operName = NIL;
>                       n->subselect = $2;
> ***************
> *** 6730,6735 ****
> --- 6766,6772 ----
>   in_expr:    select_with_parens
>                   {
>                       SubLink *n = makeNode(SubLink);
> +                     n->isExpr = false;
>                       n->subselect = $1;
>                       /* other fields will be filled later */
>                       $$ = (Node *)n;
> Index: src/backend/parser/parse_coerce.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v
> retrieving revision 2.97
> diff -c -r2.97 parse_coerce.c
> *** src/backend/parser/parse_coerce.c    26 May 2003 00:11:27 -0000    2.97
> --- src/backend/parser/parse_coerce.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 859,865 ****
>       /* Get the element type based on the array type, if we have one */
>       if (OidIsValid(array_typeid))
>       {
> !         array_typelem = get_element_type(array_typeid);
>           if (!OidIsValid(array_typelem))
>               elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
>                    format_type_be(array_typeid));
> --- 859,869 ----
>       /* Get the element type based on the array type, if we have one */
>       if (OidIsValid(array_typeid))
>       {
> !         if (array_typeid != ANYARRAYOID)
> !             array_typelem = get_element_type(array_typeid);
> !         else
> !             array_typelem = ANYELEMENTOID;
> !
>           if (!OidIsValid(array_typelem))
>               elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
>                    format_type_be(array_typeid));
> ***************
> *** 919,925 ****
>       {
>           if (!OidIsValid(array_typeid))
>           {
> !             array_typeid = get_array_type(elem_typeid);
>               if (!OidIsValid(array_typeid))
>                   elog(ERROR, "Cannot find array type for datatype %s",
>                        format_type_be(elem_typeid));
> --- 923,933 ----
>       {
>           if (!OidIsValid(array_typeid))
>           {
> !             if (elem_typeid != ANYELEMENTOID)
> !                 array_typeid = get_array_type(elem_typeid);
> !             else
> !                 array_typeid = ANYARRAYOID;
> !
>               if (!OidIsValid(array_typeid))
>                   elog(ERROR, "Cannot find array type for datatype %s",
>                        format_type_be(elem_typeid));
> ***************
> *** 1169,1174 ****
> --- 1177,1187 ----
>       /* Somewhat-fast path for domain -> base type case */
>       if (srctype == targettype)
>           return true;
> +
> +     /* Last of the fast-paths: check for matching polymorphic arrays */
> +     if (targettype == ANYARRAYOID)
> +         if (get_element_type(srctype) != InvalidOid)
> +             return true;
>
>       /* Else look in pg_cast */
>       tuple = SearchSysCache(CASTSOURCETARGET,
> Index: src/backend/parser/parse_expr.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_expr.c,v
> retrieving revision 1.148
> diff -c -r1.148 parse_expr.c
> *** src/backend/parser/parse_expr.c    29 Apr 2003 22:13:10 -0000    1.148
> --- src/backend/parser/parse_expr.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 436,441 ****
> --- 436,442 ----
>                       sublink->operName = NIL;
>                       sublink->operOids = NIL;
>                       sublink->useOr = FALSE;
> +                     sublink->isExpr = FALSE;
>                   }
>                   else if (sublink->subLinkType == EXPR_SUBLINK ||
>                            sublink->subLinkType == ARRAY_SUBLINK)
> ***************
> *** 463,468 ****
> --- 464,470 ----
>                       sublink->operName = NIL;
>                       sublink->operOids = NIL;
>                       sublink->useOr = FALSE;
> +                     sublink->isExpr = FALSE;
>                   }
>                   else
>                   {
> ***************
> *** 538,547 ****
>                            * here, because make_subplan() will insert type
>                            * coercion calls if needed.
>                            */
> !                         optup = oper(op,
> !                                      exprType(lexpr),
> !                                      exprType((Node *) tent->expr),
> !                                      false);
>                           opform = (Form_pg_operator) GETSTRUCT(optup);
>
>                           if (opform->oprresult != BOOLOID)
> --- 540,569 ----
>                            * here, because make_subplan() will insert type
>                            * coercion calls if needed.
>                            */
> !                         if (!sublink->isExpr)
> !                         {
> !                             optup = oper(op,
> !                                          exprType(lexpr),
> !                                          exprType((Node *) tent->expr),
> !                                          false);
> !                         }
> !                         else
> !                         {
> !                             Oid        exprtype = exprType((Node *) tent->expr);
> !                             Oid        elemtype = get_element_type(exprtype);
> !
> !                             if (elemtype != InvalidOid)
> !                                 optup = oper(op,
> !                                              exprType(lexpr),
> !                                              elemtype,
> !                                              false);
> !                             else
> !                                 optup = oper(op,
> !                                              exprType(lexpr),
> !                                              exprtype,
> !                                              false);
> !                         }
> !
>                           opform = (Form_pg_operator) GETSTRUCT(optup);
>
>                           if (opform->oprresult != BOOLOID)
> ***************
> *** 743,749 ****
>                           ArrayExpr  *e = (ArrayExpr *) lfirst(element);
>
>                           if (!IsA(e, ArrayExpr))
> !                             elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions");
>                           if (ndims == 0)
>                               ndims = e->ndims;
>                           else if (e->ndims != ndims)
> --- 765,771 ----
>                           ArrayExpr  *e = (ArrayExpr *) lfirst(element);
>
>                           if (!IsA(e, ArrayExpr))
> !                             elog(ERROR, "Multidimensional ARRAY[] must be built from nested array expressions");
>                           if (ndims == 0)
>                               ndims = e->ndims;
>                           else if (e->ndims != ndims)
> Index: src/backend/parser/parse_func.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_func.c,v
> retrieving revision 1.149
> diff -c -r1.149 parse_func.c
> *** src/backend/parser/parse_func.c    6 Jun 2003 15:04:02 -0000    1.149
> --- src/backend/parser/parse_func.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 336,341 ****
> --- 336,342 ----
>           aggref->target = lfirst(fargs);
>           aggref->aggstar = agg_star;
>           aggref->aggdistinct = agg_distinct;
> +         aggref->args = fargs;
>
>           /* parse_agg.c does additional aggregate-specific processing */
>           transformAggregateCall(pstate, aggref);
> Index: src/backend/parser/parse_oper.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_oper.c,v
> retrieving revision 1.64
> diff -c -r1.64 parse_oper.c
> *** src/backend/parser/parse_oper.c    26 May 2003 00:11:27 -0000    1.64
> --- src/backend/parser/parse_oper.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 137,142 ****
> --- 137,169 ----
>   equality_oper(Oid argtype, bool noError)
>   {
>       Operator    optup;
> +     Oid            elem_type = get_element_type(argtype);
> +
> +     if (OidIsValid(elem_type))
> +     {
> +         bool    found = false;
> +         /*
> +          * If the datatype is an array, look for an "=" operator for the
> +          * element datatype.  We require it to be an exact or binary-compatible
> +          * match, since most callers are not prepared to cope with adding any
> +          * run-time type coercion steps.
> +          */
> +         optup = equality_oper(elem_type, true);
> +         if (optup != NULL)
> +         {
> +             found = true;
> +             ReleaseSysCache(optup);
> +         }
> +
> +         if (!found)
> +         {
> +             if (!noError)
> +                 elog(ERROR, "Unable to identify an equality operator for " \
> +                             "array type's element type %s",
> +                              format_type_be(elem_type));
> +             return NULL;
> +         }
> +     }
>
>       /*
>        * Look for an "=" operator for the datatype.  We require it to be
> ***************
> *** 175,180 ****
> --- 202,234 ----
>   ordering_oper(Oid argtype, bool noError)
>   {
>       Operator    optup;
> +     Oid            elem_type = get_element_type(argtype);
> +
> +     if (OidIsValid(elem_type))
> +     {
> +         bool    found = false;
> +         /*
> +          * If the datatype is an array, find the array element type's equality
> +          * operator, and use its lsortop (it *must* be mergejoinable).  We use
> +          * this definition because for sorting and grouping purposes, it's
> +          * important that the equality and ordering operators are consistent.
> +          */
> +         optup = ordering_oper(elem_type, true);
> +         if (optup != NULL)
> +         {
> +             found = true;
> +             ReleaseSysCache(optup);
> +         }
> +
> +         if (!found)
> +         {
> +             if (!noError)
> +                 elog(ERROR, "Unable to identify an ordering operator for " \
> +                             "array type's element type %s",
> +                              format_type_be(elem_type));
> +             return NULL;
> +         }
> +     }
>
>       /*
>        * Find the type's equality operator, and use its lsortop (it *must*
> ***************
> *** 215,220 ****
> --- 269,289 ----
>       Oid            result;
>
>       optup = equality_oper(argtype, false);
> +     result = oprfuncid(optup);
> +     ReleaseSysCache(optup);
> +     return result;
> + }
> +
> + /*
> +  * ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper())
> +  */
> + Oid
> + ordering_oper_funcid(Oid argtype)
> + {
> +     Operator    optup;
> +     Oid            result;
> +
> +     optup = ordering_oper(argtype, false);
>       result = oprfuncid(optup);
>       ReleaseSysCache(optup);
>       return result;
> Index: src/backend/utils/adt/acl.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/acl.c,v
> retrieving revision 1.88
> diff -c -r1.88 acl.c
> *** src/backend/utils/adt/acl.c    11 Jun 2003 09:23:55 -0000    1.88
> --- src/backend/utils/adt/acl.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 427,432 ****
> --- 427,441 ----
>           a1->ai_grantor == a2->ai_grantor;
>   }
>
> + /*
> +  * user-facing version of aclitemeq() for use as the
> +  * aclitem equality operator
> +  */
> + Datum
> + aclitem_eq(PG_FUNCTION_ARGS)
> + {
> +     PG_RETURN_BOOL(aclitemeq(PG_GETARG_ACLITEM_P(0), PG_GETARG_ACLITEM_P(1)));
> + }
>
>   /*
>    * acldefault()  --- create an ACL describing default access permissions
> Index: src/backend/utils/adt/array_userfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/array_userfuncs.c,v
> retrieving revision 1.1
> diff -c -r1.1 array_userfuncs.c
> *** src/backend/utils/adt/array_userfuncs.c    8 Apr 2003 23:20:02 -0000    1.1
> --- src/backend/utils/adt/array_userfuncs.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 18,52 ****
>   #include "utils/lsyscache.h"
>   #include "utils/syscache.h"
>
> -
> - /*-----------------------------------------------------------------------------
> -  * singleton_array :
> -  *        Form a multi-dimensional array given one starting element.
> -  *
> -  * - first argument is the datum with which to build the array
> -  * - second argument is the number of dimensions the array should have;
> -  *     defaults to 1 if no second argument is provided
> -  *----------------------------------------------------------------------------
> -  */
> - Datum
> - singleton_array(PG_FUNCTION_ARGS)
> - {
> -     Oid            elem_type = get_fn_expr_argtype(fcinfo, 0);
> -     int            ndims;
> -
> -     if (elem_type == InvalidOid)
> -         elog(ERROR, "Cannot determine input datatype");
> -
> -     if (PG_NARGS() == 2)
> -         ndims = PG_GETARG_INT32(1);
> -     else
> -         ndims = 1;
> -
> -     PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
> -                                                  PG_GETARG_DATUM(0),
> -                                                  ndims));
> - }
> -
>   /*-----------------------------------------------------------------------------
>    * array_push :
>    *        push an element onto either end of a one-dimensional array
> --- 18,23 ----
> ***************
> *** 70,75 ****
> --- 41,47 ----
>       Oid            arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
>       Oid            arg0_elemid;
>       Oid            arg1_elemid;
> +     ArrayMetaState *my_extra;
>
>       if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
>           elog(ERROR, "array_push: cannot determine input data types");
> ***************
> *** 95,122 ****
>           PG_RETURN_NULL();        /* keep compiler quiet */
>       }
>
> !     /* Sanity check: do we have a one-dimensional array */
> !     if (ARR_NDIM(v) != 1)
> !         elog(ERROR, "Arrays greater than one-dimension are not supported");
> !
> !     lb = ARR_LBOUND(v);
> !     dimv = ARR_DIMS(v);
> !     if (arg0_elemid != InvalidOid)
>       {
> !         /* append newelem */
> !         int    ub = dimv[0] + lb[0] - 1;
> !         indx = ub + 1;
>       }
>       else
>       {
> !         /* prepend newelem */
> !         indx = lb[0] - 1;
>       }
>
> !     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
> !     result = array_set(v, 1, &indx, newelem, -1,
> !                        typlen, typbyval, typalign, &isNull);
>
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
> --- 67,127 ----
>           PG_RETURN_NULL();        /* keep compiler quiet */
>       }
>
> !     if (ARR_NDIM(v) == 1)
>       {
> !         lb = ARR_LBOUND(v);
> !         dimv = ARR_DIMS(v);
> !
> !         if (arg0_elemid != InvalidOid)
> !         {
> !             /* append newelem */
> !             int    ub = dimv[0] + lb[0] - 1;
> !             indx = ub + 1;
> !         }
> !         else
> !         {
> !             /* prepend newelem */
> !             indx = lb[0] - 1;
> !         }
>       }
> +     else if (ARR_NDIM(v) == 0)
> +         indx = 1;
>       else
> +         elog(ERROR, "only empty and one-dimensional arrays are supported");
> +
> +
> +     /*
> +      * We arrange to look up info about element type only once per series
> +      * of calls, assuming the element type doesn't change underneath us.
> +      */
> +     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> +     if (my_extra == NULL)
>       {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
>       }
>
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type */
> !         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typalign = my_extra->typalign;
> !     }
> !
> !     result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval,
> !                                                  typalign, &isNull);
>
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
> ***************
> *** 145,157 ****
>
>       /*
>        * We must have one of the following combinations of inputs:
> !      * 1) two arrays with ndims1 == ndims2
> !      * 2) ndims1 == ndims2 - 1
> !      * 3) ndims1 == ndims2 + 1
>        */
>       ndims1 = ARR_NDIM(v1);
>       ndims2 = ARR_NDIM(v2);
>
>       if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
>           elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
>                       "%d dimensions", ndims1, ndims2);
> --- 150,177 ----
>
>       /*
>        * We must have one of the following combinations of inputs:
> !      * 1) one empty array, and one non-empty array
> !      * 2) both arrays empty
> !      * 3) two arrays with ndims1 == ndims2
> !      * 4) ndims1 == ndims2 - 1
> !      * 5) ndims1 == ndims2 + 1
>        */
>       ndims1 = ARR_NDIM(v1);
>       ndims2 = ARR_NDIM(v2);
>
> +     /*
> +      * short circuit - if one input array is empty, and the other is not,
> +      * we return the non-empty one as the result
> +      *
> +      * if both are empty, return the first one
> +      */
> +     if (ndims1 == 0 && ndims2 > 0)
> +         PG_RETURN_ARRAYTYPE_P(v2);
> +
> +     if (ndims2 == 0)
> +         PG_RETURN_ARRAYTYPE_P(v1);
> +
> +     /* the rest fall into combo 2, 3, or 4 */
>       if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
>           elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
>                       "%d dimensions", ndims1, ndims2);
> ***************
> *** 266,412 ****
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
>
> - /*----------------------------------------------------------------------------
> -  * array_accum :
> -  *        accumulator to build a 1-D array from input values -- this can be used
> -  *        to create custom aggregates.
> -  *
> -  * This function is not marked strict, so we have to be careful about nulls.
> -  *----------------------------------------------------------------------------
> -  */
> - Datum
> - array_accum(PG_FUNCTION_ARGS)
> - {
> -     /* return NULL if both arguments are NULL */
> -     if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
> -         PG_RETURN_NULL();
> -
> -     /* create a new 1-D array from the new element if the array is NULL */
> -     if (PG_ARGISNULL(0))
> -     {
> -         Oid            tgt_type = get_fn_expr_rettype(fcinfo);
> -         Oid            tgt_elem_type;
> -
> -         if (tgt_type == InvalidOid)
> -             elog(ERROR, "Cannot determine target array type");
> -         tgt_elem_type = get_element_type(tgt_type);
> -         if (tgt_elem_type == InvalidOid)
> -             elog(ERROR, "Target type is not an array");
> -
> -         PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
> -                                                      PG_GETARG_DATUM(1),
> -                                                      1));
> -     }
> -
> -     /* return the array if the new element is NULL */
> -     if (PG_ARGISNULL(1))
> -         PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0));
> -
> -     /*
> -      * Otherwise this is equivalent to array_push.  We hack the call a little
> -      * so that array_push can see the fn_expr information.
> -      */
> -     return array_push(fcinfo);
> - }
> -
> - /*-----------------------------------------------------------------------------
> -  * array_assign :
> -  *        assign an element of an array to a new value and return the
> -  *        redefined array
> -  *----------------------------------------------------------------------------
> -  */
> - Datum
> - array_assign(PG_FUNCTION_ARGS)
> - {
> -     ArrayType  *v;
> -     int            idx_to_chg;
> -     Datum        newelem;
> -     int           *dimv,
> -                *lb, ub;
> -     ArrayType  *result;
> -     bool        isNull;
> -     Oid            element_type;
> -     int16        typlen;
> -     bool        typbyval;
> -     char        typalign;
> -
> -     v = PG_GETARG_ARRAYTYPE_P(0);
> -     idx_to_chg = PG_GETARG_INT32(1);
> -     newelem = PG_GETARG_DATUM(2);
> -
> -     /* Sanity check: do we have a one-dimensional array */
> -     if (ARR_NDIM(v) != 1)
> -         elog(ERROR, "Arrays greater than one-dimension are not supported");
> -
> -     lb = ARR_LBOUND(v);
> -     dimv = ARR_DIMS(v);
> -     ub = dimv[0] + lb[0] - 1;
> -     if (idx_to_chg < lb[0] || idx_to_chg > ub)
> -         elog(ERROR, "Cannot alter nonexistent array element: %d", idx_to_chg);
> -
> -     element_type = ARR_ELEMTYPE(v);
> -     /* Sanity check: do we have a non-zero element type */
> -     if (element_type == 0)
> -         elog(ERROR, "Invalid array element type: %u", element_type);
> -
> -     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
> -
> -     result = array_set(v, 1, &idx_to_chg, newelem, -1,
> -                        typlen, typbyval, typalign, &isNull);
> -
> -     PG_RETURN_ARRAYTYPE_P(result);
> - }
> -
> - /*-----------------------------------------------------------------------------
> -  * array_subscript :
> -  *        return specific element of an array
> -  *----------------------------------------------------------------------------
> -  */
> - Datum
> - array_subscript(PG_FUNCTION_ARGS)
> - {
> -     ArrayType  *v;
> -     int            idx;
> -     int           *dimv,
> -                *lb, ub;
> -     Datum        result;
> -     bool        isNull;
> -     Oid            element_type;
> -     int16        typlen;
> -     bool        typbyval;
> -     char        typalign;
> -
> -     v = PG_GETARG_ARRAYTYPE_P(0);
> -     idx = PG_GETARG_INT32(1);
> -
> -     /* Sanity check: do we have a one-dimensional array */
> -     if (ARR_NDIM(v) != 1)
> -         elog(ERROR, "Arrays greater than one-dimension are not supported");
> -
> -     lb = ARR_LBOUND(v);
> -     dimv = ARR_DIMS(v);
> -     ub = dimv[0] + lb[0] - 1;
> -     if (idx < lb[0] || idx > ub)
> -         elog(ERROR, "Cannot return nonexistent array element: %d", idx);
> -
> -     element_type = ARR_ELEMTYPE(v);
> -     /* Sanity check: do we have a non-zero element type */
> -     if (element_type == 0)
> -         elog(ERROR, "Invalid array element type: %u", element_type);
> -
> -     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
> -
> -     result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);
> -
> -     PG_RETURN_DATUM(result);
> - }
>
>   /*
> !  * actually does the work for singleton_array(), and array_accum() if it is
> !  * given a null input array.
>    */
>   ArrayType *
> ! create_singleton_array(Oid element_type, Datum element, int ndims)
>   {
>       Datum    dvalues[1];
>       int16    typlen;
> --- 286,300 ----
>       PG_RETURN_ARRAYTYPE_P(result);
>   }
>
>
>   /*
> !  * used by text_to_array() in varlena.c
>    */
>   ArrayType *
> ! create_singleton_array(FunctionCallInfo fcinfo,
> !                        Oid element_type,
> !                        Datum element,
> !                        int ndims)
>   {
>       Datum    dvalues[1];
>       int16    typlen;
> ***************
> *** 415,420 ****
> --- 303,309 ----
>       int        dims[MAXDIM];
>       int        lbs[MAXDIM];
>       int        i;
> +     ArrayMetaState *my_extra;
>
>       if (element_type == 0)
>           elog(ERROR, "Invalid array element type: %u", element_type);
> ***************
> *** 429,435 ****
>           lbs[i] = 1;
>       }
>
> !     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
>       return construct_md_array(dvalues, ndims, dims, lbs, element_type,
>                                 typlen, typbyval, typalign);
> --- 318,352 ----
>           lbs[i] = 1;
>       }
>
> !     /*
> !      * We arrange to look up info about element type only once per series
> !      * of calls, assuming the element type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type */
> !         get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typalign = my_extra->typalign;
> !     }
>
>       return construct_md_array(dvalues, ndims, dims, lbs, element_type,
>                                 typlen, typbyval, typalign);
> Index: src/backend/utils/adt/arrayfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v
> retrieving revision 1.89
> diff -c -r1.89 arrayfuncs.c
> *** src/backend/utils/adt/arrayfuncs.c    9 May 2003 23:01:45 -0000    1.89
> --- src/backend/utils/adt/arrayfuncs.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 21,28 ****
> --- 21,30 ----
>   #include "catalog/pg_type.h"
>   #include "libpq/pqformat.h"
>   #include "parser/parse_coerce.h"
> + #include "parser/parse_oper.h"
>   #include "utils/array.h"
>   #include "utils/builtins.h"
> + #include "utils/datum.h"
>   #include "utils/memutils.h"
>   #include "utils/lsyscache.h"
>   #include "utils/syscache.h"
> ***************
> *** 70,85 ****
>
>   #define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)
>
> - /* I/O function selector for system_cache_lookup */
> - typedef enum IOFuncSelector
> - {
> -     IOFunc_input,
> -     IOFunc_output,
> -     IOFunc_receive,
> -     IOFunc_send
> - } IOFuncSelector;
> -
> -
>   static int    ArrayCount(char *str, int *dim, char typdelim);
>   static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
>                FmgrInfo *inputproc, Oid typelem, int32 typmod,
> --- 72,77 ----
> ***************
> *** 93,102 ****
>   static void CopyArrayEls(char *p, Datum *values, int nitems,
>                int typlen, bool typbyval, char typalign,
>                bool freedata);
> - static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
> -                                 int *typlen, bool *typbyval,
> -                                 char *typdelim, Oid *typelem,
> -                                 Oid *proc, char *typalign);
>   static Datum ArrayCast(char *value, bool byval, int len);
>   static int ArrayCastAndSet(Datum src,
>                   int typlen, bool typbyval, char typalign,
> --- 85,90 ----
> ***************
> *** 119,125 ****
>                      char *destPtr,
>                      int *st, int *endp, char *srcPtr,
>                      int typlen, bool typbyval, char typalign);
> !
>
>   /*---------------------------------------------------------------------
>    * array_in :
> --- 107,113 ----
>                      char *destPtr,
>                      int *st, int *endp, char *srcPtr,
>                      int typlen, bool typbyval, char typalign);
> ! static int array_cmp(FunctionCallInfo fcinfo);
>
>   /*---------------------------------------------------------------------
>    * array_in :
> ***************
> *** 154,165 ****
>                   dim[MAXDIM],
>                   lBound[MAXDIM];
>       char        typalign;
>
> !     /* Get info about element type, including its input conversion proc */
> !     system_cache_lookup(element_type, IOFunc_input,
> !                         &typlen, &typbyval, &typdelim,
> !                         &typelem, &typinput, &typalign);
> !     fmgr_info(typinput, &inputproc);
>
>       /* Make a modifiable copy of the input */
>       /* XXX why are we allocating an extra 2 bytes here? */
> --- 142,190 ----
>                   dim[MAXDIM],
>                   lBound[MAXDIM];
>       char        typalign;
> +     ArrayMetaState *my_extra;
>
> !     /*
> !      * We arrange to look up info about element type, including its input
> !      * conversion proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its input conversion proc */
> !         get_type_metadata(element_type, IOFunc_input,
> !                             &typlen, &typbyval, &typdelim,
> !                             &typelem, &typinput, &typalign);
> !         fmgr_info(typinput, &inputproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typinput;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = inputproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typinput = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         inputproc = my_extra->proc;
> !     }
>
>       /* Make a modifiable copy of the input */
>       /* XXX why are we allocating an extra 2 bytes here? */
> ***************
> *** 636,647 ****
>                   indx[MAXDIM];
>       int            ndim,
>                  *dim;
>
>       element_type = ARR_ELEMTYPE(v);
> !     system_cache_lookup(element_type, IOFunc_output,
> !                         &typlen, &typbyval, &typdelim,
> !                         &typelem, &typoutput, &typalign);
> !     fmgr_info(typoutput, &outputproc);
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> --- 661,711 ----
>                   indx[MAXDIM];
>       int            ndim,
>                  *dim;
> +     ArrayMetaState *my_extra;
>
>       element_type = ARR_ELEMTYPE(v);
> !
> !     /*
> !      * We arrange to look up info about element type, including its input
> !      * conversion proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its output conversion proc */
> !         get_type_metadata(element_type, IOFunc_output,
> !                             &typlen, &typbyval, &typdelim,
> !                             &typelem, &typoutput, &typalign);
> !         fmgr_info(typoutput, &outputproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typoutput;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = outputproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typoutput = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         outputproc = my_extra->proc;
> !     }
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> ***************
> *** 800,805 ****
> --- 864,870 ----
>                   dim[MAXDIM],
>                   lBound[MAXDIM];
>       char        typalign;
> +     ArrayMetaState *my_extra;
>
>       /* Get the array header information */
>       ndim = pq_getmsgint(buf, 4);
> ***************
> *** 831,844 ****
>           PG_RETURN_ARRAYTYPE_P(retval);
>       }
>
> !     /* Get info about element type, including its receive conversion proc */
> !     system_cache_lookup(element_type, IOFunc_receive,
> !                         &typlen, &typbyval, &typdelim,
> !                         &typelem, &typreceive, &typalign);
> !     if (!OidIsValid(typreceive))
> !         elog(ERROR, "No binary input function available for type %s",
> !              format_type_be(element_type));
> !     fmgr_info(typreceive, &receiveproc);
>
>       dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
>                                 typlen, typbyval, typalign,
> --- 896,945 ----
>           PG_RETURN_ARRAYTYPE_P(retval);
>       }
>
> !     /*
> !      * We arrange to look up info about element type, including its receive
> !      * conversion proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its receive conversion proc */
> !         get_type_metadata(element_type, IOFunc_receive,
> !                             &typlen, &typbyval, &typdelim,
> !                             &typelem, &typreceive, &typalign);
> !         if (!OidIsValid(typreceive))
> !             elog(ERROR, "No binary input function available for type %s",
> !                  format_type_be(element_type));
> !         fmgr_info(typreceive, &receiveproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typreceive;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = receiveproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typreceive = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         receiveproc = my_extra->proc;
> !     }
>
>       dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
>                                 typlen, typbyval, typalign,
> ***************
> *** 976,990 ****
>       int            ndim,
>                  *dim;
>       StringInfoData buf;
>
>       /* Get information about the element type and the array dimensions */
>       element_type = ARR_ELEMTYPE(v);
> !     system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
> !                         &typdelim, &typelem, &typsend, &typalign);
> !     if (!OidIsValid(typsend))
> !         elog(ERROR, "No binary output function available for type %s",
> !              format_type_be(element_type));
> !     fmgr_info(typsend, &sendproc);
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> --- 1077,1130 ----
>       int            ndim,
>                  *dim;
>       StringInfoData buf;
> +     ArrayMetaState *my_extra;
>
>       /* Get information about the element type and the array dimensions */
>       element_type = ARR_ELEMTYPE(v);
> !
> !     /*
> !      * We arrange to look up info about element type, including its send
> !      * proc only once per series of calls, assuming the element
> !      * type doesn't change underneath us.
> !      */
> !     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         /* Get info about element type, including its send proc */
> !         get_type_metadata(element_type, IOFunc_send, &typlen, &typbyval,
> !                             &typdelim, &typelem, &typsend, &typalign);
> !         if (!OidIsValid(typsend))
> !             elog(ERROR, "No binary output function available for type %s",
> !                  format_type_be(element_type));
> !         fmgr_info(typsend, &sendproc);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = typsend;
> !         my_extra->typalign = typalign;
> !         my_extra->proc = sendproc;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typdelim = my_extra->typdelim;
> !         typelem = my_extra->typelem;
> !         typsend = my_extra->typiofunc;
> !         typalign = my_extra->typalign;
> !         sendproc = my_extra->proc;
> !     }
>
>       ndim = ARR_NDIM(v);
>       dim = ARR_DIMS(v);
> ***************
> *** 1476,1481 ****
> --- 1616,1641 ----
>       array = DatumGetArrayTypeP(PointerGetDatum(array));
>
>       ndim = ARR_NDIM(array);
> +
> +     /*
> +      * if number of dims is zero, i.e. an empty array, create an array
> +      * with nSubscripts dimensions, and set the lower bounds to the supplied
> +      * subscripts
> +      */
> +     if (ndim == 0)
> +     {
> +         Oid        elmtype = ARR_ELEMTYPE(array);
> +
> +         for (i = 0; i < nSubscripts; i++)
> +         {
> +             dim[i] = 1;
> +             lb[i] = indx[i];
> +         }
> +
> +         return construct_md_array(&dataValue, nSubscripts, dim, lb, elmtype,
> +                                                 elmlen, elmbyval, elmalign);
> +     }
> +
>       if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
>           elog(ERROR, "Invalid array subscripts");
>
> ***************
> *** 1632,1637 ****
> --- 1792,1822 ----
>       /* note: we assume srcArray contains no toasted elements */
>
>       ndim = ARR_NDIM(array);
> +
> +     /*
> +      * if number of dims is zero, i.e. an empty array, create an array
> +      * with nSubscripts dimensions, and set the upper and lower bounds
> +      * to the supplied subscripts
> +      */
> +     if (ndim == 0)
> +     {
> +         Datum  *dvalues;
> +         int        nelems;
> +         Oid        elmtype = ARR_ELEMTYPE(array);
> +
> +         deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
> +                                                         &dvalues, &nelems);
> +
> +         for (i = 0; i < nSubscripts; i++)
> +         {
> +             dim[i] = 1 + upperIndx[i] - lowerIndx[i];
> +             lb[i] = lowerIndx[i];
> +         }
> +
> +         return construct_md_array(dvalues, nSubscripts, dim, lb, elmtype,
> +                                                  elmlen, elmbyval, elmalign);
> +     }
> +
>       if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
>           elog(ERROR, "Invalid array subscripts");
>
> ***************
> *** 1811,1816 ****
> --- 1996,2008 ----
>       Oid            typelem;
>       Oid            proc;
>       char       *s;
> +     typedef struct {
> +         ArrayMetaState *inp_extra;
> +         ArrayMetaState *ret_extra;
> +     } am_extra;
> +     am_extra  *my_extra;
> +     ArrayMetaState *inp_extra;
> +     ArrayMetaState *ret_extra;
>
>       /* Get input array */
>       if (fcinfo->nargs < 1)
> ***************
> *** 1829,1839 ****
>       if (nitems <= 0)
>           PG_RETURN_ARRAYTYPE_P(v);
>
> !     /* Lookup source and result types. Unneeded variables are reused. */
> !     system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
> !                         &typdelim, &typelem, &proc, &inp_typalign);
> !     system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
> !                         &typdelim, &typelem, &proc, &typalign);
>
>       /* Allocate temporary array for new values */
>       values = (Datum *) palloc(nitems * sizeof(Datum));
> --- 2021,2101 ----
>       if (nitems <= 0)
>           PG_RETURN_ARRAYTYPE_P(v);
>
> !     /*
> !      * We arrange to look up info about input and return element types only
> !      * once per series of calls, assuming the element type doesn't change
> !      * underneath us.
> !      */
> !     my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(am_extra));
> !         my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
> !
> !         my_extra->inp_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         inp_extra = my_extra->inp_extra;
> !         inp_extra->element_type = InvalidOid;
> !
> !         my_extra->ret_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> !                                                      sizeof(ArrayMetaState));
> !         ret_extra = my_extra->ret_extra;
> !         ret_extra->element_type = InvalidOid;
> !     }
> !     else
> !     {
> !         inp_extra = my_extra->inp_extra;
> !         ret_extra = my_extra->ret_extra;
> !     }
> !
> !     if (inp_extra->element_type != inpType)
> !     {
> !         /* Lookup source and result types. Unneeded variables are reused. */
> !         get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
> !                             &typdelim, &typelem, &proc, &inp_typalign);
> !
> !         inp_extra->element_type = inpType;
> !         inp_extra->typlen = inp_typlen;
> !         inp_extra->typbyval = inp_typbyval;
> !         inp_extra->typdelim = typdelim;
> !         inp_extra->typelem = typelem;
> !         inp_extra->typiofunc = proc;
> !         inp_extra->typalign = inp_typalign;
> !     }
> !     else
> !     {
> !         inp_typlen = inp_extra->typlen;
> !         inp_typbyval = inp_extra->typbyval;
> !         typdelim = inp_extra->typdelim;
> !         typelem = inp_extra->typelem;
> !         proc = inp_extra->typiofunc;
> !         inp_typalign = inp_extra->typalign;
> !     }
> !
> !     if (ret_extra->element_type != retType)
> !     {
> !         /* Lookup source and result types. Unneeded variables are reused. */
> !         get_type_metadata(retType, IOFunc_input, &typlen, &typbyval,
> !                             &typdelim, &typelem, &proc, &typalign);
> !
> !         ret_extra->element_type = retType;
> !         ret_extra->typlen = typlen;
> !         ret_extra->typbyval = typbyval;
> !         ret_extra->typdelim = typdelim;
> !         ret_extra->typelem = typelem;
> !         ret_extra->typiofunc = proc;
> !         ret_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = ret_extra->typlen;
> !         typbyval = ret_extra->typbyval;
> !         typdelim = ret_extra->typdelim;
> !         typelem = ret_extra->typelem;
> !         proc = ret_extra->typiofunc;
> !         typalign = ret_extra->typalign;
> !     }
>
>       /* Allocate temporary array for new values */
>       values = (Datum *) palloc(nitems * sizeof(Datum));
> ***************
> *** 2049,2056 ****
>    *          compares two arrays for equality
>    * result :
>    *          returns true if the arrays are equal, false otherwise.
> -  *
> -  * XXX bitwise equality is pretty bogus ...
>    *-----------------------------------------------------------------------------
>    */
>   Datum
> --- 2311,2316 ----
> ***************
> *** 2058,2069 ****
>   {
>       ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
>       ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
>       bool        result = true;
>
> !     if (ARR_SIZE(array1) != ARR_SIZE(array2))
> !         result = false;
> !     else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
>           result = false;
>
>       /* Avoid leaking memory when handed toasted input. */
>       PG_FREE_IF_COPY(array1, 0);
> --- 2318,2435 ----
>   {
>       ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
>       ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
> +     char       *p1 = (char *) ARR_DATA_PTR(array1);
> +     char       *p2 = (char *) ARR_DATA_PTR(array2);
> +     int            ndims1 = ARR_NDIM(array1);
> +     int            ndims2 = ARR_NDIM(array2);
> +     int           *dims1 = ARR_DIMS(array1);
> +     int           *dims2 = ARR_DIMS(array2);
> +     int            nitems1 = ArrayGetNItems(ndims1, dims1);
> +     int            nitems2 = ArrayGetNItems(ndims2, dims2);
> +     Oid            element_type = ARR_ELEMTYPE(array1);
> +     FmgrInfo   *ae_fmgr_info = fcinfo->flinfo;
>       bool        result = true;
> +     int            typlen;
> +     bool        typbyval;
> +     char        typdelim;
> +     Oid            typelem;
> +     char        typalign;
> +     Oid            typiofunc;
> +     int            i;
> +     ArrayMetaState *my_extra;
> +     FunctionCallInfoData locfcinfo;
>
> !     /* fast path if the arrays do not have the same number of elements */
> !     if (nitems1 != nitems2)
>           result = false;
> +     else
> +     {
> +         /*
> +          * We arrange to look up the equality function only once per series of
> +          * calls, assuming the element type doesn't change underneath us.
> +          */
> +         my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
> +         if (my_extra == NULL)
> +         {
> +             ae_fmgr_info->fn_extra = MemoryContextAlloc(ae_fmgr_info->fn_mcxt,
> +                                                      sizeof(ArrayMetaState));
> +             my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
> +             my_extra->element_type = InvalidOid;
> +         }
> +
> +         if (my_extra->element_type != element_type)
> +         {
> +             Oid        opfuncid = equality_oper_funcid(element_type);
> +
> +             if (OidIsValid(opfuncid))
> +                 fmgr_info_cxt(opfuncid, &my_extra->proc, ae_fmgr_info->fn_mcxt);
> +             else
> +                 elog(ERROR,
> +                      "array_eq: cannot find equality operator for type: %u",
> +                      element_type);
> +
> +             get_type_metadata(element_type, IOFunc_output,
> +                               &typlen, &typbyval, &typdelim,
> +                               &typelem, &typiofunc, &typalign);
> +
> +             my_extra->element_type = element_type;
> +             my_extra->typlen = typlen;
> +             my_extra->typbyval = typbyval;
> +             my_extra->typdelim = typdelim;
> +             my_extra->typelem = typelem;
> +             my_extra->typiofunc = typiofunc;
> +             my_extra->typalign = typalign;
> +         }
> +         else
> +         {
> +             typlen = my_extra->typlen;
> +             typbyval = my_extra->typbyval;
> +             typdelim = my_extra->typdelim;
> +             typelem = my_extra->typelem;
> +             typiofunc = my_extra->typiofunc;
> +             typalign = my_extra->typalign;
> +         }
> +
> +         /*
> +          * apply the operator to each pair of array elements.
> +          */
> +         MemSet(&locfcinfo, 0, sizeof(locfcinfo));
> +         locfcinfo.flinfo = &my_extra->proc;
> +         locfcinfo.nargs = 2;
> +
> +         /* Loop over source data */
> +         for (i = 0; i < nitems1; i++)
> +         {
> +             Datum    elt1;
> +             Datum    elt2;
> +             bool    oprresult;
> +
> +             /* Get element pair */
> +             elt1 = fetch_att(p1, typbyval, typlen);
> +             elt2 = fetch_att(p2, typbyval, typlen);
> +
> +             p1 = att_addlength(p1, typlen, PointerGetDatum(p1));
> +             p1 = (char *) att_align(p1, typalign);
> +
> +             p2 = att_addlength(p2, typlen, PointerGetDatum(p2));
> +             p2 = (char *) att_align(p2, typalign);
> +
> +             /*
> +              * Apply the operator to the element pair
> +              */
> +             locfcinfo.arg[0] = elt1;
> +             locfcinfo.arg[1] = elt2;
> +             locfcinfo.argnull[0] = false;
> +             locfcinfo.argnull[1] = false;
> +             locfcinfo.isnull = false;
> +             oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
> +             if (!oprresult)
> +             {
> +                 result = false;
> +                 break;
> +             }
> +         }
> +     }
>
>       /* Avoid leaking memory when handed toasted input. */
>       PG_FREE_IF_COPY(array1, 0);
> ***************
> *** 2073,2125 ****
>   }
>
>
> ! /***************************************************************************/
> ! /******************|          Support  Routines              |*****************/
> ! /***************************************************************************/
>
> ! static void
> ! system_cache_lookup(Oid element_type,
> !                     IOFuncSelector which_func,
> !                     int *typlen,
> !                     bool *typbyval,
> !                     char *typdelim,
> !                     Oid *typelem,
> !                     Oid *proc,
> !                     char *typalign)
> ! {
> !     HeapTuple    typeTuple;
> !     Form_pg_type typeStruct;
> !
> !     typeTuple = SearchSysCache(TYPEOID,
> !                                ObjectIdGetDatum(element_type),
> !                                0, 0, 0);
> !     if (!HeapTupleIsValid(typeTuple))
> !         elog(ERROR, "cache lookup failed for type %u", element_type);
> !     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
> !
> !     *typlen = typeStruct->typlen;
> !     *typbyval = typeStruct->typbyval;
> !     *typdelim = typeStruct->typdelim;
> !     *typelem = typeStruct->typelem;
> !     *typalign = typeStruct->typalign;
> !     switch (which_func)
> !     {
> !         case IOFunc_input:
> !             *proc = typeStruct->typinput;
> !             break;
> !         case IOFunc_output:
> !             *proc = typeStruct->typoutput;
> !             break;
> !         case IOFunc_receive:
> !             *proc = typeStruct->typreceive;
> !             break;
> !         case IOFunc_send:
> !             *proc = typeStruct->typsend;
> !             break;
>       }
> !     ReleaseSysCache(typeTuple);
>   }
>
>   /*
>    * Fetch array element at pointer, converted correctly to a Datum
>    */
> --- 2439,2628 ----
>   }
>
>
> ! /*-----------------------------------------------------------------------------
> !  * array-array bool operators:
> !  *        Given two arrays, iterate comparison operators
> !  *        over the array. Uses logic similar to text comparison
> !  *        functions, except element-by-element instead of
> !  *        character-by-character.
> !  *----------------------------------------------------------------------------
> !  */
> ! Datum
> ! array_ne(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
> ! }
>
> ! Datum
> ! array_lt(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
> ! }
> !
> ! Datum
> ! array_gt(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
> ! }
> !
> ! Datum
> ! array_le(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
> ! }
> !
> ! Datum
> ! array_ge(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
> ! }
> !
> ! Datum
> ! btarraycmp(PG_FUNCTION_ARGS)
> ! {
> !     PG_RETURN_INT32(array_cmp(fcinfo));
> ! }
> !
> ! /*
> !  * array_cmp()
> !  * Internal comparison function for arrays.
> !  *
> !  * Returns -1, 0 or 1
> !  */
> ! static int
> ! array_cmp(FunctionCallInfo fcinfo)
> ! {
> !     ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
> !     ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
> !     FmgrInfo   *ac_fmgr_info = fcinfo->flinfo;
> !     Datum        opresult;
> !     int            result = 0;
> !     Oid            element_type = InvalidOid;
> !     int            typlen;
> !     bool        typbyval;
> !     char        typdelim;
> !     Oid            typelem;
> !     char        typalign;
> !     Oid            typiofunc;
> !     Datum       *dvalues1;
> !     int            nelems1;
> !     Datum       *dvalues2;
> !     int            nelems2;
> !     int            min_nelems;
> !     int            i;
> !     typedef struct
> !     {
> !         Oid                element_type;
> !         int                typlen;
> !         bool            typbyval;
> !         char            typdelim;
> !         Oid                typelem;
> !         Oid                typiofunc;
> !         char            typalign;
> !         FmgrInfo        eqproc;
> !         FmgrInfo        ordproc;
> !     } ac_extra;
> !     ac_extra *my_extra;
> !
> !     element_type = ARR_ELEMTYPE(array1);
> !
> !     /*
> !      * We arrange to look up the element type operator function only once
> !      * per series of calls, assuming the element type and opname don't
> !      * change underneath us.
> !      */
> !     my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
> !     if (my_extra == NULL)
> !     {
> !         ac_fmgr_info->fn_extra = MemoryContextAlloc(ac_fmgr_info->fn_mcxt,
> !                                                          sizeof(ac_extra));
> !         my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
> !         my_extra->element_type = InvalidOid;
> !     }
> !
> !     if (my_extra->element_type != element_type)
> !     {
> !         Oid        eqfuncid = equality_oper_funcid(element_type);
> !         Oid        ordfuncid = ordering_oper_funcid(element_type);
> !
> !         fmgr_info_cxt(eqfuncid, &my_extra->eqproc, ac_fmgr_info->fn_mcxt);
> !         fmgr_info_cxt(ordfuncid, &my_extra->ordproc, ac_fmgr_info->fn_mcxt);
> !
> !         if (my_extra->eqproc.fn_nargs != 2)
> !             elog(ERROR, "Equality operator does not take 2 arguments: %u",
> !                                                                  eqfuncid);
> !         if (my_extra->ordproc.fn_nargs != 2)
> !             elog(ERROR, "Ordering operator does not take 2 arguments: %u",
> !                                                                  ordfuncid);
> !
> !         get_type_metadata(element_type, IOFunc_output,
> !                           &typlen, &typbyval, &typdelim,
> !                           &typelem, &typiofunc, &typalign);
> !
> !         my_extra->element_type = element_type;
> !         my_extra->typlen = typlen;
> !         my_extra->typbyval = typbyval;
> !         my_extra->typdelim = typdelim;
> !         my_extra->typelem = typelem;
> !         my_extra->typiofunc = InvalidOid;
> !         my_extra->typalign = typalign;
> !     }
> !     else
> !     {
> !         typlen = my_extra->typlen;
> !         typbyval = my_extra->typbyval;
> !         typalign = my_extra->typalign;
> !     }
> !
> !     /* extract a C array of arg array datums */
> !     deconstruct_array(array1, element_type, typlen, typbyval, typalign,
> !                                                     &dvalues1, &nelems1);
> !
> !     deconstruct_array(array2, element_type, typlen, typbyval, typalign,
> !                                                     &dvalues2, &nelems2);
> !
> !     min_nelems = Min(nelems1, nelems2);
> !     for (i = 0; i < min_nelems; i++)
> !     {
> !         /* are they equal */
> !         opresult = FunctionCall2(&my_extra->eqproc,
> !                                  dvalues1[i], dvalues2[i]);
> !
> !         if (!DatumGetBool(opresult))
> !         {
> !             /* nope, see if arg1 is less than arg2 */
> !             opresult = FunctionCall2(&my_extra->ordproc,
> !                                      dvalues1[i], dvalues2[i]);
> !             if (DatumGetBool(opresult))
> !             {
> !                 /* arg1 is less than arg2 */
> !                 result = -1;
> !                 break;
> !             }
> !             else
> !             {
> !                 /* arg1 is greater than arg2 */
> !                 result = 1;
> !                 break;
> !             }
> !         }
>       }
> !
> !     if ((result == 0) && (nelems1 != nelems2))
> !         result = (nelems1 < nelems2) ? -1 : 1;
> !
> !     /* Avoid leaking memory when handed toasted input. */
> !     PG_FREE_IF_COPY(array1, 0);
> !     PG_FREE_IF_COPY(array2, 1);
> !
> !     return result;
>   }
>
> +
> + /***************************************************************************/
> + /******************|          Support  Routines              |*****************/
> + /***************************************************************************/
> +
>   /*
>    * Fetch array element at pointer, converted correctly to a Datum
>    */
> ***************
> *** 2423,2428 ****
> --- 2926,2943 ----
>           if (tgt_elem_type == InvalidOid)
>               elog(ERROR, "Target type is not an array");
>
> +         /*
> +          * We don't deal with domain constraints yet, so bail out.
> +          * This isn't currently a problem, because we also don't
> +          * support arrays of domain type elements either. But in the
> +          * future we might. At that point consideration should be given
> +          * to removing the check below and adding a domain constraints
> +          * check to the coercion.
> +          */
> +         if (getBaseType(tgt_elem_type) != tgt_elem_type)
> +             elog(ERROR, "array coercion to domain type elements not " \
> +                         "currently supported");
> +
>           if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
>                                      COERCION_EXPLICIT, &funcId))
>           {
> ***************
> *** 2439,2448 ****
>       }
>
>       /*
> !      * If it's binary-compatible, return the array unmodified.
>        */
>       if (my_extra->coerce_finfo.fn_oid == InvalidOid)
> !         PG_RETURN_ARRAYTYPE_P(src);
>
>       /*
>        * Use array_map to apply the function to each array element.
> --- 2954,2969 ----
>       }
>
>       /*
> !      * If it's binary-compatible, modify the element type in the array header,
> !      * but otherwise leave the array as we received it.
>        */
>       if (my_extra->coerce_finfo.fn_oid == InvalidOid)
> !     {
> !         ArrayType  *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
> !
> !         ARR_ELEMTYPE(result) = my_extra->desttype;
> !         PG_RETURN_ARRAYTYPE_P(result);
> !     }
>
>       /*
>        * Use array_map to apply the function to each array element.
> ***************
> *** 2453,2456 ****
> --- 2974,3092 ----
>       locfcinfo.arg[0] = PointerGetDatum(src);
>
>       return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
> + }
> +
> + /*
> +  * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
> +  *
> +  *    astate is working state (NULL on first call)
> +  *    rcontext is where to keep working state
> +  */
> + ArrayBuildState *
> + accumArrayResult(ArrayBuildState *astate,
> +                  Datum dvalue, bool disnull,
> +                  Oid element_type,
> +                  MemoryContext rcontext)
> + {
> +     MemoryContext arr_context,
> +                   oldcontext;
> +
> +     if (astate == NULL)
> +     {
> +         /* First time through --- initialize */
> +
> +         /* Make a temporary context to hold all the junk */
> +         arr_context = AllocSetContextCreate(rcontext,
> +                                             "accumArrayResult",
> +                                             ALLOCSET_DEFAULT_MINSIZE,
> +                                             ALLOCSET_DEFAULT_INITSIZE,
> +                                             ALLOCSET_DEFAULT_MAXSIZE);
> +         oldcontext = MemoryContextSwitchTo(arr_context);
> +         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
> +         astate->mcontext = arr_context;
> +         astate->dvalues = (Datum *)
> +             palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
> +         astate->nelems = 0;
> +         astate->element_type = element_type;
> +         get_typlenbyvalalign(element_type,
> +                              &astate->typlen,
> +                              &astate->typbyval,
> +                              &astate->typalign);
> +     }
> +     else
> +     {
> +         oldcontext = MemoryContextSwitchTo(astate->mcontext);
> +         Assert(astate->element_type == element_type);
> +         /* enlarge dvalues[] if needed */
> +         if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
> +             astate->dvalues = (Datum *)
> +                 repalloc(astate->dvalues,
> +                          (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
> +     }
> +
> +     if (disnull)
> +         elog(ERROR, "NULL elements not allowed in Arrays");
> +
> +     /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
> +     astate->dvalues[astate->nelems++] =
> +         datumCopy(dvalue, astate->typbyval, astate->typlen);
> +
> +     MemoryContextSwitchTo(oldcontext);
> +
> +     return astate;
> + }
> +
> + /*
> +  * makeArrayResult - produce final result of accumArrayResult
> +  *
> +  *    astate is working state (not NULL)
> +  *    rcontext is where to construct result
> +  */
> + Datum
> + makeArrayResult(ArrayBuildState *astate,
> +                 MemoryContext rcontext)
> + {
> +     int            dims[1];
> +     int            lbs[1];
> +
> +     dims[0] = astate->nelems;
> +     lbs[0] = 1;
> +
> +     return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
> + }
> +
> + /*
> +  * makeMdArrayResult - produce md final result of accumArrayResult
> +  *
> +  *    astate is working state (not NULL)
> +  *    rcontext is where to construct result
> +  */
> + Datum
> + makeMdArrayResult(ArrayBuildState *astate,
> +                 int ndims,
> +                 int *dims,
> +                 int *lbs,
> +                 MemoryContext rcontext)
> + {
> +     ArrayType  *result;
> +     MemoryContext oldcontext;
> +
> +     /* Build the final array result in rcontext */
> +     oldcontext = MemoryContextSwitchTo(rcontext);
> +
> +     result = construct_md_array(astate->dvalues,
> +                                 ndims,
> +                                 dims,
> +                                 lbs,
> +                                 astate->element_type,
> +                                 astate->typlen,
> +                                 astate->typbyval,
> +                                 astate->typalign);
> +
> +     MemoryContextSwitchTo(oldcontext);
> +
> +     /* Clean up all the junk */
> +     MemoryContextDelete(astate->mcontext);
> +
> +     return PointerGetDatum(result);
>   }
> Index: src/backend/utils/adt/varlena.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/varlena.c,v
> retrieving revision 1.98
> diff -c -r1.98 varlena.c
> *** src/backend/utils/adt/varlena.c    15 May 2003 15:50:19 -0000    1.98
> --- src/backend/utils/adt/varlena.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 19,29 ****
> --- 19,32 ----
>   #include "mb/pg_wchar.h"
>   #include "miscadmin.h"
>   #include "access/tuptoaster.h"
> + #include "catalog/pg_type.h"
>   #include "lib/stringinfo.h"
>   #include "libpq/crypt.h"
>   #include "libpq/pqformat.h"
> + #include "utils/array.h"
>   #include "utils/builtins.h"
>   #include "utils/pg_locale.h"
> + #include "utils/lsyscache.h"
>
>
>   typedef struct varlena unknown;
> ***************
> *** 1983,1990 ****
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else
> ! /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>
> --- 1986,1992 ----
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else                    /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>
> ***************
> *** 2004,2011 ****
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else
> ! /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>       else if ((start_posn != 0) && (end_posn == 0))
> --- 2006,2012 ----
>           if (fldnum == 1)        /* first field - just return the input
>                                    * string */
>               PG_RETURN_TEXT_P(inputstring);
> !         else                    /* otherwise return an empty string */
>               PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
>       }
>       else if ((start_posn != 0) && (end_posn == 0))
> ***************
> *** 2026,2031 ****
> --- 2027,2217 ----
>           result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn -
fldsep_len,false); 
>           PG_RETURN_TEXT_P(result_text);
>       }
> + }
> +
> + /*
> +  * text_to_array
> +  * parse input string
> +  * return text array of elements
> +  * based on provided field separator
> +  */
> + Datum
> + text_to_array(PG_FUNCTION_ARGS)
> + {
> +     text       *inputstring = PG_GETARG_TEXT_P(0);
> +     int            inputstring_len = TEXTLEN(inputstring);
> +     text       *fldsep = PG_GETARG_TEXT_P(1);
> +     int            fldsep_len = TEXTLEN(fldsep);
> +     int            fldnum;
> +     int            start_posn = 0;
> +     int            end_posn = 0;
> +     text       *result_text = NULL;
> +     ArrayBuildState *astate = NULL;
> +     MemoryContext oldcontext = CurrentMemoryContext;
> +
> +     /* return NULL for empty input string */
> +     if (inputstring_len < 1)
> +         PG_RETURN_NULL();
> +
> +     /* empty field separator
> +      * return one element, 1D, array using the input string */
> +     if (fldsep_len < 1)
> +         PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
> +                               CStringGetDatum(inputstring), 1));
> +
> +     /* start with end position holding the initial start position */
> +     end_posn = 0;
> +     for (fldnum=1;;fldnum++)    /* field number is 1 based */
> +     {
> +         Datum    dvalue;
> +         bool    disnull = false;
> +
> +         start_posn = end_posn;
> +         end_posn = text_position(PointerGetDatum(inputstring),
> +                                  PointerGetDatum(fldsep),
> +                                  fldnum);
> +
> +         if ((start_posn == 0) && (end_posn == 0))    /* fldsep not found */
> +         {
> +             if (fldnum == 1)
> +             {
> +                 /* first element
> +                  * return one element, 1D, array using the input string */
> +                 PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
> +                                       CStringGetDatum(inputstring), 1));
> +             }
> +             else
> +             {
> +                 /* otherwise create array and exit */
> +                 PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
> +             }
> +         }
> +         else if ((start_posn != 0) && (end_posn == 0))
> +         {
> +             /* last field requested */
> +             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
> +         }
> +         else if ((start_posn == 0) && (end_posn != 0))
> +         {
> +             /* first field requested */
> +             result_text = LEFT(inputstring, fldsep);
> +         }
> +         else
> +         {
> +             /* prior to last field requested */
> +             result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn -
start_posn- fldsep_len, false); 
> +         }
> +
> +         /* stash away current value */
> +         dvalue = PointerGetDatum(result_text);
> +         astate = accumArrayResult(astate, dvalue,
> +                                   disnull, TEXTOID, oldcontext);
> +
> +     }
> +
> +     /* never reached -- keep compiler quiet */
> +     PG_RETURN_NULL();
> + }
> +
> + /*
> +  * array_to_text
> +  * concatenate Cstring representation of input array elements
> +  * using provided field separator
> +  */
> + Datum
> + array_to_text(PG_FUNCTION_ARGS)
> + {
> +     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
> +     char       *fldsep = PG_TEXTARG_GET_STR(1);
> +     int            nitems, *dims, ndims;
> +     char       *p;
> +     Oid            element_type;
> +     int            typlen;
> +     bool        typbyval;
> +     char        typdelim;
> +     Oid            typoutput,
> +                 typelem;
> +     FmgrInfo    outputproc;
> +     char        typalign;
> +     StringInfo    result_str = makeStringInfo();
> +     int            i;
> +     ArrayMetaState *my_extra;
> +
> +     p = ARR_DATA_PTR(v);
> +     ndims = ARR_NDIM(v);
> +     dims = ARR_DIMS(v);
> +     nitems = ArrayGetNItems(ndims, dims);
> +
> +     /* if there are no elements, return an empty string */
> +     if (nitems == 0)
> +         PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
> +
> +     element_type = ARR_ELEMTYPE(v);
> +
> +     /*
> +      * We arrange to look up info about element type, including its output
> +      * conversion proc only once per series of calls, assuming the element
> +      * type doesn't change underneath us.
> +      */
> +     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> +     if (my_extra == NULL)
> +     {
> +         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> +                                                      sizeof(ArrayMetaState));
> +         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
> +         my_extra->element_type = InvalidOid;
> +     }
> +
> +     if (my_extra->element_type != element_type)
> +     {
> +         /* Get info about element type, including its output conversion proc */
> +         get_type_metadata(element_type, IOFunc_output,
> +                             &typlen, &typbyval, &typdelim,
> +                             &typelem, &typoutput, &typalign);
> +         fmgr_info(typoutput, &outputproc);
> +
> +         my_extra->element_type = element_type;
> +         my_extra->typlen = typlen;
> +         my_extra->typbyval = typbyval;
> +         my_extra->typdelim = typdelim;
> +         my_extra->typelem = typelem;
> +         my_extra->typiofunc = typoutput;
> +         my_extra->typalign = typalign;
> +         my_extra->proc = outputproc;
> +     }
> +     else
> +     {
> +         typlen = my_extra->typlen;
> +         typbyval = my_extra->typbyval;
> +         typdelim = my_extra->typdelim;
> +         typelem = my_extra->typelem;
> +         typoutput = my_extra->typiofunc;
> +         typalign = my_extra->typalign;
> +         outputproc = my_extra->proc;
> +     }
> +
> +     for (i = 0; i < nitems; i++)
> +     {
> +         Datum        itemvalue;
> +         char       *value;
> +
> +         itemvalue = fetch_att(p, typbyval, typlen);
> +
> +         value = DatumGetCString(FunctionCall3(&outputproc,
> +                                               itemvalue,
> +                                               ObjectIdGetDatum(typelem),
> +                                               Int32GetDatum(-1)));
> +
> +         if (i > 0)
> +             appendStringInfo(result_str, "%s%s", fldsep, value);
> +         else
> +             appendStringInfo(result_str, "%s", value);
> +
> +         p = att_addlength(p, typlen, PointerGetDatum(p));
> +         p = (char *) att_align(p, typalign);
> +     }
> +
> +     PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
>   }
>
>   #define HEXBASE 16
> Index: src/backend/utils/cache/lsyscache.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v
> retrieving revision 1.96
> diff -c -r1.96 lsyscache.c
> *** src/backend/utils/cache/lsyscache.c    22 Jun 2003 22:04:54 -0000    1.96
> --- src/backend/utils/cache/lsyscache.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 719,724 ****
> --- 719,758 ----
>   }
>
>   /*
> +  * get_func_argtypes
> +  *        Given procedure id, return the function's argument types.
> +  *        Also pass back the number of arguments.
> +  */
> + Oid *
> + get_func_argtypes(Oid funcid, int *nargs)
> + {
> +     HeapTuple        tp;
> +     Form_pg_proc    procstruct;
> +     Oid               *result = NULL;
> +     int                i;
> +
> +     tp = SearchSysCache(PROCOID,
> +                         ObjectIdGetDatum(funcid),
> +                         0, 0, 0);
> +     if (!HeapTupleIsValid(tp))
> +         elog(ERROR, "Function OID %u does not exist", funcid);
> +
> +     procstruct = (Form_pg_proc) GETSTRUCT(tp);
> +     *nargs = (int) procstruct->pronargs;
> +
> +     if (*nargs > 0)
> +     {
> +         result = (Oid *) palloc(*nargs * sizeof(Oid));
> +
> +         for (i = 0; i < *nargs; i++)
> +             result[i] = procstruct->proargtypes[i];
> +     }
> +
> +     ReleaseSysCache(tp);
> +     return result;
> + }
> +
> + /*
>    * get_func_retset
>    *        Given procedure id, return the function's proretset flag.
>    */
> ***************
> *** 1088,1093 ****
> --- 1122,1177 ----
>       *typbyval = typtup->typbyval;
>       *typalign = typtup->typalign;
>       ReleaseSysCache(tp);
> + }
> +
> + /*
> +  * get_type_metadata
> +  *
> +  *        A six-fer:    given the type OID, return typlen, typbyval, typalign,
> +  *                    typdelim, typelem, IO function Oid. The IO function
> +  *                    returned is controlled by IOFuncSelector
> +  */
> + void
> + get_type_metadata(Oid element_type,
> +                     IOFuncSelector which_func,
> +                     int *typlen,
> +                     bool *typbyval,
> +                     char *typdelim,
> +                     Oid *typelem,
> +                     Oid *proc,
> +                     char *typalign)
> + {
> +     HeapTuple    typeTuple;
> +     Form_pg_type typeStruct;
> +
> +     typeTuple = SearchSysCache(TYPEOID,
> +                                ObjectIdGetDatum(element_type),
> +                                0, 0, 0);
> +     if (!HeapTupleIsValid(typeTuple))
> +         elog(ERROR, "cache lookup failed for type %u", element_type);
> +     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
> +
> +     *typlen = typeStruct->typlen;
> +     *typbyval = typeStruct->typbyval;
> +     *typdelim = typeStruct->typdelim;
> +     *typelem = typeStruct->typelem;
> +     *typalign = typeStruct->typalign;
> +     switch (which_func)
> +     {
> +         case IOFunc_input:
> +             *proc = typeStruct->typinput;
> +             break;
> +         case IOFunc_output:
> +             *proc = typeStruct->typoutput;
> +             break;
> +         case IOFunc_receive:
> +             *proc = typeStruct->typreceive;
> +             break;
> +         case IOFunc_send:
> +             *proc = typeStruct->typsend;
> +             break;
> +     }
> +     ReleaseSysCache(typeTuple);
>   }
>
>   #ifdef NOT_USED
> Index: src/backend/utils/fmgr/fmgr.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/fmgr/fmgr.c,v
> retrieving revision 1.68
> diff -c -r1.68 fmgr.c
> *** src/backend/utils/fmgr/fmgr.c    8 Apr 2003 23:20:02 -0000    1.68
> --- src/backend/utils/fmgr/fmgr.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 1673,1675 ****
> --- 1673,1701 ----
>
>       return exprType((Node *) nth(argnum, args));
>   }
> +
> + /*
> +  * Get the OID of the function or operator
> +  *
> +  * Returns InvalidOid if information is not available
> +  */
> + Oid
> + get_fn_expr_functype(FunctionCallInfo fcinfo)
> + {
> +     Node   *expr;
> +
> +     /*
> +      * can't return anything useful if we have no FmgrInfo or if
> +      * its fn_expr node has not been initialized
> +      */
> +     if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
> +         return InvalidOid;
> +
> +     expr = fcinfo->flinfo->fn_expr;
> +     if (IsA(expr, FuncExpr))
> +         return ((FuncExpr *) expr)->funcid;
> +     else if (IsA(expr, OpExpr))
> +         return ((OpExpr *) expr)->opno;
> +     else
> +         return InvalidOid;
> + }
> Index: src/include/fmgr.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/fmgr.h,v
> retrieving revision 1.27
> diff -c -r1.27 fmgr.h
> *** src/include/fmgr.h    8 Apr 2003 23:20:04 -0000    1.27
> --- src/include/fmgr.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 18,23 ****
> --- 18,24 ----
>   #ifndef FMGR_H
>   #define FMGR_H
>
> + #include "nodes/nodes.h"
>
>   /*
>    * All functions that can be called directly by fmgr must have this signature.
> ***************
> *** 372,385 ****
>                    Datum arg6, Datum arg7, Datum arg8,
>                    Datum arg9);
>
> -
>   /*
>    * Routines in fmgr.c
>    */
>   extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
> ! extern Oid    fmgr_internal_function(const char *proname);
> ! extern Oid    get_fn_expr_rettype(FunctionCallInfo fcinfo);
> ! extern Oid    get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
>
>   /*
>    * Routines in dfmgr.c
> --- 373,386 ----
>                    Datum arg6, Datum arg7, Datum arg8,
>                    Datum arg9);
>
>   /*
>    * Routines in fmgr.c
>    */
>   extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
> ! extern Oid fmgr_internal_function(const char *proname);
> ! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo);
> ! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
> ! extern Oid get_fn_expr_functype(FunctionCallInfo fcinfo);
>
>   /*
>    * Routines in dfmgr.c
> Index: src/include/catalog/pg_amop.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amop.h,v
> retrieving revision 1.50
> diff -c -r1.50 pg_amop.h
> *** src/include/catalog/pg_amop.h    22 Jun 2003 22:04:55 -0000    1.50
> --- src/include/catalog/pg_amop.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 418,423 ****
> --- 418,432 ----
>   DATA(insert (    2098 4 f 2335 ));
>   DATA(insert (    2098 5 f 2336 ));
>
> + /*
> +  *    btree array_ops
> +  */
> +
> + DATA(insert (     397 1 f 1072 ));
> + DATA(insert (     397 2 f 1074 ));
> + DATA(insert (     397 3 f 1070 ));
> + DATA(insert (     397 4 f 1075 ));
> + DATA(insert (     397 5 f 1073 ));
>
>   /*
>    *    hash index _ops
> Index: src/include/catalog/pg_amproc.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amproc.h,v
> retrieving revision 1.38
> diff -c -r1.38 pg_amproc.h
> *** src/include/catalog/pg_amproc.h    22 Jun 2003 22:04:55 -0000    1.38
> --- src/include/catalog/pg_amproc.h    24 Jun 2003 02:35:24 -0000
> ***************
> *** 78,83 ****
> --- 78,84 ----
>
>
>   /* btree */
> + DATA(insert (     397 1  382 ));
>   DATA(insert (     421 1    357 ));
>   DATA(insert (     423 1 1596 ));
>   DATA(insert (     424 1 1693 ));
> Index: src/include/catalog/pg_opclass.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_opclass.h,v
> retrieving revision 1.51
> diff -c -r1.51 pg_opclass.h
> *** src/include/catalog/pg_opclass.h    22 Jun 2003 22:04:55 -0000    1.51
> --- src/include/catalog/pg_opclass.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 87,92 ****
> --- 87,94 ----
>    */
>
>   DATA(insert OID =  421 (    403        abstime_ops        PGNSP PGUID  702 t 0 ));
> + DATA(insert OID =  397 (    403        array_ops        PGNSP PGUID 2277 t 0 ));
> + #define ARRAY_BTREE_OPS_OID 397
>   DATA(insert OID =  422 (    402        bigbox_ops        PGNSP PGUID  603 f 0 ));
>   DATA(insert OID =  423 (    403        bit_ops            PGNSP PGUID 1560 t 0 ));
>   DATA(insert OID =  424 (    403        bool_ops        PGNSP PGUID   16 t 0 ));
> Index: src/include/catalog/pg_operator.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_operator.h,v
> retrieving revision 1.115
> diff -c -r1.115 pg_operator.h
> *** src/include/catalog/pg_operator.h    22 Jun 2003 22:04:55 -0000    1.115
> --- src/include/catalog/pg_operator.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 116,125 ****
>   DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
>   DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 
>
> ! DATA(insert OID = 329 (  "="       PGNSP PGUID b f 2277 2277 16   329 0 0 0    0 0 array_eq     eqsel eqjoinsel ));
> ! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277   0 0 0 0    0 0 array_append   -       -
));
> ! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277   0 0 0 0    0 0 array_prepend  -       -
));
> ! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277   0 0 0 0    0 0 array_cat      -       -
));
>
>   DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
>   DATA(insert OID = 353 (  "="       PGNSP PGUID b f    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
> --- 116,130 ----
>   DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
>   DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 
>
> ! DATA(insert OID = 1070 (  "="       PGNSP PGUID b f 2277 2277 16 1070 1071  1072 1072 1072 1073 array_eq eqsel
eqjoinsel)); 
> ! DATA(insert OID = 1071 (  "<>"       PGNSP PGUID b f 2277 2277 16 1071 1070  0 0 0 0 array_ne neqsel neqjoinsel ));
> ! DATA(insert OID = 1072 (  "<"       PGNSP PGUID b f 2277 2277 16 1073 1075  0 0 0 0 array_lt scalarltsel
scalarltjoinsel)); 
> ! DATA(insert OID = 1073 (  ">"       PGNSP PGUID b f 2277 2277 16 1072 1074  0 0 0 0 array_gt scalargtsel
scalargtjoinsel)); 
> ! DATA(insert OID = 1074 (  "<="       PGNSP PGUID b f 2277 2277 16 1075 1073  0 0 0 0 array_le scalarltsel
scalarltjoinsel)); 
> ! DATA(insert OID = 1075 (  ">="       PGNSP PGUID b f 2277 2277 16 1074 1072  0 0 0 0 array_ge scalargtsel
scalargtjoinsel)); 
> ! DATA(insert OID = 349 (  "||"       PGNSP PGUID b f 2277 2283 2277 0 0  0 0 0 0 array_append   -       -     ));
> ! DATA(insert OID = 374 (  "||"       PGNSP PGUID b f 2283 2277 2277 0 0  0 0 0 0 array_prepend  -       -     ));
> ! DATA(insert OID = 375 (  "||"       PGNSP PGUID b f 2277 2277 2277 0 0  0 0 0 0 array_cat      -       -     ));
>
>   DATA(insert OID = 352 (  "="       PGNSP PGUID b t    28    28    16 352     0     0     0     0     0 xideq eqsel
eqjoinsel)); 
>   DATA(insert OID = 353 (  "="       PGNSP PGUID b f    28    23    16     0     0     0     0     0     0 xideqint4
eqseleqjoinsel )); 
> ***************
> *** 425,430 ****
> --- 430,436 ----
>   DATA(insert OID = 966 (  "+"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - ));
>   DATA(insert OID = 967 (  "-"       PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclremove - - ));
>   DATA(insert OID = 968 (  "~"       PGNSP PGUID b f 1034 1033     16 0 0 0 0 0 0 aclcontains - - ));
> + DATA(insert OID = 974 (  "="       PGNSP PGUID b f 1033 1033     16 0 0 0 0 0 0 aclitemeq eqsel eqjoinsel ));
>
>   /* additional geometric operators - thomas 1997-07-09 */
>   DATA(insert OID =  969 (  "@@"       PGNSP PGUID l f    0  601    600    0  0 0 0 0 0 lseg_center - - ));
> Index: src/include/catalog/pg_proc.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v
> retrieving revision 1.304
> diff -c -r1.304 pg_proc.h
> *** src/include/catalog/pg_proc.h    22 Jun 2003 22:04:55 -0000    1.304
> --- src/include/catalog/pg_proc.h    24 Jun 2003 02:35:46 -0000
> ***************
> *** 758,763 ****
> --- 758,765 ----
>   DESCR("btree less-equal-greater");
>   DATA(insert OID = 360 (  bttextcmp           PGNSP PGUID 12 f f t f i 2 23 "25 25"    bttextcmp - _null_ ));
>   DESCR("btree less-equal-greater");
> + DATA(insert OID = 382 (  btarraycmp           PGNSP PGUID 12 f f t f i 2 23 "2277 2277"    btarraycmp - _null_ ));
> + DESCR("btree less-equal-greater");
>
>   DATA(insert OID = 361 (  lseg_distance       PGNSP PGUID 12 f f t f i 2 701 "601 601"  lseg_distance - _null_ ));
>   DESCR("distance between");
> ***************
> *** 988,1001 ****
>   DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
>   DESCR("greater-than-or-equal");
>
> - DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
> - DESCR("array equal");
> -
>   DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
>   DESCR("current user name");
>   DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
>   DESCR("session user name");
>
>   DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
>   DESCR("array dimensions");
>   DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
> --- 990,1012 ----
>   DATA(insert OID = 743 (  text_ge           PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
>   DESCR("greater-than-or-equal");
>
>   DATA(insert OID = 745 (  current_user       PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
>   DESCR("current user name");
>   DATA(insert OID = 746 (  session_user       PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
>   DESCR("session user name");
>
> + DATA(insert OID = 744 (  array_eq           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
> + DESCR("array equal");
> + DATA(insert OID = 390 (  array_ne           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ne - _null_ ));
> + DESCR("array not equal");
> + DATA(insert OID = 391 (  array_lt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_lt - _null_ ));
> + DESCR("array less than");
> + DATA(insert OID = 392 (  array_gt           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_gt - _null_ ));
> + DESCR("array greater than");
> + DATA(insert OID = 393 (  array_le           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_le - _null_ ));
> + DESCR("array less than or equal");
> + DATA(insert OID = 396 (  array_ge           PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ge - _null_ ));
> + DESCR("array greater than or equal");
>   DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
>   DESCR("array dimensions");
>   DATA(insert OID = 750 (  array_in           PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
> ***************
> *** 1006,1027 ****
>   DESCR("array lower dimension");
>   DATA(insert OID = 2092 (  array_upper       PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ ));
>   DESCR("array upper dimension");
> - DATA(insert OID = 377 (  singleton_array  PGNSP PGUID 12 f f t f i 1 2277 "2283" singleton_array - _null_ ));
> - DESCR("create array from single element");
>   DATA(insert OID = 378 (  array_append       PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ ));
>   DESCR("append element onto end of array");
>   DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
>   DESCR("prepend element onto front of array");
> - DATA(insert OID = 380 (  array_accum       PGNSP PGUID 12 f f f f i 2 2277 "2277 2283" array_accum - _null_ ));
> - DESCR("push element onto end of array, creating array if needed");
> - DATA(insert OID = 381 (  array_assign       PGNSP PGUID 12 f f t f i 3 2277 "2277 23 2283" array_assign - _null_
));
> - DESCR("assign specific array element");
> - DATA(insert OID = 382 (  array_subscript  PGNSP PGUID 12 f f t f i 2 2283 "2277 23" array_subscript - _null_ ));
> - DESCR("return specific array element");
>   DATA(insert OID = 383 (  array_cat           PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ ));
>   DESCR("concatenate two arrays");
>   DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
>   DESCR("coerce array type to another array type");
>
>   DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
>   DESCR("I/O");
> --- 1017,1034 ----
>   DESCR("array lower dimension");
>   DATA(insert OID = 2092 (  array_upper       PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ ));
>   DESCR("array upper dimension");
>   DATA(insert OID = 378 (  array_append       PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ ));
>   DESCR("append element onto end of array");
>   DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
>   DESCR("prepend element onto front of array");
>   DATA(insert OID = 383 (  array_cat           PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ ));
>   DESCR("concatenate two arrays");
>   DATA(insert OID = 384  (  array_coerce       PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
>   DESCR("coerce array type to another array type");
> + DATA(insert OID = 394 (  string_to_array   PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ ));
> + DESCR("split delimited text into text[]");
> + DATA(insert OID = 395 (  array_to_string   PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ ));
> + DESCR("concatenate array elements, using delimiter, into text");
>
>   DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
>   DESCR("I/O");
> ***************
> *** 1322,1327 ****
> --- 1329,1336 ----
>   DESCR("remove ACL item");
>   DATA(insert OID = 1037 (  aclcontains       PGNSP PGUID 12 f f t f s 2 16 "1034 1033"    aclcontains - _null_ ));
>   DESCR("does ACL contain item?");
> + DATA(insert OID = 1062 (  aclitemeq           PGNSP PGUID 12 f f t f s 2 16 "1033 1033"    aclitem_eq - _null_ ));
> + DESCR("equality operator for ACL items");
>   DATA(insert OID = 1365 (  makeaclitem       PGNSP PGUID 12 f f t f s 5 1033 "23 23 23 25 16"    makeaclitem -
_null_)); 
>   DESCR("make ACL item");
>   DATA(insert OID = 1038 (  seteval           PGNSP PGUID 12 f f t t v 1 23 "26"  seteval - _null_ ));
> Index: src/include/nodes/primnodes.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v
> retrieving revision 1.83
> diff -c -r1.83 primnodes.h
> *** src/include/nodes/primnodes.h    6 Jun 2003 15:04:03 -0000    1.83
> --- src/include/nodes/primnodes.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 226,231 ****
> --- 226,232 ----
>       Index        agglevelsup;    /* > 0 if agg belongs to outer query */
>       bool        aggstar;        /* TRUE if argument was really '*' */
>       bool        aggdistinct;    /* TRUE if it's agg(DISTINCT ...) */
> +     List       *args;            /* arguments to the aggregate */
>   } Aggref;
>
>   /* ----------------
> ***************
> *** 358,372 ****
>   /* ----------------
>    * SubLink
>    *
> !  * A SubLink represents a subselect appearing in an expression, and in some
> !  * cases also the combining operator(s) just above it.    The subLinkType
> !  * indicates the form of the expression represented:
>    *    EXISTS_SUBLINK        EXISTS(SELECT ...)
>    *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
>    *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
>    *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
>    *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
>    *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
>    * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
>    * same length as the subselect's targetlist.  MULTIEXPR will *always* have
>    * a list with more than one entry; if the subselect has just one target
> --- 359,377 ----
>   /* ----------------
>    * SubLink
>    *
> !  * A SubLink represents a subselect, or an expression, appearing in an
> !  * expression, and in some cases also the combining operator(s) just above
> !  * it.    The subLinkType indicates the form of the expression represented:
>    *    EXISTS_SUBLINK        EXISTS(SELECT ...)
>    *    ALL_SUBLINK            (lefthand) op ALL (SELECT ...)
>    *    ANY_SUBLINK            (lefthand) op ANY (SELECT ...)
>    *    MULTIEXPR_SUBLINK    (lefthand) op (SELECT ...)
>    *    EXPR_SUBLINK        (SELECT with single targetlist item ...)
>    *    ARRAY_SUBLINK        ARRAY(SELECT with single targetlist item ...)
> +  * If an expression is used in place of the subselect, it is transformed
> +  * into a simple "(SELECT expr)" in gram.y. This is to allow arrays to be
> +  * used as if they were the result of a single column subselect. If the
> +  * expression is scalar, it is treated as a one element array.
>    * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
>    * same length as the subselect's targetlist.  MULTIEXPR will *always* have
>    * a list with more than one entry; if the subselect has just one target
> ***************
> *** 415,420 ****
> --- 420,427 ----
>       SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
>       bool        useOr;            /* TRUE to combine column results with
>                                    * "OR" not "AND" */
> +     bool        isExpr;            /* TRUE if the subselect is really derived
> +                                  * from a single expression */
>       List       *lefthand;        /* list of outer-query expressions on the
>                                    * left */
>       List       *operName;        /* originally specified operator name */
> ***************
> *** 456,461 ****
> --- 463,477 ----
>       SubLinkType subLinkType;    /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
>       bool        useOr;            /* TRUE to combine column results with
>                                    * "OR" not "AND" */
> +     bool        isExpr;            /* TRUE if the subselect is really derived
> +                                  * from a single expression */
> +     /* runtime cache for single array expressions */
> +     Oid            exprtype;        /* array and element type, and other info
> +                                  * needed deconstruct the array */
> +     Oid            elemtype;
> +     int16        elmlen;
> +     bool        elmbyval;
> +     char        elmalign;
>       /* The combining operators, transformed to executable expressions: */
>       List       *exprs;            /* list of OpExpr expression trees */
>       List       *paramIds;        /* IDs of Params embedded in the above */
> Index: src/include/optimizer/clauses.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/optimizer/clauses.h,v
> retrieving revision 1.63
> diff -c -r1.63 clauses.h
> *** src/include/optimizer/clauses.h    28 May 2003 16:04:02 -0000    1.63
> --- src/include/optimizer/clauses.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 28,33 ****
> --- 28,36 ----
>   extern Node *get_leftop(Expr *clause);
>   extern Node *get_rightop(Expr *clause);
>
> + extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
> +                                     CoercionForm funcformat, List *funcargs);
> +
>   extern bool not_clause(Node *clause);
>   extern Expr *make_notclause(Expr *notclause);
>   extern Expr *get_notclausearg(Expr *notclause);
> Index: src/include/parser/parse_oper.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_oper.h,v
> retrieving revision 1.25
> diff -c -r1.25 parse_oper.h
> *** src/include/parser/parse_oper.h    29 Apr 2003 22:13:11 -0000    1.25
> --- src/include/parser/parse_oper.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 44,49 ****
> --- 44,50 ----
>   /* Convenience routines for common calls on the above */
>   extern Oid    compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError);
>   extern Oid    equality_oper_funcid(Oid argtype);
> + extern Oid  ordering_oper_funcid(Oid argtype);
>   extern Oid    ordering_oper_opid(Oid argtype);
>
>   /* Extract operator OID or underlying-function OID from an Operator tuple */
> Index: src/include/utils/acl.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/acl.h,v
> retrieving revision 1.52
> diff -c -r1.52 acl.h
> *** src/include/utils/acl.h    11 Jun 2003 09:23:55 -0000    1.52
> --- src/include/utils/acl.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 192,197 ****
> --- 192,198 ----
>   extern Datum aclremove(PG_FUNCTION_ARGS);
>   extern Datum aclcontains(PG_FUNCTION_ARGS);
>   extern Datum makeaclitem(PG_FUNCTION_ARGS);
> + extern Datum aclitem_eq(PG_FUNCTION_ARGS);
>
>   /*
>    * prototypes for functions in aclchk.c
> Index: src/include/utils/array.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v
> retrieving revision 1.38
> diff -c -r1.38 array.h
> *** src/include/utils/array.h    8 May 2003 22:19:57 -0000    1.38
> --- src/include/utils/array.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 32,37 ****
> --- 32,68 ----
>       Oid            elemtype;        /* element type OID */
>   } ArrayType;
>
> + typedef struct ArrayBuildState
> + {
> +     MemoryContext mcontext;        /* where all the temp stuff is kept */
> +     Datum       *dvalues;        /* array of accumulated Datums */
> +     /*
> +      * The allocated size of dvalues[] is always a multiple of
> +      * ARRAY_ELEMS_CHUNKSIZE
> +      */
> + #define ARRAY_ELEMS_CHUNKSIZE    64
> +     int            nelems;            /* number of valid Datums in dvalues[] */
> +     Oid            element_type;    /* data type of the Datums */
> +     int16        typlen;            /* needed info about datatype */
> +     bool        typbyval;
> +     char        typalign;
> + } ArrayBuildState;
> +
> + /*
> +  * structure to cache type metadata needed for array manipulation
> +  */
> + typedef struct ArrayMetaState
> + {
> +     Oid                element_type;
> +     int                typlen;
> +     bool            typbyval;
> +     char            typdelim;
> +     Oid                typelem;
> +     Oid                typiofunc;
> +     char            typalign;
> +     FmgrInfo        proc;
> + } ArrayMetaState;
> +
>   /*
>    * fmgr macros for array objects
>    */
> ***************
> *** 86,96 ****
>   extern Datum array_send(PG_FUNCTION_ARGS);
>   extern Datum array_length_coerce(PG_FUNCTION_ARGS);
>   extern Datum array_eq(PG_FUNCTION_ARGS);
>   extern Datum array_dims(PG_FUNCTION_ARGS);
>   extern Datum array_lower(PG_FUNCTION_ARGS);
>   extern Datum array_upper(PG_FUNCTION_ARGS);
> - extern Datum array_assign(PG_FUNCTION_ARGS);
> - extern Datum array_subscript(PG_FUNCTION_ARGS);
>   extern Datum array_type_coerce(PG_FUNCTION_ARGS);
>
>   extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
> --- 117,131 ----
>   extern Datum array_send(PG_FUNCTION_ARGS);
>   extern Datum array_length_coerce(PG_FUNCTION_ARGS);
>   extern Datum array_eq(PG_FUNCTION_ARGS);
> + extern Datum array_ne(PG_FUNCTION_ARGS);
> + extern Datum array_lt(PG_FUNCTION_ARGS);
> + extern Datum array_gt(PG_FUNCTION_ARGS);
> + extern Datum array_le(PG_FUNCTION_ARGS);
> + extern Datum array_ge(PG_FUNCTION_ARGS);
> + extern Datum btarraycmp(PG_FUNCTION_ARGS);
>   extern Datum array_dims(PG_FUNCTION_ARGS);
>   extern Datum array_lower(PG_FUNCTION_ARGS);
>   extern Datum array_upper(PG_FUNCTION_ARGS);
>   extern Datum array_type_coerce(PG_FUNCTION_ARGS);
>
>   extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
> ***************
> *** 124,130 ****
>                     Oid elmtype,
>                     int elmlen, bool elmbyval, char elmalign,
>                     Datum **elemsp, int *nelemsp);
> !
>
>   /*
>    * prototypes for functions defined in arrayutils.c
> --- 159,172 ----
>                     Oid elmtype,
>                     int elmlen, bool elmbyval, char elmalign,
>                     Datum **elemsp, int *nelemsp);
> ! extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
> !                                          Datum dvalue, bool disnull,
> !                                          Oid element_type,
> !                                          MemoryContext rcontext);
> ! extern Datum makeArrayResult(ArrayBuildState *astate,
> !                              MemoryContext rcontext);
> ! extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
> !                                int *dims, int *lbs, MemoryContext rcontext);
>
>   /*
>    * prototypes for functions defined in arrayutils.c
> ***************
> *** 141,152 ****
>   /*
>    * prototypes for functions defined in array_userfuncs.c
>    */
> - extern Datum singleton_array(PG_FUNCTION_ARGS);
>   extern Datum array_push(PG_FUNCTION_ARGS);
> - extern Datum array_accum(PG_FUNCTION_ARGS);
>   extern Datum array_cat(PG_FUNCTION_ARGS);
>
> ! extern ArrayType *create_singleton_array(Oid element_type,
>                                            Datum element,
>                                            int ndims);
>
> --- 183,193 ----
>   /*
>    * prototypes for functions defined in array_userfuncs.c
>    */
>   extern Datum array_push(PG_FUNCTION_ARGS);
>   extern Datum array_cat(PG_FUNCTION_ARGS);
>
> ! extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
> !                                          Oid element_type,
>                                            Datum element,
>                                            int ndims);
>
> Index: src/include/utils/builtins.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/builtins.h,v
> retrieving revision 1.219
> diff -c -r1.219 builtins.h
> *** src/include/utils/builtins.h    26 May 2003 00:11:28 -0000    1.219
> --- src/include/utils/builtins.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 530,535 ****
> --- 530,537 ----
>                         List **namelist);
>   extern Datum replace_text(PG_FUNCTION_ARGS);
>   extern Datum split_text(PG_FUNCTION_ARGS);
> + extern Datum text_to_array(PG_FUNCTION_ARGS);
> + extern Datum array_to_text(PG_FUNCTION_ARGS);
>   extern Datum to_hex32(PG_FUNCTION_ARGS);
>   extern Datum to_hex64(PG_FUNCTION_ARGS);
>   extern Datum md5_text(PG_FUNCTION_ARGS);
> Index: src/include/utils/lsyscache.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v
> retrieving revision 1.71
> diff -c -r1.71 lsyscache.h
> *** src/include/utils/lsyscache.h    22 Jun 2003 22:04:55 -0000    1.71
> --- src/include/utils/lsyscache.h    24 Jun 2003 02:24:06 -0000
> ***************
> *** 15,20 ****
> --- 15,29 ----
>
>   #include "access/htup.h"
>
> + /* I/O function selector for system_cache_lookup */
> + typedef enum IOFuncSelector
> + {
> +     IOFunc_input,
> +     IOFunc_output,
> +     IOFunc_receive,
> +     IOFunc_send
> + } IOFuncSelector;
> +
>   extern bool op_in_opclass(Oid opno, Oid opclass);
>   extern bool op_requires_recheck(Oid opno, Oid opclass);
>   extern Oid    get_opclass_member(Oid opclass, int16 strategy);
> ***************
> *** 41,46 ****
> --- 50,56 ----
>   extern RegProcedure get_oprjoin(Oid opno);
>   extern char *get_func_name(Oid funcid);
>   extern Oid    get_func_rettype(Oid funcid);
> + extern Oid *get_func_argtypes(Oid funcid, int *nargs);
>   extern bool get_func_retset(Oid funcid);
>   extern bool func_strict(Oid funcid);
>   extern char func_volatile(Oid funcid);
> ***************
> *** 56,61 ****
> --- 66,79 ----
>   extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
>   extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
>                        char *typalign);
> + extern void get_type_metadata(Oid element_type,
> +                                 IOFuncSelector which_func,
> +                                 int *typlen,
> +                                 bool *typbyval,
> +                                 char *typdelim,
> +                                 Oid *typelem,
> +                                 Oid *proc,
> +                                 char *typalign);
>   extern char get_typstorage(Oid typid);
>   extern int32 get_typtypmod(Oid typid);
>   extern Node *get_typdefault(Oid typid);
> Index: src/interfaces/ecpg/preproc/preproc.y
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/preproc.y,v
> retrieving revision 1.236
> diff -c -r1.236 preproc.y
> *** src/interfaces/ecpg/preproc/preproc.y    20 Jun 2003 13:36:34 -0000    1.236
> --- src/interfaces/ecpg/preproc/preproc.y    24 Jun 2003 02:24:06 -0000
> ***************
> *** 4595,4601 ****
>                   $3.type_enum != ECPGt_char &&
>                       $3.type_enum != ECPGt_unsigned_char &&
>                   atoi(this->type->type_index) >= 0)
> !                 mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
>
>               types = this;
>           }
> --- 4595,4601 ----
>                   $3.type_enum != ECPGt_char &&
>                       $3.type_enum != ECPGt_unsigned_char &&
>                   atoi(this->type->type_index) >= 0)
> !                 mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
>
>               types = this;
>           }
> ***************
> *** 5415,5421 ****
>                       $5.type_enum != ECPGt_char &&
>                       $5.type_enum != ECPGt_unsigned_char &&
>                       atoi(this->type->type_index) >= 0)
> !                     mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
>
>                   types = this;
>               }
> --- 5415,5421 ----
>                       $5.type_enum != ECPGt_char &&
>                       $5.type_enum != ECPGt_unsigned_char &&
>                       atoi(this->type->type_index) >= 0)
> !                     mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
>
>                   types = this;
>               }
> ***************
> *** 5482,5488 ****
>
>                       default:
>                           if (atoi(length) >= 0)
> !                             mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data
types");
>
>                           if (atoi(dimension) < 0)
>                               type = ECPGmake_simple_type($5.type_enum, make_str("1"));
> --- 5482,5488 ----
>
>                       default:
>                           if (atoi(length) >= 0)
> !                             mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data
types");
>
>                           if (atoi(dimension) < 0)
>                               type = ECPGmake_simple_type($5.type_enum, make_str("1"));
> Index: src/interfaces/ecpg/preproc/type.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/type.c,v
> retrieving revision 1.52
> diff -c -r1.52 type.c
> *** src/interfaces/ecpg/preproc/type.c    20 Jun 2003 12:00:59 -0000    1.52
> --- src/interfaces/ecpg/preproc/type.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 504,510 ****
>                   switch (type->u.element->type)
>                   {
>                       case ECPGt_array:
> !                         yyerror("internal error, found multi-dimensional array\n");
>                           break;
>                       case ECPGt_struct:
>                       case ECPGt_union:
> --- 504,510 ----
>                   switch (type->u.element->type)
>                   {
>                       case ECPGt_array:
> !                         yyerror("internal error, found multidimensional array\n");
>                           break;
>                       case ECPGt_struct:
>                       case ECPGt_union:
> Index: src/interfaces/ecpg/preproc/variable.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/variable.c,v
> retrieving revision 1.21
> diff -c -r1.21 variable.c
> *** src/interfaces/ecpg/preproc/variable.c    11 Jun 2003 06:39:13 -0000    1.21
> --- src/interfaces/ecpg/preproc/variable.c    24 Jun 2003 02:24:06 -0000
> ***************
> *** 436,442 ****
>       if (atoi(type_index) >= 0)
>       {
>           if (atoi(*length) >= 0)
> !             mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
>           *length = type_index;
>       }
> --- 436,442 ----
>       if (atoi(type_index) >= 0)
>       {
>           if (atoi(*length) >= 0)
> !             mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
>           *length = type_index;
>       }
> ***************
> *** 444,450 ****
>       if (atoi(type_dimension) >= 0)
>       {
>           if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
> !             mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
>           if (atoi(*dimension) >= 0)
>               *length = *dimension;
> --- 444,450 ----
>       if (atoi(type_dimension) >= 0)
>       {
>           if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
> !             mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
>           if (atoi(*dimension) >= 0)
>               *length = *dimension;
> ***************
> *** 463,472 ****
>           mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");
>
>       if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
> !         mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
>       if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
> !         mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
>       switch (type_enum)
>       {
> --- 463,472 ----
>           mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");
>
>       if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
> !         mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
>       if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
> !         mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
>       switch (type_enum)
>       {
> ***************
> *** 480,486 ****
>               }
>
>               if (atoi(*length) >= 0)
> !                 mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for structures");
>
>               break;
>           case ECPGt_varchar:
> --- 480,486 ----
>               }
>
>               if (atoi(*length) >= 0)
> !                 mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for structures");
>
>               break;
>           case ECPGt_varchar:
> ***************
> *** 525,531 ****
>               }
>
>               if (atoi(*length) >= 0)
> !                 mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for simple data types");
>
>               break;
>       }
> --- 525,531 ----
>               }
>
>               if (atoi(*length) >= 0)
> !                 mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for simple data types");
>
>               break;
>       }
> Index: src/test/regress/expected/arrays.out
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/test/regress/expected/arrays.out,v
> retrieving revision 1.11
> diff -c -r1.11 arrays.out
> *** src/test/regress/expected/arrays.out    8 Apr 2003 23:20:04 -0000    1.11
> --- src/test/regress/expected/arrays.out    24 Jun 2003 02:24:06 -0000
> ***************
> *** 178,196 ****
>   (1 row)
>
>   -- functions
> ! SELECT singleton_array(42) AS "{42}";
> !  {42}
> ! ------
> !  {42}
> ! (1 row)
> !
> ! SELECT array_append(singleton_array(42), 6) AS "{42,6}";
>    {42,6}
>   --------
>    {42,6}
>   (1 row)
>
> ! SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
>    {6,42}
>   --------
>    {6,42}
> --- 178,190 ----
>   (1 row)
>
>   -- functions
> ! SELECT array_append(array[42], 6) AS "{42,6}";
>    {42,6}
>   --------
>    {42,6}
>   (1 row)
>
> ! SELECT array_prepend(6, array[42]) AS "{6,42}";
>    {6,42}
>   --------
>    {6,42}
> ***************
> *** 212,235 ****
>    {{3,4},{5,6},{1,2}}
>   ---------------------
>    {{3,4},{5,6},{1,2}}
> - (1 row)
> -
> - SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
> -  1.2
> - -----
> -  1.2
> - (1 row)
> -
> - SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
> -  {1.1,9.99,1.3}
> - ----------------
> -  {1.1,9.99,1.3}
> - (1 row)
> -
> - SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
> -  9.99
> - ------
> -  9.99
>   (1 row)
>
>   -- operators
> --- 206,211 ----
> Index: src/test/regress/sql/arrays.sql
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/test/regress/sql/arrays.sql,v
> retrieving revision 1.10
> diff -c -r1.10 arrays.sql
> *** src/test/regress/sql/arrays.sql    8 Apr 2003 23:20:04 -0000    1.10
> --- src/test/regress/sql/arrays.sql    24 Jun 2003 02:24:06 -0000
> ***************
> *** 130,144 ****
>   SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
>
>   -- functions
> ! SELECT singleton_array(42) AS "{42}";
> ! SELECT array_append(singleton_array(42), 6) AS "{42,6}";
> ! SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
>   SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}";
>   SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
>   SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
> - SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
> - SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
> - SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
>
>   -- operators
>   SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
> --- 130,140 ----
>   SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
>
>   -- functions
> ! SELECT array_append(array[42], 6) AS "{42,6}";
> ! SELECT array_prepend(6, array[42]) AS "{6,42}";
>   SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}";
>   SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
>   SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
>
>   -- operators
>   SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Re: array support patch phase 1 patch

From
Joe Conway
Date:
Bruce Momjian wrote:
> Patch applied.  Joe, I need some text about this patch for the release
> notes.  It has so much stuff in it I don't know how to describe it.
>

OK -- I'll send you something. I need to finish the documentation also,
so I'll send both at once.

Joe




Re: array support patch phase 1 patch

From
Peter Eisentraut
Date:
Bruce Momjian writes:

> Patch applied.  Joe, I need some text about this patch for the release
> notes.  It has so much stuff in it I don't know how to describe it.

This patch still contained documentation of functions array_subscript(),
array_assign() which were not included in the final version of the code.
Can someone adjust the documentation?

--
Peter Eisentraut   peter_e@gmx.net


Re: array support patch phase 1 patch

From
Joe Conway
Date:
Peter Eisentraut wrote:
> Bruce Momjian writes:
>
>>Patch applied.  Joe, I need some text about this patch for the release
>>notes.  It has so much stuff in it I don't know how to describe it.
>
> This patch still contained documentation of functions array_subscript(),
> array_assign() which were not included in the final version of the code.
> Can someone adjust the documentation?
>

Sorry -- I thought I had gotten all of those. In any case, I need to
make a complete pass on the array docs once I'm sure what will and won't
make it into this release. I was planning to do the cleanup pass once
beta starts.

Joe