Обсуждение: plpgsql grief

Поиск
Список
Период
Сортировка

plpgsql grief

От
rob
Дата:
Hi, I'm having some real headache problems here. Apologies for the
length, i just want to get it all out now :)

I figured moving some 'simple' db code from my application to it's more
natural home in the db would work out. Bummer. Not only do i have to run
7.1 (beta 4) to be able to dynamically generate queries, I'm finding it
*extrememly* difficult to get to get my simple functions to work (plus
for the 'widest used open source db' i'm finding examples very hard to
come by)

Before I start if anyone has any pointers to coding examples (and I mean
a little more than the standard postgres docs :) I'd be eternally
greatful. Failing that, can anyone help with these two simple (ahem)
codelets :

Example 1 :

create function testfunc (text) returns int4 as '
declare sql varchar; res int4;
begin sql=''SELECT INTO res2 id FROM ''||$1 ; execute sql ; return res;
end;
' language 'plpgsql' ;

simple function to return the id field of a table (passed to the
function). ok, not a real world example, however i do this :

#select testfunc('tablenam') ;
and i get
ERROR:  parser: parse error at or near "into"

ok this is actually first things last. I'm not really bothered about
returing values into local variables and then returning them, it's just
a run through. If I can't get this right, what chance have i got at
sorting out the real work i want to do. 

Example 2 :

create function update_trans (text, integer, text, text, text, text,
text) returns boolean as '
declare tbl alias for $1 ;
begin execute ''insert into tbl (objid, objtbl, et, event, time, reason,
owner) values ($2, $3, $4, $5, now(), $6, $7)''; return 0;
end;
' language 'plpgsql' ;


# select update_trans('tablname','1' 
,'sometext','sometext','sometext','sometext','sometext') ;
ERROR:  Relation 'tbl' does not exist

dur. yeah i know it doesn't exist cause i want to pass it in parameter
1.  Tried substituting tbl with $1 and quote_ident($1) and
quote_ident(tbl) in the sql string, but that didn't work either. (BTW
anyone know of any GUI interfaces that support 7.1 - phpPgAdmin 2.1,
2.2.1 and 2.3 seem to balk on functions)

Example 2 is prelude to a larger function (not much larger - but then
this is relavitve to how easy to code it is) to monitor the changes made
by a user, what they change from and to and who/when/why.... this is
already implemented in my app code - PHP - and checking out the features
available in postgres i figured i could do some kind of looping through
the OLD and NEW dataset-array things, comparing them against each other,
sorta like this :

for ($i = 0 ; $i < count($NEW) ; $i++) {
/* since $NEW and $OLD are essentially the same we can do this */ if ($OLD[$i] != $NEW[$i])   record the change bla bla
bla

}
I'm really hoping I can, as at this rate I've spent the better part of
three days trying to figure the simple things above out and the only
thing i'm about to reach is breaking point...

Sorry for the sarcasm, I'm about to pop.

Rob


RE: plpgsql grief

От
Michael Ansley
Дата:
<p><font size="2">Hi, Rob,</font><p><font size="2">From the conversation taht Tom Lane got involved with earlier after
mylast posting on this toping I think that you need to change your first function to this:</font><p><font
size="2">createfunction testfunc (text) returns int4 as '</font><br /><font size="2">declare</font><br /><font
size="2"> sql varchar;</font><br /><font size="2">begin</font><br /><font size="2">  sql=''SELECT id AS res2 FROM
''||$1;</font><br /><font size="2">  execute sql ;</font><br /><font size="2">  return res2;</font><br /><font
size="2">end;</font><br/><font size="2">' language 'plpgsql' ;</font><p><font size="2">Please note the AS syntax rather
thanINTO.  This was a misconception on my part, which has obviously lasted longer than I'd hoped ;-)</font><p><font
size="2">createfunction update_trans (text, integer, text, text, text, text,</font><br /><font size="2">text) returns
booleanas '</font><br /><font size="2">declare</font><br /><font size="2">  sql varchar;</font><br /><font size="2"> 
tblalias for $1 ;</font><br /><font size="2">begin</font><br /><font size="2">  sql = ''insert into ''||$tbl||''(objid,
objtbl,et, event, time, reason,</font><br /><font size="2">owner) values (''||$2||'', ''||$3||'', ''||$4||'',
''||$5||'',now(), ''||$6||'', ''||$7||'')'';</font><br /><font size="2">  execute sql;</font><br /><font size="2"> 
return0;</font><br /><font size="2">end;</font><br /><font size="2">' language 'plpgsql' ;</font><br /><p><font
size="2">You'reforgetting that you have to unquote your variables, because you are constructing a string, and then
executingthat.  Using a varchar called SQL is a good habit for debugging.  It separates constructing the string from
executingthe query.</font><p><font size="2">Cheers...</font><br /><p><font size="2">MikeA</font><br /><br /><p><font
size="2">-----OriginalMessage-----</font><br /><font size="2">From: rob [<a
href="mailto:rob@dsvr.net">mailto:rob@dsvr.net</a>]</font><br/><font size="2">Sent: 08 February 2001 16:48</font><br
/><fontsize="2">To: pgsql-sql@postgresql.org</font><br /><font size="2">Subject: [SQL] plpgsql grief</font><br
/><p><fontsize="2">Hi, I'm having some real headache problems here. Apologies for the</font><br /><font
size="2">length,i just want to get it all out now :)</font><p><font size="2">I figured moving some 'simple' db code
frommy application to it's more</font><br /><font size="2">natural home in the db would work out. Bummer. Not only do i
haveto run</font><br /><font size="2">7.1 (beta 4) to be able to dynamically generate queries, I'm finding it</font><br
/><fontsize="2">*extrememly* difficult to get to get my simple functions to work (plus</font><br /><font size="2">for
the'widest used open source db' i'm finding examples very hard to</font><br /><font size="2">come by)</font><p><font
size="2">BeforeI start if anyone has any pointers to coding examples (and I mean</font><br /><font size="2">a little
morethan the standard postgres docs :) I'd be eternally</font><br /><font size="2">greatful. Failing that, can anyone
helpwith these two simple (ahem)</font><br /><font size="2">codelets :</font><p><font size="2">Example 1
:</font><p><fontsize="2">create function testfunc (text) returns int4 as '</font><br /><font size="2">declare</font><br
/><fontsize="2">  sql varchar;</font><br /><font size="2">  res int4;</font><br /><font size="2">begin</font><br
/><fontsize="2">  sql=''SELECT INTO res2 id FROM ''||$1 ;</font><br /><font size="2">  execute sql ;</font><br /><font
size="2"> return res;</font><br /><font size="2">end;</font><br /><font size="2">' language 'plpgsql' ;</font><p><font
size="2">simplefunction to return the id field of a table (passed to the</font><br /><font size="2">function). ok, not
areal world example, however i do this :</font><p><font size="2">#select testfunc('tablenam') ;</font><br /><font
size="2">andi get</font><br /><font size="2">ERROR:  parser: parse error at or near "into"</font><p><font size="2">ok
thisis actually first things last. I'm not really bothered about</font><br /><font size="2">returing values into local
variablesand then returning them, it's just</font><br /><font size="2">a run through. If I can't get this right, what
chancehave i got at</font><br /><font size="2">sorting out the real work i want to do. </font><p><font size="2">Example
2:</font><p><font size="2">create function update_trans (text, integer, text, text, text, text,</font><br /><font
size="2">text)returns boolean as '</font><br /><font size="2">declare</font><br /><font size="2">  tbl alias for $1
;</font><br/><font size="2">begin</font><br /><font size="2">  execute ''insert into tbl (objid, objtbl, et, event,
time,reason,</font><br /><font size="2">owner) values ($2, $3, $4, $5, now(), $6, $7)'';</font><br /><font size="2"> 
return0;</font><br /><font size="2">end;</font><br /><font size="2">' language 'plpgsql' ;</font><br /><p><font
size="2">#select update_trans('tablname','1' </font><br /><font
size="2">,'sometext','sometext','sometext','sometext','sometext');</font><br /><font size="2">ERROR:  Relation 'tbl'
doesnot exist</font><p><font size="2">dur. yeah i know it doesn't exist cause i want to pass it in parameter</font><br
/><fontsize="2">1.  Tried substituting tbl with $1 and quote_ident($1) and</font><br /><font size="2">quote_ident(tbl)
inthe sql string, but that didn't work either. (BTW</font><br /><font size="2">anyone know of any GUI interfaces that
support7.1 - phpPgAdmin 2.1,</font><br /><font size="2">2.2.1 and 2.3 seem to balk on functions)</font><p><font
size="2">Example2 is prelude to a larger function (not much larger - but then</font><br /><font size="2">this is
relavitveto how easy to code it is) to monitor the changes made</font><br /><font size="2">by a user, what they change
fromand to and who/when/why.... this is</font><br /><font size="2">already implemented in my app code - PHP - and
checkingout the features</font><br /><font size="2">available in postgres i figured i could do some kind of looping
through</font><br/><font size="2">the OLD and NEW dataset-array things, comparing them against each other,</font><br
/><fontsize="2">sorta like this :</font><p><font size="2">for ($i = 0 ; $i < count($NEW) ; $i++) {</font><br /><font
size="2">/*since $NEW and $OLD are essentially the same we can do this */</font><br /><font size="2">  if ($OLD[$i] !=
$NEW[$i])</font><br/><font size="2">    record the change bla bla bla</font><p><font size="2">}</font><br /><font
size="2">I'mreally hoping I can, as at this rate I've spent the better part of</font><br /><font size="2">three days
tryingto figure the simple things above out and the only</font><br /><font size="2">thing i'm about to reach is
breakingpoint...</font><p><font size="2">Sorry for the sarcasm, I'm about to pop.</font><p><font
size="2">Rob</font><code><fontsize="3"><br /><br />
**********************************************************************<br/> This email and any files transmitted with
itare confidential and<br /> intended solely for the use of the individual or entity to whom they<br /> are addressed.
Ifyou have received this email in error please notify<br /> Nick West - Global Infrastructure Manager.<br /><br /> This
footnotealso confirms that this email message has been swept by<br /> MIMEsweeper for the presence of computer
viruses.<br/><br /> www.mimesweeper.com<br /> **********************************************************************<br
/></font></code>

Re: plpgsql grief

От
"Josh Berkus"
Дата:
Rob,

> I figured moving some 'simple' db code from my
> application to it's more
> natural home in the db would work out. Bummer. Not only
> do i have to run
> 7.1 (beta 4) to be able to dynamically generate queries,
> I'm finding it
> *extrememly* difficult to get to get my simple functions
> to work (plus
> for the 'widest used open source db' i'm finding examples
> very hard to
> come by)
<rant>
Keep in mind that Open Source usually means DIY as well, or
it wouldn't be free.  If you have mission-critical problems,
pay-for support is available from two companies.

As for the PL/pgSQL documentation, everyone acknowledges
it's skimpy at best.  Several of us PL/pgSQL users plan to
write up more extensive docs *when we have time*.  The doc
writers will be volunteers, so don't hold your breath.

And, as another developer pointed out, the EXECUTE
functionality already goes beyond the scope of Microsoft's
Transact SQL, a $1000=$10,000 + product.
</rant>

In the meantime:

> Example 1 :
> 
> create function testfunc (text) returns int4 as '
> declare
>   sql varchar;
>   res int4;
> begin
>   sql=''SELECT INTO res2 id FROM ''||$1 ;
>   execute sql ;
>   return res;
> end;
> ' language 'plpgsql' ;

SELECT INTO functionality is being dropped from EXECUTE.  If
you're interested in the reasons why, we've been discussing
it on the list for the last 2 weeks; leaf throud the
archives.  

The main restriction is this:  EXECUTE passes the query to a
seperate sub-process, and as such you may not pass *any*
unexpanded variables into the EXECUTE statement.  Within
EXECUTE, those variables are out of scope.

Thus your only way to get stuff back from EXECUTE is to save
the results you want to a temporary table (using CREATE
TABLE AS ...), and read them back using a query.  Not
high-performance, but it gets the job done.  

Tom Lane and Jan Wieck have suggested that we might have
more flexible dynamic query generation for 7.2, but that's a
ways off.   

Thus, your second function should be:

> create function update_trans (text, integer, text, text,
> text, text,
> text) returns boolean as '
> declare
>   tbl alias for $1 ;
> begin
>   execute ''insert into tbl (objid, objtbl, et, event,
> time, reason,
> owner) values ('' || $2 || '', '' || $3  || '', '' || $4
|| '', '' || $5 || '', current_timestamp, '' || $6 || '',
'' || $7 || '')'';
>   return TRUE;
> end;
> ' language 'plpgsql' ;

With adjustments made to the syntax for data type delimiters
and replacing any nulls with the work NULL (and keep in mind
that Postgres functions currently have trouble with NULLS as
input parameters).

The rest is up to you ... or hire an expert.

-Josh Berkus

______AGLIO DATABASE SOLUTIONS___________________________                                      Josh Berkus Complete
informationtechnology      josh@agliodbs.com  and data management solutions       (415) 565-7293 for law firms, small
businesses       fax 621-2533   and non-profit organizations.      San Francisco
 


Re: plpgsql grief

От
Michael Fork
Дата:
> Thus your only way to get stuff back from EXECUTE is to save
> the results you want to a temporary table (using CREATE
> TABLE AS ...), and read them back using a query.  Not
> high-performance, but it gets the job done.  
> 

I believe this statement is incorrect, quoting Michael Ansley
<Michael.Ansley@intec-telecom-systems.com> from a previous e-mail:

<QUOTE>
create function testfunc (text) returns int4 as '
declare sql varchar;
begin sql=''SELECT id AS res2 FROM ''||$1 ; execute sql ; return res2;
end;
' language 'plpgsql' ;

Please note the AS syntax rather than INTO.  This was a misconception on
my part, which has obviously lasted longer than I'd hoped ;-)
</QUOTE>

I believe this was the consensus reached on the hacker's list.....

Michael Fork - CCNA - MCP - A+
Network Support - Toledo Internet Access - Toledo Ohio





Re: plpgsql grief

От
Tom Lane
Дата:
"Josh Berkus" <josh@agliodbs.com> writes:
> Thus your only way to get stuff back from EXECUTE is to save
> the results you want to a temporary table (using CREATE
> TABLE AS ...), and read them back using a query.  Not
> high-performance, but it gets the job done.  

That's not the only way; you can also use FOR ... EXECUTE, as Jan
pointed out awhile back in the other thread.

What does not work at the moment is to EXECUTE a 'SELECT INTO',
because EXECUTE just hands the string off to the main SQL parser
which knows nothing about plpgsql variables.  We'll try to improve
this for 7.2, but it's far too late to get it done for 7.1.

> Thus, your second function should be:

>> create function update_trans (text, integer, text, text,
>> text, text,
>> text) returns boolean as '
>> declare
>> tbl alias for $1 ;
>> begin
>> execute ''insert into tbl (objid, objtbl, et, event,
>> time, reason,
>> owner) values ('' || $2 || '', '' || $3  || '', '' || $4
> || '', '' || $5 || '', current_timestamp, '' || $6 || '',
> '' || $7 || '')'';
>> return TRUE;
>> end;
>> ' language 'plpgsql' ;

> With adjustments made to the syntax for data type delimiters
> and replacing any nulls with the work NULL.

Hm, good point; coping with NULLs in this context will require some
explicit programming.  Yech.  I'd recommend using quote_string for
the TEXT parameters, but that doesn't help any for the NULL case.
(I wonder if quote_string should be defined to return 'NULL' for
a NULL input?  Again, too late for 7.1, but seems like a good future
improvement.)

> (and keep in mind
> that Postgres functions currently have trouble with NULLS as
> input parameters).

Not in 7.1 they don't ...
        regards, tom lane


Re: plpgsql grief

От
Tom Lane
Дата:
Michael Ansley <Michael.Ansley@intec-telecom-systems.com> writes:
> create function testfunc (text) returns int4 as '
> declare
>   sql varchar;
> begin
>   sql=''SELECT id AS res2 FROM ''||$1 ;
>   execute sql ;
>   return res2;
> end;
> ' language 'plpgsql' ;

> Please note the AS syntax rather than INTO.

That won't work :-(
        regards, tom lane


Re: plpgsql grief

От
Josh Berkus
Дата:
Rob,

>Just need the info - i can do the rest. I also, due to my
> business requirements, need to do this as quickly as possible - maybe
> not the expert you perhaps are

<grin>  No expert at all, according to Tom Lane.

> As it goes I've implemented most of what I wanted in pl/tcl (having
> learned tcl today). That's all i need, and it makes me most happy :).

That's great!  If I need to hire a pl/tcl expert, I now know who to
e-mail ...

While you're at it, how about writing a 2-5 page pl/tcl HOWTO to help
others who follow in your (frustrated) footsteps?

> Just thought the built in procedure implmentation would be quite mature
> by now.

Implementation is in its adolescence/young adulthood.  Documentation is
still in its early childhood.  We (the user base) will fix that, but not
this month.

-Josh Berkus

-- 
______AGLIO DATABASE SOLUTIONS___________________________                                       Josh Berkus  Complete
informationtechnology      josh@agliodbs.com   and data management solutions       (415) 565-7293  for law firms, small
businesses      fax  621-2533   and non-profit organizations.       San Francisco
 


RE: plpgsql grief

От
Michael Ansley
Дата:
<p><font size="2">I thought that the discussion on this topic resolved that the AS syntax would work as I described,
andthe INTO syntax would be removed because of ambiguity, to be redeveloped at a later date?</font><p><font
size="2">-----OriginalMessage-----</font><br /><font size="2">From: Tom Lane [<a
href="mailto:tgl@sss.pgh.pa.us">mailto:tgl@sss.pgh.pa.us</a>]</font><br/><font size="2">Sent: 12 February 2001
16:46</font><br/><font size="2">To: Michael Ansley</font><br /><font size="2">Cc: 'rob';
pgsql-sql@postgresql.org</font><br/><font size="2">Subject: Re: [SQL] plpgsql grief </font><br /><p><font
size="2">MichaelAnsley <Michael.Ansley@intec-telecom-systems.com> writes:</font><br /><font size="2">> create
functiontestfunc (text) returns int4 as '</font><br /><font size="2">> declare</font><br /><font size="2">>   sql
varchar;</font><br/><font size="2">> begin</font><br /><font size="2">>   sql=''SELECT id AS res2 FROM ''||$1
;</font><br/><font size="2">>   execute sql ;</font><br /><font size="2">>   return res2;</font><br /><font
size="2">>end;</font><br /><font size="2">> ' language 'plpgsql' ;</font><p><font size="2">> Please note the
ASsyntax rather than INTO.</font><p><font size="2">That won't work :-(</font><p>                        <font
size="2">regards,tom lane</font><code><font size="3"><br /><br />
**********************************************************************<br/> This email and any files transmitted with
itare confidential and<br /> intended solely for the use of the individual or entity to whom they<br /> are addressed.
Ifyou have received this email in error please notify<br /> Nick West - Global Infrastructure Manager.<br /><br /> This
footnotealso confirms that this email message has been swept by<br /> MIMEsweeper for the presence of computer
viruses.<br/><br /> www.mimesweeper.com<br /> **********************************************************************<br
/></font></code>

Re: plpgsql grief

От
Josh Berkus
Дата:
Tom,

> > (and keep in mind
> > that Postgres functions currently have trouble with NULLS as
> > input parameters).
> 
> Not in 7.1 they don't ...

Really? Terrific.  Sadly, I have 25+ PL/pgSQL functions not set up to
accept NULLs ...

Can you point me to (or tell me where to search) the developer notes on
what improvements have been made in the function parser so that I can
take advantage of them?

-Josh Berkus


______AGLIO DATABASE SOLUTIONS___________________________                                       Josh Berkus  Complete
informationtechnology      josh@agliodbs.com   and data management solutions       (415) 565-7293  for law firms, small
businesses      fax  621-2533   and non-profit organizations.       San Francisco
 


Re: plpgsql grief

От
Josh Berkus
Дата:
Michael,

> I thought that the discussion on this topic resolved that the AS
> syntax would work as I described, and the INTO syntax would be removed
> because of ambiguity, to be redeveloped at a later date?
*************************

Tom (I believe) was referring to CREATE TABLE AS as a way to insert
queries into temporary tables.

For better results, check out Jan's syntax for FOR ... EXECUTE in his
last post.

-Josh Berkus

-- 
______AGLIO DATABASE SOLUTIONS___________________________                                       Josh Berkus  Complete
informationtechnology      josh@agliodbs.com   and data management solutions       (415) 565-7293  for law firms, small
businesses      fax  621-2533   and non-profit organizations.       San Francisco
 


Re: plpgsql grief

От
Tom Lane
Дата:
Michael Ansley <Michael.Ansley@intec-telecom-systems.com> writes:
> I thought that the discussion on this topic resolved that the AS syntax
> would work as I described, and the INTO syntax would be removed because of
> ambiguity, to be redeveloped at a later date?

INTO has indeed been removed.  However, AS does not do what you seem
to think it does; in fact, it is entirely a noise clause in this
context.
        regards, tom lane