Grammar-problems with pl/pgsql in gram.y

Started by Klaus Regerover 24 years ago11 messages
#1Klaus Reger
K.Reger@gmx.de

Hi,

I want to learn, how the pl/plsql-parser/compiler works. Therefore I planned
to implement a simple ELSIF, like oracle does.

I added the following K_ELSIF branch to gram.y, in the hope that, when ELSIF
is parsed, simply another if-structure in inserted.

---------------------------------------------------------
stmt_else :
{
PLpgSQL_stmts *new;

new = malloc(sizeof(PLpgSQL_stmts));
memset(new, 0, sizeof(PLpgSQL_stmts));
$$ = new;
elog(NOTICE, "empty ELSE detected");
}
| K_ELSIF expr_until_then proc_sect stmt_else
{
PLpgSQL_stmt_if *new;
elog(NOTICE, "ELSIF detected");
new = malloc(sizeof(PLpgSQL_stmt_if));
memset(new, 0, sizeof(PLpgSQL_stmt_if));
new->cmd_type = PLPGSQL_STMT_IF;
// new->lineno = $2;
new->cond = $2;
new->true_body = $3;
new->false_body = $4;

$$ = (PLpgSQL_stmts *)new;

}
| K_ELSE proc_sect
{
$$ = $2;
elog(NOTICE, "ELSE detected (%s)",
strdup(yytext));
}
;
--------------------------------------------------------------

A testprocedure, which looks like that:
--------------------------------------------------------------

DECLARE
iPar1PI ALIAS FOR $1;
iTmp integer;
iResult varchar;

BEGIN

iTmp = iPar1PI;

raise notice '1.0';

if iTmp IS NULL then
raise notice '2.0';
iResult = 'Echt NULL';

else
if iTmp = 0 then
raise notice '2.1.0';
iResult = 'Null (0)';

elsif (iTmp < 0) THEN
raise notice '2.1.1';
iResult = 'Negativ';

elsif (iTmp > 0) THEN
raise notice '2.1.2';
iResult = 'Positiv';

else
raise notice '2.1.3';
iResult = 'Gibts nicht!';
end if;
end if;

raise notice '3.0';

return iResult;
END;
--------------------------------------------------------------

is dumped in this way ...

--------------------------------------------------------------
Execution tree of successfully compiled PL/pgSQL function kr_test:

Functions data area:
entry 0: VAR $1 type int4 (typoid 23) atttypmod -1
entry 1: VAR found type bool (typoid 16) atttypmod -1
entry 2: VAR itmp type int4 (typoid 23) atttypmod -1
entry 3: VAR iresult type varchar (typoid 1043) atttypmod -1

Functions statements:
8:BLOCK <<*unnamed*>>
10: ASSIGN var 2 := 'SELECT $1 {$1=0}'
12: RAISE ''1.0''
14: IF 'SELECT $1 IS NULL {$1=2}' THEN
15: RAISE ''2.0''
16: ASSIGN var 3 := 'SELECT 'Echt NULL''
ELSE
19: IF 'SELECT $1 = 0 {$1=2}' THEN
20: RAISE ''2.1.0''
21: ASSIGN var 3 := 'SELECT 'Null (0)''
ELSE
ENDIF
ENDIF
37: RAISE ''3.0''
39: RETURN 'SELECT $1 {$1=3}'
END -- *unnamed*

End of execution tree of function kr_test
--------------------------------------------------------------

So my question is: Why does my inserted
PLpgSQL_stmt_if *new;
is not executed, because I do it in the same way like stmt_if does?

Who can halp me (Maybe Jan??)

Regards, Klaus

#2Jan Wieck
JanWieck@Yahoo.com
In reply to: Klaus Reger (#1)
Re: Grammar-problems with pl/pgsql in gram.y

Klaus Reger wrote:

Hi,

I want to learn, how the pl/plsql-parser/compiler works. Therefore I planned
to implement a simple ELSIF, like oracle does.

I added the following K_ELSIF branch to gram.y, in the hope that, when ELSIF
is parsed, simply another if-structure in inserted.

---------------------------------------------------------
stmt_else :
{
PLpgSQL_stmts *new;

new = malloc(sizeof(PLpgSQL_stmts));
memset(new, 0, sizeof(PLpgSQL_stmts));
$$ = new;
elog(NOTICE, "empty ELSE detected");
}
| K_ELSIF expr_until_then proc_sect stmt_else
{
PLpgSQL_stmt_if *new;
elog(NOTICE, "ELSIF detected");
new = malloc(sizeof(PLpgSQL_stmt_if));
memset(new, 0, sizeof(PLpgSQL_stmt_if));
new->cmd_type = PLPGSQL_STMT_IF;
// new->lineno = $2;
new->cond = $2;
new->true_body = $3;
new->false_body = $4;

$$ = (PLpgSQL_stmts *)new;

Here it is. stmt_else is defined as type <stmts>, not <stmt>.
The PLpgSQL_stmt_if struct has a condition query and two
statement lists (type <stmts>). You're trying to put a single
statement into the else part instead of a list of statements.

Maybe it'll work if you surround it with another
PLpgSQL_stmts struct where your new PLpgSQL_stmt_if is the
only statement in it's list. Since I have some bigger work
outstanding for PL/pgSQL, send the resulting patch (if you
get it to work) directly to me.

Jan

--

#======================================================================#
# It's easier to get forgiveness for being wrong than for being right. #
# Let's break this rule - forgive me. #
#================================================== JanWieck@Yahoo.com #

_________________________________________________________
Do You Yahoo!?
Get your free @yahoo.com address at http://mail.yahoo.com

#3Klaus Reger
K.Reger@gmx.de
In reply to: Jan Wieck (#2)
Re: Grammar-problems with pl/pgsql in gram.y

Am Mittwoch, 16. Mai 2001 16:10 schrieb Jan Wieck:

Here it is. stmt_else is defined as type <stmts>, not <stmt>.
The PLpgSQL_stmt_if struct has a condition query and two
statement lists (type <stmts>). You're trying to put a single
statement into the else part instead of a list of statements.

Thank you for the hint! That was it.

Maybe it'll work if you surround it with another
PLpgSQL_stmts struct where your new PLpgSQL_stmt_if is the
only statement in it's list. Since I have some bigger work
outstanding for PL/pgSQL, send the resulting patch (if you
get it to work) directly to me.

The patch follows this message. May you tell me what kind of work it is,
cause I'm so curous :-). By the way, the next thing I try is a
EXCEPTION WHEN OTHER-clause, like in Oracle. Let's look if I'm successful.

Ciao, Klaus

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

diff -Naurb src/gram.y src.elsif/gram.y
--- src/gram.y	Wed May 16 18:00:53 2001
+++ src.elsif/gram.y	Wed May 16 17:39:19 2001
@@ -147,6 +147,7 @@
 %token	K_DIAGNOSTICS
 %token	K_DOTDOT
 %token	K_ELSE
+%token	K_ELSIF
 %token	K_END
 %token	K_EXCEPTION
 %token	K_EXECUTE
@@ -544,6 +545,7 @@
 								new->stmts[0] = (struct PLpgSQL_stmt *)$1;

$$ = new;
+
}
;

@@ -721,8 +723,53 @@
 							memset(new, 0, sizeof(PLpgSQL_stmts));
 							$$ = new;
 					}
+				| K_ELSIF lno expr_until_then proc_sect stmt_else
+					{
+					  /*
+					   * Translate the structure:	   into:
+					   *
+					   * IF c1 THEN					   IF c1 THEN		 
+					   *	 ...						   ...				 
+					   * ELSIF c2 THEN				   ELSE 
+					   *								   IF c2 THEN	
+					   *	 ...							   ...				 
+					   * ELSE							   ELSE				 
+					   *	 ...							   ...				 
+					   * END IF							   END IF			 
+					   *							   END IF
+					   * 
+					   */
+
+						PLpgSQL_stmts	*new;
+						PLpgSQL_stmt_if *new_if;
+
+						/* first create a new if-statement */
+						new_if = malloc(sizeof(PLpgSQL_stmt_if));
+						memset(new_if, 0, sizeof(PLpgSQL_stmt_if));
+
+						new_if->cmd_type	= PLPGSQL_STMT_IF;
+						new_if->lineno		= $2;
+						new_if->cond		= $3;
+						new_if->true_body	= $4;
+						new_if->false_body	= $5;
+						
+						/* this is a 'container' for the if-statement */
+						new = malloc(sizeof(PLpgSQL_stmts));
+						memset(new, 0, sizeof(PLpgSQL_stmts));
+						
+						new->stmts_alloc = 64;
+						new->stmts_used	 = 1;
+						new->stmts = malloc(sizeof(PLpgSQL_stmt *) * new->stmts_alloc);
+						new->stmts[0] = (struct PLpgSQL_stmt *)new_if;
+
+						$$ = new;
+						
+					}
+
 				| K_ELSE proc_sect
-					{ $$ = $2; }
+					{
+						$$ = $2;				
+					}
 				;
 stmt_loop		: opt_label K_LOOP lno loop_body
@@ -1271,7 +1318,6 @@
 				break;
 		}
 	}
-
 	expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
 	expr->dtype			= PLPGSQL_DTYPE_EXPR;
 	expr->query			= strdup(plpgsql_dstring_get(&ds));
diff -Naurb src/scan.l src.elsif/scan.l
--- src/scan.l	Wed May 16 18:01:36 2001
+++ src.elsif/scan.l	Tue May 15 12:49:43 2001
@@ -99,6 +99,7 @@
 default			{ return K_DEFAULT;			}
 diagnostics		{ return K_DIAGNOSTICS;		}
 else			{ return K_ELSE;			}
+elsif           { return K_ELSIF;           }
 end				{ return K_END;				}
 exception		{ return K_EXCEPTION;		}
 execute			{ return K_EXECUTE;			}
#4Jan Wieck
JanWieck@Yahoo.com
In reply to: Klaus Reger (#3)
Re: Grammar-problems with pl/pgsql in gram.y

Klaus Reger wrote:

Am Mittwoch, 16. Mai 2001 16:10 schrieb Jan Wieck:

Here it is. stmt_else is defined as type <stmts>, not <stmt>.
The PLpgSQL_stmt_if struct has a condition query and two
statement lists (type <stmts>). You're trying to put a single
statement into the else part instead of a list of statements.

Thank you for the hint! That was it.

Maybe it'll work if you surround it with another
PLpgSQL_stmts struct where your new PLpgSQL_stmt_if is the
only statement in it's list. Since I have some bigger work
outstanding for PL/pgSQL, send the resulting patch (if you
get it to work) directly to me.

The patch follows this message. May you tell me what kind of work it is,
cause I'm so curous :-). By the way, the next thing I try is a
EXCEPTION WHEN OTHER-clause, like in Oracle. Let's look if I'm successful.

complete CURSOR support. With some enhancements in SPI plus a
little fix in ProcessPortalFetch() I have up to now

Explicit CURSOR:

DECLARE
<cursor_name> CURSOR [(<arg> <type> [, ...])]
IS <select_statement>;

The select statement can use any so far declared
variable or functions arguments in addition to the
optional cursor arguments. These will be evaluated at
OPEN time.

There is a new datatype 'refcursor'. The above
declaration will create a local variable of that type
with a default "value" of the variables name. This
"value" will be used for the global cursors name
during OPEN.

BEGIN
OPEN <cursor_name> [(<expression> [, ...])];

FETCH <cursor_name>
INTO {<record> | <row> | <var> [, ...]};

CLOSE <cursor_name>;

The FETCH command sets the global FOUND variable, so
a typical loop over a resultset looks like

LOOP
FETCH mycur INTO myrec;
EXIT WHEN NOT FOUND;
-- Process the row
END LOOP;

Reference CURSOR:

DECLARE
<cursor_name> REFCURSOR [ := <string> ];

BEGIN
OPEN <cursor_name> FOR <select_statement>;

OPEN <cursor_name> FOR EXECUTE <string>;

The new datatype 'refcursor' can be used to pass any cursor
around between functions and the application. Cursors used
inside of functions only don't need transaction blocks. Of
course, they'll not survive the current transactions end, but
if funcA() creates a cursor and then calls funcB(refcursor)
with it, there does not need to be a transaction block around
it.

What I need to do now is fixing some memory allocation issues
in PL/pgSQL and move FOR loops to use implicit cursors
internally. Your patch looks like it doesn't conflict with
any of my work. I'll commit it soon.

For the EXCEPTIONS thing, well that's another issue. We could
of course simulate/generate some of the exceptions like "no
data found" and the other one I forgot (telling that a SELECT
INTO returned multiple results). But we cannot catch a
duplicate key error, a division by zero or a referential
integrity violation, because when it happens a statement is
half way done and the only way to cleanup is rolling back the
entire transaction (for now, Vadim is working on savepoints).
So I suggest you don't spend much of your time before we have
them.

Jan

--

#======================================================================#
# It's easier to get forgiveness for being wrong than for being right. #
# Let's break this rule - forgive me. #
#================================================== JanWieck@Yahoo.com #

_________________________________________________________
Do You Yahoo!?
Get your free @yahoo.com address at http://mail.yahoo.com

#5Klaus Reger
K.Reger@gmx.de
In reply to: Jan Wieck (#4)
Re: Grammar-problems with pl/pgsql in gram.y

Am Mittwoch, 16. Mai 2001 21:29 schrieb Jan Wieck:

For the EXCEPTIONS thing, well that's another issue. We could
of course simulate/generate some of the exceptions like "no
data found" and the other one I forgot (telling that a SELECT
INTO returned multiple results). But we cannot catch a
duplicate key error, a division by zero or a referential
integrity violation, because when it happens a statement is
half way done and the only way to cleanup is rolling back the
entire transaction (for now, Vadim is working on savepoints).
So I suggest you don't spend much of your time before we have
them.

OK, I understand. For the beginning I only would like to have a possibility,
to catch any exception and create my own error handling, ignoring any
transaction-stuff. Because I have to port Procedures from Oracle to
PostgreSQL, I am looking, to imitate the way Oracle takes.

As I understand with my actual knowledge, this would mean, that every(!) call
of elog, which terminates the process, has to be caught. But this seems to
great for a new Postgres-hacker, like I am. Or do you see any other
possibility (maybe extending PLpgSQL_execstate)?

CU, Klaus

#6Jan Wieck
JanWieck@Yahoo.com
In reply to: Klaus Reger (#5)
Re: Grammar-problems with pl/pgsql in gram.y

Klaus Reger wrote:

Am Mittwoch, 16. Mai 2001 21:29 schrieb Jan Wieck:

For the EXCEPTIONS thing, well that's another issue. We could
of course simulate/generate some of the exceptions like "no
data found" and the other one I forgot (telling that a SELECT
INTO returned multiple results). But we cannot catch a
duplicate key error, a division by zero or a referential
integrity violation, because when it happens a statement is
half way done and the only way to cleanup is rolling back the
entire transaction (for now, Vadim is working on savepoints).
So I suggest you don't spend much of your time before we have
them.

OK, I understand. For the beginning I only would like to have a possibility,
to catch any exception and create my own error handling, ignoring any
transaction-stuff. Because I have to port Procedures from Oracle to
PostgreSQL, I am looking, to imitate the way Oracle takes.

As I understand with my actual knowledge, this would mean, that every(!) call
of elog, which terminates the process, has to be caught. But this seems to
great for a new Postgres-hacker, like I am. Or do you see any other
possibility (maybe extending PLpgSQL_execstate)?

Every(!) call to elog with ERROR (or more severe) level
causes finally a longjump() back into the tcop mainloop.
PL/pgSQL and PL/Tcl do catch it - PL/pgSQL to tell something
on DEBUG level and PL/Tcl mainly to unwind the Tcl
interpreters call stack. But the backend is in an
inconsistent state at that time, and any subsequent call to
access methods could cause unpredictable results up to
complete database corruption. There is no other way right now
than to go ahead and continue with transaction abort.

Imitation of the Oracle way is something many ppl around here
would appreciate, but not at the risk of corrupting the
entire database - that's too high a price.

That said, you'll have little to no chance of getting this
feature applied to the CVS. Doing EXCEPTIONS requires
savepoints and a real "back to statements start state"
functionality. The recent approach of "simulating CURSOR"
just on the PL grammar level without real cursor support in
the SPI layer failed for exactly the same reason. Before we
know "how" to do it, we cannot decide on the exact
appearence, because implementation details might "require" at
least some difference to other databases. If we accept such a
"fake" only approach, we introduce backward compatibility
problems, since somebody will tell us for sure "I used the
syntax of 7.2 already and porting my +20K line application
now ...". In PostgreSQL it's easier to get new *features*
added than existing features ripped out - and people rely on
that.

Jan

PS:
Aber la� mal den Kopf nicht h�ngen, wir finden bestimmt was
wo Du Dich austoben kannst :-)

--

#======================================================================#
# It's easier to get forgiveness for being wrong than for being right. #
# Let's break this rule - forgive me. #
#================================================== JanWieck@Yahoo.com #

_________________________________________________________
Do You Yahoo!?
Get your free @yahoo.com address at http://mail.yahoo.com

#7Jan Wieck
JanWieck@Yahoo.com
In reply to: Klaus Reger (#3)
Re: Grammar-problems with pl/pgsql in gram.y

Klaus Reger wrote:

Am Mittwoch, 16. Mai 2001 16:10 schrieb Jan Wieck:

Here it is. stmt_else is defined as type <stmts>, not <stmt>.
The PLpgSQL_stmt_if struct has a condition query and two
statement lists (type <stmts>). You're trying to put a single
statement into the else part instead of a list of statements.

Thank you for the hint! That was it.

Maybe it'll work if you surround it with another
PLpgSQL_stmts struct where your new PLpgSQL_stmt_if is the
only statement in it's list. Since I have some bigger work
outstanding for PL/pgSQL, send the resulting patch (if you
get it to work) directly to me.

The patch follows this message. May you tell me what kind of work it is,
cause I'm so curous :-). By the way, the next thing I try is a
EXCEPTION WHEN OTHER-clause, like in Oracle. Let's look if I'm successful.

Patch applied.

Really a smart solution, just touching the gram.y and scan.l
files and using the existing instruction code. Thanks for
the contribution.

What about a CASE ... WHEN ...? I think that could be
implemented in a similar way. Don't do it right now, I might
be ready to commit the cursor stuff early next week and it'll
touch alot more than just the grammar and scanner.

Jan

--

#======================================================================#
# It's easier to get forgiveness for being wrong than for being right. #
# Let's break this rule - forgive me. #
#================================================== JanWieck@Yahoo.com #

_________________________________________________________
Do You Yahoo!?
Get your free @yahoo.com address at http://mail.yahoo.com

#8Tom Lane
tgl@sss.pgh.pa.us
In reply to: Jan Wieck (#7)
Re: Grammar-problems with pl/pgsql in gram.y

Jan Wieck <JanWieck@Yahoo.com> writes:

Patch applied.

How about some documentation?

regards, tom lane

#9Klaus Reger
K.Reger@gmx.de
In reply to: Tom Lane (#8)
Re: Grammar-problems with pl/pgsql in gram.y

Am Samstag, 19. Mai 2001 00:07 schrieb Tom Lane:

Jan Wieck <JanWieck@Yahoo.com> writes:

Patch applied.

How about some documentation?

regards, tom lane

Good idea! It will follow.

Regards, Klaus

#10Klaus Reger
K.Reger@twc.de
In reply to: Tom Lane (#8)
1 attachment(s)
Re: Grammar-problems with pl/pgsql in gram.y

Am Samstag, 19. Mai 2001 00:07 schrieben Sie:

How about some documentation?

Her is some documentation. Because I don't have the tools and scripts
installed, to format sgml, I never have written SGML-docs and my english is
bad, please revisit and correct the additions.

Thank you

Klaus

--
TWC GmbH
Schlossbergring 9
79098 Freiburg i. Br.
http://www.twc.de

Attachments:

elsif_doc.difftext/plain; charset=iso-8859-1; name=elsif_doc.diffDownload
Index: plsql.sgml
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/src/sgml/plsql.sgml,v
retrieving revision 2.32
diff -w -b -i -B -c -r2.32 plsql.sgml
*** plsql.sgml	2001/05/17 21:50:16	2.32
--- plsql.sgml	2001/05/22 12:38:49
***************
*** 880,891 ****
      <title>Conditional Control: IF statements</title>
  
      <para>
!      <function>IF</function> statements let you take action
!      according to certain conditions. PL/pgSQL has three forms of
!      IF: IF-THEN, IF-THEN-ELSE, IF-THEN-ELSE IF. NOTE: All
!      PL/pgSQL IF statements need a corresponding <function>END
!      IF</function> statement. In ELSE-IF statements you need two:
!      one for the first IF and one for the second (ELSE IF).
      </para>
  
      <variablelist>
--- 880,890 ----
      <title>Conditional Control: IF statements</title>
  
      <para>
!      <function>IF</function> statements let you take action according to
!       certain conditions. PL/pgSQL has four forms of IF: IF-THEN, IF-THEN-ELSE,
!       IF-THEN-ELSE IF, IF-THEN-ELSIF-THEN-ELSE. NOTE: All PL/pgSQL IF statements need
!       a corresponding <function>END IF</function> statement. In ELSE-IF statements
!       you need two: one for the first IF and one for the second (ELSE IF).
      </para>
  
      <variablelist>
***************
*** 979,984 ****
--- 978,1018 ----
         </para>
        </listitem>
       </varlistentry>
+ 
+      <varlistentry>
+       <term>
+        IF-THEN-ELSIF-ELSE
+       </term>
+ 
+       <listitem>
+        <para>
+         IF-THEN-ELSIF-ELSE allows you, to add multiple decisions
+         in one statement. Internally it is handled as nested 
+         IF-THEN-ELSE-IF-THEN commands. The (optionally) ELSE
+         branch is called, when none of all conditions is met.
+        </para>
+ 
+        <para>
+         Here is an example:
+        </para>
+ 
+ <programlisting>
+ IF number = 0 THEN
+     result := ''zero'';
+ ELSIF number &lt; 0 THEN
+     result := ''negative'';
+ ELSIF number &gt; 0 THEN 
+     result := ''negative'';
+ ELSE
+     -- now it seems to be NULL
+     result := ''NULL'';
+ END IF;
+ </programlisting>
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+ 
      </variablelist>
     </sect3>
  
#11Bruce Momjian
pgman@candle.pha.pa.us
In reply to: Klaus Reger (#10)
1 attachment(s)
Re: Grammar-problems with pl/pgsql in gram.y

Here is the patch I applied. I cleaned it up a bit. Thanks a lot.

[ Charset ISO-8859-1 unsupported, converting... ]

Am Samstag, 19. Mai 2001 00:07 schrieben Sie:

How about some documentation?

Her is some documentation. Because I don't have the tools and scripts
installed, to format sgml, I never have written SGML-docs and my english is
bad, please revisit and correct the additions.

Thank you

Klaus

--
TWC GmbH
Schlossbergring 9
79098 Freiburg i. Br.
http://www.twc.de

[ Attachment, skipping... ]

---------------------------(end of broadcast)---------------------------
TIP 2: you can get off all lists at once with the unregister command
(send "unregister YourEmailAddressHere" to majordomo@postgresql.org)

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 853-3000
  +  If your life is a hard drive,     |  830 Blythe Avenue
  +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026

Attachments:

/bjm/difftext/plainDownload
Index: plsql.sgml
===================================================================
RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/src/sgml/plsql.sgml,v
retrieving revision 2.32
diff -w -b -i -B -c -r2.32 plsql.sgml
*** plsql.sgml	2001/05/17 21:50:16	2.32
--- plsql.sgml	2001/05/22 12:38:49
***************
*** 880,891 ****
      <title>Conditional Control: IF statements</title>
  
      <para>
!      <function>IF</function> statements let you take action
!      according to certain conditions. PL/pgSQL has three forms of
!      IF: IF-THEN, IF-THEN-ELSE, IF-THEN-ELSE IF. NOTE: All
!      PL/pgSQL IF statements need a corresponding <function>END
!      IF</function> statement. In ELSE-IF statements you need two:
!      one for the first IF and one for the second (ELSE IF).
      </para>
  
      <variablelist>
--- 880,890 ----
      <title>Conditional Control: IF statements</title>
  
      <para>
! 	<function>IF</function> statements let you execute commands based on
!       certain conditions. PL/PgSQL has four forms of IF: IF-THEN, IF-THEN-ELSE,
!       IF-THEN-ELSE IF, IF-THEN-ELSIF-THEN-ELSE. NOTE: All PL/PgSQL IF statements need
! 	a corresponding <function>END IF</function> clause. With ELSE-IF statements,
!       you need two: one for the first IF and one for the second (ELSE IF).
      </para>
  
      <variablelist>
***************
*** 979,984 ****
--- 978,1018 ----
         </para>
        </listitem>
       </varlistentry>
+ 
+      <varlistentry>
+       <term>
+        IF-THEN-ELSIF-ELSE
+       </term>
+ 
+       <listitem>
+        <para>
+         IF-THEN-ELSIF-ELSE allows you test multiple conditions
+         in one statement. Internally it is handled as nested 
+         IF-THEN-ELSE-IF-THEN commands. The optional ELSE
+         branch is executed when none of the conditions are met.
+        </para>
+ 
+        <para>
+         Here is an example:
+        </para>
+ 
+ <programlisting>
+ IF number = 0 THEN
+     result := ''zero'';
+ ELSIF number &lt; 0 THEN
+     result := ''negative'';
+ ELSIF number &gt; 0 THEN 
+     result := ''negative'';
+ ELSE
+     -- now it seems to be NULL
+     result := ''NULL'';
+ END IF;
+ </programlisting>
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+ 
      </variablelist>
     </sect3>