Thread: Grammar-problems with pl/pgsql in gram.y

Grammar-problems with pl/pgsql in gram.y

From
Klaus Reger
Date:
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
typebool (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}' THEN15:    RAISE ''2.0''16:    ASSIGN var 3 := 'SELECT  'Echt NULL''     ELSE19:    IF 'SELECT   $1
= 0 {$1=2}' THEN20:      RAISE ''2.1.0''21:      ASSIGN var 3 := 'SELECT  'Null (0)''       ELSE       ENDIF
ENDIF37: 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


Re: Grammar-problems with pl/pgsql in gram.y

From
Jan Wieck
Date:
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
listof 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



Re: Grammar-problems with pl/pgsql in gram.y

From
Klaus Reger
Date:
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        {
returnK_EXCEPTION;        }execute            { return K_EXECUTE;            }
 


Re: Grammar-problems with pl/pgsql in gram.y

From
Jan Wieck
Date:
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
additionto 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
thattype           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.
Cursorsused   inside of functions only don't need  transaction  blocks.  Of   course, they'll not survive the current
transactionsend, but   if funcA() creates a cursor and then  calls  funcB(refcursor)   with it, there does not need to
bea 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
implicitcursors   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
happensa  statement  is   half way done and the only way to cleanup is rolling back the   entire transaction (for now,
Vadimis 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



Re: Grammar-problems with pl/pgsql in gram.y

From
Klaus Reger
Date:
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


Re: Grammar-problems with pl/pgsql in gram.y

From
Jan Wieck
Date:
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,
andany 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
thesame reason.  Before  we   know   "how"  to  do  it,  we  cannot  decide  on  the  exact   appearence, because
implementationdetails 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
syntaxof 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



Re: Grammar-problems with pl/pgsql in gram.y

From
Jan Wieck
Date:
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



Re: Grammar-problems with pl/pgsql in gram.y

From
Tom Lane
Date:
Jan Wieck <JanWieck@Yahoo.com> writes:
>     Patch applied.

How about some documentation?
        regards, tom lane


Re: Grammar-problems with pl/pgsql in gram.y

From
Klaus Reger
Date:
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


Re: Grammar-problems with pl/pgsql in gram.y

From
Klaus Reger
Date:
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

Re: Grammar-problems with pl/pgsql in gram.y

From
Bruce Momjian
Date:
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
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 < 0 THEN
+     result := ''negative'';
+ ELSIF number > 0 THEN
+     result := ''negative'';
+ ELSE
+     -- now it seems to be NULL
+     result := ''NULL'';
+ END IF;
+ </programlisting>
+        </para>
+       </listitem>
+      </varlistentry>
+
+
      </variablelist>
     </sect3>