Re: review: plpgsql return a row-expression

From: Asif Rehman <asifr(dot)rehman(at)gmail(dot)com>
To: Pavel Stehule <pavel(dot)stehule(at)gmail(dot)com>
Cc: PostgreSQL Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: review: plpgsql return a row-expression
Date: 2012-11-22 17:47:19
Message-ID: CADM=JegAveRxWf6_A4Dxm+wK2jpHw2ijH2PC2C4-gSXAed1C0g@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Thanks for the review Pavel. I have taken care of the points you raised.
Please see the updated patch.

Regards,
--Asif

On Wed, Nov 21, 2012 at 1:47 AM, Pavel Stehule <pavel(dot)stehule(at)gmail(dot)com>wrote:

> related to
> http://archives.postgresql.org/message-id/CAAuGLxWpEDfwAE6DAJMF7SxEwFUsA0f68P07RetBbpf_FSaShA@mail.gmail.com
>
> * patched and compiled withou warnings
>
> * All 133 tests passed.
>
>
> but
>
> I don't like
>
> * call invalid function from anonymous block - it is messy (regress
> tests) - there is no reason why do it
>
> +create or replace function foo() returns footype as $$
> +declare
> + v record;
> + v2 record;
> +begin
> + v := (1, 'hello');
> + v2 := (1, 'hello');
> + return (v || v2);
> +end;
> +$$ language plpgsql;
> +DO $$
> +declare
> + v footype;
> +begin
> + v := foo();
> + raise info 'x = %', v.x;
> + raise info 'y = %', v.y;
> +end; $$;
> +ERROR: operator does not exist: record || record
> +LINE 1: SELECT (v || v2)
> + ^
>
> * there is some performance issue
>
> create or replace function fx2(a int)
> returns footype as $$
> declare x footype;
> begin
> x = (10,20);
> return x;
> end;
> $$ language plpgsql;
>
> postgres=# select sum(fx2.x) from generate_series(1,100000) g(i),
> lateral fx2(i);
> sum
> ---------
> 1000000
> (1 row)
>
> Time: 497.129 ms
>
> returns footype as $$
> begin
> return (10,20);
> end;
> $$ language plpgsql;
>
> postgres=# select sum(fx2.x) from generate_series(1,100000) g(i),
> lateral fx2(i);
> sum
> ---------
> 1000000
> (1 row)
>
> Time: 941.192 ms
>
> following code has same functionality and it is faster
>
> if (stmt->expr != NULL)
> {
> if (estate->retistuple)
> {
> TupleDesc tupdesc;
> Datum retval;
> Oid rettype;
> bool isnull;
> int32 tupTypmod;
> Oid tupType;
> HeapTupleHeader td;
> HeapTupleData tmptup;
>
> retval = exec_eval_expr(estate,
> stmt->expr,
> &isnull,
> &rettype);
>
> /* Source must be of RECORD or composite type */
> if (!type_is_rowtype(rettype))
> ereport(ERROR,
>
> (errcode(ERRCODE_DATATYPE_MISMATCH),
> errmsg("cannot return
> non-composite value from composite type returning function")));
>
> if (!isnull)
> {
> /* Source is a tuple Datum, so safe to
> do this: */
> td = DatumGetHeapTupleHeader(retval);
> /* Extract rowtype info and find a tupdesc
> */
> tupType = HeapTupleHeaderGetTypeId(td);
> tupTypmod = HeapTupleHeaderGetTypMod(td);
> tupdesc =
> lookup_rowtype_tupdesc(tupType, tupTypmod);
>
> /* Build a temporary HeapTuple control
> structure */
> tmptup.t_len =
> HeapTupleHeaderGetDatumLength(td);
> ItemPointerSetInvalid(&(tmptup.t_self));
> tmptup.t_tableOid = InvalidOid;
> tmptup.t_data = td;
>
> estate->retval =
> PointerGetDatum(heap_copytuple(&tmptup));
> estate->rettupdesc =
> CreateTupleDescCopy(tupdesc);
> ReleaseTupleDesc(tupdesc);
> }
>
> estate->retisnull = isnull;
> }
>
>
>
> * it is to restrictive (maybe) - almost all plpgsql' statements does
> automatic conversions (IO conversions when is necessary)
>
> create type footype2 as (a numeric, b varchar)
>
> postgres=# create or replace function fx3(a int)
> returns footype2 as
> $$
> begin
> return (10000000.22234213412342143,'ewqerwqreeeee');
> end;
> $$ language plpgsql;
> CREATE FUNCTION
> postgres=# select fx3(10);
> ERROR: returned record type does not match expected record type
> DETAIL: Returned type unknown does not match expected type character
> varying in column 2.
> CONTEXT: PL/pgSQL function fx3(integer) while casting return value to
> function's return type
> postgres=#
>
> * it doesn't support RETURN NEXT
>
> postgres=# create or replace function fx4()
> postgres-# returns setof footype as $$
> postgres$# begin
> postgres$# for i in 1..10
> postgres$# loop
> postgres$# return next (10,20);
> postgres$# end loop;
> postgres$# return;
> postgres$# end;
> postgres$# $$ language plpgsql;
> ERROR: RETURN NEXT must specify a record or row variable in function
> returning row
> LINE 6: return next (10,20);
>
> * missing any documentation
>
> * repeated code - following code is used on more places in pl_exec.c
> and maybe it is candidate for subroutine
>
> + /* Source is a
> tuple Datum, so safe to do this: */
> + td =
> DatumGetHeapTupleHeader(value);
> + /* Extract rowtype
> info and find a tupdesc */
> + tupType =
> HeapTupleHeaderGetTypeId(td);
> + tupTypmod =
> HeapTupleHeaderGetTypMod(td);
> + tupdesc =
> lookup_rowtype_tupdesc_copy(tupType, tupTypmod);
> + /* Build a
> HeapTuple control structure */
> + htup.t_len =
> HeapTupleHeaderGetDatumLength(td);
> +
> ItemPointerSetInvalid(&(htup.t_self));
> + htup.t_tableOid =
> InvalidOid;
> + htup.t_data = td;
> + tuple =
> heap_copytuple(&htup);
>
> Regards
>
> Pavel Stehule
>

Attachment Content-Type Size
return_rowtype.2.patch application/octet-stream 17.7 KB

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Heikki Linnakangas 2012-11-22 18:09:39 Re: [v9.3] Extra Daemons (Re: elegant and effective way for running jobs inside a database)
Previous Message Alvaro Herrera 2012-11-22 17:18:56 Re: [v9.3] Extra Daemons (Re: elegant and effective way for running jobs inside a database)