diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 91cea30605..1613774f26 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,6 +1,6 @@ .\" This is -*-nroff-*- .\" XXX standard disclaimer belongs here.... -.\" $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.6 2000/06/09 01:43:56 momjian Exp $ +.\" $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.7 2000/07/17 03:04:40 tgl Exp $ .TH "SYSTEM CATALOGS" INTRO 03/13/94 PostgreSQL PostgreSQL .SH "Section 7 - System Catalogs" .de LS @@ -91,20 +91,16 @@ The following catalogs relate to the class/type system. * see DEFINE AGGREGATE for an explanation of transition functions */ pg_aggregate - NameData aggname /* aggregate name (e.g., "count") */ + NameData aggname /* aggregate name (e.g., "count") */ oid aggowner /* usesysid of creator */ - regproc aggtransfn1 /* first transition function */ - regproc aggtransfn2 /* second transition function */ + regproc aggtransfn /* transition function */ regproc aggfinalfn /* final function */ oid aggbasetype /* type of data on which aggregate operates */ - oid aggtranstype1 /* type returned by aggtransfn1 */ - oid aggtranstype2 /* type returned by aggtransfn2 */ - oid aggfinaltype /* type returned by aggfinalfn */ - text agginitval1 /* external format of initial - (starting) value of aggtransfn1 */ - text agginitval2 /* external format of initial - (starting) value of aggtransfn2 */ + oid aggtranstype /* type of aggregate's transition + (state) data */ + oid aggfinaltype /* type of aggregate's final result */ + text agginitval /* external format of initial state value */ .fi .nf M pg_am diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml index 44291458d6..07b45c112a 100644 --- a/doc/src/sgml/ref/create_aggregate.sgml +++ b/doc/src/sgml/ref/create_aggregate.sgml @@ -1,5 +1,5 @@ @@ -21,20 +21,18 @@ Postgres documentation - 1999-07-20 + 2000-07-16 -CREATE AGGREGATE name ( BASETYPE = input_data_type - [ , SFUNC1 = sfunc1, STYPE1 = state1_type ] - [ , SFUNC2 = sfunc2, STYPE2 = state2_type ] +CREATE AGGREGATE name ( BASETYPE = input_data_type, + SFUNC = sfunc, STYPE = state_type [ , FINALFUNC = ffunc ] - [ , INITCOND1 = initial_condition1 ] - [ , INITCOND2 = initial_condition2 ] ) + [ , INITCOND = initial_condition ] ) - 1998-09-09 + 2000-07-16 Inputs @@ -55,57 +53,39 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( BASETYPE = <listitem> <para> The input data type on which this aggregate function operates. + This can be specified as ANY for an aggregate that does not + examine its input values + (an example is <function>count(*)</function>). </para> </listitem> </varlistentry> <varlistentry> - <term><replaceable class="PARAMETER">sfunc1</replaceable></term> + <term><replaceable class="PARAMETER">sfunc</replaceable></term> <listitem> <para> - A state transition function - to be called for every non-NULL input data value. - This must be a function of two arguments, the first being of - type <replaceable class="PARAMETER">state1_type</replaceable> + The name of the state transition function + to be called for each input data value. + This is normally a function of two arguments, the first being of + type <replaceable class="PARAMETER">state_type</replaceable> and the second of type <replaceable class="PARAMETER">input_data_type</replaceable>. - The function must return a value of - type <replaceable class="PARAMETER">state1_type</replaceable>. - This function takes the current state value 1 and the current - input data item, and returns the next state value 1. + Alternatively, for an aggregate that does not examine its input + values, the function takes just one argument of + type <replaceable class="PARAMETER">state_type</replaceable>. + In either case the function must return a value of + type <replaceable class="PARAMETER">state_type</replaceable>. + This function takes the current state value and the current + input data item, and returns the next state value. </para> </listitem> </varlistentry> <varlistentry> - <term><replaceable class="PARAMETER">state1_type</replaceable></term> + <term><replaceable class="PARAMETER">state_type</replaceable></term> <listitem> <para> - The data type for the first state value of the aggregate. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><replaceable class="PARAMETER">sfunc2</replaceable></term> - <listitem> - <para> - A state transition function - to be called for every non-NULL input data value. - This must be a function of one argument of - type <replaceable class="PARAMETER">state2_type</replaceable>, - returning a value of the same type. - This function takes the current state value 2 and - returns the next state value 2. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><replaceable class="PARAMETER">state2_type</replaceable></term> - <listitem> - <para> - The data type for the second state value of the aggregate. + The data type for the aggregate's state value. </para> </listitem> </varlistentry> @@ -114,35 +94,28 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( BASETYPE = <term><replaceable class="PARAMETER">ffunc</replaceable></term> <listitem> <para> - The final function called to compute the aggregate's result - after all input data has been traversed. - If both state values are used, the final function must - take two arguments of types - <replaceable class="PARAMETER">state1_type</replaceable> - and - <replaceable class="PARAMETER">state2_type</replaceable>. - If only one state value is used, the final function must - take a single argument of that state value's type. + The name of the final function called to compute the aggregate's + result after all input data has been traversed. The function + must take a single argument of type + <replaceable class="PARAMETER">state_type</replaceable>. The output datatype of the aggregate is defined as the return type of this function. + If <replaceable class="PARAMETER">ffunc</replaceable> + is not specified, then the ending state value is used as the + aggregate's result, and the output type is + <replaceable class="PARAMETER">state_type</replaceable>. </para> </listitem> </varlistentry> <varlistentry> - <term><replaceable class="PARAMETER">initial_condition1</replaceable></term> + <term><replaceable class="PARAMETER">initial_condition</replaceable></term> <listitem> <para> - The initial value for state value 1. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><replaceable class="PARAMETER">initial_condition2</replaceable></term> - <listitem> - <para> - The initial value for state value 2. + The initial setting for the state value. This must be a literal + constant in the form accepted for the datatype + <replaceable class="PARAMETER">state_type</replaceable>. + If not specified, the state value starts out NULL. </para> </listitem> </varlistentry> @@ -177,7 +150,7 @@ CREATE <refsect1 id="R1-SQL-CREATEAGGREGATE-1"> <refsect1info> - <date>1998-09-09</date> + <date>2000-07-16</date> </refsect1info> <title> Description @@ -199,65 +172,76 @@ CREATE of the same name and input data type as an aggregate. </para> <para> - An aggregate function is made from between one and three ordinary + An aggregate function is made from one or two ordinary functions: - two state transition functions, - <replaceable class="PARAMETER">sfunc1</replaceable> - and <replaceable class="PARAMETER">sfunc2</replaceable>, - and a final calculation function, + a state transition function + <replaceable class="PARAMETER">sfunc</replaceable>, + and an optional final calculation function <replaceable class="PARAMETER">ffunc</replaceable>. These are used as follows: <programlisting> -<replaceable class="PARAMETER">sfunc1</replaceable>( internal-state1, next-data-item ) ---> next-internal-state1 -<replaceable class="PARAMETER">sfunc2</replaceable>( internal-state2 ) ---> next-internal-state2 -<replaceable class="PARAMETER">ffunc</replaceable>(internal-state1, internal-state2) ---> aggregate-value +<replaceable class="PARAMETER">sfunc</replaceable>( internal-state, next-data-item ) ---> next-internal-state +<replaceable class="PARAMETER">ffunc</replaceable>( internal-state ) ---> aggregate-value </programlisting> </para> <para> - <productname>Postgres</productname> creates one or two temporary variables - (of data types <replaceable class="PARAMETER">stype1</replaceable> and/or - <replaceable class="PARAMETER">stype2</replaceable>) to hold the - current internal states of the aggregate. At each input data item, - the state transition function(s) are invoked to calculate new values - for the internal state values. After all the data has been processed, + <productname>Postgres</productname> creates a temporary variable + of data type <replaceable class="PARAMETER">stype</replaceable> + to hold the current internal state of the aggregate. At each input + data item, + the state transition function is invoked to calculate a new + internal state value. After all the data has been processed, the final function is invoked once to calculate the aggregate's output - value. + value. If there is no final function then the ending state value + is returned as-is. </para> - <para> - <replaceable class="PARAMETER">ffunc</replaceable> must be specified if - both transition functions are specified. If only one transition function - is used, then <replaceable class="PARAMETER">ffunc</replaceable> is - optional. The default behavior when - <replaceable class="PARAMETER">ffunc</replaceable> is not provided is - to return the ending value of the internal state value being used - (and, therefore, the aggregate's output type is the same as that - state value's type). - </para> - <para> - An aggregate function may also provide one or two initial conditions, - that is, initial values for the internal state values being used. - These are specified and stored in the database as fields of type - <type>text</type>, but they must be valid external representations - of constants of the state value datatypes. If - <replaceable class="PARAMETER">sfunc1</replaceable> is specified - without an <replaceable class="PARAMETER">initcond1</replaceable> value, - then the system does not call - <replaceable class="PARAMETER">sfunc1</replaceable> - at the first input item; instead, the internal state value 1 is - initialized with the first input value, and - <replaceable class="PARAMETER">sfunc1</replaceable> is called beginning - at the second input item. This is useful for aggregates like MIN and - MAX. Note that an aggregate using this feature will return NULL when - called with no input values. There is no comparable provision for - state value 2; if <replaceable class="PARAMETER">sfunc2</replaceable> is - specified then an <replaceable class="PARAMETER">initcond2</replaceable> is - required. + <para> + An aggregate function may provide an initial condition, + that is, an initial value for the internal state value. + This is specified and stored in the database as a field of type + <type>text</type>, but it must be a valid external representation + of a constant of the state value datatype. If it is not supplied + then the state value starts out NULL. + </para> + + <para> + If the state transition function is declared "strict" in pg_proc, + then it cannot be called with NULL inputs. With such a transition + function, aggregate execution behaves as follows. NULL input values + are ignored (the function is not called and the previous state value + is retained). If the initial state value is NULL, then the first + non-NULL input value replaces the state value, and the transition + function is invoked beginning with the second non-NULL input value. + This is handy for implementing aggregates like <function>max</function>. + Note that this behavior is only available when + <replaceable class="PARAMETER">state_type</replaceable> + is the same as + <replaceable class="PARAMETER">input_data_type</replaceable>. + When these types are different, you must supply a non-NULL initial + condition or use a non-strict transition function. + </para> + + <para> + If the state transition function is not strict, then it will be called + unconditionally at each input value, and must deal with NULL inputs + and NULL transition values for itself. This allows the aggregate + author to have full control over the aggregate's handling of NULLs. + </para> + + <para> + If the final function is declared "strict", then it will not + be called when the ending state value is NULL; instead a NULL result + will be output automatically. (Of course this is just the normal + behavior of strict functions.) In any case the final function has + the option of returning NULL. For example, the final function for + <function>avg</function> returns NULL when it sees there were zero + input tuples. </para> <refsect2 id="R2-SQL-CREATEAGGREGATE-3"> <refsect2info> - <date>1998-09-09</date> + <date>2000-07-16</date> </refsect2info> <title> Notes @@ -272,29 +256,6 @@ CREATE in any order, not just the order illustrated above. </para> - <para> - It is possible to specify aggregate functions - that have varying combinations of state and final functions. - For example, the <function>count</function> aggregate requires - <replaceable class="PARAMETER">sfunc2</replaceable> - (an incrementing function) but not - <replaceable class="PARAMETER">sfunc1</replaceable> or - <replaceable class="PARAMETER">ffunc</replaceable>, - whereas the <function>sum</function> aggregate requires - <replaceable class="PARAMETER">sfunc1</replaceable> (an addition - function) but not <replaceable class="PARAMETER">sfunc2</replaceable> or - <replaceable class="PARAMETER">ffunc</replaceable>, and the - <function>avg</function> - aggregate requires - both state functions as - well as a <replaceable class="PARAMETER">ffunc</replaceable> (a division - function) to produce its - answer. In any case, at least one state function must be - defined, and any <replaceable class="PARAMETER">sfunc2</replaceable> must - have a corresponding - <replaceable class="PARAMETER">initcond2</replaceable>. - </para> - </refsect2> </refsect1> diff --git a/doc/src/sgml/ref/drop_aggregate.sgml b/doc/src/sgml/ref/drop_aggregate.sgml index 465f5d6421..8efc31a732 100644 --- a/doc/src/sgml/ref/drop_aggregate.sgml +++ b/doc/src/sgml/ref/drop_aggregate.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_aggregate.sgml,v 1.7 2000/05/18 14:24:32 momjian Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_aggregate.sgml,v 1.8 2000/07/17 03:04:41 tgl Exp $ Postgres documentation --> @@ -49,7 +49,7 @@ DROP AGGREGATE <replaceable class="PARAMETER">name</replaceable> <replaceable cl <para> The type of an existing aggregate function. (Refer to the <citetitle>PostgreSQL User's Guide</citetitle> for - further information about data types). + further information about data types.) <comment>This should become a cross-reference rather than a hard-coded chapter number</comment> </para> @@ -80,7 +80,7 @@ DROP </varlistentry> <varlistentry> <term><computeroutput> -NOTICE RemoveAggregate: aggregate '<replaceable class="parameter">agg</replaceable>' for '<replaceable class="parameter">type</replaceable>' does not exist +ERROR: RemoveAggregate: aggregate '<replaceable class="parameter">agg</replaceable>' for '<replaceable class="parameter">type</replaceable>' does not exist </computeroutput></term> <listitem> <para> diff --git a/doc/src/sgml/xaggr.sgml b/doc/src/sgml/xaggr.sgml index 8d5cb93a2d..c1e32f9b01 100644 --- a/doc/src/sgml/xaggr.sgml +++ b/doc/src/sgml/xaggr.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/xaggr.sgml,v 1.7 2000/03/31 03:27:41 thomas Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/xaggr.sgml,v 1.8 2000/07/17 03:04:40 tgl Exp $ --> <chapter id="xaggr"> @@ -16,39 +16,20 @@ $Header: /cvsroot/pgsql/doc/src/sgml/xaggr.sgml,v 1.7 2000/03/31 03:27:41 thomas an initial value for the state, and a state transition function. The state transition function is just an ordinary function that could also be used outside the - context of the aggregate. + context of the aggregate. A <firstterm>final function</firstterm> + can also be specified, in case the desired output of the aggregate + is different from the data that needs to be kept in the running + state value. </para> <para> - Actually, in order to make it easier to construct useful - aggregates from existing functions, an aggregate can have - one or two separate state values, one or two transition - functions to update those state values, and a - <firstterm>final function</firstterm> that computes the - actual aggregate result from the ending state values. + Thus, in addition to the input and result datatypes seen by a user + of the aggregate, there is an internal state-value datatype that + may be different from both the input and result types. </para> <para> - Thus there can be as many as four datatypes involved: - the type of the input data items, the type of the aggregate's - result, and the types of the two state values. Only the - input and result datatypes are seen by a user of the aggregate. - </para> - - <para> - Some state transition functions need to look at each successive - input to compute the next state value, while others ignore the - specific input value and simply update their internal state. - (The most useful example of the second kind is a running count - of the number of input items.) The <productname>Postgres</productname> - aggregate machinery defines <acronym>sfunc1</acronym> for - an aggregate as a function that is passed both the old state - value and the current input value, while <acronym>sfunc2</acronym> - is a function that is passed only the old state value. - </para> - - <para> - If we define an aggregate that uses only <acronym>sfunc1</acronym>, + If we define an aggregate that does not use a final function, we have an aggregate that computes a running function of the attribute values from each instance. "Sum" is an example of this kind of aggregate. "Sum" starts at @@ -60,10 +41,10 @@ $Header: /cvsroot/pgsql/doc/src/sgml/xaggr.sgml,v 1.7 2000/03/31 03:27:41 thomas <programlisting> CREATE AGGREGATE complex_sum ( - sfunc1 = complex_add, + sfunc = complex_add, basetype = complex, - stype1 = complex, - initcond1 = '(0,0)' + stype = complex, + initcond = '(0,0)' ); SELECT complex_sum(a) FROM test_complex; @@ -81,67 +62,48 @@ SELECT complex_sum(a) FROM test_complex; </para> <para> - If we define only <acronym>sfunc2</acronym>, we are - specifying an aggregate - that computes a running function that is independent of - the attribute values from each instance. - "Count" is the most common example of this kind of - aggregate. "Count" starts at zero and adds one to its - running total for each instance, ignoring the instance - value. Here, we use the built-in - <acronym>int4inc</acronym> routine to do - the work for us. This routine increments (adds one to) - its argument. - - <programlisting> -CREATE AGGREGATE my_count ( - sfunc2 = int4inc, -- add one - basetype = int4, - stype2 = int4, - initcond2 = '0' -); - -SELECT my_count(*) as emp_count from EMP; - - +----------+ - |emp_count | - +----------+ - |5 | - +----------+ - </programlisting> + The above definition of "Sum" will return zero (the initial + state condition) if there are no non-null input values. + Perhaps we want to return NULL in that case instead --- SQL92 + expects "Sum" to behave that way. We can do this simply by + omitting the "initcond" phrase, so that the initial state + condition is NULL. Ordinarily this would mean that the sfunc + would need to check for a NULL state-condition input, but for + "Sum" and some other simple aggregates like "Max" and "Min", + it's sufficient to insert the first non-null input value into + the state variable and then start applying the transition function + at the second non-null input value. <productname>Postgres</productname> + will do that automatically if the initial condition is NULL and + the transition function is marked "strict" (ie, not to be called + for NULL inputs). </para> <para> - "Average" is an example of an aggregate that requires - both a function to compute the running sum and a function - to compute the running count. When all of the - instances have been processed, the final answer for the - aggregate is the running sum divided by the running - count. We use the <acronym>int4pl</acronym> and - <acronym>int4inc</acronym> routines we used - before as well as the <productname>Postgres</productname> integer division - routine, <acronym>int4div</acronym>, to compute the division of the sum by - the count. - + Another bit of default behavior for a "strict" transition function + is that the previous state value is retained unchanged whenever a + NULL input value is encountered. Thus, NULLs are ignored. If you + need some other behavior for NULL inputs, just define your transition + function as non-strict, and code it to test for NULL inputs and do + whatever is needed. + </para> + + <para> + "Average" is a more complex example of an aggregate. It requires + two pieces of running state: the sum of the inputs and the count + of the number of inputs. The final result is obtained by dividing + these quantities. Average is typically implemented by using a + two-element array as the transition state value. For example, + the built-in implementation of <function>avg(float8)</function> + looks like: + <programlisting> -CREATE AGGREGATE my_average ( - sfunc1 = int4pl, -- sum - basetype = int4, - stype1 = int4, - sfunc2 = int4inc, -- count - stype2 = int4, - finalfunc = int4div, -- division - initcond1 = '0', - initcond2 = '0' +CREATE AGGREGATE avg ( + sfunc = float8_accum, + basetype = float8, + stype = _float8, + finalfunc = float8_avg, + initcond = '{0,0}' ); - -SELECT my_average(salary) as emp_average FROM EMP; - - +------------+ - |emp_average | - +------------+ - |1640 | - +------------+ </programlisting> </para> diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index f068c897ec..e3fa7c5535 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.34 2000/07/05 23:11:07 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.35 2000/07/17 03:04:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -21,6 +21,8 @@ #include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "miscadmin.h" +#include "parser/parse_coerce.h" +#include "parser/parse_func.h" #include "utils/builtins.h" #include "utils/syscache.h" @@ -36,13 +38,7 @@ * Currently, redefining aggregates using the same name is not * supported. In such a case, a warning is printed that the * aggregate already exists. If such is not the case, a new tuple - * is created and inserted in the aggregate relation. The fields - * of this tuple are aggregate name, owner id, 2 transition functions - * (called aggtransfn1 and aggtransfn2), final function (aggfinalfn), - * type of data on which aggtransfn1 operates (aggbasetype), return - * types of the two transition functions (aggtranstype1 and - * aggtranstype2), final return type (aggfinaltype), and initial values - * for the two state transition functions (agginitval1 and agginitval2). + * is created and inserted in the aggregate relation. * All types and functions must have been defined * prior to defining the aggregate. * @@ -50,31 +46,27 @@ */ void AggregateCreate(char *aggName, - char *aggtransfn1Name, - char *aggtransfn2Name, + char *aggtransfnName, char *aggfinalfnName, char *aggbasetypeName, - char *aggtransfn1typeName, - char *aggtransfn2typeName, - char *agginitval1, - char *agginitval2) + char *aggtranstypeName, + char *agginitval) { - int i; Relation aggdesc; HeapTuple tup; char nulls[Natts_pg_aggregate]; Datum values[Natts_pg_aggregate]; Form_pg_proc proc; - Oid xfn1 = InvalidOid; - Oid xfn2 = InvalidOid; - Oid ffn = InvalidOid; - Oid xbase = InvalidOid; - Oid xret1 = InvalidOid; - Oid xret2 = InvalidOid; - Oid fret = InvalidOid; + Oid transfn; + Oid finalfn = InvalidOid; /* can be omitted */ + Oid basetype; + Oid transtype; + Oid finaltype; Oid fnArgs[FUNC_MAX_ARGS]; + int nargs; NameData aname; TupleDesc tupDesc; + int i; MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid)); @@ -82,143 +74,112 @@ AggregateCreate(char *aggName, if (!aggName) elog(ERROR, "AggregateCreate: no aggregate name supplied"); - if (!aggtransfn1Name && !aggtransfn2Name) - elog(ERROR, "AggregateCreate: aggregate must have at least one transition function"); + if (!aggtransfnName) + elog(ERROR, "AggregateCreate: aggregate must have a transition function"); - if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName) - elog(ERROR, "AggregateCreate: Aggregate must have final function with both transition functions"); - - /* handle the aggregate's base type (input data type) */ + /* + * Handle the aggregate's base type (input data type). This can be + * specified as 'ANY' for a data-independent transition function, + * such as COUNT(*). + */ tup = SearchSysCacheTuple(TYPENAME, PointerGetDatum(aggbasetypeName), 0, 0, 0); - if (!HeapTupleIsValid(tup)) - elog(ERROR, "AggregateCreate: Type '%s' undefined", aggbasetypeName); - xbase = tup->t_data->t_oid; + if (HeapTupleIsValid(tup)) + { + basetype = tup->t_data->t_oid; + Assert(OidIsValid(basetype)); + } + else + { + if (strcasecmp(aggbasetypeName, "ANY") != 0) + elog(ERROR, "AggregateCreate: Type '%s' undefined", + aggbasetypeName); + basetype = InvalidOid; + } /* make sure there is no existing agg of same name and base type */ tup = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggName), - ObjectIdGetDatum(xbase), + ObjectIdGetDatum(basetype), 0, 0); if (HeapTupleIsValid(tup)) elog(ERROR, "AggregateCreate: aggregate '%s' with base type '%s' already exists", aggName, aggbasetypeName); - /* handle transfn1 and transtype1 */ - if (aggtransfn1Name) - { - tup = SearchSysCacheTuple(TYPENAME, - PointerGetDatum(aggtransfn1typeName), - 0, 0, 0); - if (!HeapTupleIsValid(tup)) - elog(ERROR, "AggregateCreate: Type '%s' undefined", - aggtransfn1typeName); - xret1 = tup->t_data->t_oid; + /* handle transtype */ + tup = SearchSysCacheTuple(TYPENAME, + PointerGetDatum(aggtranstypeName), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "AggregateCreate: Type '%s' undefined", + aggtranstypeName); + transtype = tup->t_data->t_oid; + Assert(OidIsValid(transtype)); - fnArgs[0] = xret1; - fnArgs[1] = xbase; - tup = SearchSysCacheTuple(PROCNAME, - PointerGetDatum(aggtransfn1Name), - Int32GetDatum(2), - PointerGetDatum(fnArgs), - 0); - if (!HeapTupleIsValid(tup)) - elog(ERROR, "AggregateCreate: '%s('%s', '%s') does not exist", - aggtransfn1Name, aggtransfn1typeName, aggbasetypeName); - if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret1) - elog(ERROR, "AggregateCreate: return type of '%s' is not '%s'", - aggtransfn1Name, aggtransfn1typeName); - xfn1 = tup->t_data->t_oid; - if (!OidIsValid(xfn1) || !OidIsValid(xret1) || - !OidIsValid(xbase)) - elog(ERROR, "AggregateCreate: bogus function '%s'", aggtransfn1Name); + /* handle transfn */ + fnArgs[0] = transtype; + if (OidIsValid(basetype)) + { + fnArgs[1] = basetype; + nargs = 2; + } + else + { + nargs = 1; + } + tup = SearchSysCacheTuple(PROCNAME, + PointerGetDatum(aggtransfnName), + Int32GetDatum(nargs), + PointerGetDatum(fnArgs), + 0); + if (!HeapTupleIsValid(tup)) + func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL); + transfn = tup->t_data->t_oid; + proc = (Form_pg_proc) GETSTRUCT(tup); + if (proc->prorettype != transtype) + elog(ERROR, "AggregateCreate: return type of '%s' is not '%s'", + aggtransfnName, aggtranstypeName); + Assert(OidIsValid(transfn)); + /* + * If the transfn is strict and the initval is NULL, make sure + * input type and transtype are the same (or at least binary- + * compatible), so that it's OK to use the first input value + * as the initial transValue. + */ + if (((Form_pg_proc) GETSTRUCT(tup))->proisstrict && agginitval == NULL) + { + if (basetype != transtype && + ! IS_BINARY_COMPATIBLE(basetype, transtype)) + elog(ERROR, "AggregateCreate: must not omit initval when transfn is strict and transtype is not compatible with input type"); } - /* handle transfn2 and transtype2 */ - if (aggtransfn2Name) + /* handle finalfn, if supplied */ + if (aggfinalfnName) { - tup = SearchSysCacheTuple(TYPENAME, - PointerGetDatum(aggtransfn2typeName), - 0, 0, 0); - if (!HeapTupleIsValid(tup)) - elog(ERROR, "AggregateCreate: Type '%s' undefined", - aggtransfn2typeName); - xret2 = tup->t_data->t_oid; - - fnArgs[0] = xret2; + fnArgs[0] = transtype; fnArgs[1] = 0; tup = SearchSysCacheTuple(PROCNAME, - PointerGetDatum(aggtransfn2Name), + PointerGetDatum(aggfinalfnName), Int32GetDatum(1), PointerGetDatum(fnArgs), 0); if (!HeapTupleIsValid(tup)) - elog(ERROR, "AggregateCreate: '%s'('%s') does not exist", - aggtransfn2Name, aggtransfn2typeName); - if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret2) - elog(ERROR, "AggregateCreate: return type of '%s' is not '%s'", - aggtransfn2Name, aggtransfn2typeName); - xfn2 = tup->t_data->t_oid; - if (!OidIsValid(xfn2) || !OidIsValid(xret2)) - elog(ERROR, "AggregateCreate: bogus function '%s'", aggtransfn2Name); - } - - /* handle finalfn */ - if (aggfinalfnName) - { - int nargs = 0; - - if (OidIsValid(xret1)) - fnArgs[nargs++] = xret1; - if (OidIsValid(xret2)) - fnArgs[nargs++] = xret2; - fnArgs[nargs] = 0; /* make sure slot 2 is empty if just 1 arg */ - tup = SearchSysCacheTuple(PROCNAME, - PointerGetDatum(aggfinalfnName), - Int32GetDatum(nargs), - PointerGetDatum(fnArgs), - 0); - if (!HeapTupleIsValid(tup)) - { - if (nargs == 2) - elog(ERROR, "AggregateCreate: '%s'('%s','%s') does not exist", - aggfinalfnName, aggtransfn1typeName, aggtransfn2typeName); - else if (OidIsValid(xret1)) - elog(ERROR, "AggregateCreate: '%s'('%s') does not exist", - aggfinalfnName, aggtransfn1typeName); - else - elog(ERROR, "AggregateCreate: '%s'('%s') does not exist", - aggfinalfnName, aggtransfn2typeName); - } - ffn = tup->t_data->t_oid; + func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL); + finalfn = tup->t_data->t_oid; proc = (Form_pg_proc) GETSTRUCT(tup); - fret = proc->prorettype; - if (!OidIsValid(ffn) || !OidIsValid(fret)) - elog(ERROR, "AggregateCreate: bogus function '%s'", aggfinalfnName); + finaltype = proc->prorettype; + Assert(OidIsValid(finalfn)); } else { - /* - * If no finalfn, aggregate result type is type of the sole state - * value (we already checked there is only one) + * If no finalfn, aggregate result type is type of the state value */ - if (OidIsValid(xret1)) - fret = xret1; - else - fret = xret2; + finaltype = transtype; } - Assert(OidIsValid(fret)); - - /* - * If transition function 2 is defined, it must have an initial value, - * whereas transition function 1 need not, which allows max and min - * aggregates to return NULL if they are evaluated on empty sets. - */ - if (OidIsValid(xfn2) && !agginitval2) - elog(ERROR, "AggregateCreate: transition function 2 MUST have an initial value"); + Assert(OidIsValid(finaltype)); /* initialize nulls and values */ for (i = 0; i < Natts_pg_aggregate; i++) @@ -229,25 +190,17 @@ AggregateCreate(char *aggName, namestrcpy(&aname, aggName); values[Anum_pg_aggregate_aggname - 1] = NameGetDatum(&aname); values[Anum_pg_aggregate_aggowner - 1] = Int32GetDatum(GetUserId()); - values[Anum_pg_aggregate_aggtransfn1 - 1] = ObjectIdGetDatum(xfn1); - values[Anum_pg_aggregate_aggtransfn2 - 1] = ObjectIdGetDatum(xfn2); - values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(ffn); - values[Anum_pg_aggregate_aggbasetype - 1] = ObjectIdGetDatum(xbase); - values[Anum_pg_aggregate_aggtranstype1 - 1] = ObjectIdGetDatum(xret1); - values[Anum_pg_aggregate_aggtranstype2 - 1] = ObjectIdGetDatum(xret2); - values[Anum_pg_aggregate_aggfinaltype - 1] = ObjectIdGetDatum(fret); + values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn); + values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn); + values[Anum_pg_aggregate_aggbasetype - 1] = ObjectIdGetDatum(basetype); + values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(transtype); + values[Anum_pg_aggregate_aggfinaltype - 1] = ObjectIdGetDatum(finaltype); - if (agginitval1) - values[Anum_pg_aggregate_agginitval1 - 1] = - DirectFunctionCall1(textin, CStringGetDatum(agginitval1)); + if (agginitval) + values[Anum_pg_aggregate_agginitval - 1] = + DirectFunctionCall1(textin, CStringGetDatum(agginitval)); else - nulls[Anum_pg_aggregate_agginitval1 - 1] = 'n'; - - if (agginitval2) - values[Anum_pg_aggregate_agginitval2 - 1] = - DirectFunctionCall1(textin, CStringGetDatum(agginitval2)); - else - nulls[Anum_pg_aggregate_agginitval2 - 1] = 'n'; + nulls[Anum_pg_aggregate_agginitval - 1] = 'n'; aggdesc = heap_openr(AggregateRelationName, RowExclusiveLock); tupDesc = aggdesc->rd_att; @@ -271,11 +224,9 @@ AggregateCreate(char *aggName, } Datum -AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull) +AggNameGetInitVal(char *aggName, Oid basetype, bool *isNull) { HeapTuple tup; - Relation aggRel; - int initValAttno; Oid transtype, typinput, typelem; @@ -285,15 +236,6 @@ AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull) Assert(PointerIsValid(aggName)); Assert(PointerIsValid(isNull)); - Assert(xfuncno == 1 || xfuncno == 2); - - /* - * since we will have to use fastgetattr (in case one or both init - * vals are NULL), we will need to open the relation. Do that first - * to ensure we don't get a stale tuple from the cache. - */ - - aggRel = heap_openr(AggregateRelationName, AccessShareLock); tup = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggName), @@ -302,29 +244,19 @@ AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull) if (!HeapTupleIsValid(tup)) elog(ERROR, "AggNameGetInitVal: cache lookup failed for aggregate '%s'", aggName); - if (xfuncno == 1) - { - transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype1; - initValAttno = Anum_pg_aggregate_agginitval1; - } - else - { - /* can only be 1 or 2 */ - transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype2; - initValAttno = Anum_pg_aggregate_agginitval2; - } + transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype; - textInitVal = fastgetattr(tup, initValAttno, - RelationGetDescr(aggRel), - isNull); + /* + * initval is potentially null, so don't try to access it as a struct + * field. Must do it the hard way with SysCacheGetAttr. + */ + textInitVal = SysCacheGetAttr(AGGNAME, tup, + Anum_pg_aggregate_agginitval, + isNull); if (*isNull) - { - heap_close(aggRel, AccessShareLock); - return PointerGetDatum(NULL); - } - strInitVal = DatumGetCString(DirectFunctionCall1(textout, textInitVal)); + return (Datum) 0; - heap_close(aggRel, AccessShareLock); + strInitVal = DatumGetCString(DirectFunctionCall1(textout, textInitVal)); tup = SearchSysCacheTuple(TYPEOID, ObjectIdGetDatum(transtype), diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c index d3da6cc2b2..b90ef61a3b 100644 --- a/src/backend/commands/define.c +++ b/src/backend/commands/define.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.44 2000/07/03 23:09:33 wieck Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.45 2000/07/17 03:04:44 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -484,16 +484,12 @@ DefineOperator(char *oprName, */ void DefineAggregate(char *aggName, List *parameters) - { - char *stepfunc1Name = NULL; - char *stepfunc2Name = NULL; + char *transfuncName = NULL; char *finalfuncName = NULL; char *baseType = NULL; - char *stepfunc1Type = NULL; - char *stepfunc2Type = NULL; - char *init1 = NULL; - char *init2 = NULL; + char *transType = NULL; + char *initval = NULL; List *pl; foreach(pl, parameters) @@ -501,47 +497,28 @@ DefineAggregate(char *aggName, List *parameters) DefElem *defel = (DefElem *) lfirst(pl); /* - * sfunc1 + * sfunc1, stype1, and initcond1 are accepted as obsolete spellings + * for sfunc, stype, initcond. */ - if (!strcasecmp(defel->defname, "sfunc1")) - stepfunc1Name = defGetString(defel); - else if (!strcasecmp(defel->defname, "basetype")) - baseType = defGetString(defel); - else if (!strcasecmp(defel->defname, "stype1")) - { - stepfunc1Type = defGetString(defel); - - /* - * sfunc2 - */ - } - else if (!strcasecmp(defel->defname, "sfunc2")) - stepfunc2Name = defGetString(defel); - else if (!strcasecmp(defel->defname, "stype2")) - { - stepfunc2Type = defGetString(defel); - - /* - * final - */ - } - else if (!strcasecmp(defel->defname, "finalfunc")) - { + if (strcasecmp(defel->defname, "sfunc") == 0) + transfuncName = defGetString(defel); + else if (strcasecmp(defel->defname, "sfunc1") == 0) + transfuncName = defGetString(defel); + else if (strcasecmp(defel->defname, "finalfunc") == 0) finalfuncName = defGetString(defel); - - /* - * initial conditions - */ - } - else if (!strcasecmp(defel->defname, "initcond1")) - init1 = defGetString(defel); - else if (!strcasecmp(defel->defname, "initcond2")) - init2 = defGetString(defel); + else if (strcasecmp(defel->defname, "basetype") == 0) + baseType = defGetString(defel); + else if (strcasecmp(defel->defname, "stype") == 0) + transType = defGetString(defel); + else if (strcasecmp(defel->defname, "stype1") == 0) + transType = defGetString(defel); + else if (strcasecmp(defel->defname, "initcond") == 0) + initval = defGetString(defel); + else if (strcasecmp(defel->defname, "initcond1") == 0) + initval = defGetString(defel); else - { elog(NOTICE, "DefineAggregate: attribute \"%s\" not recognized", defel->defname); - } } /* @@ -549,31 +526,20 @@ DefineAggregate(char *aggName, List *parameters) */ if (baseType == NULL) elog(ERROR, "Define: \"basetype\" unspecified"); - if (stepfunc1Name != NULL) - { - if (stepfunc1Type == NULL) - elog(ERROR, "Define: \"stype1\" unspecified"); - } - if (stepfunc2Name != NULL) - { - if (stepfunc2Type == NULL) - elog(ERROR, "Define: \"stype2\" unspecified"); - } + if (transType == NULL) + elog(ERROR, "Define: \"stype\" unspecified"); + if (transfuncName == NULL) + elog(ERROR, "Define: \"sfunc\" unspecified"); /* * Most of the argument-checking is done inside of AggregateCreate */ - AggregateCreate(aggName, /* aggregate name */ - stepfunc1Name, /* first step function name */ - stepfunc2Name, /* second step function name */ - finalfuncName, /* final function name */ - baseType, /* type of object being aggregated */ - stepfunc1Type, /* return type of first function */ - stepfunc2Type, /* return type of second function */ - init1, /* first initial condition */ - init2); /* second initial condition */ - - /* XXX free palloc'd memory */ + AggregateCreate(aggName, /* aggregate name */ + transfuncName, /* step function name */ + finalfuncName, /* final function name */ + baseType, /* type of data being aggregated */ + transType, /* transition data type */ + initval); /* initial condition */ } /* diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 4698fa850c..deaaf51df2 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.63 2000/07/05 23:11:11 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.64 2000/07/17 03:04:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -26,6 +26,7 @@ #include "commands/user.h" #include "libpq/crypt.h" #include "miscadmin.h" +#include "utils/array.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/syscache.h" diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index fd9d761ffc..48cd8aa169 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.73 2000/07/12 02:37:00 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.74 2000/07/17 03:04:51 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,6 +40,7 @@ #include "executor/execdebug.h" #include "executor/functions.h" #include "executor/nodeSubplan.h" +#include "utils/array.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/fcache2.h" diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index 40bbbd916f..a3f66d20ca 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -12,16 +12,19 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.12 2000/07/12 02:37:01 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.13 2000/07/17 03:04:53 tgl Exp $ * *------------------------------------------------------------------------- */ #include <sys/types.h> #include <sys/file.h> + #include "postgres.h" #include "executor/executor.h" +#include "utils/memutils.h" + /* ---------------------------------------------------------------- * ExecScan diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index 1ac5c3c9e2..547a946b4c 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -3,36 +3,38 @@ * nodeAgg.c * Routines to handle aggregate nodes. * - * ExecAgg evaluates each aggregate in the following steps: (initcond1, - * initcond2 are the initial values and sfunc1, sfunc2, and finalfunc are - * the transition functions.) + * ExecAgg evaluates each aggregate in the following steps: * - * value1 = initcond1 - * value2 = initcond2 + * transvalue = initcond * foreach input_value do - * value1 = sfunc1(value1, input_value) - * value2 = sfunc2(value2) - * value1 = finalfunc(value1, value2) + * transvalue = transfunc(transvalue, input_value) + * result = finalfunc(transvalue) * - * If initcond1 is NULL then the first non-NULL input_value is - * assigned directly to value1. sfunc1 isn't applied until value1 - * is non-NULL. + * If a finalfunc is not supplied then the result is just the ending + * value of transvalue. * - * sfunc1 is never applied when the current tuple's input_value is NULL. - * sfunc2 is applied for each tuple if the aggref is marked 'usenulls', - * otherwise it is only applied when input_value is not NULL. - * (usenulls was formerly used for COUNT(*), but is no longer needed for - * that purpose; as of 10/1999 the support for usenulls is dead code. - * I have not removed it because it seems like a potentially useful - * feature for user-defined aggregates. We'd just need to add a - * flag column to pg_aggregate and a parameter to CREATE AGGREGATE...) + * If transfunc is marked "strict" in pg_proc and initcond is NULL, + * then the first non-NULL input_value is assigned directly to transvalue, + * and transfunc isn't applied until the second non-NULL input_value. + * The agg's input type and transtype must be the same in this case! + * + * If transfunc is marked "strict" then NULL input_values are skipped, + * keeping the previous transvalue. If transfunc is not strict then it + * is called for every input tuple and must deal with NULL initcond + * or NULL input_value for itself. + * + * If finalfunc is marked "strict" then it is not called when the + * ending transvalue is NULL, instead a NULL result is created + * automatically (this is just the usual handling of strict functions, + * of course). A non-strict finalfunc can make its own choice of + * what to return for a NULL ending transvalue. * * * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.69 2000/07/12 02:37:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.70 2000/07/17 03:04:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -45,6 +47,7 @@ #include "executor/executor.h" #include "executor/nodeAgg.h" #include "optimizer/clauses.h" +#include "parser/parse_coerce.h" #include "parser/parse_expr.h" #include "parser/parse_oper.h" #include "parser/parse_type.h" @@ -67,16 +70,15 @@ typedef struct AggStatePerAggData Aggref *aggref; /* Oids of transfer functions */ - Oid xfn1_oid; - Oid xfn2_oid; - Oid finalfn_oid; + Oid transfn_oid; + Oid finalfn_oid; /* may be InvalidOid */ /* * fmgr lookup data for transfer functions --- only valid when - * corresponding oid is not InvalidOid + * corresponding oid is not InvalidOid. Note in particular that + * fn_strict flags are kept here. */ - FmgrInfo xfn1; - FmgrInfo xfn2; + FmgrInfo transfn; FmgrInfo finalfn; /* @@ -94,12 +96,10 @@ typedef struct AggStatePerAggData FmgrInfo equalfn; /* - * initial values from pg_aggregate entry + * initial value from pg_aggregate entry */ - Datum initValue1; /* for transtype1 */ - Datum initValue2; /* for transtype2 */ - bool initValue1IsNull, - initValue2IsNull; + Datum initValue; + bool initValueIsNull; /* * We need the len and byval info for the agg's input, result, and @@ -107,45 +107,42 @@ typedef struct AggStatePerAggData */ int inputtypeLen, resulttypeLen, - transtype1Len, - transtype2Len; + transtypeLen; bool inputtypeByVal, resulttypeByVal, - transtype1ByVal, - transtype2ByVal; + transtypeByVal; /* * These values are working state that is initialized at the start of * an input tuple group and updated for each input tuple. * * For a simple (non DISTINCT) aggregate, we just feed the input values - * straight to the transition functions. If it's DISTINCT, we pass + * straight to the transition function. If it's DISTINCT, we pass * the input values into a Tuplesort object; then at completion of the * input tuple group, we scan the sorted values, eliminate duplicates, - * and run the transition functions on the rest. + * and run the transition function on the rest. */ Tuplesortstate *sortstate; /* sort object, if a DISTINCT agg */ - Datum value1, /* current transfer values 1 and 2 */ - value2; - bool value1IsNull, - value2IsNull; - bool noInitValue; /* true if value1 not set yet */ + Datum transValue; + bool transValueIsNull; + + bool noTransValue; /* true if transValue not set yet */ /* - * Note: right now, noInitValue always has the same value as - * value1IsNull. But we should keep them separate because once the - * fmgr interface is fixed, we'll need to distinguish a null returned - * by transfn1 from a null we haven't yet replaced with an input - * value. + * Note: noTransValue initially has the same value as transValueIsNull, + * and if true both are cleared to false at the same time. They are + * not the same though: if transfn later returns a NULL, we want to + * keep that NULL and not auto-replace it with a later input value. + * Only the first non-NULL input will be auto-substituted. */ } AggStatePerAggData; static void initialize_aggregate(AggStatePerAgg peraggstate); -static void advance_transition_functions(AggStatePerAgg peraggstate, - Datum newVal, bool isNull); +static void advance_transition_function(AggStatePerAgg peraggstate, + Datum newVal, bool isNull); static void process_sorted_aggregate(AggState *aggstate, AggStatePerAgg peraggstate); static void finalize_aggregate(AggStatePerAgg peraggstate, @@ -182,144 +179,118 @@ initialize_aggregate(AggStatePerAgg peraggstate) } /* - * (Re)set value1 and value2 to their initial values. + * (Re)set transValue to the initial value. * - * Note that when the initial values are pass-by-ref, we just reuse - * them without copying for each group. Hence, transition function - * had better not scribble on its input! + * Note that when the initial value is pass-by-ref, we just reuse it + * without copying for each group. Hence, transition function + * had better not scribble on its input, or it will fail for GROUP BY! */ - peraggstate->value1 = peraggstate->initValue1; - peraggstate->value1IsNull = peraggstate->initValue1IsNull; - peraggstate->value2 = peraggstate->initValue2; - peraggstate->value2IsNull = peraggstate->initValue2IsNull; + peraggstate->transValue = peraggstate->initValue; + peraggstate->transValueIsNull = peraggstate->initValueIsNull; /* ------------------------------------------ - * If the initial value for the first transition function - * doesn't exist in the pg_aggregate table then we will let - * the first value returned from the outer procNode become - * the initial value. (This is useful for aggregates like - * max{} and min{}.) The noInitValue flag signals that we - * still need to do this. + * If the initial value for the transition state doesn't exist in the + * pg_aggregate table then we will let the first non-NULL value returned + * from the outer procNode become the initial value. (This is useful for + * aggregates like max() and min().) The noTransValue flag signals that + * we still need to do this. * ------------------------------------------ */ - peraggstate->noInitValue = peraggstate->initValue1IsNull; + peraggstate->noTransValue = peraggstate->initValueIsNull; } /* - * Given a new input value, advance the transition functions of an aggregate. + * Given a new input value, advance the transition function of an aggregate. * - * When called, CurrentMemoryContext should be the context we want transition - * function results to be delivered into on this cycle. - * - * Note: if the agg does not have usenulls set, null inputs will be filtered - * out before reaching here. + * When called, CurrentMemoryContext should be the context we want the + * transition function result to be delivered into on this cycle. */ static void -advance_transition_functions(AggStatePerAgg peraggstate, - Datum newVal, bool isNull) +advance_transition_function(AggStatePerAgg peraggstate, + Datum newVal, bool isNull) { FunctionCallInfoData fcinfo; - MemSet(&fcinfo, 0, sizeof(fcinfo)); - - /* - * XXX reconsider isNULL handling here - */ - if (OidIsValid(peraggstate->xfn1_oid) && !isNull) + if (peraggstate->transfn.fn_strict) { - if (peraggstate->noInitValue) + if (isNull) { - /* - * value1 has not been initialized. This is the first non-NULL - * input value. We use it as the initial value for value1. - * - * XXX We assume, without having checked, that the agg's input - * type is binary-compatible with its transtype1! + * For a strict transfn, nothing happens at a NULL input tuple; + * we just keep the prior transValue. However, if the transtype + * is pass-by-ref, we have to copy it into the new context + * because the old one is going to get reset. + */ + if (!peraggstate->transValueIsNull) + peraggstate->transValue = datumCopy(peraggstate->transValue, + peraggstate->transtypeByVal, + peraggstate->transtypeLen); + return; + } + if (peraggstate->noTransValue) + { + /* + * transValue has not been initialized. This is the first non-NULL + * input value. We use it as the initial value for transValue. + * (We already checked that the agg's input type is binary- + * compatible with its transtype, so straight copy here is OK.) * * We had better copy the datum if it is pass-by-ref, since * the given pointer may be pointing into a scan tuple that * will be freed on the next iteration of the scan. */ - peraggstate->value1 = datumCopy(newVal, - peraggstate->transtype1ByVal, - peraggstate->transtype1Len); - peraggstate->value1IsNull = false; - peraggstate->noInitValue = false; + peraggstate->transValue = datumCopy(newVal, + peraggstate->transtypeByVal, + peraggstate->transtypeLen); + peraggstate->transValueIsNull = false; + peraggstate->noTransValue = false; + return; } - else + if (peraggstate->transValueIsNull) { - /* apply transition function 1 */ - fcinfo.flinfo = &peraggstate->xfn1; - fcinfo.nargs = 2; - fcinfo.arg[0] = peraggstate->value1; - fcinfo.argnull[0] = peraggstate->value1IsNull; - fcinfo.arg[1] = newVal; - fcinfo.argnull[1] = isNull; - if (fcinfo.flinfo->fn_strict && - (peraggstate->value1IsNull || isNull)) - { - /* don't call a strict function with NULL inputs */ - newVal = (Datum) 0; - fcinfo.isnull = true; - } - else - newVal = FunctionCallInvoke(&fcinfo); /* - * If the transition function was uncooperative, it may have - * given us a pass-by-ref result that points at the scan tuple - * or the prior-cycle working memory. Copy it into the active - * context if it doesn't look right. + * Don't call a strict function with NULL inputs. Note it is + * possible to get here despite the above tests, if the transfn + * is strict *and* returned a NULL on a prior cycle. If that + * happens we will propagate the NULL all the way to the end. */ - if (!peraggstate->transtype1ByVal && !fcinfo.isnull && - ! MemoryContextContains(CurrentMemoryContext, - DatumGetPointer(newVal))) - newVal = datumCopy(newVal, - peraggstate->transtype1ByVal, - peraggstate->transtype1Len); - peraggstate->value1 = newVal; - peraggstate->value1IsNull = fcinfo.isnull; + return; } } - if (OidIsValid(peraggstate->xfn2_oid)) - { - /* apply transition function 2 */ - fcinfo.flinfo = &peraggstate->xfn2; - fcinfo.nargs = 1; - fcinfo.arg[0] = peraggstate->value2; - fcinfo.argnull[0] = peraggstate->value2IsNull; - fcinfo.isnull = false; /* must reset after use by xfn1 */ - if (fcinfo.flinfo->fn_strict && peraggstate->value2IsNull) - { - /* don't call a strict function with NULL inputs */ - newVal = (Datum) 0; - fcinfo.isnull = true; - } - else - newVal = FunctionCallInvoke(&fcinfo); - /* - * If the transition function was uncooperative, it may have - * given us a pass-by-ref result that points at the scan tuple - * or the prior-cycle working memory. Copy it into the active - * context if it doesn't look right. - */ - if (!peraggstate->transtype2ByVal && !fcinfo.isnull && - ! MemoryContextContains(CurrentMemoryContext, - DatumGetPointer(newVal))) - newVal = datumCopy(newVal, - peraggstate->transtype2ByVal, - peraggstate->transtype2Len); - peraggstate->value2 = newVal; - peraggstate->value2IsNull = fcinfo.isnull; - } + /* OK to call the transition function */ + MemSet(&fcinfo, 0, sizeof(fcinfo)); + fcinfo.flinfo = &peraggstate->transfn; + fcinfo.nargs = 2; + fcinfo.arg[0] = peraggstate->transValue; + fcinfo.argnull[0] = peraggstate->transValueIsNull; + fcinfo.arg[1] = newVal; + fcinfo.argnull[1] = isNull; + + newVal = FunctionCallInvoke(&fcinfo); + + /* + * If the transition function was uncooperative, it may have + * given us a pass-by-ref result that points at the scan tuple + * or the prior-cycle working memory. Copy it into the active + * context if it doesn't look right. + */ + if (!peraggstate->transtypeByVal && !fcinfo.isnull && + ! MemoryContextContains(CurrentMemoryContext, + DatumGetPointer(newVal))) + newVal = datumCopy(newVal, + peraggstate->transtypeByVal, + peraggstate->transtypeLen); + + peraggstate->transValue = newVal; + peraggstate->transValueIsNull = fcinfo.isnull; } /* - * Run the transition functions for a DISTINCT aggregate. This is called + * Run the transition function for a DISTINCT aggregate. This is called * after we have completed entering all the input values into the sort - * object. We complete the sort, read out the value in sorted order, and - * run the transition functions on each non-duplicate value. + * object. We complete the sort, read out the values in sorted order, + * and run the transition function on each non-duplicate value. * * When called, CurrentMemoryContext should be the per-query context. */ @@ -346,13 +317,13 @@ process_sorted_aggregate(AggState *aggstate, { /* * DISTINCT always suppresses nulls, per SQL spec, regardless of - * the aggregate's usenulls setting. + * the transition function's strictness. */ if (isNull) continue; /* * Clear and select the current working context for evaluation of - * the equality function and transition functions. + * the equality function and transition function. */ MemoryContextReset(aggstate->agg_cxt[aggstate->which_cxt]); oldContext = @@ -365,11 +336,14 @@ process_sorted_aggregate(AggState *aggstate, /* equal to prior, so forget this one */ if (!peraggstate->inputtypeByVal) pfree(DatumGetPointer(newVal)); - /* note we do NOT flip contexts in this case... */ + /* + * note we do NOT flip contexts in this case, so no need to + * copy prior transValue to other context. + */ } else { - advance_transition_functions(peraggstate, newVal, false); + advance_transition_function(peraggstate, newVal, false); /* * Make the other context current so that this transition * result is preserved. @@ -402,48 +376,19 @@ static void finalize_aggregate(AggStatePerAgg peraggstate, Datum *resultVal, bool *resultIsNull) { - FunctionCallInfoData fcinfo; - - MemSet(&fcinfo, 0, sizeof(fcinfo)); - /* - * Apply the agg's finalfn, or substitute the appropriate - * transition value if there is no finalfn. - * - * XXX For now, only apply finalfn if we got at least one non-null input - * value. This prevents zero divide in AVG(). If we had cleaner - * handling of null inputs/results in functions, we could probably - * take out this hack and define the result for no inputs as whatever - * finalfn returns for null input. + * Apply the agg's finalfn if one is provided, else return transValue. */ - if (OidIsValid(peraggstate->finalfn_oid) && - !peraggstate->noInitValue) + if (OidIsValid(peraggstate->finalfn_oid)) { + FunctionCallInfoData fcinfo; + + MemSet(&fcinfo, 0, sizeof(fcinfo)); fcinfo.flinfo = &peraggstate->finalfn; - if (peraggstate->finalfn.fn_nargs > 1) - { - fcinfo.nargs = 2; - fcinfo.arg[0] = peraggstate->value1; - fcinfo.argnull[0] = peraggstate->value1IsNull; - fcinfo.arg[1] = peraggstate->value2; - fcinfo.argnull[1] = peraggstate->value2IsNull; - } - else if (OidIsValid(peraggstate->xfn1_oid)) - { - fcinfo.nargs = 1; - fcinfo.arg[0] = peraggstate->value1; - fcinfo.argnull[0] = peraggstate->value1IsNull; - } - else if (OidIsValid(peraggstate->xfn2_oid)) - { - fcinfo.nargs = 1; - fcinfo.arg[0] = peraggstate->value2; - fcinfo.argnull[0] = peraggstate->value2IsNull; - } - else - elog(ERROR, "ExecAgg: no valid transition functions??"); - if (fcinfo.flinfo->fn_strict && - (fcinfo.argnull[0] || fcinfo.argnull[1])) + fcinfo.nargs = 1; + fcinfo.arg[0] = peraggstate->transValue; + fcinfo.argnull[0] = peraggstate->transValueIsNull; + if (fcinfo.flinfo->fn_strict && peraggstate->transValueIsNull) { /* don't call a strict function with NULL inputs */ *resultVal = (Datum) 0; @@ -455,20 +400,12 @@ finalize_aggregate(AggStatePerAgg peraggstate, *resultIsNull = fcinfo.isnull; } } - else if (OidIsValid(peraggstate->xfn1_oid)) - { - /* Return value1 */ - *resultVal = peraggstate->value1; - *resultIsNull = peraggstate->value1IsNull; - } - else if (OidIsValid(peraggstate->xfn2_oid)) - { - /* Return value2 */ - *resultVal = peraggstate->value2; - *resultIsNull = peraggstate->value2IsNull; - } else - elog(ERROR, "ExecAgg: no valid transition functions??"); + { + *resultVal = peraggstate->transValue; + *resultIsNull = peraggstate->transValueIsNull; + } + /* * If result is pass-by-ref, make sure it is in the right context. */ @@ -588,11 +525,11 @@ ExecAgg(Agg *node) newVal = ExecEvalExpr(aggref->target, econtext, &isNull, &isDone); - if (isNull && !aggref->usenulls) - continue; /* ignore this tuple for this agg */ - if (aggref->aggdistinct) { + /* in DISTINCT mode, we may ignore nulls */ + if (isNull) + continue; /* putdatum has to be called in per-query context */ MemoryContextSwitchTo(oldContext); tuplesort_putdatum(peraggstate->sortstate, @@ -600,8 +537,10 @@ ExecAgg(Agg *node) MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); } else - advance_transition_functions(peraggstate, - newVal, isNull); + { + advance_transition_function(peraggstate, + newVal, isNull); + } } /* @@ -889,8 +828,7 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent) HeapTuple aggTuple; Form_pg_aggregate aggform; Type typeInfo; - Oid xfn1_oid, - xfn2_oid, + Oid transfn_oid, finalfn_oid; /* Mark Aggref node with its associated index in the result array */ @@ -913,53 +851,51 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent) peraggstate->resulttypeLen = typeLen(typeInfo); peraggstate->resulttypeByVal = typeByVal(typeInfo); - peraggstate->initValue1 = + typeInfo = typeidType(aggform->aggtranstype); + peraggstate->transtypeLen = typeLen(typeInfo); + peraggstate->transtypeByVal = typeByVal(typeInfo); + + peraggstate->initValue = AggNameGetInitVal(aggname, aggform->aggbasetype, - 1, - &peraggstate->initValue1IsNull); + &peraggstate->initValueIsNull); - peraggstate->initValue2 = - AggNameGetInitVal(aggname, - aggform->aggbasetype, - 2, - &peraggstate->initValue2IsNull); - - peraggstate->xfn1_oid = xfn1_oid = aggform->aggtransfn1; - peraggstate->xfn2_oid = xfn2_oid = aggform->aggtransfn2; + peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn; peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn; - if (OidIsValid(xfn1_oid)) - { - fmgr_info(xfn1_oid, &peraggstate->xfn1); - /* If a transfn1 is specified, transtype1 had better be, too */ - typeInfo = typeidType(aggform->aggtranstype1); - peraggstate->transtype1Len = typeLen(typeInfo); - peraggstate->transtype1ByVal = typeByVal(typeInfo); - } - - if (OidIsValid(xfn2_oid)) - { - fmgr_info(xfn2_oid, &peraggstate->xfn2); - /* If a transfn2 is specified, transtype2 had better be, too */ - typeInfo = typeidType(aggform->aggtranstype2); - peraggstate->transtype2Len = typeLen(typeInfo); - peraggstate->transtype2ByVal = typeByVal(typeInfo); - /* ------------------------------------------ - * If there is a second transition function, its initial - * value must exist -- as it does not depend on data values, - * we have no other way of determining an initial value. - * ------------------------------------------ - */ - if (peraggstate->initValue2IsNull) - elog(ERROR, "ExecInitAgg: agginitval2 is null"); - } - + 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 + * input type and transtype are the same (or at least binary- + * compatible), so that it's OK to use the first input value + * as the initial transValue. This should have been checked at + * agg definition time, but just in case... + */ + if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull) + { + /* + * Note: use the type from the input expression here, + * not aggform->aggbasetype, because the latter might be 0. + * (Consider COUNT(*).) + */ + Oid inputType = exprType(aggref->target); + + if (inputType != aggform->aggtranstype && + ! IS_BINARY_COMPATIBLE(inputType, aggform->aggtranstype)) + elog(ERROR, "Aggregate %s needs to have compatible input type and transition type", + aggname); + } + if (aggref->aggdistinct) { + /* + * Note: use the type from the input expression here, + * not aggform->aggbasetype, because the latter might be 0. + * (Consider COUNT(*).) + */ Oid inputType = exprType(aggref->target); Operator eq_operator; Form_pg_operator pgopform; diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9c2d293858..f63ffe4943 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * - * $Id: nodeHash.c,v 1.49 2000/07/12 02:37:03 tgl Exp $ + * $Id: nodeHash.c,v 1.50 2000/07/17 03:04:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -30,6 +30,8 @@ #include "miscadmin.h" #include "parser/parse_expr.h" #include "parser/parse_type.h" +#include "utils/memutils.h" + static int hashFunc(Datum key, int len, bool byVal); diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index e136c13154..54af882db1 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.31 2000/07/12 02:37:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.32 2000/07/17 03:04:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,8 @@ #include "executor/nodeHash.h" #include "executor/nodeHashjoin.h" #include "optimizer/clauses.h" +#include "utils/memutils.h" + static TupleTableSlot *ExecHashJoinOuterGetTuple(Plan *node, Plan *parent, HashJoinState *hjstate); diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 0186e39436..70b98a97e3 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.17 2000/07/12 02:37:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.18 2000/07/17 03:04:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,10 +18,13 @@ * ExecInitNestLoop - initialize the join * ExecEndNestLoop - shut down the join */ + #include "postgres.h" #include "executor/execdebug.h" #include "executor/nodeNestloop.h" +#include "utils/memutils.h" + /* ---------------------------------------------------------------- * ExecNestLoop(node) diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index a1daaf52c4..770cc47ccc 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -34,7 +34,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.14 2000/07/12 02:37:04 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.15 2000/07/17 03:04:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -43,6 +43,8 @@ #include "executor/executor.h" #include "executor/nodeResult.h" +#include "utils/memutils.h" + /* ---------------------------------------------------------------- * ExecResult(node) diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c index 929ddad5aa..8d3af03664 100644 --- a/src/backend/libpq/be-fsstubs.c +++ b/src/backend/libpq/be-fsstubs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/be-fsstubs.c,v 1.49 2000/07/07 21:12:53 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/be-fsstubs.c,v 1.50 2000/07/17 03:04:54 tgl Exp $ * * NOTES * This should be moved to a more appropriate place. It is here @@ -43,6 +43,8 @@ #include "libpq/be-fsstubs.h" #include "libpq/libpq-fs.h" #include "storage/large_object.h" +#include "utils/memutils.h" + /* [PA] is Pascal André <andre@via.ecp.fr> */ diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index bc305382df..4013a0f77b 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -19,7 +19,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.116 2000/07/12 02:37:04 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.117 2000/07/17 03:04:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -843,7 +843,6 @@ _copyAggref(Aggref *from) newnode->basetype = from->basetype; newnode->aggtype = from->aggtype; Node_Copy(from, newnode, target); - newnode->usenulls = from->usenulls; newnode->aggstar = from->aggstar; newnode->aggdistinct = from->aggdistinct; newnode->aggno = from->aggno; /* probably not needed */ diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index b881478618..b1772e6436 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -24,7 +24,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.68 2000/07/12 02:37:04 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.69 2000/07/17 03:05:01 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -257,8 +257,6 @@ _equalAggref(Aggref *a, Aggref *b) return false; if (!equal(a->target, b->target)) return false; - if (a->usenulls != b->usenulls) - return false; if (a->aggstar != b->aggstar) return false; if (a->aggdistinct != b->aggdistinct) diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index f6fed84506..c561ad5126 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.122 2000/07/15 00:01:37 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.123 2000/07/17 03:05:01 tgl Exp $ * * NOTES * Every (plan) node in POSTGRES has an associated "out" routine which @@ -729,12 +729,10 @@ _outAggref(StringInfo str, Aggref *node) appendStringInfo(str, " AGGREG :aggname "); _outToken(str, node->aggname); appendStringInfo(str, " :basetype %u :aggtype %u :target ", - node->basetype, - node->aggtype); + node->basetype, node->aggtype); _outNode(str, node->target); - appendStringInfo(str, " :usenulls %s :aggstar %s :aggdistinct %s ", - node->usenulls ? "true" : "false", + appendStringInfo(str, " :aggstar %s :aggdistinct %s ", node->aggstar ? "true" : "false", node->aggdistinct ? "true" : "false"); /* aggno is not dumped */ diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 4754cbc327..b9916ce6b0 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.92 2000/07/12 02:37:06 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.93 2000/07/17 03:05:01 tgl Exp $ * * NOTES * Most of the read functions for plan nodes are tested. (In fact, they @@ -1117,10 +1117,6 @@ _readAggref() token = lsptok(NULL, &length); /* eat :target */ local_node->target = nodeRead(true); /* now read it */ - token = lsptok(NULL, &length); /* eat :usenulls */ - token = lsptok(NULL, &length); /* get usenulls */ - local_node->usenulls = (token[0] == 't') ? true : false; - token = lsptok(NULL, &length); /* eat :aggstar */ token = lsptok(NULL, &length); /* get aggstar */ local_node->aggstar = (token[0] == 't') ? true : false; diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 600371a09f..bbc8f5c707 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.38 2000/06/15 03:32:19 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.39 2000/07/17 03:05:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -189,18 +189,16 @@ ParseAgg(ParseState *pstate, char *aggname, Oid basetype, { HeapTuple theAggTuple; Form_pg_aggregate aggform; - Oid fintype; - Oid xfn1; - Oid vartype; Aggref *aggref; - bool usenulls = false; theAggTuple = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggname), ObjectIdGetDatum(basetype), 0, 0); + /* shouldn't happen --- caller should have checked already */ if (!HeapTupleIsValid(theAggTuple)) - elog(ERROR, "Aggregate %s does not exist", aggname); + agg_error("ParseAgg", aggname, basetype); + aggform = (Form_pg_aggregate) GETSTRUCT(theAggTuple); /* * There used to be a really ugly hack for count(*) here. @@ -209,43 +207,18 @@ ParseAgg(ParseState *pstate, char *aggname, Oid basetype, * does the right thing. (It didn't use to do the right thing, * because the optimizer had the wrong ideas about semantics of * queries without explicit variables. Fixed as of Oct 1999 --- tgl.) - * - * Since "1" never evaluates as null, we currently have no need of the - * "usenulls" flag, but it should be kept around; in fact, we should - * extend the pg_aggregate table to let usenulls be specified as an - * attribute of user-defined aggregates. In the meantime, usenulls is - * just always set to "false". */ - aggform = (Form_pg_aggregate) GETSTRUCT(theAggTuple); - fintype = aggform->aggfinaltype; - xfn1 = aggform->aggtransfn1; - - /* only aggregates with transfn1 need a base type */ - if (OidIsValid(xfn1)) - { - basetype = aggform->aggbasetype; - vartype = exprType(lfirst(args)); - if ((basetype != vartype) - && (!IS_BINARY_COMPATIBLE(basetype, vartype))) - { - Type tp1, - tp2; - - tp1 = typeidType(basetype); - tp2 = typeidType(vartype); - elog(ERROR, "Aggregate type mismatch" - "\n\t%s() works on %s, not on %s", - aggname, typeTypeName(tp1), typeTypeName(tp2)); - } - } + /* + * We assume caller has already checked that given args are compatible + * with the agg's basetype. + */ aggref = makeNode(Aggref); aggref->aggname = pstrdup(aggname); aggref->basetype = aggform->aggbasetype; - aggref->aggtype = fintype; + aggref->aggtype = aggform->aggfinaltype; aggref->target = lfirst(args); - aggref->usenulls = usenulls; aggref->aggstar = agg_star; aggref->aggdistinct = agg_distinct; @@ -268,10 +241,9 @@ agg_error(char *caller, char *aggname, Oid basetypeID) */ if (basetypeID == InvalidOid) - elog(ERROR, "%s: aggregate '%s' for all types does not exist", caller, aggname); + elog(ERROR, "%s: aggregate '%s' for all types does not exist", + caller, aggname); else - { - elog(ERROR, "%s: aggregate '%s' for '%s' does not exist", caller, aggname, - typeidTypeName(basetypeID)); - } + elog(ERROR, "%s: aggregate '%s' for '%s' does not exist", + caller, aggname, typeidTypeName(basetypeID)); } diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index b22aff762a..3002345336 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.156 2000/07/12 22:59:04 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.157 2000/07/17 03:05:04 tgl Exp $ * * NOTES * @@ -33,6 +33,7 @@ * *------------------------------------------------------------------------- */ + #include "postgres.h" #include <unistd.h> @@ -80,6 +81,7 @@ #include "tcop/tcopprot.h" #include "utils/exc.h" #include "utils/guc.h" +#include "utils/memutils.h" #define INVALID_SOCK (-1) diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c index 12a8372c7c..23a2dcf1e2 100644 --- a/src/backend/storage/lmgr/lock.c +++ b/src/backend/storage/lmgr/lock.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.70 2000/06/28 03:32:07 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.71 2000/07/17 03:05:08 tgl Exp $ * * NOTES * Outside modules can create a lock table and acquire/release @@ -20,7 +20,7 @@ * Interface: * * LockAcquire(), LockRelease(), LockMethodTableInit(), - * LockMethodTableRename(), LockReleaseAll, LockOwners() + * LockMethodTableRename(), LockReleaseAll, * LockResolveConflicts(), GrantLock() * * NOTE: This module is used to define new lock tables. The @@ -35,9 +35,11 @@ #include <signal.h> #include "postgres.h" + #include "access/xact.h" #include "miscadmin.h" #include "storage/proc.h" +#include "utils/memutils.h" #include "utils/ps_status.h" static int WaitOnLock(LOCKMETHOD lockmethod, LOCK *lock, LOCKMODE lockmode); @@ -1722,181 +1724,6 @@ nxtl: ; return false; } -#ifdef NOT_USED -/* - * Return an array with the pids of all processes owning a lock. - * This works only for user locks because normal locks have no - * pid information in the corresponding XIDLookupEnt. - */ -ArrayType * -LockOwners(LOCKMETHOD lockmethod, LOCKTAG *locktag) -{ - XIDLookupEnt *xidLook = NULL; - SPINLOCK masterLock; - LOCK *lock; - SHMEM_OFFSET lock_offset; - int count = 0; - LOCKMETHODTABLE *lockMethodTable; - HTAB *xidTable; - bool found; - int ndims, - nitems, - hdrlen, - size; - int lbounds[1], - hbounds[1]; - ArrayType *array; - int *data_ptr; - - /* Assume that no one will modify the result */ - static int empty_array[] = {20, 1, 0, 0, 0}; - -#ifdef LOCK_DEBUG - if (lockmethod == USER_LOCKMETHOD && Trace_userlocks) - elog(DEBUG, "LockOwners: user lock tag [%u]", locktag->objId.blkno); -#endif - - /* This must be changed when short term locks will be used */ - locktag->lockmethod = lockmethod; - - Assert((lockmethod >= MIN_LOCKMETHOD) && (lockmethod < NumLockMethods)); - lockMethodTable = LockMethodTable[lockmethod]; - if (!lockMethodTable) - { - elog(NOTICE, "lockMethodTable is null in LockOwners"); - return (ArrayType *) &empty_array; - } - - if (LockingIsDisabled) - return (ArrayType *) &empty_array; - - masterLock = lockMethodTable->ctl->masterLock; - SpinAcquire(masterLock); - - /* - * Find a lock with this tag - */ - Assert(lockMethodTable->lockHash->hash == tag_hash); - lock = (LOCK *) hash_search(lockMethodTable->lockHash, (Pointer) locktag, - HASH_FIND, &found); - - /* - * let the caller print its own error message, too. Do not elog(WARN). - */ - if (!lock) - { - SpinRelease(masterLock); - elog(NOTICE, "LockOwners: locktable corrupted"); - return (ArrayType *) &empty_array; - } - - if (!found) - { - SpinRelease(masterLock); - elog(NOTICE, "LockOwners: no such lock"); - return (ArrayType *) &empty_array; - } - LOCK_PRINT("LockOwners: found", lock, 0); - Assert((lock->nHolding > 0) && (lock->nActive > 0)); - Assert(lock->nActive <= lock->nHolding); - lock_offset = MAKE_OFFSET(lock); - - /* Construct a 1-dimensional array */ - ndims = 1; - hdrlen = ARR_OVERHEAD(ndims); - lbounds[0] = 0; - hbounds[0] = lock->nActive; - size = hdrlen + sizeof(int) * hbounds[0]; - array = (ArrayType *) palloc(size); - MemSet(array, 0, size); - memmove((char *) array, (char *) &size, sizeof(int)); - memmove((char *) ARR_NDIM_PTR(array), (char *) &ndims, sizeof(int)); - memmove((char *) ARR_DIMS(array), (char *) hbounds, ndims * sizeof(int)); - memmove((char *) ARR_LBOUND(array), (char *) lbounds, ndims * sizeof(int)); - SET_LO_FLAG(false, array); - data_ptr = (int *) ARR_DATA_PTR(array); - - xidTable = lockMethodTable->xidHash; - hash_seq(NULL); - nitems = 0; - while ((xidLook = (XIDLookupEnt *) hash_seq(xidTable)) && - (xidLook != (XIDLookupEnt *) TRUE)) - { - if (count++ > 1000) - { - elog(NOTICE, "LockOwners: possible loop, giving up"); - break; - } - - if (xidLook->tag.pid == 0) - { - XID_PRINT("LockOwners: no pid", xidLook); - continue; - } - - if (!xidLook->tag.lock) - { - XID_PRINT("LockOwners: NULL LOCK", xidLook); - continue; - } - - if (xidLook->tag.lock != lock_offset) - { - XID_PRINT("LockOwners: different lock", xidLook); - continue; - } - - if (LOCK_LOCKMETHOD(*lock) != lockmethod) - { - XID_PRINT("LockOwners: other table", xidLook); - continue; - } - - if (xidLook->nHolding <= 0) - { - XID_PRINT("LockOwners: not holding", xidLook); - continue; - } - - if (nitems >= hbounds[0]) - { - elog(NOTICE, "LockOwners: array size exceeded"); - break; - } - - /* - * Check that the holding process is still alive by sending him an - * unused (ignored) signal. If the kill fails the process is not - * alive. - */ - if ((xidLook->tag.pid != MyProcPid) \ - &&(kill(xidLook->tag.pid, SIGCHLD)) != 0) - { - /* Return a negative pid to signal that process is dead */ - data_ptr[nitems++] = -(xidLook->tag.pid); - XID_PRINT("LockOwners: not alive", xidLook); - /* XXX - TODO: remove this entry and update lock stats */ - continue; - } - - /* Found a process holding the lock */ - XID_PRINT("LockOwners: holding", xidLook); - data_ptr[nitems++] = xidLook->tag.pid; - } - - SpinRelease(masterLock); - - /* Adjust the actual size of the array */ - hbounds[0] = nitems; - size = hdrlen + sizeof(int) * hbounds[0]; - memmove((char *) array, (char *) &size, sizeof(int)); - memmove((char *) ARR_DIMS(array), (char *) hbounds, ndims * sizeof(int)); - - return array; -} - -#endif /* NOT_USED */ - #ifdef LOCK_DEBUG /* * Dump all locks in the proc->lockQueue. Must have already acquired diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c index 5baf6935d0..2207af4fa1 100644 --- a/src/backend/storage/smgr/md.c +++ b/src/backend/storage/smgr/md.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.73 2000/07/10 04:32:00 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.74 2000/07/17 03:05:11 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -22,8 +22,9 @@ #include "catalog/catalog.h" #include "miscadmin.h" #include "storage/smgr.h" -#include "utils/inval.h" /* ImmediateSharedRelationCacheInvalidate() - * */ +#include "utils/inval.h" +#include "utils/memutils.h" + #undef DIAGNOSTIC diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index fce4e2cc28..e87492fe55 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.169 2000/07/12 17:38:45 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.170 2000/07/17 03:05:14 tgl Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -56,6 +56,7 @@ #include "storage/proc.h" #include "utils/exc.h" #include "utils/guc.h" +#include "utils/memutils.h" #include "utils/ps_status.h" #include "utils/temprel.h" #ifdef MULTIBYTE @@ -1411,7 +1412,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[]) if (!IsUnderPostmaster) { puts("\nPOSTGRES backend interactive interface "); - puts("$Revision: 1.169 $ $Date: 2000/07/12 17:38:45 $\n"); + puts("$Revision: 1.170 $ $Date: 2000/07/17 03:05:14 $\n"); } /* diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index 57818afb9e..104a82cde3 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.36 2000/07/12 02:37:15 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.37 2000/07/17 03:05:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,8 +19,10 @@ #include "executor/execdefs.h" #include "executor/executor.h" #include "tcop/pquery.h" +#include "utils/memutils.h" #include "utils/ps_status.h" + static char *CreateOperationTag(int operationType); diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 3fc0061304..907082a726 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.60 2000/07/03 23:09:50 wieck Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.61 2000/07/17 03:05:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,7 +19,6 @@ #include "catalog/catalog.h" #include "catalog/pg_type.h" -#include "fmgr.h" #include "libpq/be-fsstubs.h" #include "libpq/libpq-fs.h" #include "storage/fd.h" @@ -29,7 +28,8 @@ #define ASSGN "=" -/* An array has the following internal structure: +/* + * An array has the following internal structure: * <nbytes> - total number of bytes * <ndim> - number of dimensions of the array * <flags> - bit mask of flags @@ -38,20 +38,18 @@ * <actual data> - whatever is the stored data */ -/*-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-*/ static int _ArrayCount(char *str, int *dim, int typdelim); -static char *_ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim, +static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim, FmgrInfo *inputproc, Oid typelem, int32 typmod, char typdelim, int typlen, bool typbyval, char typalign, int *nbytes); - #ifdef LOARRAY static char *_ReadLOArray(char *str, int *nbytes, int *fd, bool *chunkFlag, int ndim, int *dim, int baseSize); - #endif -static void _CopyArrayEls(char **values, char *p, int nitems, int typlen, - char typalign, bool typbyval); +static void CopyArrayEls(char *p, Datum *values, int nitems, + bool typbyval, int typlen, char typalign, + bool freedata); static void system_cache_lookup(Oid element_type, bool input, int *typlen, bool *typbyval, char *typdelim, Oid *typelem, Oid *proc, char *typalign); @@ -101,7 +99,7 @@ array_in(PG_FUNCTION_ARGS) int i, nitems; int32 nbytes; - char *dataPtr; + Datum *dataPtr; ArrayType *retval; int ndim, dim[MAXDIM], @@ -187,32 +185,29 @@ array_in(PG_FUNCTION_ARGS) retval = (ArrayType *) palloc(sizeof(ArrayType)); MemSet(retval, 0, sizeof(ArrayType)); *(int32 *) retval = sizeof(ArrayType); - PG_RETURN_POINTER(retval); + PG_RETURN_ARRAYTYPE_P(retval); } if (*p == '{') { /* array not a large object */ - dataPtr = (char *) _ReadArrayStr(p, nitems, ndim, dim, &inputproc, typelem, - typmod, typdelim, typlen, typbyval, typalign, - &nbytes); + dataPtr = ReadArrayStr(p, nitems, ndim, dim, &inputproc, typelem, + typmod, typdelim, typlen, typbyval, typalign, + &nbytes); nbytes += ARR_OVERHEAD(ndim); retval = (ArrayType *) palloc(nbytes); MemSet(retval, 0, nbytes); - memmove(retval, (char *) &nbytes, sizeof(int)); - memmove((char *) ARR_NDIM_PTR(retval), (char *) &ndim, sizeof(int)); + retval->size = nbytes; + retval->ndim = ndim; SET_LO_FLAG(false, retval); - memmove((char *) ARR_DIMS(retval), (char *) dim, ndim * sizeof(int)); - memmove((char *) ARR_LBOUND(retval), (char *) lBound, - ndim * sizeof(int)); + memcpy((char *) ARR_DIMS(retval), (char *) dim, + ndim * sizeof(int)); + memcpy((char *) ARR_LBOUND(retval), (char *) lBound, + ndim * sizeof(int)); - /* - * dataPtr is an array of arbitraystuff even though its type is - * char* cast to char** to pass to _CopyArrayEls for now - jolly - */ - _CopyArrayEls((char **) dataPtr, - ARR_DATA_PTR(retval), nitems, - typlen, typalign, typbyval); + CopyArrayEls(ARR_DATA_PTR(retval), dataPtr, nitems, + typbyval, typlen, typalign, true); + pfree(dataPtr); } else { @@ -226,8 +221,8 @@ array_in(PG_FUNCTION_ARGS) nbytes = bytes + ARR_OVERHEAD(ndim); retval = (ArrayType *) palloc(nbytes); MemSet(retval, 0, nbytes); - memmove(retval, (char *) &nbytes, sizeof(int)); - memmove((char *) ARR_NDIM_PTR(retval), (char *) &ndim, sizeof(int)); + retval->size = nbytes; + retval->ndim = ndim; SET_LO_FLAG(true, retval); SET_CHUNK_FLAG(chunked, retval); memmove((char *) ARR_DIMS(retval), (char *) dim, ndim * sizeof(int)); @@ -238,7 +233,7 @@ array_in(PG_FUNCTION_ARGS) PG_RETURN_NULL(); } pfree(string_save); - PG_RETURN_POINTER(retval); + PG_RETURN_ARRAYTYPE_P(retval); } /*----------------------------------------------------------------------------- @@ -331,50 +326,51 @@ _ArrayCount(char *str, int *dim, int typdelim) } /*--------------------------------------------------------------------------- - * _ReadArrayStr : - * parses the array string pointed by "arrayStr" and converts it in the + * ReadArrayStr : + * parses the array string pointed by "arrayStr" and converts it to * internal format. The external format expected is like C array * declaration. Unspecified elements are initialized to zero for fixed length * base types and to empty varlena structures for variable length base * types. * result : - * returns the internal representation of the array elements - * nbytes is set to the size of the array in its internal representation. + * returns a palloc'd array of Datum representations of the array elements. + * If element type is pass-by-ref, the Datums point to palloc'd values. + * *nbytes is set to the amount of data space needed for the array, + * including alignment padding but not including array header overhead. *--------------------------------------------------------------------------- */ -static char * -_ReadArrayStr(char *arrayStr, - int nitems, - int ndim, - int *dim, - FmgrInfo *inputproc, /* function used for the - * conversion */ - Oid typelem, - int32 typmod, - char typdelim, - int typlen, - bool typbyval, - char typalign, - int *nbytes) +static Datum * +ReadArrayStr(char *arrayStr, + int nitems, + int ndim, + int *dim, + FmgrInfo *inputproc, + Oid typelem, + int32 typmod, + char typdelim, + int typlen, + bool typbyval, + char typalign, + int *nbytes) { int i, nest_level = 0; + Datum *values; char *p, *q, - *r, - **values; + *r; bool scanning_string = false; int indx[MAXDIM], prod[MAXDIM]; bool eoArray = false; mda_get_prod(ndim, dim, prod); - for (i = 0; i < ndim; indx[i++] = 0); - /* read array enclosed within {} */ - values = (char **) palloc(nitems * sizeof(char *)); - MemSet(values, 0, nitems * sizeof(char *)); + values = (Datum *) palloc(nitems * sizeof(Datum)); + MemSet(values, 0, nitems * sizeof(Datum)); + MemSet(indx, 0, sizeof(indx)); q = p = arrayStr; + /* read array enclosed within {} */ while (!eoArray) { bool done = false; @@ -442,53 +438,56 @@ _ReadArrayStr(char *arrayStr, *q = '\0'; if (i >= nitems) elog(ERROR, "array_in: illformed array constant"); - values[i] = (char *) FunctionCall3(inputproc, - CStringGetDatum(p), - ObjectIdGetDatum(typelem), - Int32GetDatum(typmod)); + values[i] = FunctionCall3(inputproc, + CStringGetDatum(p), + ObjectIdGetDatum(typelem), + Int32GetDatum(typmod)); p = ++q; + /* + * if not at the end of the array skip white space + */ if (!eoArray) - - /* - * if not at the end of the array skip white space - */ while (isspace((int) *q)) { p++; q++; } } + /* + * Initialize any unset items and compute total data space needed + */ if (typlen > 0) { *nbytes = nitems * typlen; if (!typbyval) for (i = 0; i < nitems; i++) - if (!values[i]) + if (values[i] == (Datum) 0) { - values[i] = palloc(typlen); - MemSet(values[i], 0, typlen); + values[i] = PointerGetDatum(palloc(typlen)); + MemSet(DatumGetPointer(values[i]), 0, typlen); } } else { - for (i = 0, *nbytes = 0; i < nitems; i++) + *nbytes = 0; + for (i = 0; i < nitems; i++) { - if (values[i]) + if (values[i] != (Datum) 0) { if (typalign == 'd') - *nbytes += MAXALIGN(*(int32 *) values[i]); + *nbytes += MAXALIGN(VARSIZE(DatumGetPointer(values[i]))); else - *nbytes += INTALIGN(*(int32 *) values[i]); + *nbytes += INTALIGN(VARSIZE(DatumGetPointer(values[i]))); } else { *nbytes += sizeof(int32); - values[i] = palloc(sizeof(int32)); - *(int32 *) values[i] = sizeof(int32); + values[i] = PointerGetDatum(palloc(sizeof(int32))); + VARATT_SIZEP(DatumGetPointer(values[i])) = sizeof(int32); } } } - return (char *) values; + return values; } @@ -565,26 +564,39 @@ _ReadLOArray(char *str, #endif +/*---------- + * Copy data into an array object from a temporary array of Datums. + * + * p: pointer to start of array data area + * values: array of Datums to be copied + * nitems: number of Datums to be copied + * typbyval, typlen, typalign: info about element datatype + * freedata: if TRUE and element type is pass-by-ref, pfree data values + * referenced by Datums after copying them. + *---------- + */ static void -_CopyArrayEls(char **values, - char *p, - int nitems, - int typlen, - char typalign, - bool typbyval) +CopyArrayEls(char *p, + Datum *values, + int nitems, + bool typbyval, + int typlen, + char typalign, + bool freedata) { int i; + int inc; + + if (typbyval) + freedata = false; for (i = 0; i < nitems; i++) { - int inc; - - inc = ArrayCastAndSet((Datum) values[i], typbyval, typlen, p); + inc = ArrayCastAndSet(values[i], typbyval, typlen, p); p += inc; - if (!typbyval) - pfree(values[i]); + if (freedata) + pfree(DatumGetPointer(values[i])); } - pfree(values); } /*------------------------------------------------------------------------- @@ -596,7 +608,7 @@ _CopyArrayEls(char **values, Datum array_out(PG_FUNCTION_ARGS) { - ArrayType *v = (ArrayType *) PG_GETARG_VARLENA_P(0); + ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); Oid element_type = PG_GETARG_OID(1); int typlen; bool typbyval; @@ -786,7 +798,7 @@ array_out(PG_FUNCTION_ARGS) Datum array_dims(PG_FUNCTION_ARGS) { - ArrayType *v = (ArrayType *) PG_GETARG_VARLENA_P(0); + ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); text *result; char *p; int nbytes, @@ -821,8 +833,8 @@ array_dims(PG_FUNCTION_ARGS) /*--------------------------------------------------------------------------- * array_ref : * This routine takes an array pointer and an index array and returns - * a pointer to the referred element if element is passed by - * reference otherwise returns the value of the referred element. + * the referenced item as a Datum. Note that for a pass-by-reference + * datatype, the returned Datum is a pointer into the array object. *--------------------------------------------------------------------------- */ Datum @@ -905,7 +917,7 @@ array_ref(ArrayType *array, { /* not by value */ char *tempdata = palloc(elmlen); - memmove(tempdata, DatumGetPointer(result), elmlen); + memcpy(tempdata, DatumGetPointer(result), elmlen); result = PointerGetDatum(tempdata); } pfree(v); @@ -1003,14 +1015,15 @@ array_clip(ArrayType *array, #endif bytes = strlen(newname) + 1 + ARR_OVERHEAD(nSubscripts); newArr = (ArrayType *) palloc(bytes); - memmove(newArr, array, sizeof(ArrayType)); - memmove(newArr, &bytes, sizeof(int)); - memmove(ARR_DIMS(newArr), span, nSubscripts * sizeof(int)); - memmove(ARR_LBOUND(newArr), lowerIndx, nSubscripts * sizeof(int)); + newArr->size = bytes; + newArr->ndim = array->ndim; + newArr->flags = array->flags; + memcpy(ARR_DIMS(newArr), span, nSubscripts * sizeof(int)); + memcpy(ARR_LBOUND(newArr), lowerIndx, nSubscripts * sizeof(int)); strcpy(ARR_DATA_PTR(newArr), newname); rsize = compute_size(lowerIndx, upperIndx, nSubscripts, elmlen); - if (rsize < MAX_BUFF_SIZE) + if (rsize < BLCKSZ) { char *buff; @@ -1072,10 +1085,11 @@ array_clip(ArrayType *array, bytes += ARR_OVERHEAD(nSubscripts); } newArr = (ArrayType *) palloc(bytes); - memmove(newArr, array, sizeof(ArrayType)); - memmove(newArr, &bytes, sizeof(int)); - memmove(ARR_DIMS(newArr), span, nSubscripts * sizeof(int)); - memmove(ARR_LBOUND(newArr), lowerIndx, nSubscripts * sizeof(int)); + newArr->size = bytes; + newArr->ndim = array->ndim; + newArr->flags = array->flags; + memcpy(ARR_DIMS(newArr), span, nSubscripts * sizeof(int)); + memcpy(ARR_LBOUND(newArr), lowerIndx, nSubscripts * sizeof(int)); _ArrayRange(lowerIndx, upperIndx, elmlen, ARR_DATA_PTR(newArr), array, 1); return newArr; } @@ -1322,7 +1336,7 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType) { ArrayType *v; ArrayType *result; - char **values; + Datum *values; char *elt; int *dim; int ndim; @@ -1338,14 +1352,13 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType) Oid proc; char typalign; char *s; - char *p; /* Get input array */ if (fcinfo->nargs < 1) elog(ERROR, "array_map: invalid nargs: %d", fcinfo->nargs); if (PG_ARGISNULL(0)) elog(ERROR, "array_map: null input array"); - v = (ArrayType *) PG_GETARG_VARLENA_P(0); + v = PG_GETARG_ARRAYTYPE_P(0); /* Large objects not yet supported */ if (ARR_IS_LO(v) == true) @@ -1357,7 +1370,7 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType) /* Check for empty array */ if (nitems <= 0) - PG_RETURN_POINTER(v); + PG_RETURN_ARRAYTYPE_P(v); /* Lookup source and result types. Unneeded variables are reused. */ system_cache_lookup(inpType, false, &inp_typlen, &inp_typbyval, @@ -1366,8 +1379,8 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType) &typdelim, &typelem, &proc, &typalign); /* Allocate temporary array for new values */ - values = (char **) palloc(nitems * sizeof(char *)); - MemSet(values, 0, nitems * sizeof(char *)); + values = (Datum *) palloc(nitems * sizeof(Datum)); + MemSet(values, 0, nitems * sizeof(Datum)); /* Loop over source data */ s = (char *) ARR_DATA_PTR(v); @@ -1411,30 +1424,16 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType) fcinfo->arg[0] = (Datum) elt; fcinfo->argnull[0] = false; fcinfo->isnull = false; - p = (char *) FunctionCallInvoke(fcinfo); + values[i] = FunctionCallInvoke(fcinfo); if (fcinfo->isnull) elog(ERROR, "array_map: cannot handle NULL in array"); - /* Update values and total result size */ + /* Update total result size */ if (typbyval) - { - values[i] = p; nbytes += typlen; - } else - { - int len; - - len = ((typlen > 0) ? typlen : INTALIGN(*(int32 *) p)); - /* Needed because _CopyArrayEls tries to pfree items */ - if (p == elt) - { - p = (char *) palloc(len); - memcpy(p, elt, len); - } - values[i] = p; - nbytes += len; - } + nbytes += ((typlen > 0) ? typlen : + INTALIGN(VARSIZE(DatumGetPointer(values[i])))); } /* Allocate and initialize the result array */ @@ -1442,18 +1441,130 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType) result = (ArrayType *) palloc(nbytes); MemSet(result, 0, nbytes); - memcpy((char *) result, (char *) &nbytes, sizeof(int)); - memcpy((char *) ARR_NDIM_PTR(result), (char *) &ndim, sizeof(int)); - memcpy((char *) ARR_DIMS(result), ARR_DIMS(v), 2 * ndim * sizeof(int)); + result->size = nbytes; + result->ndim = ndim; + memcpy(ARR_DIMS(result), ARR_DIMS(v), 2 * ndim * sizeof(int)); - /* Copy new values into the result array. values is pfreed. */ - _CopyArrayEls((char **) values, - ARR_DATA_PTR(result), nitems, - typlen, typalign, typbyval); + /* Note: do not risk trying to pfree the results of the called function */ + CopyArrayEls(ARR_DATA_PTR(result), values, nitems, + typbyval, typlen, typalign, false); + pfree(values); - PG_RETURN_POINTER(result); + PG_RETURN_ARRAYTYPE_P(result); } +/*---------- + * construct_array --- simple method for constructing an array object + * + * elems: array of Datum items to become the array contents + * nelems: number of items + * elmbyval, elmlen, elmalign: info for the datatype of the items + * + * A palloc'd 1-D array object is constructed and returned. Note that + * elem values will be copied into the object even if pass-by-ref type. + * NULL element values are not supported. + *---------- + */ +ArrayType * +construct_array(Datum *elems, int nelems, + bool elmbyval, int elmlen, char elmalign) +{ + ArrayType *result; + int nbytes; + int i; + + if (elmlen > 0) + { + /* XXX what about alignment? */ + nbytes = elmlen * nelems; + } + else + { + /* varlena type */ + nbytes = 0; + for (i = 0; i < nelems; i++) + nbytes += INTALIGN(VARSIZE(DatumGetPointer(elems[i]))); + } + + /* Allocate and initialize 1-D result array */ + nbytes += ARR_OVERHEAD(1); + result = (ArrayType *) palloc(nbytes); + + result->size = nbytes; + result->ndim = 1; + result->flags = 0; + ARR_DIMS(result)[0] = nelems; + ARR_LBOUND(result)[0] = 1; + + CopyArrayEls(ARR_DATA_PTR(result), elems, nelems, + elmbyval, elmlen, elmalign, false); + + return result; +} + +/*---------- + * deconstruct_array --- simple method for extracting data from an array + * + * array: array object to examine (must not be NULL) + * elmbyval, elmlen, elmalign: info for the datatype of the items + * elemsp: return value, set to point to palloc'd array of Datum values + * nelemsp: return value, set to number of extracted values + * + * If array elements are pass-by-ref data type, the returned Datums will + * be pointers into the array object. + *---------- + */ +void +deconstruct_array(ArrayType *array, + bool elmbyval, int elmlen, char elmalign, + Datum **elemsp, int *nelemsp) +{ + Datum *elems; + int nelems; + char *p; + int i; + + nelems = getNitems(ARR_NDIM(array), ARR_DIMS(array)); + if (nelems <= 0) + { + *elemsp = NULL; + *nelemsp = 0; + return; + } + *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum)); + *nelemsp = nelems; + + p = ARR_DATA_PTR(array); + for (i = 0; i < nelems; i++) + { + if (elmbyval) + { + switch (elmlen) + { + case 1: + elems[i] = CharGetDatum(*p); + break; + case 2: + elems[i] = Int16GetDatum(*(int16 *) p); + break; + case 4: + elems[i] = Int32GetDatum(*(int32 *) p); + break; + } + p += elmlen; + } + else + { + elems[i] = PointerGetDatum(p); + if (elmlen > 0) + p += elmlen; + else + p += INTALIGN(VARSIZE(p)); + } + } +} + + /*----------------------------------------------------------------------------- * array_eq : * compares two arrays for equality @@ -1464,8 +1575,8 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType) Datum array_eq(PG_FUNCTION_ARGS) { - ArrayType *array1 = (ArrayType *) PG_GETARG_VARLENA_P(0); - ArrayType *array2 = (ArrayType *) PG_GETARG_VARLENA_P(1); + ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0); + ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1); if (*(int32 *) array1 != *(int32 *) array2) PG_RETURN_BOOL(false); @@ -1493,14 +1604,11 @@ system_cache_lookup(Oid element_type, typeTuple = SearchSysCacheTuple(TYPEOID, ObjectIdGetDatum(element_type), 0, 0, 0); - if (!HeapTupleIsValid(typeTuple)) - { - elog(ERROR, "array_out: Cache lookup failed for type %u\n", + elog(ERROR, "array_out: Cache lookup failed for type %u", element_type); - return; - } typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); + *typlen = typeStruct->typlen; *typbyval = typeStruct->typbyval; *typdelim = typeStruct->typdelim; @@ -1536,7 +1644,11 @@ _ArrayCast(char *value, bool byval, int len) return 0; } - +/* + * Copy datum to *dest and return total space used (including align padding) + * + * XXX this routine needs to be told typalign too! + */ static int ArrayCastAndSet(Datum src, bool typbyval, @@ -1560,16 +1672,26 @@ ArrayCastAndSet(Datum src, case 4: *(int32 *) dest = DatumGetInt32(src); break; + default: + elog(ERROR, "ArrayCastAndSet: unexpected typlen"); + break; } + /* For by-val types, assume no alignment padding is needed */ + inc = typlen; } else + { memmove(dest, DatumGetPointer(src), typlen); - inc = typlen; + /* XXX WRONG: need to consider type's alignment requirement */ + inc = typlen; + } } else { - memmove(dest, DatumGetPointer(src), *(int32 *) DatumGetPointer(src)); - inc = (INTALIGN(*(int32 *) DatumGetPointer(src))); + /* varlena type */ + memmove(dest, DatumGetPointer(src), VARSIZE(DatumGetPointer(src))); + /* XXX WRONG: should use MAXALIGN or type's alignment requirement */ + inc = INTALIGN(VARSIZE(DatumGetPointer(src))); } return inc; } diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index 0b6a0db2ea..bfa439f415 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/float.c,v 1.64 2000/07/12 22:59:08 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/float.c,v 1.65 2000/07/17 03:05:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,7 +17,7 @@ * Basic float4 ops: * float4in, float4out, float4abs, float4um * Basic float8 ops: - * float8in, float8inAd, float8out, float8outAd, float8abs, float8um + * float8in, float8out, float8abs, float8um * Arithmetic operators: * float4pl, float4mi, float4mul, float4div * float8pl, float8mi, float8mul, float8div @@ -64,6 +64,7 @@ #endif #include "fmgr.h" +#include "utils/array.h" #include "utils/builtins.h" static void CheckFloat8Val(double val); @@ -90,7 +91,6 @@ static void CheckFloat8Val(double val); #ifndef atof extern double atof(const char *p); - #endif #ifndef HAVE_CBRT @@ -100,9 +100,8 @@ static double cbrt(double x); #else #if !defined(nextstep) extern double cbrt(double x); - -#endif #endif +#endif /* HAVE_CBRT */ #ifndef HAVE_RINT #define rint my_rint @@ -110,10 +109,9 @@ static double rint(double x); #else extern double rint(double x); +#endif /* HAVE_RINT */ -#endif - -#endif +#endif /* NeXT check */ /* ========== USER I/O ROUTINES ========== */ @@ -453,7 +451,6 @@ float8smaller(float64 arg1, float64 arg2) * float4mi - returns a pointer to arg1 - arg2 * float4mul - returns a pointer to arg1 * arg2 * float4div - returns a pointer to arg1 / arg2 - * float4inc - returns a poniter to arg1 + 1.0 */ float32 float4pl(float32 arg1, float32 arg2) @@ -527,29 +524,11 @@ float4div(float32 arg1, float32 arg2) return result; } -float32 -float4inc(float32 arg1) -{ - float32 result; - double val; - - if (!arg1) - return (float32) NULL; - - val = *arg1 + (float32data) 1.0; - - CheckFloat4Val(val); - result = (float32) palloc(sizeof(float32data)); - *result = val; - return result; -} - /* * float8pl - returns a pointer to arg1 + arg2 * float8mi - returns a pointer to arg1 - arg2 * float8mul - returns a pointer to arg1 * arg2 * float8div - returns a pointer to arg1 / arg2 - * float8inc - returns a pointer to arg1 + 1.0 */ float64 float8pl(float64 arg1, float64 arg2) @@ -622,22 +601,6 @@ float8div(float64 arg1, float64 arg2) return result; } -float64 -float8inc(float64 arg1) -{ - float64 result; - double val; - - if (!arg1) - return (float64) NULL; - - val = *arg1 + (float64data) 1.0; - CheckFloat8Val(val); - result = (float64) palloc(sizeof(float64data)); - *result = val; - return result; -} - /* * ==================== @@ -1572,10 +1535,181 @@ setseed(float64 seed) } /* setseed() */ + /* - * ==================== - * ARITHMETIC OPERATORS - * ==================== + * ========================= + * FLOAT AGGREGATE OPERATORS + * ========================= + * + * float8_accum - accumulate for AVG(), STDDEV(), etc + * float4_accum - same, but input data is float4 + * float8_avg - produce final result for float AVG() + * float8_variance - produce final result for float VARIANCE() + * float8_stddev - produce final result for float STDDEV() + * + * The transition datatype for all these aggregates is a 3-element array + * of float8, holding the values N, sum(X), sum(X*X) in that order. + * + * Note that we represent N as a float to avoid having to build a special + * datatype. Given a reasonable floating-point implementation, there should + * be no accuracy loss unless N exceeds 2 ^ 52 or so (by which time the + * user will have doubtless lost interest anyway...) + */ + +static float8 * +check_float8_array(ArrayType *transarray, const char *caller) +{ + /* + * We expect the input to be a 3-element float array; verify that. + * We don't need to use deconstruct_array() since the array data + * is just going to look like a C array of 3 float8 values. + */ + if (ARR_SIZE(transarray) != (ARR_OVERHEAD(1) + 3 * sizeof(float8)) || + ARR_NDIM(transarray) != 1 || + ARR_DIMS(transarray)[0] != 3) + elog(ERROR, "%s: expected 3-element float8 array", caller); + return (float8 *) ARR_DATA_PTR(transarray); +} + +Datum +float8_accum(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + float8 newval = PG_GETARG_FLOAT8(1); + float8 *transvalues; + float8 N, + sumX, + sumX2; + Datum transdatums[3]; + ArrayType *result; + + transvalues = check_float8_array(transarray, "float8_accum"); + N = transvalues[0]; + sumX = transvalues[1]; + sumX2 = transvalues[2]; + + N += 1.0; + sumX += newval; + sumX2 += newval * newval; + + transdatums[0] = Float8GetDatumFast(N); + transdatums[1] = Float8GetDatumFast(sumX); + transdatums[2] = Float8GetDatumFast(sumX2); + + result = construct_array(transdatums, 3, + false /* float8 byval */, sizeof(float8), 'd'); + + PG_RETURN_ARRAYTYPE_P(result); +} + +Datum +float4_accum(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + float4 newval4 = PG_GETARG_FLOAT4(1); + float8 *transvalues; + float8 N, + sumX, + sumX2, + newval; + Datum transdatums[3]; + ArrayType *result; + + transvalues = check_float8_array(transarray, "float4_accum"); + N = transvalues[0]; + sumX = transvalues[1]; + sumX2 = transvalues[2]; + + /* Do arithmetic in float8 for best accuracy */ + newval = newval4; + + N += 1.0; + sumX += newval; + sumX2 += newval * newval; + + transdatums[0] = Float8GetDatumFast(N); + transdatums[1] = Float8GetDatumFast(sumX); + transdatums[2] = Float8GetDatumFast(sumX2); + + result = construct_array(transdatums, 3, + false /* float8 byval */, sizeof(float8), 'd'); + + PG_RETURN_ARRAYTYPE_P(result); +} + +Datum +float8_avg(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + float8 *transvalues; + float8 N, + sumX; + + transvalues = check_float8_array(transarray, "float8_avg"); + N = transvalues[0]; + sumX = transvalues[1]; + /* ignore sumX2 */ + + /* SQL92 defines AVG of no values to be NULL */ + if (N == 0.0) + PG_RETURN_NULL(); + + PG_RETURN_FLOAT8(sumX / N); +} + +Datum +float8_variance(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + float8 *transvalues; + float8 N, + sumX, + sumX2; + + transvalues = check_float8_array(transarray, "float8_variance"); + N = transvalues[0]; + sumX = transvalues[1]; + sumX2 = transvalues[2]; + + /* We define VARIANCE of no values to be NULL, of 1 value to be 0 */ + if (N == 0.0) + PG_RETURN_NULL(); + + if (N <= 1.0) + PG_RETURN_FLOAT8(0.0); + + PG_RETURN_FLOAT8((N * sumX2 - sumX * sumX) / (N * (N - 1.0))); +} + +Datum +float8_stddev(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + float8 *transvalues; + float8 N, + sumX, + sumX2; + + transvalues = check_float8_array(transarray, "float8_stddev"); + N = transvalues[0]; + sumX = transvalues[1]; + sumX2 = transvalues[2]; + + /* We define STDDEV of no values to be NULL, of 1 value to be 0 */ + if (N == 0.0) + PG_RETURN_NULL(); + + if (N <= 1.0) + PG_RETURN_FLOAT8(0.0); + + PG_RETURN_FLOAT8(sqrt((N * sumX2 - sumX * sumX) / (N * (N - 1.0)))); +} + + +/* + * ==================================== + * MIXED-PRECISION ARITHMETIC OPERATORS + * ==================================== */ /* diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c index bf7758c186..7133142c0b 100644 --- a/src/backend/utils/adt/int.c +++ b/src/backend/utils/adt/int.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/int.c,v 1.40 2000/07/12 22:59:08 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/int.c,v 1.41 2000/07/17 03:05:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -650,14 +650,6 @@ int2div(PG_FUNCTION_ARGS) PG_RETURN_INT16(arg1 / arg2); } -Datum -int2inc(PG_FUNCTION_ARGS) -{ - int16 arg = PG_GETARG_INT16(0); - - PG_RETURN_INT16(arg + 1); -} - Datum int24pl(PG_FUNCTION_ARGS) { diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 5748986bbf..437bb69b83 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -5,7 +5,7 @@ * * 1998 Jan Wieck * - * $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.31 2000/06/15 03:32:29 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.32 2000/07/17 03:05:18 tgl Exp $ * * ---------- */ @@ -18,6 +18,7 @@ #include <errno.h> #include <sys/types.h> +#include "utils/array.h" #include "utils/builtins.h" #include "utils/int8.h" #include "utils/numeric.h" @@ -1230,49 +1231,6 @@ numeric_inc(Numeric num) } -/* ---------- - * numeric_dec() - - * - * Decrement a number by one - * ---------- - */ -Numeric -numeric_dec(Numeric num) -{ - NumericVar arg; - Numeric res; - - /* ---------- - * Handle NULL - * ---------- - */ - if (num == NULL) - return NULL; - - /* ---------- - * Handle NaN - * ---------- - */ - if (NUMERIC_IS_NAN(num)) - return make_result(&const_nan); - - /* ---------- - * Compute the result and return it - * ---------- - */ - init_var(&arg); - - set_var_from_num(num, &arg); - - sub_var(&arg, &const_one, &arg); - res = make_result(&arg); - - free_var(&arg); - - return res; -} - - /* ---------- * numeric_smaller() - * @@ -1733,24 +1691,24 @@ numeric_int4(Numeric num) } -Numeric -int8_numeric(int64 *val) +Datum +int8_numeric(PG_FUNCTION_ARGS) { + Datum val = PG_GETARG_DATUM(0); Numeric res; NumericVar result; char *tmp; init_var(&result); - tmp = DatumGetCString(DirectFunctionCall1(int8out, - PointerGetDatum(val))); + tmp = DatumGetCString(DirectFunctionCall1(int8out, val)); set_var_from_str(tmp, &result); res = make_result(&result); free_var(&result); pfree(tmp); - return res; + PG_RETURN_NUMERIC(res); } @@ -1939,6 +1897,369 @@ numeric_float4(Numeric num) } +/* ---------------------------------------------------------------------- + * + * Aggregate functions + * + * The transition datatype for all these aggregates is a 3-element array + * of Numeric, holding the values N, sum(X), sum(X*X) in that order. + * + * We represent N as a numeric mainly to avoid having to build a special + * datatype; it's unlikely it'd overflow an int4, but ... + * + * ---------------------------------------------------------------------- + */ + +static ArrayType * +do_numeric_accum(ArrayType *transarray, Numeric newval) +{ + Datum *transdatums; + int ndatums; + Numeric N, + sumX, + sumX2; + ArrayType *result; + + /* We assume the input is array of numeric */ + deconstruct_array(transarray, + false, -1, 'i', + &transdatums, &ndatums); + if (ndatums != 3) + elog(ERROR, "do_numeric_accum: expected 3-element numeric array"); + N = DatumGetNumeric(transdatums[0]); + sumX = DatumGetNumeric(transdatums[1]); + sumX2 = DatumGetNumeric(transdatums[2]); + + N = numeric_inc(N); + sumX = numeric_add(sumX, newval); + sumX2 = numeric_add(sumX2, numeric_mul(newval, newval)); + + transdatums[0] = NumericGetDatum(N); + transdatums[1] = NumericGetDatum(sumX); + transdatums[2] = NumericGetDatum(sumX2); + + result = construct_array(transdatums, 3, + false, -1, 'i'); + + return result; +} + +Datum +numeric_accum(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + Numeric newval = PG_GETARG_NUMERIC(1); + + PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval)); +} + +/* + * Integer data types all use Numeric accumulators to share code and + * avoid risk of overflow. + */ + +Datum +int2_accum(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + Datum newval2 = PG_GETARG_DATUM(1); + Numeric newval; + + newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric, newval2)); + + PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval)); +} + +Datum +int4_accum(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + Datum newval4 = PG_GETARG_DATUM(1); + Numeric newval; + + newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric, newval4)); + + PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval)); +} + +Datum +int8_accum(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + Datum newval8 = PG_GETARG_DATUM(1); + Numeric newval; + + newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8)); + + PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval)); +} + +Datum +numeric_avg(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + Datum *transdatums; + int ndatums; + Numeric N, + sumX; + + /* We assume the input is array of numeric */ + deconstruct_array(transarray, + false, -1, 'i', + &transdatums, &ndatums); + if (ndatums != 3) + elog(ERROR, "numeric_avg: expected 3-element numeric array"); + N = DatumGetNumeric(transdatums[0]); + sumX = DatumGetNumeric(transdatums[1]); + /* ignore sumX2 */ + + /* SQL92 defines AVG of no values to be NULL */ + /* N is zero iff no digits (cf. numeric_uminus) */ + if (N->varlen == NUMERIC_HDRSZ) + PG_RETURN_NULL(); + + PG_RETURN_NUMERIC(numeric_div(sumX, N)); +} + +Datum +numeric_variance(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + Datum *transdatums; + int ndatums; + Numeric N, + sumX, + sumX2, + res; + NumericVar vN, + vsumX, + vsumX2, + vNminus1; + + /* We assume the input is array of numeric */ + deconstruct_array(transarray, + false, -1, 'i', + &transdatums, &ndatums); + if (ndatums != 3) + elog(ERROR, "numeric_variance: expected 3-element numeric array"); + N = DatumGetNumeric(transdatums[0]); + sumX = DatumGetNumeric(transdatums[1]); + sumX2 = DatumGetNumeric(transdatums[2]); + + if (NUMERIC_IS_NAN(N) || NUMERIC_IS_NAN(sumX) || NUMERIC_IS_NAN(sumX2)) + PG_RETURN_NUMERIC(make_result(&const_nan)); + + /* We define VARIANCE of no values to be NULL, of 1 value to be 0 */ + /* N is zero iff no digits (cf. numeric_uminus) */ + if (N->varlen == NUMERIC_HDRSZ) + PG_RETURN_NULL(); + + init_var(&vN); + set_var_from_num(N, &vN); + + init_var(&vNminus1); + sub_var(&vN, &const_one, &vNminus1); + + if (cmp_var(&vNminus1, &const_zero) <= 0) + { + free_var(&vN); + free_var(&vNminus1); + PG_RETURN_NUMERIC(make_result(&const_zero)); + } + + init_var(&vsumX); + set_var_from_num(sumX, &vsumX); + init_var(&vsumX2); + set_var_from_num(sumX2, &vsumX2); + + mul_var(&vsumX, &vsumX, &vsumX); /* now vsumX contains sumX * sumX */ + mul_var(&vN, &vsumX2, &vsumX2); /* now vsumX2 contains N * sumX2 */ + sub_var(&vsumX2, &vsumX, &vsumX2); /* N * sumX2 - sumX * sumX */ + mul_var(&vN, &vNminus1, &vNminus1); /* N * (N - 1) */ + div_var(&vsumX2, &vNminus1, &vsumX); /* variance */ + + res = make_result(&vsumX); + + free_var(&vN); + free_var(&vNminus1); + free_var(&vsumX); + free_var(&vsumX2); + + PG_RETURN_NUMERIC(res); +} + +Datum +numeric_stddev(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + Datum *transdatums; + int ndatums; + Numeric N, + sumX, + sumX2, + res; + NumericVar vN, + vsumX, + vsumX2, + vNminus1; + + /* We assume the input is array of numeric */ + deconstruct_array(transarray, + false, -1, 'i', + &transdatums, &ndatums); + if (ndatums != 3) + elog(ERROR, "numeric_stddev: expected 3-element numeric array"); + N = DatumGetNumeric(transdatums[0]); + sumX = DatumGetNumeric(transdatums[1]); + sumX2 = DatumGetNumeric(transdatums[2]); + + if (NUMERIC_IS_NAN(N) || NUMERIC_IS_NAN(sumX) || NUMERIC_IS_NAN(sumX2)) + PG_RETURN_NUMERIC(make_result(&const_nan)); + + /* We define STDDEV of no values to be NULL, of 1 value to be 0 */ + /* N is zero iff no digits (cf. numeric_uminus) */ + if (N->varlen == NUMERIC_HDRSZ) + PG_RETURN_NULL(); + + init_var(&vN); + set_var_from_num(N, &vN); + + init_var(&vNminus1); + sub_var(&vN, &const_one, &vNminus1); + + if (cmp_var(&vNminus1, &const_zero) <= 0) + { + free_var(&vN); + free_var(&vNminus1); + PG_RETURN_NUMERIC(make_result(&const_zero)); + } + + init_var(&vsumX); + set_var_from_num(sumX, &vsumX); + init_var(&vsumX2); + set_var_from_num(sumX2, &vsumX2); + + mul_var(&vsumX, &vsumX, &vsumX); /* now vsumX contains sumX * sumX */ + mul_var(&vN, &vsumX2, &vsumX2); /* now vsumX2 contains N * sumX2 */ + sub_var(&vsumX2, &vsumX, &vsumX2); /* N * sumX2 - sumX * sumX */ + mul_var(&vN, &vNminus1, &vNminus1); /* N * (N - 1) */ + div_var(&vsumX2, &vNminus1, &vsumX); /* variance */ + sqrt_var(&vsumX, &vsumX); /* stddev */ + + res = make_result(&vsumX); + + free_var(&vN); + free_var(&vNminus1); + free_var(&vsumX); + free_var(&vsumX2); + + PG_RETURN_NUMERIC(res); +} + + +/* + * SUM transition functions for integer datatypes. + * + * We use a Numeric accumulator to avoid overflow. Because SQL92 defines + * the SUM() of no values to be NULL, not zero, the initial condition of + * the transition data value needs to be NULL. This means we can't rely + * on ExecAgg to automatically insert the first non-null data value into + * the transition data: it doesn't know how to do the type conversion. + * The upshot is that these routines have to be marked non-strict and + * handle substitution of the first non-null input themselves. + */ + +Datum +int2_sum(PG_FUNCTION_ARGS) +{ + Numeric oldsum, + newval; + + if (PG_ARGISNULL(0)) + { + /* No non-null input seen so far... */ + if (PG_ARGISNULL(1)) + PG_RETURN_NULL(); /* still no non-null */ + /* This is the first non-null input. */ + newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric, + PG_GETARG_DATUM(1))); + PG_RETURN_NUMERIC(newval); + } + + oldsum = PG_GETARG_NUMERIC(0); + + /* Leave sum unchanged if new input is null. */ + if (PG_ARGISNULL(1)) + PG_RETURN_NUMERIC(oldsum); + + /* OK to do the addition. */ + newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric, + PG_GETARG_DATUM(1))); + + PG_RETURN_NUMERIC(numeric_add(oldsum, newval)); +} + +Datum +int4_sum(PG_FUNCTION_ARGS) +{ + Numeric oldsum, + newval; + + if (PG_ARGISNULL(0)) + { + /* No non-null input seen so far... */ + if (PG_ARGISNULL(1)) + PG_RETURN_NULL(); /* still no non-null */ + /* This is the first non-null input. */ + newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric, + PG_GETARG_DATUM(1))); + PG_RETURN_NUMERIC(newval); + } + + oldsum = PG_GETARG_NUMERIC(0); + + /* Leave sum unchanged if new input is null. */ + if (PG_ARGISNULL(1)) + PG_RETURN_NUMERIC(oldsum); + + /* OK to do the addition. */ + newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric, + PG_GETARG_DATUM(1))); + + PG_RETURN_NUMERIC(numeric_add(oldsum, newval)); +} + +Datum +int8_sum(PG_FUNCTION_ARGS) +{ + Numeric oldsum, + newval; + + if (PG_ARGISNULL(0)) + { + /* No non-null input seen so far... */ + if (PG_ARGISNULL(1)) + PG_RETURN_NULL(); /* still no non-null */ + /* This is the first non-null input. */ + newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, + PG_GETARG_DATUM(1))); + PG_RETURN_NUMERIC(newval); + } + + oldsum = PG_GETARG_NUMERIC(0); + + /* Leave sum unchanged if new input is null. */ + if (PG_ARGISNULL(1)) + PG_RETURN_NUMERIC(oldsum); + + /* OK to do the addition. */ + newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, + PG_GETARG_DATUM(1))); + + PG_RETURN_NUMERIC(numeric_add(oldsum, newval)); +} + + /* ---------------------------------------------------------------------- * * Local functions follow @@ -2574,30 +2895,33 @@ add_var(NumericVar *var1, NumericVar *var2, NumericVar *result) */ switch (cmp_abs(var1, var2)) { - case 0: /* ---------- - * ABS(var1) == ABS(var2) - * result = ZERO - * ---------- - */ + case 0: + /* ---------- + * ABS(var1) == ABS(var2) + * result = ZERO + * ---------- + */ zero_var(result); result->rscale = MAX(var1->rscale, var2->rscale); result->dscale = MAX(var1->dscale, var2->dscale); break; - case 1: /* ---------- - * ABS(var1) > ABS(var2) - * result = +(ABS(var1) - ABS(var2)) - * ---------- - */ + case 1: + /* ---------- + * ABS(var1) > ABS(var2) + * result = +(ABS(var1) - ABS(var2)) + * ---------- + */ sub_abs(var1, var2, result); result->sign = NUMERIC_POS; break; - case -1: /* ---------- - * ABS(var1) < ABS(var2) - * result = -(ABS(var2) - ABS(var1)) - * ---------- - */ + case -1: + /* ---------- + * ABS(var1) < ABS(var2) + * result = -(ABS(var2) - ABS(var1)) + * ---------- + */ sub_abs(var2, var1, result); result->sign = NUMERIC_NEG; break; @@ -2615,30 +2939,33 @@ add_var(NumericVar *var1, NumericVar *var2, NumericVar *result) */ switch (cmp_abs(var1, var2)) { - case 0: /* ---------- - * ABS(var1) == ABS(var2) - * result = ZERO - * ---------- - */ + case 0: + /* ---------- + * ABS(var1) == ABS(var2) + * result = ZERO + * ---------- + */ zero_var(result); result->rscale = MAX(var1->rscale, var2->rscale); result->dscale = MAX(var1->dscale, var2->dscale); break; - case 1: /* ---------- - * ABS(var1) > ABS(var2) - * result = -(ABS(var1) - ABS(var2)) - * ---------- - */ + case 1: + /* ---------- + * ABS(var1) > ABS(var2) + * result = -(ABS(var1) - ABS(var2)) + * ---------- + */ sub_abs(var1, var2, result); result->sign = NUMERIC_NEG; break; - case -1: /* ---------- - * ABS(var1) < ABS(var2) - * result = +(ABS(var2) - ABS(var1)) - * ---------- - */ + case -1: + /* ---------- + * ABS(var1) < ABS(var2) + * result = +(ABS(var2) - ABS(var1)) + * ---------- + */ sub_abs(var2, var1, result); result->sign = NUMERIC_POS; break; @@ -2693,30 +3020,33 @@ sub_var(NumericVar *var1, NumericVar *var2, NumericVar *result) */ switch (cmp_abs(var1, var2)) { - case 0: /* ---------- - * ABS(var1) == ABS(var2) - * result = ZERO - * ---------- - */ + case 0: + /* ---------- + * ABS(var1) == ABS(var2) + * result = ZERO + * ---------- + */ zero_var(result); result->rscale = MAX(var1->rscale, var2->rscale); result->dscale = MAX(var1->dscale, var2->dscale); break; - case 1: /* ---------- - * ABS(var1) > ABS(var2) - * result = +(ABS(var1) - ABS(var2)) - * ---------- - */ + case 1: + /* ---------- + * ABS(var1) > ABS(var2) + * result = +(ABS(var1) - ABS(var2)) + * ---------- + */ sub_abs(var1, var2, result); result->sign = NUMERIC_POS; break; - case -1: /* ---------- - * ABS(var1) < ABS(var2) - * result = -(ABS(var2) - ABS(var1)) - * ---------- - */ + case -1: + /* ---------- + * ABS(var1) < ABS(var2) + * result = -(ABS(var2) - ABS(var1)) + * ---------- + */ sub_abs(var2, var1, result); result->sign = NUMERIC_NEG; break; @@ -2734,30 +3064,33 @@ sub_var(NumericVar *var1, NumericVar *var2, NumericVar *result) */ switch (cmp_abs(var1, var2)) { - case 0: /* ---------- - * ABS(var1) == ABS(var2) - * result = ZERO - * ---------- - */ + case 0: + /* ---------- + * ABS(var1) == ABS(var2) + * result = ZERO + * ---------- + */ zero_var(result); result->rscale = MAX(var1->rscale, var2->rscale); result->dscale = MAX(var1->dscale, var2->dscale); break; - case 1: /* ---------- - * ABS(var1) > ABS(var2) - * result = -(ABS(var1) - ABS(var2)) - * ---------- - */ + case 1: + /* ---------- + * ABS(var1) > ABS(var2) + * result = -(ABS(var1) - ABS(var2)) + * ---------- + */ sub_abs(var1, var2, result); result->sign = NUMERIC_NEG; break; - case -1: /* ---------- - * ABS(var1) < ABS(var2) - * result = +(ABS(var2) - ABS(var1)) - * ---------- - */ + case -1: + /* ---------- + * ABS(var1) < ABS(var2) + * result = +(ABS(var2) - ABS(var1)) + * ---------- + */ sub_abs(var2, var1, result); result->sign = NUMERIC_POS; break; @@ -2817,7 +3150,7 @@ mul_var(NumericVar *var1, NumericVar *var2, NumericVar *result) for (i2 = var2->ndigits - 1; i2 >= 0; i2--) { - sum = sum + res_digits[i] + var1->digits[i1] * var2->digits[i2]; + sum += res_digits[i] + var1->digits[i1] * var2->digits[i2]; res_digits[i--] = sum % 10; sum /= 10; } @@ -3067,7 +3400,6 @@ div_var(NumericVar *var1, NumericVar *var2, NumericVar *result) /* * Tidy up - * */ digitbuf_free(dividend.buf); for (i = 1; i < 10; i++) @@ -3552,6 +3884,11 @@ add_abs(NumericVar *var1, NumericVar *var2, NumericVar *result) i1, i2; int carry = 0; + /* copy these values into local vars for speed in inner loop */ + int var1ndigits = var1->ndigits; + int var2ndigits = var2->ndigits; + NumericDigit *var1digits = var1->digits; + NumericDigit *var2digits = var2->digits; res_weight = MAX(var1->weight, var2->weight) + 1; res_rscale = MAX(var1->rscale, var2->rscale); @@ -3569,15 +3906,25 @@ add_abs(NumericVar *var1, NumericVar *var2, NumericVar *result) { i1--; i2--; - if (i1 >= 0 && i1 < var1->ndigits) - carry += var1->digits[i1]; - if (i2 >= 0 && i2 < var2->ndigits) - carry += var2->digits[i2]; + if (i1 >= 0 && i1 < var1ndigits) + carry += var1digits[i1]; + if (i2 >= 0 && i2 < var2ndigits) + carry += var2digits[i2]; - res_digits[i] = carry % 10; - carry /= 10; + if (carry >= 10) + { + res_digits[i] = carry - 10; + carry = 1; + } + else + { + res_digits[i] = carry; + carry = 0; + } } + Assert(carry == 0); /* else we failed to allow for carry out */ + while (res_ndigits > 0 && *res_digits == 0) { res_digits++; @@ -3623,6 +3970,11 @@ sub_abs(NumericVar *var1, NumericVar *var2, NumericVar *result) i1, i2; int borrow = 0; + /* copy these values into local vars for speed in inner loop */ + int var1ndigits = var1->ndigits; + int var2ndigits = var2->ndigits; + NumericDigit *var1digits = var1->digits; + NumericDigit *var2digits = var2->digits; res_weight = var1->weight; res_rscale = MAX(var1->rscale, var2->rscale); @@ -3640,10 +3992,10 @@ sub_abs(NumericVar *var1, NumericVar *var2, NumericVar *result) { i1--; i2--; - if (i1 >= 0 && i1 < var1->ndigits) - borrow += var1->digits[i1]; - if (i2 >= 0 && i2 < var2->ndigits) - borrow -= var2->digits[i2]; + if (i1 >= 0 && i1 < var1ndigits) + borrow += var1digits[i1]; + if (i2 >= 0 && i2 < var2ndigits) + borrow -= var2digits[i2]; if (borrow < 0) { @@ -3657,6 +4009,8 @@ sub_abs(NumericVar *var1, NumericVar *var2, NumericVar *result) } } + Assert(borrow == 0); /* else caller gave us var1 < var2 */ + while (res_ndigits > 0 && *res_digits == 0) { res_digits++; diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index b4736dd6ae..0730d56147 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.33 2000/07/12 22:59:09 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.34 2000/07/17 03:05:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -29,6 +29,7 @@ #include "access/hash.h" #include "access/xact.h" #include "miscadmin.h" +#include "utils/array.h" #include "utils/builtins.h" @@ -882,10 +883,6 @@ overlaps_timestamp(PG_FUNCTION_ARGS) /*---------------------------------------------------------- * "Arithmetic" operators on date/times. - * timestamp_foo returns foo as an object (pointer) that - * can be passed between languages. - * timestamp_xx is an internal routine which returns the - * actual value. *---------------------------------------------------------*/ Datum @@ -1150,7 +1147,6 @@ interval_larger(PG_FUNCTION_ARGS) PG_RETURN_INTERVAL_P(result); } - Datum interval_pl(PG_FUNCTION_ARGS) { @@ -1232,6 +1228,90 @@ interval_div(PG_FUNCTION_ARGS) PG_RETURN_INTERVAL_P(result); } +/* + * interval_accum and interval_avg implement the AVG(interval) aggregate. + * + * The transition datatype for this aggregate is a 2-element array of + * intervals, where the first is the running sum and the second contains + * the number of values so far in its 'time' field. This is a bit ugly + * but it beats inventing a specialized datatype for the purpose. + */ + +Datum +interval_accum(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + Interval *newval = PG_GETARG_INTERVAL_P(1); + Datum *transdatums; + int ndatums; + Interval sumX, + N; + Interval *newsum; + ArrayType *result; + + /* We assume the input is array of interval */ + deconstruct_array(transarray, + false, 12, 'd', + &transdatums, &ndatums); + if (ndatums != 2) + elog(ERROR, "interval_accum: expected 2-element interval array"); + /* + * XXX memcpy, instead of just extracting a pointer, to work around + * buggy array code: it won't ensure proper alignment of Interval + * objects on machines where double requires 8-byte alignment. + * That should be fixed, but in the meantime... + */ + memcpy(&sumX, DatumGetIntervalP(transdatums[0]), sizeof(Interval)); + memcpy(&N, DatumGetIntervalP(transdatums[1]), sizeof(Interval)); + + newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl, + IntervalPGetDatum(&sumX), + IntervalPGetDatum(newval))); + N.time += 1; + + transdatums[0] = IntervalPGetDatum(newsum); + transdatums[1] = IntervalPGetDatum(&N); + + result = construct_array(transdatums, 2, + false, 12, 'd'); + + PG_RETURN_ARRAYTYPE_P(result); +} + +Datum +interval_avg(PG_FUNCTION_ARGS) +{ + ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); + Datum *transdatums; + int ndatums; + Interval sumX, + N; + + /* We assume the input is array of interval */ + deconstruct_array(transarray, + false, 12, 'd', + &transdatums, &ndatums); + if (ndatums != 2) + elog(ERROR, "interval_avg: expected 2-element interval array"); + /* + * XXX memcpy, instead of just extracting a pointer, to work around + * buggy array code: it won't ensure proper alignment of Interval + * objects on machines where double requires 8-byte alignment. + * That should be fixed, but in the meantime... + */ + memcpy(&sumX, DatumGetIntervalP(transdatums[0]), sizeof(Interval)); + memcpy(&N, DatumGetIntervalP(transdatums[1]), sizeof(Interval)); + + /* SQL92 defines AVG of no values to be NULL */ + if (N.time == 0) + PG_RETURN_NULL(); + + return DirectFunctionCall2(interval_div, + IntervalPGetDatum(&sumX), + Float8GetDatum(N.time)); +} + + /* timestamp_age() * Calculate time difference while retaining year/month fields. * Note that this does not result in an accurate absolute time span diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index b15840eea5..8e8e81feb4 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -22,7 +22,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.158 2000/07/11 13:07:17 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.159 2000/07/17 03:05:20 tgl Exp $ * * Modifications - 6/10/96 - dave@bensoft.com - version 1.13.dhb * @@ -1421,22 +1421,16 @@ clearAggInfo(AggInfo *agginfo, int numArgs) free(agginfo[i].oid); if (agginfo[i].aggname) free(agginfo[i].aggname); - if (agginfo[i].aggtransfn1) - free(agginfo[i].aggtransfn1); - if (agginfo[i].aggtransfn2) - free(agginfo[i].aggtransfn2); + if (agginfo[i].aggtransfn) + free(agginfo[i].aggtransfn); if (agginfo[i].aggfinalfn) free(agginfo[i].aggfinalfn); - if (agginfo[i].aggtranstype1) - free(agginfo[i].aggtranstype1); + if (agginfo[i].aggtranstype) + free(agginfo[i].aggtranstype); if (agginfo[i].aggbasetype) free(agginfo[i].aggbasetype); - if (agginfo[i].aggtranstype2) - free(agginfo[i].aggtranstype2); - if (agginfo[i].agginitval1) - free(agginfo[i].agginitval1); - if (agginfo[i].agginitval2) - free(agginfo[i].agginitval2); + if (agginfo[i].agginitval) + free(agginfo[i].agginitval); if (agginfo[i].usename) free(agginfo[i].usename); } @@ -1463,22 +1457,19 @@ getAggregates(int *numAggs) int i_oid; int i_aggname; - int i_aggtransfn1; - int i_aggtransfn2; + int i_aggtransfn; int i_aggfinalfn; - int i_aggtranstype1; + int i_aggtranstype; int i_aggbasetype; - int i_aggtranstype2; - int i_agginitval1; - int i_agginitval2; + int i_agginitval; int i_usename; /* find all user-defined aggregates */ appendPQExpBuffer(query, - "SELECT pg_aggregate.oid, aggname, aggtransfn1, aggtransfn2, " - "aggfinalfn, aggtranstype1, aggbasetype, aggtranstype2, " - "agginitval1, agginitval2, usename from pg_aggregate, pg_user " + "SELECT pg_aggregate.oid, aggname, aggtransfn, " + "aggfinalfn, aggtranstype, aggbasetype, " + "agginitval, usename from pg_aggregate, pg_user " "where aggowner = usesysid"); res = PQexec(g_conn, query->data); @@ -1497,28 +1488,22 @@ getAggregates(int *numAggs) i_oid = PQfnumber(res, "oid"); i_aggname = PQfnumber(res, "aggname"); - i_aggtransfn1 = PQfnumber(res, "aggtransfn1"); - i_aggtransfn2 = PQfnumber(res, "aggtransfn2"); + i_aggtransfn = PQfnumber(res, "aggtransfn"); i_aggfinalfn = PQfnumber(res, "aggfinalfn"); - i_aggtranstype1 = PQfnumber(res, "aggtranstype1"); + i_aggtranstype = PQfnumber(res, "aggtranstype"); i_aggbasetype = PQfnumber(res, "aggbasetype"); - i_aggtranstype2 = PQfnumber(res, "aggtranstype2"); - i_agginitval1 = PQfnumber(res, "agginitval1"); - i_agginitval2 = PQfnumber(res, "agginitval2"); + i_agginitval = PQfnumber(res, "agginitval"); i_usename = PQfnumber(res, "usename"); for (i = 0; i < ntups; i++) { agginfo[i].oid = strdup(PQgetvalue(res, i, i_oid)); agginfo[i].aggname = strdup(PQgetvalue(res, i, i_aggname)); - agginfo[i].aggtransfn1 = strdup(PQgetvalue(res, i, i_aggtransfn1)); - agginfo[i].aggtransfn2 = strdup(PQgetvalue(res, i, i_aggtransfn2)); + agginfo[i].aggtransfn = strdup(PQgetvalue(res, i, i_aggtransfn)); agginfo[i].aggfinalfn = strdup(PQgetvalue(res, i, i_aggfinalfn)); - agginfo[i].aggtranstype1 = strdup(PQgetvalue(res, i, i_aggtranstype1)); + agginfo[i].aggtranstype = strdup(PQgetvalue(res, i, i_aggtranstype)); agginfo[i].aggbasetype = strdup(PQgetvalue(res, i, i_aggbasetype)); - agginfo[i].aggtranstype2 = strdup(PQgetvalue(res, i, i_aggtranstype2)); - agginfo[i].agginitval1 = strdup(PQgetvalue(res, i, i_agginitval1)); - agginfo[i].agginitval2 = strdup(PQgetvalue(res, i, i_agginitval2)); + agginfo[i].agginitval = strdup(PQgetvalue(res, i, i_agginitval)); agginfo[i].usename = strdup(PQgetvalue(res, i, i_usename)); } @@ -2902,69 +2887,32 @@ dumpAggs(Archive *fout, AggInfo *agginfo, int numAggs, PQExpBuffer q = createPQExpBuffer(); PQExpBuffer delq = createPQExpBuffer(); PQExpBuffer aggSig = createPQExpBuffer(); - PQExpBuffer sfunc1 = createPQExpBuffer(); - PQExpBuffer sfunc2 = createPQExpBuffer(); - PQExpBuffer basetype = createPQExpBuffer(); - PQExpBuffer finalfunc = createPQExpBuffer(); - char comma1[2], - comma2[2]; + PQExpBuffer details = createPQExpBuffer(); for (i = 0; i < numAggs; i++) { - - resetPQExpBuffer(sfunc1); - resetPQExpBuffer(sfunc2); - resetPQExpBuffer(basetype); - resetPQExpBuffer(finalfunc); + resetPQExpBuffer(details); /* skip all the builtin oids */ if (atoi(agginfo[i].oid) < g_last_builtin_oid) continue; - appendPQExpBuffer(basetype, + appendPQExpBuffer(details, "BASETYPE = %s, ", fmtId(findTypeByOid(tinfo, numTypes, agginfo[i].aggbasetype), false)); - if (!(strcmp(agginfo[i].aggtransfn1, "-") == 0)) - { - appendPQExpBuffer(sfunc1, - "SFUNC1 = %s, STYPE1 = %s", - agginfo[i].aggtransfn1, - fmtId(findTypeByOid(tinfo, numTypes, agginfo[i].aggtranstype1), false)); - if (agginfo[i].agginitval1) - appendPQExpBuffer(sfunc1, ", INITCOND1 = '%s'", - agginfo[i].agginitval1); + appendPQExpBuffer(details, + "SFUNC = %s, STYPE = %s", + agginfo[i].aggtransfn, + fmtId(findTypeByOid(tinfo, numTypes, agginfo[i].aggtranstype), false)); - } - - if (!(strcmp(agginfo[i].aggtransfn2, "-") == 0)) - { - appendPQExpBuffer(sfunc2, - "SFUNC2 = %s, STYPE2 = %s", - agginfo[i].aggtransfn2, - fmtId(findTypeByOid(tinfo, numTypes, agginfo[i].aggtranstype2), false)); - if (agginfo[i].agginitval2) - appendPQExpBuffer(sfunc2, ", INITCOND2 = '%s'", - agginfo[i].agginitval2); - } + if (agginfo[i].agginitval) + appendPQExpBuffer(details, ", INITCOND = '%s'", + agginfo[i].agginitval); if (!(strcmp(agginfo[i].aggfinalfn, "-") == 0)) - appendPQExpBuffer(finalfunc, "FINALFUNC = %s", agginfo[i].aggfinalfn); - if (sfunc1->data[0] != '\0' && sfunc2->data[0] != '\0') - { - comma1[0] = ','; - comma1[1] = '\0'; - } - else - comma1[0] = '\0'; - - if (finalfunc->data[0] != '\0' && (sfunc1->data[0] != '\0' || sfunc2->data[0] != '\0')) - { - comma2[0] = ','; - comma2[1] = '\0'; - } - else - comma2[0] = '\0'; + appendPQExpBuffer(details, ", FINALFUNC = %s", + agginfo[i].aggfinalfn); resetPQExpBuffer(aggSig); appendPQExpBuffer(aggSig, "%s %s", agginfo[i].aggname, @@ -2974,14 +2922,9 @@ dumpAggs(Archive *fout, AggInfo *agginfo, int numAggs, appendPQExpBuffer(delq, "DROP AGGREGATE %s;\n", aggSig->data); resetPQExpBuffer(q); - appendPQExpBuffer(q, "CREATE AGGREGATE %s ( %s %s%s %s%s %s );\n", + appendPQExpBuffer(q, "CREATE AGGREGATE %s ( %s );\n", agginfo[i].aggname, - basetype->data, - sfunc1->data, - comma1, - sfunc2->data, - comma2, - finalfunc->data); + details->data); ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "AGGREGATE", NULL, q->data, delq->data, agginfo[i].usename, NULL, NULL); diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 609868dbcd..8ad340f64b 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_dump.h,v 1.49 2000/07/04 14:25:28 momjian Exp $ + * $Id: pg_dump.h,v 1.50 2000/07/17 03:05:20 tgl Exp $ * * Modifications - 6/12/96 - dave@bensoft.com - version 1.13.dhb.2 * @@ -133,14 +133,11 @@ typedef struct _aggInfo { char *oid; char *aggname; - char *aggtransfn1; - char *aggtransfn2; + char *aggtransfn; char *aggfinalfn; - char *aggtranstype1; + char *aggtranstype; char *aggbasetype; - char *aggtranstype2; - char *agginitval1; - char *agginitval2; + char *agginitval; char *usename; } AggInfo; diff --git a/src/include/c.h b/src/include/c.h index 603c5bbfe8..60abde1736 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: c.h,v 1.78 2000/07/12 22:59:12 petere Exp $ + * $Id: c.h,v 1.79 2000/07/17 03:05:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -587,6 +587,25 @@ extern Datum Float8GetDatum(float8 X); #define Float64GetDatum(X) PointerGetDatum(X) +/* + * Int64GetDatumFast + * Float4GetDatumFast + * Float8GetDatumFast + * + * These macros are intended to allow writing code that does not depend on + * whether int64, float4, float8 are pass-by-reference types, while not + * sacrificing performance when they are. The argument must be a variable + * that will exist and have the same value for as long as the Datum is needed. + * In the pass-by-ref case, the address of the variable is taken to use as + * the Datum. In the pass-by-val case, these will be the same as the non-Fast + * macros. + */ + +#define Int64GetDatumFast(X) PointerGetDatum(&(X)) +#define Float4GetDatumFast(X) PointerGetDatum(&(X)) +#define Float8GetDatumFast(X) PointerGetDatum(&(X)) + + /* ---------------------------------------------------------------- * Section 5: IsValid macros for system types * ---------------------------------------------------------------- diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 0d4aec6c7e..80be91a70f 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catversion.h,v 1.36 2000/07/07 19:24:41 petere Exp $ + * $Id: catversion.h,v 1.37 2000/07/17 03:05:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200007071 +#define CATALOG_VERSION_NO 200007161 #endif diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h index 2350d73856..1c061cd668 100644 --- a/src/include/catalog/pg_aggregate.h +++ b/src/include/catalog/pg_aggregate.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_aggregate.h,v 1.26 2000/05/30 04:24:55 tgl Exp $ + * $Id: pg_aggregate.h,v 1.27 2000/07/17 03:05:23 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -32,30 +32,25 @@ * cpp turns this into typedef struct FormData_pg_aggregate * * aggname name of the aggregate - * aggtransfn1 transition function 1 - * aggtransfn2 transition function 2 + * aggowner owner (creator) of the aggregate + * aggtransfn transition function * aggfinalfn final function * aggbasetype type of data on which aggregate operates - * aggtranstype1 output types for transition func 1 - * aggtranstype2 output types for transition func 2 - * aggfinaltype output type for final function - * agginitval1 initial aggregate value - * agginitval2 initial value for transition state 2 + * aggtranstype type of aggregate's transition (state) data + * aggfinaltype type of aggregate's final result + * agginitval initial value for transition state * ---------------------------------------------------------------- */ CATALOG(pg_aggregate) { NameData aggname; int4 aggowner; - regproc aggtransfn1; - regproc aggtransfn2; + regproc aggtransfn; regproc aggfinalfn; Oid aggbasetype; - Oid aggtranstype1; - Oid aggtranstype2; + Oid aggtranstype; Oid aggfinaltype; - text agginitval1; /* VARIABLE LENGTH FIELD */ - text agginitval2; /* VARIABLE LENGTH FIELD */ + text agginitval; /* VARIABLE LENGTH FIELD */ } FormData_pg_aggregate; /* ---------------- @@ -70,18 +65,15 @@ typedef FormData_pg_aggregate *Form_pg_aggregate; * ---------------- */ -#define Natts_pg_aggregate 11 +#define Natts_pg_aggregate 8 #define Anum_pg_aggregate_aggname 1 #define Anum_pg_aggregate_aggowner 2 -#define Anum_pg_aggregate_aggtransfn1 3 -#define Anum_pg_aggregate_aggtransfn2 4 -#define Anum_pg_aggregate_aggfinalfn 5 -#define Anum_pg_aggregate_aggbasetype 6 -#define Anum_pg_aggregate_aggtranstype1 7 -#define Anum_pg_aggregate_aggtranstype2 8 -#define Anum_pg_aggregate_aggfinaltype 9 -#define Anum_pg_aggregate_agginitval1 10 -#define Anum_pg_aggregate_agginitval2 11 +#define Anum_pg_aggregate_aggtransfn 3 +#define Anum_pg_aggregate_aggfinalfn 4 +#define Anum_pg_aggregate_aggbasetype 5 +#define Anum_pg_aggregate_aggtranstype 6 +#define Anum_pg_aggregate_aggfinaltype 7 +#define Anum_pg_aggregate_agginitval 8 /* ---------------- @@ -89,70 +81,84 @@ typedef FormData_pg_aggregate *Form_pg_aggregate; * --------------- */ -DATA(insert OID = 0 ( avg PGUID int8pl int4inc int84div 20 20 23 20 _null_ 0 )); -DATA(insert OID = 0 ( avg PGUID int4pl int4inc int4div 23 23 23 23 _null_ 0 )); -DATA(insert OID = 0 ( avg PGUID int2pl int2inc int2div 21 21 21 21 _null_ 0 )); -DATA(insert OID = 0 ( avg PGUID float4pl float4inc float4div 700 700 700 700 _null_ 0.0 )); -DATA(insert OID = 0 ( avg PGUID float8pl float8inc float8div 701 701 701 701 _null_ 0.0 )); -DATA(insert OID = 0 ( avg PGUID cash_pl float8inc cash_div_flt8 790 790 701 790 _null_ 0.0 )); -DATA(insert OID = 0 ( avg PGUID interval_pl float8inc interval_div 1186 1186 701 1186 _null_ 0.0 )); -DATA(insert OID = 0 ( avg PGUID numeric_add numeric_inc numeric_div 1700 1700 1700 1700 _null_ 0 )); +DATA(insert OID = 0 ( avg PGUID int8_accum numeric_avg 20 1231 1700 "{0,0,0}" )); +DATA(insert OID = 0 ( avg PGUID int4_accum numeric_avg 23 1231 1700 "{0,0,0}" )); +DATA(insert OID = 0 ( avg PGUID int2_accum numeric_avg 21 1231 1700 "{0,0,0}" )); +DATA(insert OID = 0 ( avg PGUID numeric_accum numeric_avg 1700 1231 1700 "{0,0,0}" )); +DATA(insert OID = 0 ( avg PGUID float4_accum float8_avg 700 1022 701 "{0,0,0}" )); +DATA(insert OID = 0 ( avg PGUID float8_accum float8_avg 701 1022 701 "{0,0,0}" )); +DATA(insert OID = 0 ( avg PGUID interval_accum interval_avg 1186 1187 1186 "{0,0}" )); -DATA(insert OID = 0 ( sum PGUID int8pl - - 20 20 0 20 _null_ _null_ )); -DATA(insert OID = 0 ( sum PGUID int4pl - - 23 23 0 23 _null_ _null_ )); -DATA(insert OID = 0 ( sum PGUID int2pl - - 21 21 0 21 _null_ _null_ )); -DATA(insert OID = 0 ( sum PGUID float4pl - - 700 700 0 700 _null_ _null_ )); -DATA(insert OID = 0 ( sum PGUID float8pl - - 701 701 0 701 _null_ _null_ )); -DATA(insert OID = 0 ( sum PGUID cash_pl - - 790 790 0 790 _null_ _null_ )); -DATA(insert OID = 0 ( sum PGUID interval_pl - - 1186 1186 0 1186 _null_ _null_ )); -DATA(insert OID = 0 ( sum PGUID numeric_add - - 1700 1700 0 1700 _null_ _null_ )); +DATA(insert OID = 0 ( sum PGUID int8_sum - 20 1700 1700 _null_ )); +DATA(insert OID = 0 ( sum PGUID int4_sum - 23 1700 1700 _null_ )); +DATA(insert OID = 0 ( sum PGUID int2_sum - 21 1700 1700 _null_ )); +DATA(insert OID = 0 ( sum PGUID float4pl - 700 700 700 _null_ )); +DATA(insert OID = 0 ( sum PGUID float8pl - 701 701 701 _null_ )); +DATA(insert OID = 0 ( sum PGUID cash_pl - 790 790 790 _null_ )); +DATA(insert OID = 0 ( sum PGUID interval_pl - 1186 1186 1186 _null_ )); +DATA(insert OID = 0 ( sum PGUID numeric_add - 1700 1700 1700 _null_ )); -DATA(insert OID = 0 ( max PGUID int8larger - - 20 20 0 20 _null_ _null_ )); -DATA(insert OID = 0 ( max PGUID int4larger - - 23 23 0 23 _null_ _null_ )); -DATA(insert OID = 0 ( max PGUID int2larger - - 21 21 0 21 _null_ _null_ )); -DATA(insert OID = 0 ( max PGUID float4larger - - 700 700 0 700 _null_ _null_ )); -DATA(insert OID = 0 ( max PGUID float8larger - - 701 701 0 701 _null_ _null_ )); -DATA(insert OID = 0 ( max PGUID int4larger - - 702 702 0 702 _null_ _null_ )); -DATA(insert OID = 0 ( max PGUID date_larger - - 1082 1082 0 1082 _null_ _null_ )); -DATA(insert OID = 0 ( max PGUID time_larger - - 1083 1083 0 1083 _null_ _null_ )); -DATA(insert OID = 0 ( max PGUID timetz_larger - - 1266 1266 0 1266 _null_ _null_ )); -DATA(insert OID = 0 ( max PGUID cashlarger - - 790 790 0 790 _null_ _null_ )); -DATA(insert OID = 0 ( max PGUID timestamp_larger - - 1184 1184 0 1184 _null_ _null_ )); -DATA(insert OID = 0 ( max PGUID interval_larger - - 1186 1186 0 1186 _null_ _null_ )); -DATA(insert OID = 0 ( max PGUID text_larger - - 25 25 0 25 _null_ _null_ )); -DATA(insert OID = 0 ( max PGUID numeric_larger - - 1700 1700 0 1700 _null_ _null_ )); +DATA(insert OID = 0 ( max PGUID int8larger - 20 20 20 _null_ )); +DATA(insert OID = 0 ( max PGUID int4larger - 23 23 23 _null_ )); +DATA(insert OID = 0 ( max PGUID int2larger - 21 21 21 _null_ )); +DATA(insert OID = 0 ( max PGUID float4larger - 700 700 700 _null_ )); +DATA(insert OID = 0 ( max PGUID float8larger - 701 701 701 _null_ )); +DATA(insert OID = 0 ( max PGUID int4larger - 702 702 702 _null_ )); +DATA(insert OID = 0 ( max PGUID date_larger - 1082 1082 1082 _null_ )); +DATA(insert OID = 0 ( max PGUID time_larger - 1083 1083 1083 _null_ )); +DATA(insert OID = 0 ( max PGUID timetz_larger - 1266 1266 1266 _null_ )); +DATA(insert OID = 0 ( max PGUID cashlarger - 790 790 790 _null_ )); +DATA(insert OID = 0 ( max PGUID timestamp_larger - 1184 1184 1184 _null_ )); +DATA(insert OID = 0 ( max PGUID interval_larger - 1186 1186 1186 _null_ )); +DATA(insert OID = 0 ( max PGUID text_larger - 25 25 25 _null_ )); +DATA(insert OID = 0 ( max PGUID numeric_larger - 1700 1700 1700 _null_ )); -DATA(insert OID = 0 ( min PGUID int8smaller - - 20 20 0 20 _null_ _null_ )); -DATA(insert OID = 0 ( min PGUID int4smaller - - 23 23 0 23 _null_ _null_ )); -DATA(insert OID = 0 ( min PGUID int2smaller - - 21 21 0 21 _null_ _null_ )); -DATA(insert OID = 0 ( min PGUID float4smaller - - 700 700 0 700 _null_ _null_ )); -DATA(insert OID = 0 ( min PGUID float8smaller - - 701 701 0 701 _null_ _null_ )); -DATA(insert OID = 0 ( min PGUID int4smaller - - 702 702 0 702 _null_ _null_ )); -DATA(insert OID = 0 ( min PGUID date_smaller - - 1082 1082 0 1082 _null_ _null_ )); -DATA(insert OID = 0 ( min PGUID time_smaller - - 1083 1083 0 1083 _null_ _null_ )); -DATA(insert OID = 0 ( min PGUID timetz_smaller - - 1266 1266 0 1266 _null_ _null_ )); -DATA(insert OID = 0 ( min PGUID cashsmaller - - 790 790 0 790 _null_ _null_ )); -DATA(insert OID = 0 ( min PGUID timestamp_smaller - - 1184 1184 0 1184 _null_ _null_ )); -DATA(insert OID = 0 ( min PGUID interval_smaller - - 1186 1186 0 1186 _null_ _null_ )); -DATA(insert OID = 0 ( min PGUID text_smaller - - 25 25 0 25 _null_ _null_ )); -DATA(insert OID = 0 ( min PGUID numeric_smaller - - 1700 1700 0 1700 _null_ _null_ )); +DATA(insert OID = 0 ( min PGUID int8smaller - 20 20 20 _null_ )); +DATA(insert OID = 0 ( min PGUID int4smaller - 23 23 23 _null_ )); +DATA(insert OID = 0 ( min PGUID int2smaller - 21 21 21 _null_ )); +DATA(insert OID = 0 ( min PGUID float4smaller - 700 700 700 _null_ )); +DATA(insert OID = 0 ( min PGUID float8smaller - 701 701 701 _null_ )); +DATA(insert OID = 0 ( min PGUID int4smaller - 702 702 702 _null_ )); +DATA(insert OID = 0 ( min PGUID date_smaller - 1082 1082 1082 _null_ )); +DATA(insert OID = 0 ( min PGUID time_smaller - 1083 1083 1083 _null_ )); +DATA(insert OID = 0 ( min PGUID timetz_smaller - 1266 1266 1266 _null_ )); +DATA(insert OID = 0 ( min PGUID cashsmaller - 790 790 790 _null_ )); +DATA(insert OID = 0 ( min PGUID timestamp_smaller - 1184 1184 1184 _null_ )); +DATA(insert OID = 0 ( min PGUID interval_smaller - 1186 1186 1186 _null_ )); +DATA(insert OID = 0 ( min PGUID text_smaller - 25 25 25 _null_ )); +DATA(insert OID = 0 ( min PGUID numeric_smaller - 1700 1700 1700 _null_ )); -DATA(insert OID = 0 ( count PGUID - int4inc - 0 0 23 23 _null_ 0 )); +/* + * Using int4inc for count() is cheating a little, since it really only + * takes 1 parameter not 2, but nodeAgg.c won't complain ... + */ +DATA(insert OID = 0 ( count PGUID int4inc - 0 23 23 0 )); + +DATA(insert OID = 0 ( variance PGUID int8_accum numeric_variance 20 1231 1700 "{0,0,0}" )); +DATA(insert OID = 0 ( variance PGUID int4_accum numeric_variance 23 1231 1700 "{0,0,0}" )); +DATA(insert OID = 0 ( variance PGUID int2_accum numeric_variance 21 1231 1700 "{0,0,0}" )); +DATA(insert OID = 0 ( variance PGUID float4_accum float8_variance 700 1022 701 "{0,0,0}" )); +DATA(insert OID = 0 ( variance PGUID float8_accum float8_variance 701 1022 701 "{0,0,0}" )); +DATA(insert OID = 0 ( variance PGUID numeric_accum numeric_variance 1700 1231 1700 "{0,0,0}" )); + +DATA(insert OID = 0 ( stddev PGUID int8_accum numeric_stddev 20 1231 1700 "{0,0,0}" )); +DATA(insert OID = 0 ( stddev PGUID int4_accum numeric_stddev 23 1231 1700 "{0,0,0}" )); +DATA(insert OID = 0 ( stddev PGUID int2_accum numeric_stddev 21 1231 1700 "{0,0,0}" )); +DATA(insert OID = 0 ( stddev PGUID float4_accum float8_stddev 700 1022 701 "{0,0,0}" )); +DATA(insert OID = 0 ( stddev PGUID float8_accum float8_stddev 701 1022 701 "{0,0,0}" )); +DATA(insert OID = 0 ( stddev PGUID numeric_accum numeric_stddev 1700 1231 1700 "{0,0,0}" )); /* * prototypes for functions in pg_aggregate.c */ extern void AggregateCreate(char *aggName, - char *aggtransfn1Name, - char *aggtransfn2Name, + char *aggtransfnName, char *aggfinalfnName, char *aggbasetypeName, - char *aggtransfn1typeName, - char *aggtransfn2typeName, - char *agginitval1, - char *agginitval2); + char *aggtranstypeName, + char *agginitval); extern Datum AggNameGetInitVal(char *aggName, Oid basetype, - int xfuncno, bool *isNull); + bool *isNull); #endif /* PG_AGGREGATE_H */ diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index 06bb7546cd..20ea41ec0f 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_operator.h,v 1.76 2000/06/05 07:28:59 tgl Exp $ + * $Id: pg_operator.h,v 1.77 2000/07/17 03:05:23 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -323,12 +323,12 @@ DATA(insert OID = 636 ( "-" PGUID 0 b t f 18 18 18 0 0 0 0 charmi - - )) DATA(insert OID = 637 ( "*" PGUID 0 b t f 18 18 18 0 0 0 0 charmul - - )); DATA(insert OID = 638 ( "/" PGUID 0 b t f 18 18 18 0 0 0 0 chardiv - - )); -DATA(insert OID = 639 ( "~" PGUID 0 b t f 19 25 16 0 640 0 0 nameregexeq eqsel eqjoinsel )); +DATA(insert OID = 639 ( "~" PGUID 0 b t f 19 25 16 0 640 0 0 nameregexeq regexeqsel regexeqjoinsel )); #define OID_NAME_REGEXEQ_OP 639 -DATA(insert OID = 640 ( "!~" PGUID 0 b t f 19 25 16 0 639 0 0 nameregexne neqsel neqjoinsel )); -DATA(insert OID = 641 ( "~" PGUID 0 b t f 25 25 16 0 642 0 0 textregexeq eqsel eqjoinsel )); +DATA(insert OID = 640 ( "!~" PGUID 0 b t f 19 25 16 0 639 0 0 nameregexne regexnesel regexnejoinsel )); +DATA(insert OID = 641 ( "~" PGUID 0 b t f 25 25 16 0 642 0 0 textregexeq regexeqsel regexeqjoinsel )); #define OID_TEXT_REGEXEQ_OP 641 -DATA(insert OID = 642 ( "!~" PGUID 0 b t f 25 25 16 0 641 0 0 textregexne neqsel neqjoinsel )); +DATA(insert OID = 642 ( "!~" PGUID 0 b t f 25 25 16 0 641 0 0 textregexne regexnesel regexnejoinsel )); DATA(insert OID = 643 ( "<>" PGUID 0 b t f 19 19 16 643 93 0 0 namene neqsel neqjoinsel )); DATA(insert OID = 654 ( "||" PGUID 0 b t f 25 25 25 0 0 0 0 textcat - - )); @@ -449,9 +449,9 @@ DATA(insert OID = 974 ( "||" PGUID 0 b t f 1042 1042 1042 0 0 0 0 textc DATA(insert OID = 979 ( "||" PGUID 0 b t f 1043 1043 1043 0 0 0 0 textcat - - )); DATA(insert OID = 1054 ( "=" PGUID 0 b t f 1042 1042 16 1054 1057 1058 1058 bpchareq eqsel eqjoinsel )); -DATA(insert OID = 1055 ( "~" PGUID 0 b t f 1042 25 16 0 1056 0 0 textregexeq eqsel eqjoinsel )); +DATA(insert OID = 1055 ( "~" PGUID 0 b t f 1042 25 16 0 1056 0 0 textregexeq regexeqsel regexeqjoinsel )); #define OID_BPCHAR_REGEXEQ_OP 1055 -DATA(insert OID = 1056 ( "!~" PGUID 0 b t f 1042 25 16 0 1055 0 0 textregexne neqsel neqjoinsel )); +DATA(insert OID = 1056 ( "!~" PGUID 0 b t f 1042 25 16 0 1055 0 0 textregexne regexnesel regexnejoinsel )); DATA(insert OID = 1057 ( "<>" PGUID 0 b t f 1042 1042 16 1057 1054 0 0 bpcharne neqsel neqjoinsel )); DATA(insert OID = 1058 ( "<" PGUID 0 b t f 1042 1042 16 1060 1061 0 0 bpcharlt scalarltsel scalarltjoinsel )); DATA(insert OID = 1059 ( "<=" PGUID 0 b t f 1042 1042 16 1061 1060 0 0 bpcharle scalarltsel scalarltjoinsel )); @@ -459,9 +459,9 @@ DATA(insert OID = 1060 ( ">" PGUID 0 b t f 1042 1042 16 1058 1059 0 0 bpcha DATA(insert OID = 1061 ( ">=" PGUID 0 b t f 1042 1042 16 1059 1058 0 0 bpcharge scalargtsel scalargtjoinsel )); DATA(insert OID = 1062 ( "=" PGUID 0 b t t 1043 1043 16 1062 1065 1066 1066 varchareq eqsel eqjoinsel )); -DATA(insert OID = 1063 ( "~" PGUID 0 b t f 1043 25 16 0 1064 0 0 textregexeq eqsel eqjoinsel )); +DATA(insert OID = 1063 ( "~" PGUID 0 b t f 1043 25 16 0 1064 0 0 textregexeq regexeqsel regexeqjoinsel )); #define OID_VARCHAR_REGEXEQ_OP 1063 -DATA(insert OID = 1064 ( "!~" PGUID 0 b t f 1043 25 16 0 1063 0 0 textregexne neqsel neqjoinsel )); +DATA(insert OID = 1064 ( "!~" PGUID 0 b t f 1043 25 16 0 1063 0 0 textregexne regexnesel regexnejoinsel )); DATA(insert OID = 1065 ( "<>" PGUID 0 b t f 1043 1043 16 1065 1062 0 0 varcharne neqsel neqjoinsel )); DATA(insert OID = 1066 ( "<" PGUID 0 b t f 1043 1043 16 1068 1069 0 0 varcharlt scalarltsel scalarltjoinsel )); DATA(insert OID = 1067 ( "<=" PGUID 0 b t f 1043 1043 16 1069 1068 0 0 varcharle scalarltsel scalarltjoinsel )); @@ -527,32 +527,32 @@ DATA(insert OID = 1158 ( "!" PGUID 0 r t f 21 0 23 0 0 0 0 int2fac - - )); DATA(insert OID = 1175 ( "!!" PGUID 0 l t f 0 21 23 0 0 0 0 int2fac - - )); /* LIKE hacks by Keith Parks. */ -DATA(insert OID = 1207 ( "~~" PGUID 0 b t f 19 25 16 0 1208 0 0 namelike eqsel eqjoinsel )); +DATA(insert OID = 1207 ( "~~" PGUID 0 b t f 19 25 16 0 1208 0 0 namelike likesel likejoinsel )); #define OID_NAME_LIKE_OP 1207 -DATA(insert OID = 1208 ( "!~~" PGUID 0 b t f 19 25 16 0 1207 0 0 namenlike neqsel neqjoinsel )); -DATA(insert OID = 1209 ( "~~" PGUID 0 b t f 25 25 16 0 1210 0 0 textlike eqsel eqjoinsel )); +DATA(insert OID = 1208 ( "!~~" PGUID 0 b t f 19 25 16 0 1207 0 0 namenlike nlikesel nlikejoinsel )); +DATA(insert OID = 1209 ( "~~" PGUID 0 b t f 25 25 16 0 1210 0 0 textlike likesel likejoinsel )); #define OID_TEXT_LIKE_OP 1209 -DATA(insert OID = 1210 ( "!~~" PGUID 0 b t f 25 25 16 0 1209 0 0 textnlike neqsel neqjoinsel )); -DATA(insert OID = 1211 ( "~~" PGUID 0 b t f 1042 25 16 0 1212 0 0 textlike eqsel eqjoinsel )); +DATA(insert OID = 1210 ( "!~~" PGUID 0 b t f 25 25 16 0 1209 0 0 textnlike nlikesel nlikejoinsel )); +DATA(insert OID = 1211 ( "~~" PGUID 0 b t f 1042 25 16 0 1212 0 0 textlike likesel likejoinsel )); #define OID_BPCHAR_LIKE_OP 1211 -DATA(insert OID = 1212 ( "!~~" PGUID 0 b t f 1042 25 16 0 1211 0 0 textnlike neqsel neqjoinsel )); -DATA(insert OID = 1213 ( "~~" PGUID 0 b t f 1043 25 16 0 1214 0 0 textlike eqsel eqjoinsel )); +DATA(insert OID = 1212 ( "!~~" PGUID 0 b t f 1042 25 16 0 1211 0 0 textnlike nlikesel nlikejoinsel )); +DATA(insert OID = 1213 ( "~~" PGUID 0 b t f 1043 25 16 0 1214 0 0 textlike likesel likejoinsel )); #define OID_VARCHAR_LIKE_OP 1213 -DATA(insert OID = 1214 ( "!~~" PGUID 0 b t f 1043 25 16 0 1213 0 0 textnlike neqsel neqjoinsel )); +DATA(insert OID = 1214 ( "!~~" PGUID 0 b t f 1043 25 16 0 1213 0 0 textnlike nlikesel nlikejoinsel )); /* case-insensitive LIKE hacks */ -DATA(insert OID = 1226 ( "~*" PGUID 0 b t f 19 25 16 0 1227 0 0 nameicregexeq eqsel eqjoinsel )); +DATA(insert OID = 1226 ( "~*" PGUID 0 b t f 19 25 16 0 1227 0 0 nameicregexeq icregexeqsel icregexeqjoinsel )); #define OID_NAME_ICREGEXEQ_OP 1226 -DATA(insert OID = 1227 ( "!~*" PGUID 0 b t f 19 25 16 0 1226 0 0 nameicregexne neqsel neqjoinsel )); -DATA(insert OID = 1228 ( "~*" PGUID 0 b t f 25 25 16 0 1229 0 0 texticregexeq eqsel eqjoinsel )); +DATA(insert OID = 1227 ( "!~*" PGUID 0 b t f 19 25 16 0 1226 0 0 nameicregexne icregexnesel icregexnejoinsel )); +DATA(insert OID = 1228 ( "~*" PGUID 0 b t f 25 25 16 0 1229 0 0 texticregexeq icregexeqsel icregexeqjoinsel )); #define OID_TEXT_ICREGEXEQ_OP 1228 -DATA(insert OID = 1229 ( "!~*" PGUID 0 b t f 25 25 16 0 1228 0 0 texticregexne neqsel neqjoinsel )); -DATA(insert OID = 1232 ( "~*" PGUID 0 b t f 1043 25 16 0 1233 0 0 texticregexeq eqsel eqjoinsel )); +DATA(insert OID = 1229 ( "!~*" PGUID 0 b t f 25 25 16 0 1228 0 0 texticregexne icregexnesel icregexnejoinsel )); +DATA(insert OID = 1232 ( "~*" PGUID 0 b t f 1043 25 16 0 1233 0 0 texticregexeq icregexeqsel icregexeqjoinsel )); #define OID_VARCHAR_ICREGEXEQ_OP 1232 -DATA(insert OID = 1233 ( "!~*" PGUID 0 b t f 1043 25 16 0 1232 0 0 texticregexne neqsel neqjoinsel )); -DATA(insert OID = 1234 ( "~*" PGUID 0 b t f 1042 25 16 0 1235 0 0 texticregexeq eqsel eqjoinsel )); +DATA(insert OID = 1233 ( "!~*" PGUID 0 b t f 1043 25 16 0 1232 0 0 texticregexne icregexnesel icregexnejoinsel )); +DATA(insert OID = 1234 ( "~*" PGUID 0 b t f 1042 25 16 0 1235 0 0 texticregexeq icregexeqsel icregexeqjoinsel )); #define OID_BPCHAR_ICREGEXEQ_OP 1234 -DATA(insert OID = 1235 ( "!~*" PGUID 0 b t f 1042 25 16 0 1234 0 0 texticregexne neqsel neqjoinsel )); +DATA(insert OID = 1235 ( "!~*" PGUID 0 b t f 1042 25 16 0 1234 0 0 texticregexne icregexnesel icregexnejoinsel )); /* timestamp operators */ /* name, owner, prec, kind, isleft, canhash, left, right, result, com, negate, lsortop, rsortop, oprcode, operrest, oprjoin */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 538299773d..c92ce065fb 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_proc.h,v 1.147 2000/07/14 22:17:56 tgl Exp $ + * $Id: pg_proc.h,v 1.148 2000/07/17 03:05:25 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -433,8 +433,8 @@ DATA(insert OID = 206 ( float4um PGUID 11 f t t t 1 f 700 "700" 100 0 0 100 DESCR("negate"); DATA(insert OID = 207 ( float4abs PGUID 11 f t t t 1 f 700 "700" 100 0 0 100 float4abs - )); DESCR("absolute value"); -DATA(insert OID = 208 ( float4inc PGUID 11 f t t t 1 f 700 "700" 100 0 0 100 float4inc - )); -DESCR("increment"); +DATA(insert OID = 208 ( float4_accum PGUID 12 f t t t 2 f 1022 "1022 700" 100 0 0 100 float4_accum - )); +DESCR("aggregate transition function"); DATA(insert OID = 209 ( float4larger PGUID 11 f t t t 2 f 700 "700 700" 100 0 0 100 float4larger - )); DESCR("larger of two"); DATA(insert OID = 211 ( float4smaller PGUID 11 f t t t 2 f 700 "700 700" 100 0 0 100 float4smaller - )); @@ -461,8 +461,8 @@ DATA(insert OID = 220 ( float8um PGUID 11 f t t t 1 f 701 "701" 100 0 0 100 DESCR("negate"); DATA(insert OID = 221 ( float8abs PGUID 11 f t t t 1 f 701 "701" 100 0 0 100 float8abs - )); DESCR("absolute value"); -DATA(insert OID = 222 ( float8inc PGUID 11 f t t t 1 f 701 "701" 100 0 0 100 float8inc - )); -DESCR("increment"); +DATA(insert OID = 222 ( float8_accum PGUID 12 f t t t 2 f 1022 "1022 701" 100 0 0 100 float8_accum - )); +DESCR("aggregate transition function"); DATA(insert OID = 223 ( float8larger PGUID 11 f t t t 2 f 701 "701 701" 100 0 0 100 float8larger - )); DESCR("larger of two"); DATA(insert OID = 224 ( float8smaller PGUID 11 f t t t 2 f 701 "701 701" 100 0 0 100 float8smaller - )); @@ -1004,8 +1004,6 @@ DESCR("large object export"); DATA(insert OID = 766 ( int4inc PGUID 12 f t t t 1 f 23 "23" 100 0 0 100 int4inc - )); DESCR("increment"); -DATA(insert OID = 767 ( int2inc PGUID 12 f t t t 1 f 21 "21" 100 0 0 100 int2inc - )); -DESCR("increment"); DATA(insert OID = 768 ( int4larger PGUID 12 f t t t 2 f 23 "23 23" 100 0 0 100 int4larger - )); DESCR("larger of two"); DATA(insert OID = 769 ( int4smaller PGUID 12 f t t t 2 f 23 "23 23" 100 0 0 100 int4smaller - )); @@ -1181,8 +1179,6 @@ DATA(insert OID = 944 ( char PGUID 12 f t t t 1 f 18 "25" 100 0 0 100 tex DESCR("convert text to char"); DATA(insert OID = 946 ( text PGUID 12 f t t t 1 f 25 "18" 100 0 0 100 char_text - )); DESCR("convert char to text"); -DATA(insert OID = 948 ( varchar PGUID 12 f t t t 1 f 25 "1043" 100 0 0 100 bpchar_char - )); -DESCR("convert varchar() to text"); DATA(insert OID = 950 ( istrue PGUID 12 f t t f 1 f 16 "16" 100 0 0 100 istrue - )); DESCR("bool is true (not false or unknown)"); @@ -2395,8 +2391,6 @@ DATA(insert OID = 1746 ( float8 PGUID 11 f t t t 1 f 701 "1700" 100 0 0 100 DESCR("(internal)"); DATA(insert OID = 1764 ( numeric_inc PGUID 11 f t t t 1 f 1700 "1700" 100 0 0 100 numeric_inc - )); DESCR("increment by one"); -DATA(insert OID = 1765 ( numeric_dec PGUID 11 f t t t 1 f 1700 "1700" 100 0 0 100 numeric_dec - )); -DESCR("decrement by one"); DATA(insert OID = 1766 ( numeric_smaller PGUID 11 f t t t 2 f 1700 "1700 1700" 100 0 0 100 numeric_smaller - )); DESCR("smaller of two numbers"); DATA(insert OID = 1767 ( numeric_larger PGUID 11 f t t t 2 f 1700 "1700 1700" 100 0 0 100 numeric_larger - )); @@ -2407,7 +2401,7 @@ DATA(insert OID = 1771 ( numeric_uminus PGUID 11 f t t t 1 f 1700 "1700" 100 0 DESCR("negate"); DATA(insert OID = 1779 ( int8 PGUID 11 f t t t 1 f 20 "1700" 100 0 0 100 numeric_int8 - )); DESCR("(internal)"); -DATA(insert OID = 1781 ( numeric PGUID 11 f t t t 1 f 1700 "20" 100 0 0 100 int8_numeric - )); +DATA(insert OID = 1781 ( numeric PGUID 12 f t t t 1 f 1700 "20" 100 0 0 100 int8_numeric - )); DESCR("(internal)"); DATA(insert OID = 1782 ( numeric PGUID 12 f t t t 1 f 1700 "21" 100 0 0 100 int2_numeric - )); DESCR("(internal)"); @@ -2465,6 +2459,38 @@ DESCR("join selectivity of NOT LIKE"); DATA(insert OID = 1829 ( icregexnejoinsel PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100 icregexnejoinsel - )); DESCR("join selectivity of case-insensitive regex non-match"); +/* Aggregate-related functions */ +DATA(insert OID = 1830 ( float8_avg PGUID 12 f t t t 1 f 701 "1022" 100 0 0 100 float8_avg - )); +DESCR("AVG aggregate final function"); +DATA(insert OID = 1831 ( float8_variance PGUID 12 f t t t 1 f 701 "1022" 100 0 0 100 float8_variance - )); +DESCR("VARIANCE aggregate final function"); +DATA(insert OID = 1832 ( float8_stddev PGUID 12 f t t t 1 f 701 "1022" 100 0 0 100 float8_stddev - )); +DESCR("STDDEV aggregate final function"); +DATA(insert OID = 1833 ( numeric_accum PGUID 12 f t t t 2 f 1231 "1231 1700" 100 0 0 100 numeric_accum - )); +DESCR("aggregate transition function"); +DATA(insert OID = 1834 ( int2_accum PGUID 12 f t t t 2 f 1231 "1231 21" 100 0 0 100 int2_accum - )); +DESCR("aggregate transition function"); +DATA(insert OID = 1835 ( int4_accum PGUID 12 f t t t 2 f 1231 "1231 23" 100 0 0 100 int4_accum - )); +DESCR("aggregate transition function"); +DATA(insert OID = 1836 ( int8_accum PGUID 12 f t t t 2 f 1231 "1231 20" 100 0 0 100 int8_accum - )); +DESCR("aggregate transition function"); +DATA(insert OID = 1837 ( numeric_avg PGUID 12 f t t t 1 f 1700 "1231" 100 0 0 100 numeric_avg - )); +DESCR("AVG aggregate final function"); +DATA(insert OID = 1838 ( numeric_variance PGUID 12 f t t t 1 f 1700 "1231" 100 0 0 100 numeric_variance - )); +DESCR("VARIANCE aggregate final function"); +DATA(insert OID = 1839 ( numeric_stddev PGUID 12 f t t t 1 f 1700 "1231" 100 0 0 100 numeric_stddev - )); +DESCR("STDDEV aggregate final function"); +DATA(insert OID = 1840 ( int2_sum PGUID 12 f t t f 2 f 1700 "1700 21" 100 0 0 100 int2_sum - )); +DESCR("SUM(int2) transition function"); +DATA(insert OID = 1841 ( int4_sum PGUID 12 f t t f 2 f 1700 "1700 23" 100 0 0 100 int4_sum - )); +DESCR("SUM(int4) transition function"); +DATA(insert OID = 1842 ( int8_sum PGUID 12 f t t f 2 f 1700 "1700 20" 100 0 0 100 int8_sum - )); +DESCR("SUM(int8) transition function"); +DATA(insert OID = 1843 ( interval_accum PGUID 12 f t t t 2 f 1187 "1187 1186" 100 0 0 100 interval_accum - )); +DESCR("aggregate transition function"); +DATA(insert OID = 1844 ( interval_avg PGUID 12 f t t t 1 f 1186 "1187" 100 0 0 100 interval_avg - )); +DESCR("AVG aggregate final function"); + /* * prototypes for functions pg_proc.c diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 6f675873cc..2cf59ca50c 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: primnodes.h,v 1.43 2000/06/12 19:40:49 momjian Exp $ + * $Id: primnodes.h,v 1.44 2000/07/17 03:05:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -301,10 +301,9 @@ typedef struct Iter * basetype - base type Oid of the aggregate (ie, input type) * aggtype - type Oid of final result of the aggregate * target - attribute or expression we are aggregating on - * usenulls - TRUE to accept null values as inputs * aggstar - TRUE if argument was really '*' - * aggdistinct - TRUE if arguments were labeled DISTINCT - * aggno - workspace for nodeAgg.c executor + * aggdistinct - TRUE if it's agg(DISTINCT ...) + * aggno - workspace for executor (see nodeAgg.c) * ---------------- */ typedef struct Aggref @@ -314,7 +313,6 @@ typedef struct Aggref Oid basetype; Oid aggtype; Node *target; - bool usenulls; bool aggstar; bool aggdistinct; int aggno; diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h index 7ec383abe7..195842811a 100644 --- a/src/include/storage/lock.h +++ b/src/include/storage/lock.h @@ -7,18 +7,16 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: lock.h,v 1.38 2000/05/31 00:28:38 petere Exp $ + * $Id: lock.h,v 1.39 2000/07/17 03:05:30 tgl Exp $ * *------------------------------------------------------------------------- */ #ifndef LOCK_H_ #define LOCK_H_ -#include "postgres.h" #include "storage/ipc.h" #include "storage/itemptr.h" #include "storage/shmem.h" -#include "utils/array.h" extern SPINLOCK LockMgrLock; typedef int LOCKMASK; diff --git a/src/include/utils/array.h b/src/include/utils/array.h index 4fb296671d..4d915e0665 100644 --- a/src/include/utils/array.h +++ b/src/include/utils/array.h @@ -11,7 +11,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: array.h,v 1.25 2000/06/13 07:35:30 tgl Exp $ + * $Id: array.h,v 1.26 2000/07/17 03:05:32 tgl Exp $ * * NOTES * XXX the data array should be MAXALIGN'd -- notice that the array @@ -24,15 +24,27 @@ #define ARRAY_H #include "fmgr.h" -#include "utils/memutils.h" +/* + * Arrays are varlena objects, so must meet the varlena convention that + * the first int32 of the object contains the total object size in bytes. + */ typedef struct { - int size; /* total array size (in bytes) */ + int32 size; /* total array size (varlena requirement) */ int ndim; /* # of dimensions */ int flags; /* implementation flags */ } ArrayType; +/* + * fmgr macros for array objects + */ +#define DatumGetArrayTypeP(X) ((ArrayType *) PG_DETOAST_DATUM(X)) +#define DatumGetArrayTypePCopy(X) ((ArrayType *) PG_DETOAST_DATUM_COPY(X)) +#define PG_GETARG_ARRAYTYPE_P(n) DatumGetArrayTypeP(PG_GETARG_DATUM(n)) +#define PG_GETARG_ARRAYTYPE_P_COPY(n) DatumGetArrayTypePCopy(PG_GETARG_DATUM(n)) +#define PG_RETURN_ARRAYTYPE_P(x) PG_RETURN_POINTER(x) + /* * bitmask of ArrayType flags field: * 1st bit - large object flag @@ -43,11 +55,9 @@ typedef struct #define ARR_CHK_FLAG (0x2) #define ARR_OBJ_MASK (0x1c) -#define ARR_FLAGS(a) ((ArrayType *) a)->flags #define ARR_SIZE(a) (((ArrayType *) a)->size) - #define ARR_NDIM(a) (((ArrayType *) a)->ndim) -#define ARR_NDIM_PTR(a) (&(((ArrayType *) a)->ndim)) +#define ARR_FLAGS(a) (((ArrayType *) a)->flags) #define ARR_IS_LO(a) \ (((ArrayType *) a)->flags & ARR_LOB_FLAG) @@ -102,7 +112,6 @@ typedef struct #define RETURN_NULL(type) do { *isNull = true; return (type) 0; } while (0) #define NAME_LEN 30 -#define MAX_BUFF_SIZE BLCKSZ typedef struct { @@ -134,6 +143,12 @@ extern ArrayType *array_assgn(ArrayType *array, int nSubscripts, bool elmbyval, int elmlen, bool *isNull); extern Datum array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType); +extern ArrayType *construct_array(Datum *elems, int nelems, + bool elmbyval, int elmlen, char elmalign); +extern void deconstruct_array(ArrayType *array, + bool elmbyval, int elmlen, char elmalign, + Datum **elemsp, int *nelemsp); + extern int _LOtransfer(char **destfd, int size, int nitems, char **srcfd, int isSrcLO, int isDestLO); extern char *_array_newLO(int *fd, int flag); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 07e732f1e8..db772d6ecf 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: builtins.h,v 1.123 2000/07/09 21:30:21 petere Exp $ + * $Id: builtins.h,v 1.124 2000/07/17 03:05:32 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -110,7 +110,6 @@ extern Datum int2mi(PG_FUNCTION_ARGS); extern Datum int2mul(PG_FUNCTION_ARGS); extern Datum int2div(PG_FUNCTION_ARGS); extern Datum int2abs(PG_FUNCTION_ARGS); -extern Datum int2inc(PG_FUNCTION_ARGS); extern Datum int24pl(PG_FUNCTION_ARGS); extern Datum int24mi(PG_FUNCTION_ARGS); extern Datum int24mul(PG_FUNCTION_ARGS); @@ -207,12 +206,10 @@ extern float32 float4pl(float32 arg1, float32 arg2); extern float32 float4mi(float32 arg1, float32 arg2); extern float32 float4mul(float32 arg1, float32 arg2); extern float32 float4div(float32 arg1, float32 arg2); -extern float32 float4inc(float32 arg1); extern float64 float8pl(float64 arg1, float64 arg2); extern float64 float8mi(float64 arg1, float64 arg2); extern float64 float8mul(float64 arg1, float64 arg2); extern float64 float8div(float64 arg1, float64 arg2); -extern float64 float8inc(float64 arg1); extern bool float4eq(float32 arg1, float32 arg2); extern bool float4ne(float32 arg1, float32 arg2); extern bool float4lt(float32 arg1, float32 arg2); @@ -261,6 +258,11 @@ extern float64 radians(float64 arg1); extern float64 dtan(float64 arg1); extern float64 drandom(void); extern int32 setseed(float64 seed); +extern Datum float8_accum(PG_FUNCTION_ARGS); +extern Datum float4_accum(PG_FUNCTION_ARGS); +extern Datum float8_avg(PG_FUNCTION_ARGS); +extern Datum float8_variance(PG_FUNCTION_ARGS); +extern Datum float8_stddev(PG_FUNCTION_ARGS); extern float64 float48pl(float32 arg1, float64 arg2); extern float64 float48mi(float32 arg1, float64 arg2); @@ -545,7 +547,6 @@ extern Numeric numeric_mul(Numeric num1, Numeric num2); extern Numeric numeric_div(Numeric num1, Numeric num2); extern Numeric numeric_mod(Numeric num1, Numeric num2); extern Numeric numeric_inc(Numeric num); -extern Numeric numeric_dec(Numeric num); extern Numeric numeric_smaller(Numeric num1, Numeric num2); extern Numeric numeric_larger(Numeric num1, Numeric num2); extern Numeric numeric_sqrt(Numeric num); @@ -555,14 +556,24 @@ extern Numeric numeric_log(Numeric num1, Numeric num2); extern Numeric numeric_power(Numeric num1, Numeric num2); extern Datum int4_numeric(PG_FUNCTION_ARGS); extern int32 numeric_int4(Numeric num); -extern Numeric int8_numeric(int64 *val); +extern Datum int8_numeric(PG_FUNCTION_ARGS); extern int64 *numeric_int8(Numeric num); extern Datum int2_numeric(PG_FUNCTION_ARGS); extern Datum numeric_int2(PG_FUNCTION_ARGS); -extern Numeric float4_numeric(float32 val); -extern float32 numeric_float4(Numeric num); extern Numeric float8_numeric(float64 val); extern float64 numeric_float8(Numeric num); +extern Numeric float4_numeric(float32 val); +extern float32 numeric_float4(Numeric num); +extern Datum numeric_accum(PG_FUNCTION_ARGS); +extern Datum int2_accum(PG_FUNCTION_ARGS); +extern Datum int4_accum(PG_FUNCTION_ARGS); +extern Datum int8_accum(PG_FUNCTION_ARGS); +extern Datum numeric_avg(PG_FUNCTION_ARGS); +extern Datum numeric_variance(PG_FUNCTION_ARGS); +extern Datum numeric_stddev(PG_FUNCTION_ARGS); +extern Datum int2_sum(PG_FUNCTION_ARGS); +extern Datum int4_sum(PG_FUNCTION_ARGS); +extern Datum int8_sum(PG_FUNCTION_ARGS); /* lztext.c */ extern lztext *lztextin(char *str); diff --git a/src/include/utils/numeric.h b/src/include/utils/numeric.h index 1a0dd692db..8e6412eabe 100644 --- a/src/include/utils/numeric.h +++ b/src/include/utils/numeric.h @@ -5,7 +5,7 @@ * * 1998 Jan Wieck * - * $Header: /cvsroot/pgsql/src/include/utils/numeric.h,v 1.10 2000/06/13 07:35:31 tgl Exp $ + * $Header: /cvsroot/pgsql/src/include/utils/numeric.h,v 1.11 2000/07/17 03:05:32 tgl Exp $ * * ---------- */ @@ -55,7 +55,7 @@ * all leading and trailing zeroes (except there will be a trailing zero * in the last byte, if the number of digits is odd). In particular, * if the value is zero, there will be no digits at all! The weight is - * arbitrary in this case, but we normally set it to zero. + * arbitrary in that case, but we normally set it to zero. * ---------- */ typedef struct NumericData @@ -75,9 +75,11 @@ typedef NumericData *Numeric; * fmgr interface macros */ -#define DatumGetNumeric(X) ((Numeric) PG_DETOAST_DATUM(X)) -#define NumericGetDatum(X) PointerGetDatum(X) -#define PG_GETARG_NUMERIC(n) DatumGetNumeric(PG_GETARG_DATUM(n)) -#define PG_RETURN_NUMERIC(x) return NumericGetDatum(x) +#define DatumGetNumeric(X) ((Numeric) PG_DETOAST_DATUM(X)) +#define DatumGetNumericCopy(X) ((Numeric) PG_DETOAST_DATUM_COPY(X)) +#define NumericGetDatum(X) PointerGetDatum(X) +#define PG_GETARG_NUMERIC(n) DatumGetNumeric(PG_GETARG_DATUM(n)) +#define PG_GETARG_NUMERIC_COPY(n) DatumGetNumericCopy(PG_GETARG_DATUM(n)) +#define PG_RETURN_NUMERIC(x) return NumericGetDatum(x) #endif /* _PG_NUMERIC_H_ */ diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h index 61db8e7d20..b848c894a0 100644 --- a/src/include/utils/timestamp.h +++ b/src/include/utils/timestamp.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: timestamp.h,v 1.8 2000/06/19 03:54:48 tgl Exp $ + * $Id: timestamp.h,v 1.9 2000/07/17 03:05:32 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -171,6 +171,8 @@ extern Datum interval_mi(PG_FUNCTION_ARGS); extern Datum interval_mul(PG_FUNCTION_ARGS); extern Datum mul_d_interval(PG_FUNCTION_ARGS); extern Datum interval_div(PG_FUNCTION_ARGS); +extern Datum interval_accum(PG_FUNCTION_ARGS); +extern Datum interval_avg(PG_FUNCTION_ARGS); extern Datum timestamp_mi(PG_FUNCTION_ARGS); extern Datum timestamp_pl_span(PG_FUNCTION_ARGS); diff --git a/src/pl/tcl/test/test_setup.sql b/src/pl/tcl/test/test_setup.sql index fe71584e1a..7faabc1296 100644 --- a/src/pl/tcl/test/test_setup.sql +++ b/src/pl/tcl/test/test_setup.sql @@ -386,28 +386,33 @@ create function tcl_int4add(int4,int4) returns int4 as ' return [expr $1 + $2] ' language 'pltcl'; -create function tcl_int4div(int4,int4) returns int4 as ' - return [expr $1 / $2] +-- We use split(n) as a quick-and-dirty way of parsing the input array +-- value, which comes in as a string like '{1,2}'. There are better ways... + +create function tcl_int4_accum(_int4,int4) returns _int4 as ' + set state [split $1 "{,}"] + set newsum [expr {[lindex $state 1] + $2}] + set newcnt [expr {[lindex $state 2] + 1}] + return "{$newsum,$newcnt}" ' language 'pltcl'; -create function tcl_int4inc(int4) returns int4 as ' - return [expr $1 + 1] +create function tcl_int4_avg(_int4) returns int4 as ' + set state [split $1 "{,}"] + return [expr {[lindex $state 1] / [lindex $state 2]}] ' language 'pltcl'; create aggregate tcl_avg ( - sfunc1 = tcl_int4add, + sfunc = tcl_int4_accum, basetype = int4, - stype1 = int4, - sfunc2 = tcl_int4inc, - stype2 = int4, - finalfunc = tcl_int4div, - initcond2 = '0' + stype = _int4, + finalfunc = tcl_int4_avg, + initcond = '{0,0}' ); create aggregate tcl_sum ( - sfunc1 = tcl_int4add, + sfunc = tcl_int4add, basetype = int4, - stype1 = int4, + stype = int4, initcond1 = '0' ); diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index 92936e3414..aaee72c01a 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -2,18 +2,20 @@ -- AGGREGATES -- SELECT avg(four) AS avg_1 FROM onek; - avg_1 -------- - 1 + avg_1 +-------------- + 1.5000000000 (1 row) SELECT avg(a) AS avg_32 FROM aggtest WHERE a < 100; - avg_32 --------- - 32 + avg_32 +--------------- + 32.6666666667 (1 row) -SELECT avg(b) AS avg_107_943 FROM aggtest; +-- In 7.1, avg(float4) is computed using float8 arithmetic. +-- Round the result to 3 digits to avoid platform-specific results. +SELECT avg(b)::numeric(10,3) AS avg_107_943 FROM aggtest; avg_107_943 ------------- 107.943 @@ -116,9 +118,9 @@ select ten, count(four), sum(DISTINCT four) from onek group by ten; (10 rows) SELECT newavg(four) AS avg_1 FROM onek; - avg_1 -------- - 1 + avg_1 +-------------- + 1.5000000000 (1 row) SELECT newsum(four) AS sum_1500 FROM onek; diff --git a/src/test/regress/expected/create_aggregate.out b/src/test/regress/expected/create_aggregate.out index d7e46fffac..ef83c886a1 100644 --- a/src/test/regress/expected/create_aggregate.out +++ b/src/test/regress/expected/create_aggregate.out @@ -3,18 +3,17 @@ -- -- all functions CREATEd CREATE AGGREGATE newavg ( - sfunc1 = int4pl, basetype = int4, stype1 = int4, - sfunc2 = int4inc, stype2 = int4, - finalfunc = int4div, - initcond1 = '0', initcond2 = '0' + sfunc = int4_accum, basetype = int4, stype = _numeric, + finalfunc = numeric_avg, + initcond1 = '{0,0,0}' ); --- sfunc1 (value-dependent) only +-- without finalfunc; test obsolete spellings 'sfunc1' etc CREATE AGGREGATE newsum ( sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0' ); --- sfunc2 (value-independent) only +-- value-independent transition function CREATE AGGREGATE newcnt ( - sfunc2 = int4inc, basetype = int4, stype2 = int4, - initcond2 = '0' + sfunc = int4inc, basetype = 'any', stype = int4, + initcond = '0' ); diff --git a/src/test/regress/expected/errors.out b/src/test/regress/expected/errors.out index 8332d4d894..66a5c3a5c8 100644 --- a/src/test/regress/expected/errors.out +++ b/src/test/regress/expected/errors.out @@ -99,46 +99,18 @@ end; NOTICE: COMMIT: no transaction in progress -- -- DEFINE AGGREGATE - --- left out finalfunc -create aggregate newavg1 (sfunc1 = int4pl, - basetype = int4, - stype1 = int4, - sfunc2 = int4inc, - stype2 = int4, - initcond1 = '0', - initcond2 = '0'); -ERROR: AggregateCreate: Aggregate must have final function with both transition functions --- sfunc return type disagreement -create aggregate newavg2 (sfunc1 = int4pl, - basetype = int4, - stype1 = int4, - sfunc2 = int2inc, - stype2 = int2, - finalfunc = int4div, - initcond1 = '0', - initcond2 = '0'); -ERROR: AggregateCreate: 'int4div'('int4','int2') does not exist -- sfunc/finalfunc type disagreement -create aggregate newavg3 (sfunc1 = int4pl, +create aggregate newavg2 (sfunc = int4pl, basetype = int4, - stype1 = int4, - sfunc2 = int4inc, - stype2 = int4, - finalfunc = int2div, - initcond1 = '0', - initcond2 = '0'); -ERROR: AggregateCreate: 'int2div'('int4','int4') does not exist + stype = int4, + finalfunc = int2um, + initcond = '0'); +ERROR: AggregateCreate: function 'int2um(int4)' does not exist -- left out basetype -create aggregate newcnt1 (sfunc2 = int4inc, - stype2 = int4, - initcond2 = '0'); +create aggregate newcnt1 (sfunc = int4inc, + stype = int4, + initcond = '0'); ERROR: Define: "basetype" unspecified --- left out initcond2 (for sfunc2) -create aggregate newcnt1 (sfunc2 = int4inc, - basetype = int4, - stype2 = int4); -ERROR: AggregateCreate: transition function 2 MUST have an initial value -- -- REMOVE INDEX diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out index a42d3d89f0..a2b0ad9e3e 100644 --- a/src/test/regress/expected/oidjoins.out +++ b/src/test/regress/expected/oidjoins.out @@ -1,20 +1,12 @@ -- -- This is created by pgsql/contrib/findoidjoins/make_oidjoin_check -- -SELECT oid, pg_aggregate.aggtransfn1 +SELECT oid, pg_aggregate.aggtransfn FROM pg_aggregate -WHERE pg_aggregate.aggtransfn1 != 0 AND - NOT EXISTS(SELECT * FROM pg_proc AS t1 WHERE t1.oid = pg_aggregate.aggtransfn1); - oid | aggtransfn1 ------+------------- -(0 rows) - -SELECT oid, pg_aggregate.aggtransfn2 -FROM pg_aggregate -WHERE pg_aggregate.aggtransfn2 != 0 AND - NOT EXISTS(SELECT * FROM pg_proc AS t1 WHERE t1.oid = pg_aggregate.aggtransfn2); - oid | aggtransfn2 ------+------------- +WHERE pg_aggregate.aggtransfn != 0 AND + NOT EXISTS(SELECT * FROM pg_proc AS t1 WHERE t1.oid = pg_aggregate.aggtransfn); + oid | aggtransfn +-----+------------ (0 rows) SELECT oid, pg_aggregate.aggfinalfn @@ -33,20 +25,12 @@ WHERE pg_aggregate.aggbasetype != 0 AND -----+------------- (0 rows) -SELECT oid, pg_aggregate.aggtranstype1 +SELECT oid, pg_aggregate.aggtranstype FROM pg_aggregate -WHERE pg_aggregate.aggtranstype1 != 0 AND - NOT EXISTS(SELECT * FROM pg_type AS t1 WHERE t1.oid = pg_aggregate.aggtranstype1); - oid | aggtranstype1 ------+--------------- -(0 rows) - -SELECT oid, pg_aggregate.aggtranstype2 -FROM pg_aggregate -WHERE pg_aggregate.aggtranstype2 != 0 AND - NOT EXISTS(SELECT * FROM pg_type AS t1 WHERE t1.oid = pg_aggregate.aggtranstype2); - oid | aggtranstype2 ------+--------------- +WHERE pg_aggregate.aggtranstype != 0 AND + NOT EXISTS(SELECT * FROM pg_type AS t1 WHERE t1.oid = pg_aggregate.aggtranstype); + oid | aggtranstype +-----+-------------- (0 rows) SELECT oid, pg_aggregate.aggfinaltype @@ -217,6 +201,22 @@ WHERE pg_class.relam != 0 AND -----+------- (0 rows) +SELECT oid, pg_class.reltoastrelid +FROM pg_class +WHERE pg_class.reltoastrelid != 0 AND + NOT EXISTS(SELECT * FROM pg_class AS t1 WHERE t1.oid = pg_class.reltoastrelid); + oid | reltoastrelid +-----+--------------- +(0 rows) + +SELECT oid, pg_class.reltoastidxid +FROM pg_class +WHERE pg_class.reltoastidxid != 0 AND + NOT EXISTS(SELECT * FROM pg_class AS t1 WHERE t1.oid = pg_class.reltoastidxid); + oid | reltoastidxid +-----+--------------- +(0 rows) + SELECT oid, pg_index.indexrelid FROM pg_index WHERE pg_index.indexrelid != 0 AND diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 893b4bca83..70f0134693 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -84,9 +84,8 @@ WHERE p1.oid != p2.oid AND (p1.prorettype < p2.prorettype); prorettype | prorettype ------------+------------ - 18 | 25 25 | 1043 -(2 rows) +(1 row) SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0] FROM pg_proc AS p1, pg_proc AS p2 @@ -98,8 +97,7 @@ WHERE p1.oid != p2.oid AND proargtypes | proargtypes -------------+------------- 25 | 1043 - 1042 | 1043 -(2 rows) +(1 row) SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1] FROM pg_proc AS p1, pg_proc AS p2 @@ -434,83 +432,40 @@ WHERE p1.oprjoin = p2.oid AND -- **************** pg_aggregate **************** -- Look for illegal values in pg_aggregate fields. --- aggbasetype can only be 0 if transfn1 is not present (eg, count(*)) --- or itself takes a wild-card input; we check the latter case below. SELECT p1.oid, p1.aggname FROM pg_aggregate as p1 -WHERE (p1.aggbasetype = 0 AND p1.aggtransfn1 != 0) OR aggfinaltype = 0; +WHERE aggtransfn = 0 OR aggtranstype = 0 OR aggfinaltype = 0; oid | aggname -----+--------- (0 rows) --- Check combinations of transfer functions. --- Although either transfn1 or transfn2 can be null, --- it makes no sense for both to be. And if both are defined, --- presumably there should be a finalfn to combine their results. --- We also check that transtypes are null just when corresponding --- transfns are. Also, if there is no finalfn then the output type --- must be the transtype the result will be taken from. +-- If there is no finalfn then the output type must be the transtype. SELECT p1.oid, p1.aggname FROM pg_aggregate as p1 -WHERE p1.aggtransfn1 = 0 AND p1.aggtransfn2 = 0; +WHERE p1.aggfinalfn = 0 AND p1.aggfinaltype != p1.aggtranstype; oid | aggname -----+--------- (0 rows) -SELECT p1.oid, p1.aggname -FROM pg_aggregate as p1 -WHERE p1.aggtransfn1 != 0 AND p1.aggtransfn2 = 0 AND - (p1.aggtranstype1 = 0 OR p1.aggtranstype2 != 0 OR - (p1.aggfinalfn = 0 AND p1.aggfinaltype != p1.aggtranstype1)); - oid | aggname ------+--------- -(0 rows) - -SELECT p1.oid, p1.aggname -FROM pg_aggregate as p1 -WHERE p1.aggtransfn1 = 0 AND p1.aggtransfn2 != 0 AND - (p1.aggtranstype1 != 0 OR p1.aggtranstype2 = 0 OR - (p1.aggfinalfn = 0 AND p1.aggfinaltype != p1.aggtranstype2)); - oid | aggname ------+--------- -(0 rows) - -SELECT p1.oid, p1.aggname -FROM pg_aggregate as p1 -WHERE p1.aggtransfn1 != 0 AND p1.aggtransfn2 != 0 AND - (p1.aggtranstype1 = 0 OR p1.aggtranstype2 = 0 OR - p1.aggfinalfn = 0); - oid | aggname ------+--------- -(0 rows) - --- Cross-check transfn1 (if present) against its entry in pg_proc. +-- Cross-check transfn against its entry in pg_proc. -- FIXME: what about binary-compatible types? +-- NOTE: in 7.1, this search finds max and min on abstime, which are +-- implemented using int4larger/int4smaller. Until we have +-- some cleaner way of dealing with binary-equivalent types, just leave +-- those two tuples in the expected output. SELECT p1.oid, p1.aggname, p2.oid, p2.proname FROM pg_aggregate AS p1, pg_proc AS p2 -WHERE p1.aggtransfn1 = p2.oid AND - (p2.proretset OR p2.pronargs != 2 --- diked out until we find a way of marking binary-compatible types --- OR --- p1.aggtranstype1 != p2.prorettype OR --- p1.aggtranstype1 != p2.proargtypes[0] OR --- p1.aggbasetype != p2.proargtypes[1] -); - oid | aggname | oid | proname ------+---------+-----+--------- -(0 rows) - --- Cross-check transfn2 (if present) against its entry in pg_proc. --- FIXME: what about binary-compatible types? -SELECT p1.oid, p1.aggname, p2.oid, p2.proname -FROM pg_aggregate AS p1, pg_proc AS p2 -WHERE p1.aggtransfn2 = p2.oid AND - (p2.proretset OR p1.aggtranstype2 != p2.prorettype OR - p2.pronargs != 1 OR - p1.aggtranstype2 != p2.proargtypes[0]); - oid | aggname | oid | proname ------+---------+-----+--------- -(0 rows) +WHERE p1.aggtransfn = p2.oid AND + (p2.proretset OR + p1.aggtranstype != p2.prorettype OR + p1.aggtranstype != p2.proargtypes[0] OR + NOT ((p2.pronargs = 2 AND p1.aggbasetype = p2.proargtypes[1]) OR + (p2.pronargs = 1 AND p1.aggbasetype = 0))); + oid | aggname | oid | proname +-------+---------+-----+------------- + 16978 | max | 768 | int4larger + 16992 | min | 769 | int4smaller +(2 rows) -- Cross-check finalfn (if present) against its entry in pg_proc. -- FIXME: what about binary-compatible types? @@ -518,9 +473,8 @@ SELECT p1.oid, p1.aggname, p2.oid, p2.proname FROM pg_aggregate AS p1, pg_proc AS p2 WHERE p1.aggfinalfn = p2.oid AND (p2.proretset OR p1.aggfinaltype != p2.prorettype OR - p2.pronargs != 2 OR - p1.aggtranstype1 != p2.proargtypes[0] OR - p1.aggtranstype2 != p2.proargtypes[1]); + p2.pronargs != 1 OR + p1.aggtranstype != p2.proargtypes[0]); oid | aggname | oid | proname -----+---------+-----+--------- (0 rows) diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql index 45b8dd990f..33c8fb5f35 100644 --- a/src/test/regress/sql/aggregates.sql +++ b/src/test/regress/sql/aggregates.sql @@ -6,7 +6,10 @@ SELECT avg(four) AS avg_1 FROM onek; SELECT avg(a) AS avg_32 FROM aggtest WHERE a < 100; -SELECT avg(b) AS avg_107_943 FROM aggtest; +-- In 7.1, avg(float4) is computed using float8 arithmetic. +-- Round the result to 3 digits to avoid platform-specific results. + +SELECT avg(b)::numeric(10,3) AS avg_107_943 FROM aggtest; SELECT avg(gpa) AS avg_3_4 FROM ONLY student; diff --git a/src/test/regress/sql/create_aggregate.sql b/src/test/regress/sql/create_aggregate.sql index 046f945738..5d42ed057e 100644 --- a/src/test/regress/sql/create_aggregate.sql +++ b/src/test/regress/sql/create_aggregate.sql @@ -4,21 +4,20 @@ -- all functions CREATEd CREATE AGGREGATE newavg ( - sfunc1 = int4pl, basetype = int4, stype1 = int4, - sfunc2 = int4inc, stype2 = int4, - finalfunc = int4div, - initcond1 = '0', initcond2 = '0' + sfunc = int4_accum, basetype = int4, stype = _numeric, + finalfunc = numeric_avg, + initcond1 = '{0,0,0}' ); --- sfunc1 (value-dependent) only +-- without finalfunc; test obsolete spellings 'sfunc1' etc CREATE AGGREGATE newsum ( sfunc1 = int4pl, basetype = int4, stype1 = int4, initcond1 = '0' ); --- sfunc2 (value-independent) only +-- value-independent transition function CREATE AGGREGATE newcnt ( - sfunc2 = int4inc, basetype = int4, stype2 = int4, - initcond2 = '0' + sfunc = int4inc, basetype = 'any', stype = int4, + initcond = '0' ); diff --git a/src/test/regress/sql/errors.sql b/src/test/regress/sql/errors.sql index ffcb5fcdb7..86c4e4fe60 100644 --- a/src/test/regress/sql/errors.sql +++ b/src/test/regress/sql/errors.sql @@ -114,45 +114,18 @@ end; -- -- DEFINE AGGREGATE - --- left out finalfunc -create aggregate newavg1 (sfunc1 = int4pl, - basetype = int4, - stype1 = int4, - sfunc2 = int4inc, - stype2 = int4, - initcond1 = '0', - initcond2 = '0'); - --- sfunc return type disagreement -create aggregate newavg2 (sfunc1 = int4pl, - basetype = int4, - stype1 = int4, - sfunc2 = int2inc, - stype2 = int2, - finalfunc = int4div, - initcond1 = '0', - initcond2 = '0'); -- sfunc/finalfunc type disagreement -create aggregate newavg3 (sfunc1 = int4pl, +create aggregate newavg2 (sfunc = int4pl, basetype = int4, - stype1 = int4, - sfunc2 = int4inc, - stype2 = int4, - finalfunc = int2div, - initcond1 = '0', - initcond2 = '0'); + stype = int4, + finalfunc = int2um, + initcond = '0'); -- left out basetype -create aggregate newcnt1 (sfunc2 = int4inc, - stype2 = int4, - initcond2 = '0'); - --- left out initcond2 (for sfunc2) -create aggregate newcnt1 (sfunc2 = int4inc, - basetype = int4, - stype2 = int4); +create aggregate newcnt1 (sfunc = int4inc, + stype = int4, + initcond = '0'); -- diff --git a/src/test/regress/sql/oidjoins.sql b/src/test/regress/sql/oidjoins.sql index 7c6b4d99ef..b7ea1f63ea 100644 --- a/src/test/regress/sql/oidjoins.sql +++ b/src/test/regress/sql/oidjoins.sql @@ -1,14 +1,10 @@ -- -- This is created by pgsql/contrib/findoidjoins/make_oidjoin_check -- -SELECT oid, pg_aggregate.aggtransfn1 +SELECT oid, pg_aggregate.aggtransfn FROM pg_aggregate -WHERE pg_aggregate.aggtransfn1 != 0 AND - NOT EXISTS(SELECT * FROM pg_proc AS t1 WHERE t1.oid = pg_aggregate.aggtransfn1); -SELECT oid, pg_aggregate.aggtransfn2 -FROM pg_aggregate -WHERE pg_aggregate.aggtransfn2 != 0 AND - NOT EXISTS(SELECT * FROM pg_proc AS t1 WHERE t1.oid = pg_aggregate.aggtransfn2); +WHERE pg_aggregate.aggtransfn != 0 AND + NOT EXISTS(SELECT * FROM pg_proc AS t1 WHERE t1.oid = pg_aggregate.aggtransfn); SELECT oid, pg_aggregate.aggfinalfn FROM pg_aggregate WHERE pg_aggregate.aggfinalfn != 0 AND @@ -17,14 +13,10 @@ SELECT oid, pg_aggregate.aggbasetype FROM pg_aggregate WHERE pg_aggregate.aggbasetype != 0 AND NOT EXISTS(SELECT * FROM pg_type AS t1 WHERE t1.oid = pg_aggregate.aggbasetype); -SELECT oid, pg_aggregate.aggtranstype1 +SELECT oid, pg_aggregate.aggtranstype FROM pg_aggregate -WHERE pg_aggregate.aggtranstype1 != 0 AND - NOT EXISTS(SELECT * FROM pg_type AS t1 WHERE t1.oid = pg_aggregate.aggtranstype1); -SELECT oid, pg_aggregate.aggtranstype2 -FROM pg_aggregate -WHERE pg_aggregate.aggtranstype2 != 0 AND - NOT EXISTS(SELECT * FROM pg_type AS t1 WHERE t1.oid = pg_aggregate.aggtranstype2); +WHERE pg_aggregate.aggtranstype != 0 AND + NOT EXISTS(SELECT * FROM pg_type AS t1 WHERE t1.oid = pg_aggregate.aggtranstype); SELECT oid, pg_aggregate.aggfinaltype FROM pg_aggregate WHERE pg_aggregate.aggfinaltype != 0 AND @@ -109,6 +101,14 @@ SELECT oid, pg_class.relam FROM pg_class WHERE pg_class.relam != 0 AND NOT EXISTS(SELECT * FROM pg_am AS t1 WHERE t1.oid = pg_class.relam); +SELECT oid, pg_class.reltoastrelid +FROM pg_class +WHERE pg_class.reltoastrelid != 0 AND + NOT EXISTS(SELECT * FROM pg_class AS t1 WHERE t1.oid = pg_class.reltoastrelid); +SELECT oid, pg_class.reltoastidxid +FROM pg_class +WHERE pg_class.reltoastidxid != 0 AND + NOT EXISTS(SELECT * FROM pg_class AS t1 WHERE t1.oid = pg_class.reltoastidxid); SELECT oid, pg_index.indexrelid FROM pg_index WHERE pg_index.indexrelid != 0 AND diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql index 40fa91bf46..d655e02eb8 100644 --- a/src/test/regress/sql/opr_sanity.sql +++ b/src/test/regress/sql/opr_sanity.sql @@ -363,66 +363,32 @@ WHERE p1.oprjoin = p2.oid AND -- **************** pg_aggregate **************** -- Look for illegal values in pg_aggregate fields. --- aggbasetype can only be 0 if transfn1 is not present (eg, count(*)) --- or itself takes a wild-card input; we check the latter case below. SELECT p1.oid, p1.aggname FROM pg_aggregate as p1 -WHERE (p1.aggbasetype = 0 AND p1.aggtransfn1 != 0) OR aggfinaltype = 0; +WHERE aggtransfn = 0 OR aggtranstype = 0 OR aggfinaltype = 0; --- Check combinations of transfer functions. --- Although either transfn1 or transfn2 can be null, --- it makes no sense for both to be. And if both are defined, --- presumably there should be a finalfn to combine their results. --- We also check that transtypes are null just when corresponding --- transfns are. Also, if there is no finalfn then the output type --- must be the transtype the result will be taken from. +-- If there is no finalfn then the output type must be the transtype. SELECT p1.oid, p1.aggname FROM pg_aggregate as p1 -WHERE p1.aggtransfn1 = 0 AND p1.aggtransfn2 = 0; +WHERE p1.aggfinalfn = 0 AND p1.aggfinaltype != p1.aggtranstype; -SELECT p1.oid, p1.aggname -FROM pg_aggregate as p1 -WHERE p1.aggtransfn1 != 0 AND p1.aggtransfn2 = 0 AND - (p1.aggtranstype1 = 0 OR p1.aggtranstype2 != 0 OR - (p1.aggfinalfn = 0 AND p1.aggfinaltype != p1.aggtranstype1)); - -SELECT p1.oid, p1.aggname -FROM pg_aggregate as p1 -WHERE p1.aggtransfn1 = 0 AND p1.aggtransfn2 != 0 AND - (p1.aggtranstype1 != 0 OR p1.aggtranstype2 = 0 OR - (p1.aggfinalfn = 0 AND p1.aggfinaltype != p1.aggtranstype2)); - -SELECT p1.oid, p1.aggname -FROM pg_aggregate as p1 -WHERE p1.aggtransfn1 != 0 AND p1.aggtransfn2 != 0 AND - (p1.aggtranstype1 = 0 OR p1.aggtranstype2 = 0 OR - p1.aggfinalfn = 0); - --- Cross-check transfn1 (if present) against its entry in pg_proc. +-- Cross-check transfn against its entry in pg_proc. -- FIXME: what about binary-compatible types? +-- NOTE: in 7.1, this search finds max and min on abstime, which are +-- implemented using int4larger/int4smaller. Until we have +-- some cleaner way of dealing with binary-equivalent types, just leave +-- those two tuples in the expected output. SELECT p1.oid, p1.aggname, p2.oid, p2.proname FROM pg_aggregate AS p1, pg_proc AS p2 -WHERE p1.aggtransfn1 = p2.oid AND - (p2.proretset OR p2.pronargs != 2 --- diked out until we find a way of marking binary-compatible types --- OR --- p1.aggtranstype1 != p2.prorettype OR --- p1.aggtranstype1 != p2.proargtypes[0] OR --- p1.aggbasetype != p2.proargtypes[1] -); - --- Cross-check transfn2 (if present) against its entry in pg_proc. --- FIXME: what about binary-compatible types? - -SELECT p1.oid, p1.aggname, p2.oid, p2.proname -FROM pg_aggregate AS p1, pg_proc AS p2 -WHERE p1.aggtransfn2 = p2.oid AND - (p2.proretset OR p1.aggtranstype2 != p2.prorettype OR - p2.pronargs != 1 OR - p1.aggtranstype2 != p2.proargtypes[0]); +WHERE p1.aggtransfn = p2.oid AND + (p2.proretset OR + p1.aggtranstype != p2.prorettype OR + p1.aggtranstype != p2.proargtypes[0] OR + NOT ((p2.pronargs = 2 AND p1.aggbasetype = p2.proargtypes[1]) OR + (p2.pronargs = 1 AND p1.aggbasetype = 0))); -- Cross-check finalfn (if present) against its entry in pg_proc. -- FIXME: what about binary-compatible types? @@ -431,9 +397,8 @@ SELECT p1.oid, p1.aggname, p2.oid, p2.proname FROM pg_aggregate AS p1, pg_proc AS p2 WHERE p1.aggfinalfn = p2.oid AND (p2.proretset OR p1.aggfinaltype != p2.prorettype OR - p2.pronargs != 2 OR - p1.aggtranstype1 != p2.proargtypes[0] OR - p1.aggtranstype2 != p2.proargtypes[1]); + p2.pronargs != 1 OR + p1.aggtranstype != p2.proargtypes[0]); -- **************** pg_opclass **************** diff --git a/src/tutorial/complex.source b/src/tutorial/complex.source index 361007ced9..e0859a6797 100644 --- a/src/tutorial/complex.source +++ b/src/tutorial/complex.source @@ -7,7 +7,7 @@ -- -- Copyright (c) 1994, Regents of the University of California -- --- $Id: complex.source,v 1.7 2000/03/28 02:49:19 tgl Exp $ +-- $Id: complex.source,v 1.8 2000/07/17 03:05:41 tgl Exp $ -- --------------------------------------------------------------------------- @@ -111,10 +111,10 @@ SELECT a + '(1.0,1.0)'::complex AS aa, ----------------------------- CREATE AGGREGATE complex_sum ( - sfunc1 = complex_add, + sfunc = complex_add, basetype = complex, - stype1 = complex, - initcond1 = '(0,0)' + stype = complex, + initcond = '(0,0)' ); SELECT complex_sum(a) FROM test_complex;