Обсуждение: pl/pgsql: END verbosity
In PL/PgSQL, "END LOOP" is used to terminate loop blocks, and "END IF" is used to terminate IF blocks. This is needlessly verbose: we could simply accept "END" in both cases without syntactic ambiguity. I'd like to make this change, so that END can be used to terminate any kind of block. There's no need to remove support for the present syntax, of course, so there's no backward compatibility concern. Oracle's PL/SQL does require "END IF" and "END LOOP", but folks interested in maximum compatibility can always use those forms if they like. Any objections? -Neil
Neil, > In PL/PgSQL, "END LOOP" is used to terminate loop blocks, and "END IF" > is used to terminate IF blocks. This is needlessly verbose: we could > simply accept "END" in both cases without syntactic ambiguity. I'd like > to make this change, so that END can be used to terminate any kind of > block. There's no need to remove support for the present syntax, of > course, so there's no backward compatibility concern. Oracle's PL/SQL > does require "END IF" and "END LOOP", but folks interested in maximum > compatibility can always use those forms if they like. No problem from me. Since the parser checks for block closure for all block types, I can't see how this would be a problem. -- --Josh Josh Berkus Aglio Database Solutions San Francisco
Neil Conway said: > In PL/PgSQL, "END LOOP" is used to terminate loop blocks, and "END IF" > is used to terminate IF blocks. This is needlessly verbose: we could > simply accept "END" in both cases without syntactic ambiguity. I'd like > to make this change, so that END can be used to terminate any kind of > block. There's no need to remove support for the present syntax, of > course, so there's no backward compatibility concern. Oracle's PL/SQL > does require "END IF" and "END LOOP", but folks interested in maximum > compatibility can always use those forms if they like. > > Any objections? > I'm unkeen. I see no technical advantage - it's just a matter of taste. We advertise that plpgsql is similar to plsql - we should not do anything to make that less so IMNSHO. Terseness is not always good, redundancy is not always bad. cheers andrew
"Andrew Dunstan" <andrew@dunslane.net> writes: > Neil Conway said: >> Any objections? > I'm unkeen. I see no technical advantage - it's just a matter of taste. We > advertise that plpgsql is similar to plsql - we should not do anything to > make that less so IMNSHO. Terseness is not always good, redundancy is not > always bad. That was my reaction too, though I'm too tired at this hour to phrase it so well ;-). The long-term point in my mind is that removing syntactical redundancy always reduces the ability to detect errors or report errors acccurately; and it may limit our freedom to introduce new features later. Consider for example the possibility that Oracle's next release adds some new frammish that can't be duplicated because we chose not to distinguish various forms of "END xxx" ... regards, tom lane
Andrew Dunstan wrote: > I'm unkeen. I see no technical advantage - it's just a matter of taste. There is no "technical advantage" to case insensitive keywords, or dollar quoting, or a variety of other programming language features that don't change functionality but exist to make using the programming language easier. > We advertise that plpgsql is similar to plsql - we should not do > anything to make that less so IMNSHO. Do you *really* mean that? This principle would mean we should reject patches like the CONTINUE statement patch I just applied, for example, as PL/SQL has no such construct. In any case, I think you are overestimating the value of strict PL/SQL compatibility. IMHO, PL/PgSQL should be a useful procedural programming language first, and a reimplementation of PL/SQL second. We should provide an equivalent feature (not necessarily with the same syntax) for all of PL/SQL's useful features, but I don't see the value in copying Oracle when PL/SQL's implementation of a feature is ugly, broken, or inconsistent with the rest of Postgres. It's not as if complete source-level compatibility with PL/SQL has been a goal for PL/PgSQL anyway (and besides, there are other people, like EnterpriseDB, who can provide that for those who need it). > Terseness is not always good, redundancy is not always bad. Granted -- but why is redundancy a good thing here? -Neil
Tom Lane wrote: > The long-term point in my mind is that removing syntactical > redundancy always reduces the ability to detect errors or report > errors acccurately Lexical scoping is unambiguous in a language like PL/PgSQL. Since it is simple to determine whether a given END matches an IF, LOOP, or BEGIN, I don't see how it would reduce our "ability to detect errors or report errors accurately". > Consider for example the possibility that Oracle's next release adds > some new frammish that can't be duplicated because we chose not to > distinguish various forms of "END xxx" ... As lexical scoping is still unambiguous, we could actually add a K_LOOP / K_IF token to the input stream, if that would make you happier :) (Of course I'm not suggesting this -- the point is that as far as the parser is concerned, we should have precisely the same information for disambiguating the input as we used to have.) BTW, I notice that Oracle actually allows: <<label>> LOOP -- ... END LOOP label; whereas we don't allow the optional label following END LOOP. Which goes to my general point: this frammish has existed in PL/SQL for a while, but it's not as if people are clamoring for us to implement it. I would wager that most people care about having *equivalent* features to PL/SQL, not exactly identical syntax. For example, the lack of autonomous transactions is something people have asked for in the past, because it *does* make porting PL/SQL applications more difficult. I can't see anyone losing any sleep because we are slightly more relaxed about the input we accept. -Neil
Neil Conway said: > Andrew Dunstan wrote: >> I'm unkeen. I see no technical advantage - it's just a matter of >> taste. > > There is no "technical advantage" to case insensitive keywords, or > dollar quoting, or a variety of other programming language features > that don't change functionality but exist to make using the > programming language easier. > But this doesn't make it easier to use - users don't just include those who write it. The antecedent language of these, Ada, from which this syntax comes, was explicitly designed to be reader-friendly as opposed to writer-friendly, and this is a part of that. I can tell you from experience of programming Ada a long time ago that I have been profoundly grateful that this was required in the language when disentangling a badly written 1000+ line long multibranch IF statement. And I still find myself having to hunt for what sort of block a } is closing in C, and I still find it annoying. >> We advertise that plpgsql is similar to plsql - we should not do >> anything to make that less so IMNSHO. > > Do you *really* mean that? This principle would mean we should reject > patches like the CONTINUE statement patch I just applied, for example, > as PL/SQL has no such construct. > Well, perhaps I should have qualified that a bit - we shouldn't do it gratuitously. Getting the effect of CONTINUE for nested loops can be sufficiently hard that it is arguable that implementing it is not just syntactic sugar. I seem to recall muttering about how implementing GOTO wasn't worth the trouble. > >> Terseness is not always good, redundancy is not always bad. > > Granted -- but why is redundancy a good thing here? > see above cheers andrew
Andrew Dunstan wrote: > But this doesn't make it easier to use - users don't just include those who > write it. The antecedent language of these, Ada, from which this syntax > comes, was explicitly designed to be reader-friendly as opposed to > writer-friendly, and this is a part of that. IMHO it is just needless verbiage that makes programs both harder to read *and* harder to write, albeit marginally so. I think there is a reason why Ada-style block terminators are in the minority among block-structured languages :) But obviously this is a matter of taste -- does anyone else like or dislike the current syntax? -Neil
On Thu, Jun 23, 2005 at 01:41:49AM +1000, Neil Conway wrote: > Andrew Dunstan wrote: > >But this doesn't make it easier to use - users don't just include those who > >write it. The antecedent language of these, Ada, from which this syntax > >comes, was explicitly designed to be reader-friendly as opposed to > >writer-friendly, and this is a part of that. > > IMHO it is just needless verbiage that makes programs both harder to > read *and* harder to write, albeit marginally so. I think there is a > reason why Ada-style block terminators are in the minority among > block-structured languages :) > > But obviously this is a matter of taste -- does anyone else like or > dislike the current syntax? "Like" is a bit strong. But it does make functions written in it easier to read. And given that the primary debugging methodolofy for pl/pgsql is "Look at it hard and see what might be incorrect" I can't see that as a bad thing. I'd trade a whole lot of "harder to write" for even some "likely to work". Cheers, Steve
On Wed, Jun 22, 2005 at 09:23:17AM -0700, Steve Atkins wrote: > On Thu, Jun 23, 2005 at 01:41:49AM +1000, Neil Conway wrote: > > Andrew Dunstan wrote: > > >But this doesn't make it easier to use - users don't just include those who > > >write it. The antecedent language of these, Ada, from which this syntax > > >comes, was explicitly designed to be reader-friendly as opposed to > > >writer-friendly, and this is a part of that. > > > > IMHO it is just needless verbiage that makes programs both harder to > > read *and* harder to write, albeit marginally so. I think there is a > > reason why Ada-style block terminators are in the minority among > > block-structured languages :) > > > > But obviously this is a matter of taste -- does anyone else like or > > dislike the current syntax? > > "Like" is a bit strong. But it does make functions written in it easier > to read. And given that the primary debugging methodolofy for pl/pgsql > is "Look at it hard and see what might be incorrect" I can't see that > as a bad thing. Yeah, while we don't have good debugging support in pl/pgsql we shouldn't be making it harder to read. (FWIW, yes, I think it's useful for those keywords to be required when you have to look at homongous functions.) -- Alvaro Herrera (<alvherre[a]surnet.cl>) "No renuncies a nada. No te aferres a nada."
On Tue, 21 Jun 2005, Andrew Dunstan wrote: > Neil Conway said: > > In PL/PgSQL, "END LOOP" is used to terminate loop blocks, and "END IF" > > is used to terminate IF blocks. This is needlessly verbose: we could > > simply accept "END" in both cases without syntactic ambiguity. I'd like > > to make this change, so that END can be used to terminate any kind of > > block. There's no need to remove support for the present syntax, of > > course, so there's no backward compatibility concern. Oracle's PL/SQL > > does require "END IF" and "END LOOP", but folks interested in maximum > > compatibility can always use those forms if they like. > > Hello, I prefer actual syntax too, Neil. The reason isn't compatibility with Oracle, but better readibility - it's mean more work with finishing code but less with debugging Regards Pavel
> > Neil Conway said: > > > In PL/PgSQL, "END LOOP" is used to terminate loop blocks, and "END IF" > > > is used to terminate IF blocks. This is needlessly verbose: we could > > > simply accept "END" in both cases without syntactic ambiguity. I'd > like > > > to make this change, so that END can be used to terminate any kind of > > > block. There's no need to remove support for the present syntax, of > > > course, so there's no backward compatibility concern. Oracle's PL/SQL > > > does require "END IF" and "END LOOP", but folks interested in maximum > > > compatibility can always use those forms if they like. > > > > > Hello, > > I prefer actual syntax too, Neil. The reason isn't compatibility with > Oracle, but better readibility - it's mean more work with finishing code > but less with debugging COBOL, which is a kissing-cousin of pl/sql, allows this. You can have a line terminator (a period) or a specialized block terminator. Based on my experience here I would suggest not allowing a choice. It's a famous source of bugs. Merlin
<font face="Verdana, Helvetica, Arial"><span style="font-size:12.0px">Hi All,<br /><br /> Sorry if this isn’t the right forumfor this question... But, I noticed that Alvaro wrote<br /><br /> “... we don't have good debugging support in pl/pgsql...”<br /></span></font><blockquote><font face="Verdana, Helvetica, Arial"><span style="font-size:12.0px"><br /></span></font></blockquote><fontface="Verdana, Helvetica, Arial"><span style="font-size:12.0px">I got to thinking it’dbe kewl if PgAdmin3 supported an interactive debugger for pl/pgsql. If anyone’s interested in expertly tackling sucha community project, with some financial sponsorship from EDB, please contact me privately.<br /><br /> --Denis Lussier<br/> Founder & Chief Dweeb<br /> EnterpriseDB<br /><br /></span></font>
Denis Lussier <denisl@enterprisedb.com> writes: > I got to thinking it'd be kewl if PgAdmin3 supported an interactive debugger > for pl/pgsql. That's been kicked around before, although I don't think anyone wants to tie it to pgAdmin specifically. Check the archives... regards, tom lane
On 6/22/2005 1:29 AM, Neil Conway wrote: > Tom Lane wrote: >> The long-term point in my mind is that removing syntactical >> redundancy always reduces the ability to detect errors or report >> errors acccurately > > Lexical scoping is unambiguous in a language like PL/PgSQL. Since it is > simple to determine whether a given END matches an IF, LOOP, or BEGIN, I > don't see how it would reduce our "ability to detect errors or report > errors accurately". > >> Consider for example the possibility that Oracle's next release adds >> some new frammish that can't be duplicated because we chose not to >> distinguish various forms of "END xxx" ... > > As lexical scoping is still unambiguous, we could actually add a K_LOOP > / K_IF token to the input stream, if that would make you happier :) (Of > course I'm not suggesting this -- the point is that as far as the parser > is concerned, we should have precisely the same information for > disambiguating the input as we used to have.) > > BTW, I notice that Oracle actually allows: > > <<label>> > LOOP > -- ... > END LOOP label; But what if they decide to allow LOOP -- ... IF condition THEN EXIT; END LOOP; at some point? There you'd get ambiguity. Jan > > whereas we don't allow the optional label following END LOOP. Which goes > to my general point: this frammish has existed in PL/SQL for a while, > but it's not as if people are clamoring for us to implement it. I would > wager that most people care about having *equivalent* features to > PL/SQL, not exactly identical syntax. For example, the lack of > autonomous transactions is something people have asked for in the past, > because it *does* make porting PL/SQL applications more difficult. I > can't see anyone losing any sleep because we are slightly more relaxed > about the input we accept. > > -Neil > > ---------------------------(end of broadcast)--------------------------- > TIP 6: Have you searched our list archives? > > http://archives.postgresql.org -- #======================================================================# # It's easier to get forgiveness for being wrong than for being right. # # Let's break this rule - forgive me. # #================================================== JanWieck@Yahoo.com #
Tom Lane wrote: >Denis Lussier <denisl@enterprisedb.com> writes: > > >>I got to thinking it'd be kewl if PgAdmin3 supported an interactive debugger >>for pl/pgsql. >> >> > >That's been kicked around before, although I don't think anyone wants to >tie it to pgAdmin specifically. Check the archives... > > I didn't find anything relevant after a quick search, but if memory serves, one of the objections to PgAdmin was that it was windows only. This of course is no longer true as of PgAdmin III 1.0. It now support Win32, Linux and FreeBSD. So perhaps that objection is no longer valid.
On Thu, Jun 23, 2005 at 01:40:18PM -0400, Matthew T. O'Connor wrote: > Tom Lane wrote: > > >Denis Lussier <denisl@enterprisedb.com> writes: > > > > > >>I got to thinking it'd be kewl if PgAdmin3 supported an interactive > >>debugger for pl/pgsql. > > > >That's been kicked around before, although I don't think anyone wants to > >tie it to pgAdmin specifically. Check the archives... > > I didn't find anything relevant after a quick search, but if memory > serves, one of the objections to PgAdmin was that it was windows only. > This of course is no longer true as of PgAdmin III 1.0. It now support > Win32, Linux and FreeBSD. So perhaps that objection is no longer valid. I think the point is that we will have to make some modifications to PL/pgSQL, so why not make sure we write something that any tool can use? Say, a well-defined BE/FE protocol extension. -- Alvaro Herrera (<alvherre[a]surnet.cl>) "Sallah, I said NO camels! That's FIVE camels; can't you count?" (Indiana Jones)
Alvaro, I agree, a protocol seems to generally be the best option. -Jonah Alvaro Herrera wrote: >On Thu, Jun 23, 2005 at 01:40:18PM -0400, Matthew T. O'Connor wrote: > > >>Tom Lane wrote: >> >> >> >>>Denis Lussier <denisl@enterprisedb.com> writes: >>> >>> >>> >>> >>>>I got to thinking it'd be kewl if PgAdmin3 supported an interactive >>>>debugger for pl/pgsql. >>>> >>>> >>>That's been kicked around before, although I don't think anyone wants to >>>tie it to pgAdmin specifically. Check the archives... >>> >>> >>I didn't find anything relevant after a quick search, but if memory >>serves, one of the objections to PgAdmin was that it was windows only. >>This of course is no longer true as of PgAdmin III 1.0. It now support >>Win32, Linux and FreeBSD. So perhaps that objection is no longer valid. >> >> > >I think the point is that we will have to make some modifications to >PL/pgSQL, so why not make sure we write something that any tool can use? >Say, a well-defined BE/FE protocol extension. > > >
Denis, all, > I got to thinking it¹d be kewl if PgAdmin3 supported an interactive > debugger for pl/pgsql. If anyone¹s interested in expertly tackling such a > community project, with some financial sponsorship from EDB, please contact > me privately. Just FYI, EMS Hitech has a windows-only PL/pgSQL debugger. So it's apparently possible even with the current tech. Overally, though, we'd want to support something command-line like the Perl debug shell. Then any tool could use it. -- Josh Berkus Aglio Database Solutions San Francisco
On Thursday 23 June 2005 14:21, Josh Berkus wrote: > Denis, all, > > > I got to thinking it¹d be kewl if PgAdmin3 supported an interactive > > debugger for pl/pgsql. If anyone¹s interested in expertly tackling such > > a community project, with some financial sponsorship from EDB, please > > contact me privately. > > Just FYI, EMS Hitech has a windows-only PL/pgSQL debugger. So it's > apparently possible even with the current tech. > IIRC thier debugger is little more than BEGIN; SELECT foo(); ROLLBACK; which isn't great as far as debuggers go. > Overally, though, we'd want to support something command-line like the Perl > debug shell. Then any tool could use it. Uh... a lot of tools have issues executing stuff from the command line... what we need is something sql driven, or at least that opeates at that level, inside the db. -- Robert Treat Build A Brighter Lamp :: Linux Apache {middleware} PostgreSQL
On Wednesday 22 June 2005 11:41, Neil Conway wrote: > Andrew Dunstan wrote: > > But this doesn't make it easier to use - users don't just include those > > who write it. The antecedent language of these, Ada, from which this > > syntax comes, was explicitly designed to be reader-friendly as opposed to > > writer-friendly, and this is a part of that. > > IMHO it is just needless verbiage that makes programs both harder to > read *and* harder to write, albeit marginally so. I think there is a > reason why Ada-style block terminators are in the minority among > block-structured languages :) > > But obviously this is a matter of taste -- does anyone else like or > dislike the current syntax? > -1 on the proposal to me... ambiguous END statements just looks like trouble to me. I'd actually rather see you implement label...END LOOP label if you felt like you had to change *something*. -- Robert Treat Build A Brighter Lamp :: Linux Apache {middleware} PostgreSQL
Jan Wieck wrote: > But what if they decide to allow > > LOOP > -- ... > IF condition THEN > EXIT; > END LOOP; > > at some point? There you'd get ambiguity. ISTM this would be ambiguous in any case: IF condition1 THEN foo; IF condition2 THEN bar; END IF; -Neil
My understanding is that EMS Hitech is just doing client side debugging.
That is they are taking your function and creating a new process to follow the flow of the program. So if they mess up something you may thing your program is doing one thing when it is really doing something else. Maybe I'm wrong here but I assume that is what is going on. While this is better than nothing, it doesn't compare to a built in API in pl/pgsql that would allow any tool to hook into a function and debug. Would love to work on this if I had the low level programming skills that the main hackers have.
On 6/23/05, Josh Berkus <josh@agliodbs.com> wrote:
Denis, all,
> I got to thinking it¹d be kewl if PgAdmin3 supported an interactive
> debugger for pl/pgsql. If anyone¹s interested in expertly tackling such a
> community project, with some financial sponsorship from EDB, please contact
> me privately.
Just FYI, EMS Hitech has a windows-only PL/pgSQL debugger. So it's apparently
possible even with the current tech.
Overally, though, we'd want to support something command-line like the Perl
debug shell. Then any tool could use it.
--
Josh Berkus
Aglio Database Solutions
San Francisco
---------------------------(end of broadcast)---------------------------
TIP 3: if posting/reading through Usenet, please send an appropriate
subscribe-nomail command to majordomo@postgresql.org so that your
message can get through to the mailing list cleanly
Am Mittwoch, 22. Juni 2005 04:17 schrieb Neil Conway: > In PL/PgSQL, "END LOOP" is used to terminate loop blocks, and "END IF" > is used to terminate IF blocks. This is needlessly verbose: It is required by the SQL standard.
Hello this patch allows optional using label with END and END LOOP. Ending label has only informational value, but can enhance readability large block and enhance likeness with Oracle. <<main>>LOOP ... ... END LOOP<<main>>; Regards Pavel Stehule
Peter Eisentraut wrote: > It is required by the SQL standard. No, it isn't -- PL/PgSQL is not defined by the SQL standard. I guess you're referring to SQL/PSM, but that has only a passing resemblance to PL/PgSQL. Implementing SQL/PSM in some form would definitely be worth doing (especially now that MySQL have), but I haven't seen any plans to do that by adapting PL/PgSQL to SQL/PSM. In any case, there are plenty of cases in which we accept a superset of the syntax defined by the SQL standard -- DROP TABLE { RESTRICT | CASCADE }, for example. We have never interpreted compliance with the SQL specification to mean that we must *only* accept the standard's syntax and nothing else. -Neil
Neil Conway wrote: > No, it isn't -- PL/PgSQL is not defined by the SQL standard. I guess > you're referring to SQL/PSM, but that has only a passing resemblance > to PL/PgSQL. Implementing SQL/PSM in some form would definitely be > worth doing (especially now that MySQL have), but I haven't seen any > plans to do that by adapting PL/PgSQL to SQL/PSM. I don't claim to recall the details, but we have frequently referred to the SQL standard when resolving issues about PL/pgSQL's syntax. > In any case, there are plenty of cases in which we accept a superset > of the syntax defined by the SQL standard -- DROP TABLE { RESTRICT | > CASCADE }, for example. We have never interpreted compliance with the > SQL specification to mean that we must *only* accept the standard's > syntax and nothing else. The cases were we accept a superset of the SQL standard are either additional features, backward compatibility, or compatibility to other systems -- none of which seem to apply here. -- Peter Eisentraut http://developer.postgresql.org/~petere/
On Mon, 27 Jun 2005, Neil Conway wrote: > Peter Eisentraut wrote: > > It is required by the SQL standard. > > No, it isn't -- PL/PgSQL is not defined by the SQL standard. I guess > you're referring to SQL/PSM, but that has only a passing resemblance to > PL/PgSQL. Implementing SQL/PSM in some form would definitely be worth > doing (especially now that MySQL have), but I haven't seen any plans to > do that by adapting PL/PgSQL to SQL/PSM. PL/pgSQL is different language than SQL/PSM and is little bit nonsenc adapting them to SQL/PSM. PL/SQL live still - Oracle did some enhancing, and we can do it too. Some parts both languages are similar and some enough different. I had plan start develop new interpret for SQL/PSM two years ago, but I hadn't knowleages at that time. Situation is different now. If anybody wont to work on SQL/PSM I will go too. Solution is another pl - pl/psm. We can adapt gram.y plpgsql to psm Regards Pavel Stehule
Pavel Stehule wrote: > this patch allows optional using label with END and END LOOP. Ending label > has only informational value, but can enhance readability large block and > enhance likeness with Oracle. > > <<main>>LOOP > ... > ... > END LOOP<<main>>; Attached is a revised version of this patch. Changes / comments: - AFAICS Oracle's syntax is actually <<label>> LOOP ... END LOOP label; i.e. the ending block label isn't enclosed in <<>>. I've adjusted the patch accordingly. - your patch broke EXIT and CONTINUE, as running the regression tests would have made clear. - yyerror() will set plpgsql_error_lineno, so you needn't do it yourself. I changed it to use ereport(ERROR) anyway, as it seems a bit more appropriate. I'm not quite happy with the error message text: ERROR: end label "outer_label" differs from block's label "inner_label" CONTEXT: compile of PL/pgSQL function "end_label3" near line 6 ERROR: end label "outer_label" specified for unlabelled block CONTEXT: compile of PL/pgSQL function "end_label4" near line 5 suggestions for improvement are welcome. BTW, I notice that some but not all the call sites of ereport(ERROR) in PL/PgSQL's gram.y set plpgsql_error_lineno. Is there a reason for this? Barring any objections, I'll apply the attached patch to CVS tomorrow. -Neil Index: doc/src/sgml/plpgsql.sgml =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/doc/src/sgml/plpgsql.sgml,v retrieving revision 1.74 diff -c -r1.74 plpgsql.sgml *** doc/src/sgml/plpgsql.sgml 22 Jun 2005 01:35:02 -0000 1.74 --- doc/src/sgml/plpgsql.sgml 1 Jul 2005 11:43:36 -0000 *************** *** 456,462 **** <replaceable>declarations</replaceable> </optional> BEGIN <replaceable>statements</replaceable> ! END; </synopsis> </para> --- 456,462 ---- <replaceable>declarations</replaceable> </optional> BEGIN <replaceable>statements</replaceable> ! END <optional> <replaceable>label</replaceable> </optional>; </synopsis> </para> *************** *** 1789,1806 **** <title><literal>LOOP</></title> <synopsis> ! <optional><<<replaceable>label</replaceable>>></optional> LOOP <replaceable>statements</replaceable> ! END LOOP; </synopsis> <para> ! <literal>LOOP</> defines an unconditional loop that is repeated indefinitely ! until terminated by an <literal>EXIT</> or <command>RETURN</command> ! statement. The optional label can be used by <literal>EXIT</> statements in ! nested loops to specify which level of nesting should be ! terminated. </para> </sect3> --- 1789,1807 ---- <title><literal>LOOP</></title> <synopsis> ! <optional> <<<replaceable>label</replaceable>>> </optional> LOOP <replaceable>statements</replaceable> ! END LOOP <optional> <replaceable>label</replaceable> </optional>; </synopsis> <para> ! <literal>LOOP</> defines an unconditional loop that is repeated ! indefinitely until terminated by an <literal>EXIT</> or ! <command>RETURN</command> statement. The optional ! <replaceable>label</replaceable> can be used by <literal>EXIT</> ! and <literal>CONTINUE</literal> statements in nested loops to ! specify which loop the statement should be applied to. </para> </sect3> *************** *** 1920,1929 **** </indexterm> <synopsis> ! <optional><<<replaceable>label</replaceable>>></optional> WHILE <replaceable>expression</replaceable> LOOP <replaceable>statements</replaceable> ! END LOOP; </synopsis> <para> --- 1921,1930 ---- </indexterm> <synopsis> ! <optional> <<<replaceable>label</replaceable>>> </optional> WHILE <replaceable>expression</replaceable> LOOP <replaceable>statements</replaceable> ! END LOOP <optional> <replaceable>label</replaceable> </optional>; </synopsis> <para> *************** *** 1951,1960 **** <title><literal>FOR</> (integer variant)</title> <synopsis> ! <optional><<<replaceable>label</replaceable>>></optional> FOR <replaceable>name</replaceable> IN <optional> REVERSE </optional> <replaceable>expression</replaceable> .. <replaceable>expression</replaceable>LOOP <replaceable>statements</replaceable> ! END LOOP; </synopsis> <para> --- 1952,1961 ---- <title><literal>FOR</> (integer variant)</title> <synopsis> ! <optional> <<<replaceable>label</replaceable>>> </optional> FOR <replaceable>name</replaceable> IN <optional> REVERSE </optional> <replaceable>expression</replaceable> .. <replaceable>expression</replaceable>LOOP <replaceable>statements</replaceable> ! END LOOP <optional> <replaceable>labal</replaceable> </optional>; </synopsis> <para> *************** *** 1997,2006 **** the results of a query and manipulate that data accordingly. The syntax is: <synopsis> ! <optional><<<replaceable>label</replaceable>>></optional> FOR <replaceable>record_or_row</replaceable> IN <replaceable>query</replaceable> LOOP <replaceable>statements</replaceable> ! END LOOP; </synopsis> The record or row variable is successively assigned each row resulting from the <replaceable>query</replaceable> (which must be a --- 1998,2007 ---- the results of a query and manipulate that data accordingly. The syntax is: <synopsis> ! <optional> <<<replaceable>label</replaceable>>> </optional> FOR <replaceable>record_or_row</replaceable> IN <replaceable>query</replaceable> LOOP <replaceable>statements</replaceable> ! END LOOP <optional> <replaceable>label</replaceable> </optional>; </synopsis> The record or row variable is successively assigned each row resulting from the <replaceable>query</replaceable> (which must be a *************** *** 2036,2045 **** The <literal>FOR-IN-EXECUTE</> statement is another way to iterate over rows: <synopsis> ! <optional><<<replaceable>label</replaceable>>></optional> FOR <replaceable>record_or_row</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> LOOP <replaceable>statements</replaceable> ! END LOOP; </synopsis> This is like the previous form, except that the source <command>SELECT</command> statement is specified as a string --- 2037,2046 ---- The <literal>FOR-IN-EXECUTE</> statement is another way to iterate over rows: <synopsis> ! <optional> <<<replaceable>label</replaceable>>> </optional> FOR <replaceable>record_or_row</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> LOOP <replaceable>statements</replaceable> ! END LOOP <optional> <replaceable>label</replaceable> </optional>; </synopsis> This is like the previous form, except that the source <command>SELECT</command> statement is specified as a string Index: src/pl/plpgsql/src/gram.y =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/pl/plpgsql/src/gram.y,v retrieving revision 1.77 diff -c -r1.77 gram.y *** src/pl/plpgsql/src/gram.y 22 Jun 2005 01:35:02 -0000 1.77 --- src/pl/plpgsql/src/gram.y 1 Jul 2005 12:08:45 -0000 *************** *** 56,61 **** --- 56,63 ---- PLpgSQL_datum *initial_datum); static void check_sql_expr(const char *stmt); static void plpgsql_sql_error_callback(void *arg); + static void check_labels(const char *start_label, + const char *end_label); %} *************** *** 69,75 **** int lineno; } varname; struct ! { char *name; int lineno; PLpgSQL_rec *rec; --- 71,77 ---- int lineno; } varname; struct ! { char *name; int lineno; PLpgSQL_rec *rec; *************** *** 81,86 **** --- 83,93 ---- int n_initvars; int *initvarnos; } declhdr; + struct + { + char *end_label; + List *stmts; + } loop_body; List *list; PLpgSQL_type *dtype; PLpgSQL_datum *scalar; /* a VAR, RECFIELD, or TRIGARG */ *************** *** 119,129 **** %type <forvariable> for_variable %type <stmt> for_control ! %type <str> opt_lblname opt_label ! %type <str> opt_exitlabel %type <str> execsql_start ! %type <list> proc_sect proc_stmts stmt_else loop_body %type <stmt> proc_stmt pl_block %type <stmt> stmt_assign stmt_if stmt_loop stmt_while stmt_exit %type <stmt> stmt_return stmt_return_next stmt_raise stmt_execsql --- 126,136 ---- %type <forvariable> for_variable %type <stmt> for_control ! %type <str> opt_lblname opt_block_label opt_label %type <str> execsql_start ! %type <list> proc_sect proc_stmts stmt_else ! %type <loop_body> loop_body %type <stmt> proc_stmt pl_block %type <stmt> stmt_assign stmt_if stmt_loop stmt_while stmt_exit %type <stmt> stmt_return stmt_return_next stmt_raise stmt_execsql *************** *** 248,257 **** | ';' ; ! pl_block : decl_sect K_BEGIN lno proc_sect exception_sect K_END { PLpgSQL_stmt_block *new; new = palloc0(sizeof(PLpgSQL_stmt_block)); new->cmd_type = PLPGSQL_STMT_BLOCK; --- 255,266 ---- | ';' ; ! pl_block : decl_sect K_BEGIN lno proc_sect exception_sect K_END opt_label { PLpgSQL_stmt_block *new; + check_labels($1.label, $7); + new = palloc0(sizeof(PLpgSQL_stmt_block)); new->cmd_type = PLPGSQL_STMT_BLOCK; *************** *** 269,275 **** ; ! decl_sect : opt_label { plpgsql_ns_setlocal(false); $$.label = $1; --- 278,284 ---- ; ! decl_sect : opt_block_label { plpgsql_ns_setlocal(false); $$.label = $1; *************** *** 277,283 **** $$.initvarnos = NULL; plpgsql_add_initdatums(NULL); } ! | opt_label decl_start { plpgsql_ns_setlocal(false); $$.label = $1; --- 286,292 ---- $$.initvarnos = NULL; plpgsql_add_initdatums(NULL); } ! | opt_block_label decl_start { plpgsql_ns_setlocal(false); $$.label = $1; *************** *** 285,291 **** $$.initvarnos = NULL; plpgsql_add_initdatums(NULL); } ! | opt_label decl_start decl_stmts { plpgsql_ns_setlocal(false); if ($3 != NULL) --- 294,300 ---- $$.initvarnos = NULL; plpgsql_add_initdatums(NULL); } ! | opt_block_label decl_start decl_stmts { plpgsql_ns_setlocal(false); if ($3 != NULL) *************** *** 409,415 **** plpgsql_ns_setlocal(false); query = read_sql_stmt(""); plpgsql_ns_setlocal(true); ! $$ = query; } ; --- 418,424 ---- plpgsql_ns_setlocal(false); query = read_sql_stmt(""); plpgsql_ns_setlocal(true); ! $$ = query; } ; *************** *** 757,763 **** * ... ... * ELSE ELSE * ... ... ! * END IF END IF * END IF */ PLpgSQL_stmt_if *new_if; --- 766,772 ---- * ... ... * ELSE ELSE * ... ... ! * END IF END IF * END IF */ PLpgSQL_stmt_if *new_if; *************** *** 776,786 **** | K_ELSE proc_sect { ! $$ = $2; } ; ! stmt_loop : opt_label K_LOOP lno loop_body { PLpgSQL_stmt_loop *new; --- 785,795 ---- | K_ELSE proc_sect { ! $$ = $2; } ; ! stmt_loop : opt_block_label K_LOOP lno loop_body { PLpgSQL_stmt_loop *new; *************** *** 788,802 **** new->cmd_type = PLPGSQL_STMT_LOOP; new->lineno = $3; new->label = $1; ! new->body = $4; plpgsql_ns_pop(); $$ = (PLpgSQL_stmt *)new; } ; ! stmt_while : opt_label K_WHILE lno expr_until_loop loop_body { PLpgSQL_stmt_while *new; --- 797,812 ---- new->cmd_type = PLPGSQL_STMT_LOOP; new->lineno = $3; new->label = $1; ! new->body = $4.stmts; + check_labels($1, $4.end_label); plpgsql_ns_pop(); $$ = (PLpgSQL_stmt *)new; } ; ! stmt_while : opt_block_label K_WHILE lno expr_until_loop loop_body { PLpgSQL_stmt_while *new; *************** *** 805,819 **** new->lineno = $3; new->label = $1; new->cond = $4; ! new->body = $5; plpgsql_ns_pop(); $$ = (PLpgSQL_stmt *)new; } ; ! stmt_for : opt_label K_FOR for_control loop_body { /* This runs after we've scanned the loop body */ if ($3->cmd_type == PLPGSQL_STMT_FORI) --- 815,830 ---- new->lineno = $3; new->label = $1; new->cond = $4; ! new->body = $5.stmts; + check_labels($1, $5.end_label); plpgsql_ns_pop(); $$ = (PLpgSQL_stmt *)new; } ; ! stmt_for : opt_block_label K_FOR for_control loop_body { /* This runs after we've scanned the loop body */ if ($3->cmd_type == PLPGSQL_STMT_FORI) *************** *** 822,828 **** new = (PLpgSQL_stmt_fori *) $3; new->label = $1; ! new->body = $4; $$ = (PLpgSQL_stmt *) new; } else if ($3->cmd_type == PLPGSQL_STMT_FORS) --- 833,839 ---- new = (PLpgSQL_stmt_fori *) $3; new->label = $1; ! new->body = $4.stmts; $$ = (PLpgSQL_stmt *) new; } else if ($3->cmd_type == PLPGSQL_STMT_FORS) *************** *** 831,837 **** new = (PLpgSQL_stmt_fors *) $3; new->label = $1; ! new->body = $4; $$ = (PLpgSQL_stmt *) new; } else --- 842,848 ---- new = (PLpgSQL_stmt_fors *) $3; new->label = $1; ! new->body = $4.stmts; $$ = (PLpgSQL_stmt *) new; } else *************** *** 841,850 **** Assert($3->cmd_type == PLPGSQL_STMT_DYNFORS); new = (PLpgSQL_stmt_dynfors *) $3; new->label = $1; ! new->body = $4; $$ = (PLpgSQL_stmt *) new; } /* close namespace started in opt_label */ plpgsql_ns_pop(); } --- 852,862 ---- Assert($3->cmd_type == PLPGSQL_STMT_DYNFORS); new = (PLpgSQL_stmt_dynfors *) $3; new->label = $1; ! new->body = $4.stmts; $$ = (PLpgSQL_stmt *) new; } + check_labels($1, $4.end_label); /* close namespace started in opt_label */ plpgsql_ns_pop(); } *************** *** 1037,1043 **** } ; ! stmt_exit : exit_type lno opt_exitlabel opt_exitcond { PLpgSQL_stmt_exit *new; --- 1049,1055 ---- } ; ! stmt_exit : exit_type lno opt_label opt_exitcond { PLpgSQL_stmt_exit *new; *************** *** 1245,1252 **** } ; ! loop_body : proc_sect K_END K_LOOP ';' ! { $$ = $1; } ; stmt_execsql : execsql_start lno --- 1257,1267 ---- } ; ! loop_body : proc_sect K_END K_LOOP opt_label ';' ! { ! $$.stmts = $1; ! $$.end_label = $4; ! } ; stmt_execsql : execsql_start lno *************** *** 1262,1268 **** } ; ! stmt_dynexecute : K_EXECUTE lno { PLpgSQL_stmt_dynexecute *new; PLpgSQL_expr *expr; --- 1277,1283 ---- } ; ! stmt_dynexecute : K_EXECUTE lno { PLpgSQL_stmt_dynexecute *new; PLpgSQL_expr *expr; *************** *** 1418,1424 **** errmsg("cursor \"%s\" has no arguments", $3->refname))); } ! if (tok != ';') { plpgsql_error_lineno = plpgsql_scanner_lineno(); --- 1433,1439 ---- errmsg("cursor \"%s\" has no arguments", $3->refname))); } ! if (tok != ';') { plpgsql_error_lineno = plpgsql_scanner_lineno(); *************** *** 1596,1602 **** { $$ = plpgsql_read_expression(K_LOOP, "LOOP"); } ; ! opt_label : { plpgsql_ns_push(NULL); $$ = NULL; --- 1611,1617 ---- { $$ = plpgsql_read_expression(K_LOOP, "LOOP"); } ; ! opt_block_label : { plpgsql_ns_push(NULL); $$ = NULL; *************** *** 1608,1621 **** } ; ! opt_exitlabel : ! { $$ = NULL; } | T_LABEL { ! char *name; ! ! plpgsql_convert_ident(yytext, &name, 1); ! $$ = name; } | T_WORD { --- 1623,1637 ---- } ; ! opt_label : ! { ! $$ = NULL; ! } | T_LABEL { ! char *label_name; ! plpgsql_convert_ident(yytext, &label_name, 1); ! $$ = label_name; } | T_WORD { *************** *** 2210,2213 **** --- 2226,2254 ---- errposition(0); } + static void + check_labels(const char *start_label, const char *end_label) + { + if (end_label) + { + if (!start_label) + { + plpgsql_error_lineno = plpgsql_scanner_lineno(); + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("end label \"%s\" specified for unlabelled block", + end_label))); + } + + if (strcmp(start_label, end_label) != 0) + { + plpgsql_error_lineno = plpgsql_scanner_lineno(); + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("end label \"%s\" differs from block's label \"%s\"", + end_label, start_label))); + } + } + } + #include "pl_scan.c" Index: src/test/regress/expected/plpgsql.out =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/test/regress/expected/plpgsql.out,v retrieving revision 1.36 diff -c -r1.36 plpgsql.out *** src/test/regress/expected/plpgsql.out 22 Jun 2005 07:28:47 -0000 1.36 --- src/test/regress/expected/plpgsql.out 1 Jul 2005 11:53:55 -0000 *************** *** 2491,2497 **** (1 row) drop function raise_exprs(); ! -- continue statement create table conttesttbl(idx serial, v integer); NOTICE: CREATE TABLE will create implicit sequence "conttesttbl_idx_seq" for serial column "conttesttbl.idx" insert into conttesttbl(v) values(10); --- 2491,2497 ---- (1 row) drop function raise_exprs(); ! -- continue statement create table conttesttbl(idx serial, v integer); NOTICE: CREATE TABLE will create implicit sequence "conttesttbl_idx_seq" for serial column "conttesttbl.idx" insert into conttesttbl(v) values(10); *************** *** 2532,2538 **** for _i in 1..10 loop begin -- applies to outer loop, not the nested begin block ! continue when _i < 5; raise notice '%', _i; end; end loop; --- 2532,2538 ---- for _i in 1..10 loop begin -- applies to outer loop, not the nested begin block ! continue when _i < 5; raise notice '%', _i; end; end loop; *************** *** 2666,2668 **** --- 2666,2723 ---- drop function continue_test2(); drop function continue_test3(); drop table conttesttbl; + -- verbose end block and end loop + create function end_label1() returns void as $$ + <<blbl>> + begin + <<flbl1>> + for _i in 1 .. 10 loop + exit flbl1; + end loop flbl1; + <<flbl2>> + for _i in 1 .. 10 loop + exit flbl2; + end loop; + end blbl; + $$ language plpgsql; + select end_label1(); + end_label1 + ------------ + + (1 row) + + drop function end_label1(); + -- should fail: undefined end label + create function end_label2() returns void as $$ + begin + for _i in 1 .. 10 loop + exit; + end loop flbl1; + end; + $$ language plpgsql; + ERROR: no such label at or near "flbl1" at character 101 + LINE 5: end loop flbl1; + ^ + -- should fail: end label does not match start label + create function end_label3() returns void as $$ + <<outer_label>> + begin + <<inner_label>> + for _i in 1 .. 10 loop + exit; + end loop outer_label; + end; + $$ language plpgsql; + ERROR: end label "outer_label" differs from block's label "inner_label" + CONTEXT: compile of PL/pgSQL function "end_label3" near line 6 + -- should fail: end label on a block without a start label + create function end_label4() returns void as $$ + <<outer_label>> + begin + for _i in 1 .. 10 loop + exit; + end loop outer_label; + end; + $$ language plpgsql; + ERROR: end label "outer_label" specified for unlabelled block + CONTEXT: compile of PL/pgSQL function "end_label4" near line 5 Index: src/test/regress/sql/plpgsql.sql =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/test/regress/sql/plpgsql.sql,v retrieving revision 1.31 diff -c -r1.31 plpgsql.sql *** src/test/regress/sql/plpgsql.sql 22 Jun 2005 07:28:47 -0000 1.31 --- src/test/regress/sql/plpgsql.sql 1 Jul 2005 11:43:36 -0000 *************** *** 2113,2119 **** select raise_exprs(); drop function raise_exprs(); ! -- continue statement create table conttesttbl(idx serial, v integer); insert into conttesttbl(v) values(10); insert into conttesttbl(v) values(20); --- 2113,2119 ---- select raise_exprs(); drop function raise_exprs(); ! -- continue statement create table conttesttbl(idx serial, v integer); insert into conttesttbl(v) values(10); insert into conttesttbl(v) values(20); *************** *** 2154,2160 **** for _i in 1..10 loop begin -- applies to outer loop, not the nested begin block ! continue when _i < 5; raise notice '%', _i; end; end loop; --- 2154,2160 ---- for _i in 1..10 loop begin -- applies to outer loop, not the nested begin block ! continue when _i < 5; raise notice '%', _i; end; end loop; *************** *** 2232,2234 **** --- 2232,2282 ---- drop function continue_test2(); drop function continue_test3(); drop table conttesttbl; + + -- verbose end block and end loop + create function end_label1() returns void as $$ + <<blbl>> + begin + <<flbl1>> + for _i in 1 .. 10 loop + exit flbl1; + end loop flbl1; + <<flbl2>> + for _i in 1 .. 10 loop + exit flbl2; + end loop; + end blbl; + $$ language plpgsql; + + select end_label1(); + drop function end_label1(); + + -- should fail: undefined end label + create function end_label2() returns void as $$ + begin + for _i in 1 .. 10 loop + exit; + end loop flbl1; + end; + $$ language plpgsql; + + -- should fail: end label does not match start label + create function end_label3() returns void as $$ + <<outer_label>> + begin + <<inner_label>> + for _i in 1 .. 10 loop + exit; + end loop outer_label; + end; + $$ language plpgsql; + + -- should fail: end label on a block without a start label + create function end_label4() returns void as $$ + <<outer_label>> + begin + for _i in 1 .. 10 loop + exit; + end loop outer_label; + end; + $$ language plpgsql;
Neil Conway <neilc@samurai.com> writes: > BTW, I notice that some but not all the call sites of ereport(ERROR) in > PL/PgSQL's gram.y set plpgsql_error_lineno. Is there a reason for this? Without looking at the code, I think it may be that you only need to set the variable if you want the error to point someplace different than the last "lno" nonterminal. regards, tom lane
Pavel Stehule wrote: > this patch allows optional using label with END and END LOOP. Ending label > has only informational value, but can enhance readability large block and > enhance likeness with Oracle. Reviewed and applied -- thanks for the patch. -Neil