Repair.
authorPeter Eisentraut
Sat, 17 Mar 2001 18:08:14 +0000 (18:08 +0000)
committerPeter Eisentraut
Sat, 17 Mar 2001 18:08:14 +0000 (18:08 +0000)
doc/src/sgml/plsql.sgml

index 1313e759c6b1f28f12d6ea469893baf2be947d6e..3f162f6f50af192060a8bb1ebb94a2b7df7af8db 100644 (file)
@@ -1,9 +1,9 @@
 
 
  
-  PL/pgSQL - <acronym>SQL<acronym> Procedural Language
+  PL/pgSQL - <acronym>SQL<<span class="marked">/</span>acronym> Procedural Language
 
  
   PL/pgSQL is a loadable procedural language for the
@@ -12,8 +12,8 @@ $Header: /cvsroot/pgsql/doc/src/sgml/Attic/plsql.sgml,v 2.23 2001/03/17 01:53:22
    
  
   This package was originally written by Jan Wieck. This
-  documentation was re-organized and in part written 
-  by Roberto Mello ([email protected]).
+  documentation was in part written 
+  by Roberto Mello ([email protected]).
  
 
   
@@ -80,20 +80,23 @@ $Header: /cvsroot/pgsql/doc/src/sgml/Attic/plsql.sgml,v 2.23 2001/03/17 01:53:22
   This means that you have to be careful about your user-defined
   functions. For example:
 
- 
+
 CREATE FUNCTION populate() RETURNS INTEGER AS '
 DECLARE
     -- Declarations
 BEGIN
     PERFORM my_function();
 END;
-' language 'plpgsql';
-    If you CREATE the above function, it will reference the ID for
-    my_function() in its bytecode. Later, if you DROP and re-CREATE 
-  my_function(), populate() will not be able to find my_function()
-    anymore. You'll have to re-CREATE populate().
+' LANGUAGE 'plpgsql';
+
+    If you create the above function, it will reference the OID for
+    my_function() in its bytecode. Later, if you
+    drop and re-create my_function(), then
+    populate() will not be able to find
+    my_function() anymore. You would then have to
+    re-create populate().
    
+
    
     Because PL/pgSQL saves execution plans in this way, queries that appear
     directly in a PL/pgSQL function must refer to the same tables and fields
@@ -116,23 +119,26 @@ END;
    
     
      
-      Better performance
+      Better performance (see )
      
     
+
     
      
-      SQL Support
+      SQL support (see )
      
      
+
     
      
-      Portability
+      Portability (see )
      
     
    
 
    
     Better Performance
+
     
      SQL is the language PostgreSQL (and
      most other Relational Databases) use as query
@@ -140,6 +146,7 @@ END;
      SQL statement must be executed
      individually by the database server.
     
+
     
      That means that your client application must send each
      query to the database server, wait for it to process it,
@@ -149,6 +156,7 @@ END;
      overhead if your client is on a different machine than
      the database server.
     
+
     
      With PL/pgSQL you can group a block of computation and a
      series of queries inside the
@@ -159,8 +167,10 @@ END;
      considerable performance increase by using PL/pgSQL. 
     
    
+
    
     SQL Support
+
     
      PL/pgSQL adds the power of a procedural language to the
      flexibility and ease of SQL. With
@@ -168,8 +178,10 @@ END;
      and functions of SQL.    
     
    
+
    
     Portability
+
     
      Because PL/pgSQL functions run inside PostgreSQL, these
      functions will run on any platform where PostgreSQL
@@ -186,12 +198,14 @@ END;
     if you have developed in other database procedural languages,
     such as Oracle's PL/SQL. Two good ways of developing in
     PL/pgSQL are:
+
     
      
       
        Using a text editor and reloading the file with psql
       
      
+
      
       
        Using PostgreSQL's GUI Tool: pgaccess
@@ -199,6 +213,7 @@ END;
      
     
    
+
    
     One good way to develop in PL/pgSQL is to simply use the text
     editor of your choice to create your functions, and in another
@@ -208,33 +223,33 @@ END;
     always DROP your function before creating it. That way
     when you reload the file, it'll drop your functions and then
     re-create them. For example:
-   
-   
+
 drop function testfunc(integer);
 create function testfunc(integer) return integer as '
     ....
 end;
 ' language 'plpgsql';
+
+   
 
+   
     When you load the file for the first time,
     PostgreSQL will raise a warning saying this
     function doesn't exist and go on to create it. To load an SQL
     file (filename.sql) into a database named "dbname", use the command:
-   
-   
+
 psql -f filename.sql dbname
+
+   
 
+   
     Another good way to develop in PL/pgSQL is using
     PostgreSQL's GUI tool: pgaccess. It does some
     nice things for you, like escaping single-quotes, and making
     it easy to recreate and debug functions.
    
   
 
 
   
 
@@ -247,31 +262,31 @@ psql -f filename.sql dbname
     Structure of PL/pgSQL
 
     
-   PL/pgSQL is a block structured, case
-   insensitive language. All keywords and identifiers can be 
-   used in mixed upper- and lower-case. A block is defined as:
-  
+     PL/pgSQL is a block structured language. All
+     keywords and identifiers can be used in mixed upper and
+     lower-case. A block is defined as:
 
- 
+
 <<label>>
 DECLARE
     declarations
 BEGIN
     statements
 END;
+
+    
 
     
      There can be any number of sub-blocks in the statement section
      of a block. Sub-blocks can be used to hide variables from outside a
      block of statements.
     
+
     
-   The variables declared in the declarations section preceding a
-   block are initialized to their default values every time the
-   block is entered, not only once per function call. For example:
-    
+     The variables declared in the declarations section preceding a
+     block are initialized to their default values every time the
+     block is entered, not only once per function call. For example:
+
 CREATE FUNCTION somefunc() RETURNS INTEGER AS '
 DECLARE
    quantity INTEGER := 30;
@@ -289,8 +304,9 @@ BEGIN
 
    RAISE NOTICE ''Quantity here is %'',quantity;  -- Quantity here is 50
 END;
-' language 'plpgsql';
+' LANGUAGE 'plpgsql';
+
+    
 
     
      It is important not to confuse the use of BEGIN/END for
@@ -325,31 +341,34 @@ END;
      sub-blocks must be declared in the declarations section of a block.
      The exception being the loop variable of a FOR loop iterating over a range
      of integer values. 
-  
-  
+    
+
+    
      PL/pgSQL variables can have any SQL datatype, such as
      INTEGERVARCHAR and
      CHAR. All variables have as default value the
      SQL NULL value. 
     
-  
+
+    
      Here are some examples of variable declarations:
-  
+
 user_id INTEGER;
 quantity NUMBER(5);
 url VARCHAR;
+
+    
 
     
      Constants and Variables With Default Values
 
      
       The declarations have the following syntax:
-     
+
 name  CONSTANT  type  NOT NULL   { DEFAULT | := } value ;
+
+     
+
      
       The value of variables declared as CONSTANT cannot be changed. If NOT NULL
       is specified, an assignment of a NULL value results in a runtime
@@ -357,6 +376,7 @@ url VARCHAR;
       SQL NULL value, all variables declared as NOT NULL
       must also have a default value specified.
      
+
      
       The default value is evaluated every time the function is called. So
       assigning 'now' to a variable of type
@@ -364,14 +384,15 @@ url VARCHAR;
       time of the actual function call, not when the function was
       precompiled into its bytecode.
      
+
      
       Examples:
-     
+
 quantity INTEGER := 32;
 url varchar := ''http://mysite.com'';
 user_id CONSTANT INTEGER := 10;
-     
+
+     
     
 
     
@@ -381,15 +402,14 @@ user_id CONSTANT INTEGER := 10;
       Variables passed to functions are named with the identifiers
       $1$2,
       etc. (maximum is 16). Some examples:
-     
+
 CREATE FUNCTION sales_tax(REAL) RETURNS REAL AS '
 DECLARE
     subtotal ALIAS FOR $1;
 BEGIN
     return subtotal * 0.06;
 END;
-' language 'plpgsql';
+' LANGUAGE 'plpgsql';
 
 
 CREATE FUNCTION instr(VARCHAR,INTEGER) RETURNS INTEGER AS '
@@ -399,8 +419,9 @@ DECLARE
 BEGIN
     -- Some computations here
 END;
-' language 'plpgsql';
-     
+' LANGUAGE 'plpgsql';
+
+     
     
 
     
@@ -427,10 +448,11 @@ END;
          named user_id in your
          users table. To declare a variable with
          the same datatype as users you do:
-        
+
 user_id users.user_id%TYPE;
+
+        
+
         
          By using %TYPE you don't need to know
          the datatype of the structure you are referencing,
@@ -449,22 +471,23 @@ user_id users.user_id%TYPE;
        
        
         
- Declares a row with the structure of the given table. table must be
- an existing table or view name of the database. The fields of the row
- are accessed in the dot notation. Parameters to a function can
- be composite types (complete table rows). In that case, the
- corresponding identifier $n will be a rowtype, but it
- must be aliased using the ALIAS command described above.
+         Declares a row with the structure of the given
+    table. table must be an existing
+    table or view name of the database. The fields of the row are
+    accessed in the dot notation. Parameters to a function can be
+    composite types (complete table rows). In that case, the
+    corresponding identifier $n will be a rowtype, but it must be
+    aliased using the ALIAS command described above.
         
 
         
-  Only the user
- attributes of a table row are accessible in the row, no OID or other
- system attributes (because the row could be from a view).
- The fields of the rowtype inherit the table's field sizes 
or precision for char() etc. data types.
+         Only the user attributes of a table row are accessible in the
+    row, no OID or other system attributes (because the row could
+    be from a view).  The fields of the rowtype inherit the
+    table's field sizes or precision for char()
   etc. data types.
         
- 
+
 DECLARE
     users_rec users%ROWTYPE;
   user_id users%TYPE;
@@ -493,8 +516,8 @@ create function cs_refresh_one_mv(integer) returns integer as '
 
         return 1;
 end;
-' language 'plpgsql';
-        
+' LANGUAGE 'plpgsql';
+
        
       
      
@@ -504,20 +527,22 @@ end;
      </div> <div class="diff ctx">       RENAME</div> <div class="diff ctx">      
+
      
       Using RENAME you can change the name of a variable, record
       or row. This is useful if NEW or OLD should be referenced
       by another name inside a trigger procedure.
      
+
      
       Syntax and examples:
-     
+
 RENAME oldname TO newname;
 
 RENAME id TO user_id;
 RENAME this_var TO that_var;
-     
+
+     
     
    
 
@@ -535,9 +560,9 @@ RENAME this_var TO that_var;
      it is impossible for the PL/pgSQL parser
      to identify real constant values other than the NULL keyword. All
      expressions are evaluated internally by executing a query
- 
+
 SELECT expression
- 
+
      using the SPI manager. In the expression, occurrences of variable
      identifiers are substituted by parameters and the actual values from
      the variables are passed to the executor in the parameter array. All
@@ -545,13 +570,14 @@ SELECT expression
      saved once.  The only exception to this rule is an EXECUTE statement
      if parsing of a query is needed each time it is encountered.
     
+
     
      The type checking done by the Postgres
      main parser has some side
      effects to the interpretation of constant values. In detail there
      is a difference between what these two functions do:
 
- 
+
 CREATE FUNCTION logfunc1 (text) RETURNS timestamp AS '
     DECLARE
         logtxt ALIAS FOR $1;
@@ -560,11 +586,11 @@ CREATE FUNCTION logfunc1 (text) RETURNS timestamp AS '
         RETURN ''now'';
     END;
 ' LANGUAGE 'plpgsql';
- 
+
 
      and
 
- 
+
 CREATE FUNCTION logfunc2 (text) RETURNS timestamp AS '
     DECLARE
         logtxt ALIAS FOR $1;
@@ -575,7 +601,7 @@ CREATE FUNCTION logfunc2 (text) RETURNS timestamp AS '
         RETURN curtime;
     END;
 ' LANGUAGE 'plpgsql';
- 
+
 
      In the case of logfunc1(), the 
      Postgres main parser knows when 
@@ -588,6 +614,7 @@ CREATE FUNCTION logfunc2 (text) RETURNS timestamp AS '
      backend. Needless to say that this isn't what the
      programmer wanted.
     
+
     
      In the case of logfunc2(), the 
      Postgres main parser does not know
@@ -599,6 +626,7 @@ CREATE FUNCTION logfunc2 (text) RETURNS timestamp AS '
      text_out() and timestamp_in()
      functions for the conversion.
     
+
     
      This type checking done by the Postgres main
      parser got implemented after PL/pgSQL was nearly done.
@@ -608,6 +636,7 @@ CREATE FUNCTION logfunc2 (text) RETURNS timestamp AS '
      variable in the above manner is currently the only way in PL/pgSQL to get
      those values interpreted correctly.
     
+
     
      If record fields are used in expressions or statements, the data types of
      fields should not change between calls of one and the same expression.
@@ -632,9 +661,9 @@ CREATE FUNCTION logfunc2 (text) RETURNS timestamp AS '
     
      An assignment of a value to a variable or row/record field is
      written as:
- 
+
 identifier := expression;
- 
+
 
      If the expressions result data type doesn't match the variables
      data type, or the variable has a size/precision that is known
@@ -644,10 +673,10 @@ CREATE FUNCTION logfunc2 (text) RETURNS timestamp AS '
      result in runtime errors generated by the types input functions.
     
 
- 
+
 user_id := 20;
 tax := subtotal * 0.06;
- 
+
    
 
    
@@ -659,26 +688,25 @@ tax := subtotal * 0.06;
      is to execute a SELECT query or doing an assignment (resulting
      in a PL/pgSQL internal SELECT). 
     
+
     
      But there are cases where someone is not interested in the 
      function's result. In these cases, use the PERFORM
      statement.
-    
+
 PERFORM query
-    
-     executes a SELECT query over the
+
+     This executes a SELECT query over the
      SPI manager and discards the result. Identifiers like local
      variables are still substituted into parameters.
     
- 
+
 PERFORM create_mv(''cs_session_page_requests_mv'',''
      select   session_id, page_id, count(*) as n_hits,
               sum(dwell_time) as dwell_time, count(dwell_time) as dwell_count
      from     cs_fact_table
      group by session_id, page_id '');
- 
+
    
    
    
@@ -690,14 +718,16 @@ PERFORM create_mv(''cs_session_page_requests_mv'',''
      generate other functions. PL/pgSQL provides the EXECUTE
      statement for these occasions.
     
+
     
- 
+
 EXECUTE query-string
-         where query-string is a string of
-         type text containing the query to be
-         executed.
-       
+
+     where query-string is a string of type
+     text containing the query
+     to be executed.
+    
+
     
     When working with dynamic queries you will have to face
     escaping of single quotes in PL/pgSQL. Please refer to the
@@ -705,34 +735,35 @@ EXECUTE query-string
     for a detailed explanation that will save you some effort.
     
      
-       
-        Unlike all other queries in PL/pgSQL, a
-        query run by an EXECUTE statement
-        is not prepared and saved just once during the life of the
-        server.  Instead, the query is
-        prepared each time the statement is run. The
-        query-string can be dynamically created
-        within the procedure to perform actions on variable tables and
-        fields.
-       
+    
+     Unlike all other queries in PL/pgSQL, a
+     query run by an EXECUTE statement is
+     not prepared and saved just once during the life of the server.
+     Instead, the query is prepared each
+     time the statement is run. The
+     query-string can be dynamically
+     created within the procedure to perform actions on variable
+     tables and fields.
+    
   
-       
-        The results from SELECT queries are discarded by EXECUTE, and
-        SELECT INTO is not currently supported within EXECUTE.  So, the
-        only way to extract a result from a dynamically-created SELECT
-        is to use the FOR ... EXECUTE form described later.
-       
+    
+     The results from SELECT queries are discarded by EXECUTE, and
+     SELECT INTO is not currently supported within EXECUTE.  So, the
+     only way to extract a result from a dynamically-created SELECT is
+     to use the FOR ... EXECUTE form described later.
+    
 
-       
-        An example:
- 
+    
+     An example:
+
 EXECUTE ''UPDATE tbl SET ''
         || quote_ident(fieldname)
         || '' = ''
         || quote_literal(newvalue)
         || '' WHERE ...'';
- 
+
     
+
     
      This example shows use of the functions
      quote_ident(TEXT) and
@@ -744,10 +775,11 @@ EXECUTE ''UPDATE tbl SET ''
      quote_literal().  Both take the
      appropriate steps to return the input text enclosed in single
      or double quotes and with any embedded special characters.
-       
+    
+
     
-    Here is a much larger example of a dynamic query and EXECUTE:
- 
+     Here is a much larger example of a dynamic query and EXECUTE:
+
 CREATE FUNCTION cs_update_referrer_type_proc() RETURNS INTEGER AS '
 DECLARE
     referrer_keys RECORD;  -- Declare a generic record to be used in a FOR
@@ -778,46 +810,51 @@ BEGIN
     
     EXECUTE a_output; 
 end; 
-' language 'plpgsql';
- 
+' LANGUAGE 'plpgsql';
+
     
    
 
-     
-      Obtaining other results status
-       
+   
+    Obtaining other results status
+
+    
+
 GET DIAGNOSTICS variable = item  , ... 
- This command allows retrieval of system status indicators.  Each
item is a keyword identifying a state
- value to be assigned to the specified variable (which should be of
- the right datatype to receive it).  The currently available status
- items are ROW_COUNT, the number of rows processed by
- the last SQL query sent down to the SQL engine; and
RESULT_OID, the Oid of the last row inserted by the
- most recent SQL query.  Note that RESULT_OID is only
- useful after an INSERT query.
-       
-     
+
+
+     This command allows retrieval of system status indicators.  Each
+     item is a keyword identifying a state
+     value to be assigned to the specified variable (which should be
+     of the right datatype to receive it).  The currently available
+     status items are ROW_COUNT, the number of rows
+     processed by the last SQL query sent down to
+     the SQL engine; and RESULT_OID,
+     the Oid of the last row inserted by the most recent
+     SQL query.  Note that RESULT_OID
+     is only useful after an INSERT query.
+    
+   
    
    
     Returning from a function
 
     
- 
+
 RETURN expression
- The function terminates and the value of expression
- will be returned to the upper executor. The return value of a function
- cannot be undefined. If control reaches the end of the top-level block
- of the function without hitting a RETURN statement, a runtime error
- will occur.
-       
-       
- The expressions result will be automatically casted into the
- function's return type as described for assignments.
-       
+
+     The function terminates and the value of
+     expression will be returned to the
+     upper executor. The return value of a function cannot be
+     undefined. If control reaches the end of the top-level block of
+     the function without hitting a RETURN statement, a runtime error
+     will occur.
+    
+
+    
+     The expressions result will be automatically casted into the
+     function's return type as described for assignments.
+    
    
   
 
@@ -833,7 +870,7 @@ RETURN expression
     flexible and powerful way. 
    
     
-   trol-structures-conditionals">
+   
     Conditional Control: IF statements
 
     
@@ -850,17 +887,18 @@ RETURN expression
       
        IF-THEN
       
+
       
        
         IF-THEN statements is the simplest form of an IF. The
         statements between THEN and END IF will be executed if
         the condition is true. Otherwise, the statements
         following END IF will be executed.
- 
+
 IF v_user_id <> 0 THEN
     UPDATE users SET email = v_email WHERE user_id = v_user_id;
 END IF;
- 
+
        
       
      
@@ -869,12 +907,13 @@ END IF;
       
        IF-THEN-ELSE
       
+
       
        
         IF-THEN-ELSE statements adds to IF-THEN by letting you
         specify the statements that should be executed if the
         condition evaluates to FALSE.
- 
+
 IF parentid IS NULL or parentid = ''''
 THEN 
     return fullname;
@@ -889,12 +928,13 @@ IF v_count > 0 THEN
 ELSE 
     return ''f'';
 END IF;
-        
+
        
+
        
         IF statements can be nested and in the following
         example:
- 
+
 IF demo_row.sex = ''m'' THEN
   pretty_sex := ''man'';
 ELSE
@@ -902,7 +942,7 @@ ELSE
     pretty_sex := ''woman'';
   END IF;
 END IF;
-        
+
        
       
      
@@ -911,6 +951,7 @@ END IF;
       
        IF-THEN-ELSE IF
       
+
       
        
         When you use the "ELSE IF" statement, you are actually
@@ -918,17 +959,18 @@ END IF;
         statement. Thus you need one END IF statement for each
         nested IF and one for the parent IF-ELSE.
        
+
        
         For example:
- 
+
 IF demo_row.sex = ''m'' THEN
    pretty_sex := ''man'';
 ELSE IF demo_row.sex = ''f'' THEN
         pretty_sex := ''woman'';
      END IF;
 END IF;
- 
-              
+
+       
       
      
     
@@ -942,19 +984,21 @@ END IF;
      control the flow of execution of your PL/pgSQL program
      iteratively.
     
+
     
      
       
        LOOP
       
+
       
        
- 
+
 <<label>>
 LOOP
     statements
 END LOOP;
- 
+
         An unconditional loop that must be terminated explicitly
         by an EXIT statement. The optional label can be used by
         EXIT statements of nested loops to specify which level of
@@ -967,11 +1011,12 @@ END LOOP;
       
        EXIT
       
+
       
        
- 
+
 EXIT  label   WHEN expression ;
- 
+
         If no label is given,
         the innermost loop is terminated and the
         statement following END LOOP is executed next.
@@ -981,9 +1026,10 @@ EXIT  label   WHEN 
         continues with the statement after the loops/blocks corresponding
         END.
        
+
        
         Examples:
- 
+
 LOOP
     -- some computations
     IF count > 0 THEN
@@ -1002,7 +1048,7 @@ BEGIN
         EXIT;  -- illegal. Can't use EXIT outside of a LOOP
     END IF;
 END;
-        
+
        
       
      
@@ -1011,23 +1057,20 @@ END;
       
        WHILE
       
+
       
        
         With the WHILE statement, you can loop through a
         sequence of statements as long as the evaluation of
         the condition expression is true.
-       
-       
+
 <<label>>
 WHILE expression LOOP
     statements
 END LOOP;
-       
+
         For example:
-       
+
 WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP
     -- some computations here
 END LOOP;
@@ -1035,7 +1078,7 @@ END LOOP;
 WHILE NOT boolean_expression LOOP
     -- some computations here
 END LOOP;
- 
+
        
       
      
@@ -1044,24 +1087,27 @@ END LOOP;
       
        FOR
       
+
       
        
- 
+
 <<label>>
 FOR name IN  REVERSE  expression .. expression LOOP
     statements
 END LOOP;
- 
+
         A loop that iterates over a range of integer values. The variable
         name is automatically created as type
         integer and exists only inside the loop. The two expressions giving
         the lower and upper bound of the range are evaluated only when entering
         the loop. The iteration step is always 1.
        
+
        
-        Some examples of FOR loops (see  for
-        iterating over records in FOR loops):
+        Some examples of FOR loops (see 
+        linkend="plpgsql-description-records"> for iterating over
+        records in FOR loops):
+
 FOR i IN 1..10 LOOP
   -- some expressions here
 
@@ -1071,7 +1117,7 @@ END LOOP;
 FOR i IN REVERSE 1..10 LOOP
     -- some expressions here
 END LOOP;
- 
+
        
       
      
@@ -1083,6 +1129,7 @@ END LOOP;
 
   
    Working with RECORDs
+
    
     Records are similar to rowtypes, but they have no predefined structure.
     They are used in selections and FOR loops to hold one actual
@@ -1091,46 +1138,51 @@ END LOOP;
 
    
     Declaration
+
     
      One variables of type RECORD can be used for different
      selections. Accessing a record or an attempt to assign
      a value to a record field when there is no actual row in it results
      in a runtime error. They can be declared like this:
     
+
     
- 
+
 name RECORD;
- 
+
     
    
+
    
     Assignments
+
     
      An assignment of a complete selection into a record or row can
      be done by:
- 
+
 SELECT  INTO target expressions FROM ...;
-     target can be a record, a row variable or a
-     comma separated list of variables and record-/row-fields. Note that
-          this is quite different from Postgres' normal interpretation of
-          SELECT INTO, which is that the INTO target is a newly created table.
-          (If you want to create a table from a SELECT result inside a PL/pgSQL
-          function, use the equivalent syntax CREATE TABLE AS SELECT.)
+
+     target can be a record, a row variable
+     or a comma separated list of variables and
+     record-/row-fields. Note that this is quite different from
+     Postgres' normal interpretation of SELECT INTO, which is that the
+     INTO target is a newly created table.  (If you want to create a
+     table from a SELECT result inside a PL/pgSQL function, use the
+     equivalent syntax CREATE TABLE AS SELECT.)
     
+
     
      If a row or a variable list is used as target, the selected values
      must exactly match the structure of the target(s) or a runtime error
      occurs. The FROM keyword can be followed by any valid qualification,
      grouping, sorting etc. that can be given for a SELECT statement.
     
+
     
      Once a record or row has been assigned to a RECORD variable,
      you can use the "." (dot) notation to access fields in that
      record:
-    
-    
+
 DECLARE
     users_rec RECORD;
     full_name varchar;
@@ -1138,26 +1190,29 @@ BEGIN
     SELECT INTO users_rec * FROM users WHERE user_id=3;
 
   full_name := users_rec.first_name || '' '' || users_rec.last_name;
-     
+
     
+
     
-     There is a special variable named FOUND of type boolean that can be used
-     immediately after a SELECT INTO to check if an assignment had success.
+     There is a special variable named FOUND of type
+     boolean that can be used immediately after a SELECT
+     INTO to check if an assignment had success.
   
-     
+
 SELECT INTO myrec * FROM EMP WHERE empname = myname;
 IF NOT FOUND THEN
     RAISE EXCEPTION ''employee % not found'', myname;
 END IF;
- 
+
 
      You can also use the IS NULL (or ISNULL) conditionals to
      test for NULLity of a RECORD/ROW. If the selection returns
      multiple rows, only the first is moved into the target
      fields. All others are silently discarded.
     
+
     
- 
+
 DECLARE
     users_rec RECORD;
     full_name varchar;
@@ -1170,9 +1225,10 @@ BEGIN
         return ''http://'';
     END IF;
 END;
- 
+
     
    
+
    
     Iterating Through Records
 
@@ -1180,20 +1236,19 @@ END;
      Using a special type of FOR loop, you can iterate through
      the results of a query and manipulate that data
      accordingly. The syntax is as follow:
-    
-    
+
 <<label>>
 FOR record | row IN select_clause LOOP
     statements
 END LOOP;
- 
+
      The record or row is assigned all the rows 
      resulting from the select clause and the loop body executed 
      for each. Here is an example:
     
+
     
- 
+
 create function cs_refresh_mviews () returns integer as '
 DECLARE
      mviews RECORD;
@@ -1218,60 +1273,62 @@ BEGIN
      return 1;
 end;
 ' language 'plpgsql';
- 
+
 
-          If the loop is terminated with an EXIT statement, 
-     the last assigned row is still accessible after the loop.
+     If the loop is terminated with an EXIT statement, the last
+     assigned row is still accessible after the loop.
     
-        
-         The FOR-IN EXECUTE statement is another way to iterate over
-         records:
-        
-        
+
+    
+     The FOR-IN EXECUTE statement is another way to iterate over
+     records:
+
 <<label>>
 FOR record | row IN EXECUTE text_expression LOOP 
     statements
 END LOOP;
- 
-         This is like the previous form, except that the source SELECT
-         statement is specified as a string expression, which is evaluated
-         and re-planned on each entry to the FOR loop.  This allows the
-         programmer to choose the speed of a pre-planned query or the
-         flexibility of a dynamic query, just as with a plain EXECUTE
-         statement.
-        
+
+     This is like the previous form, except that the source SELECT
+     statement is specified as a string expression, which is evaluated
+     and re-planned on each entry to the FOR loop.  This allows the
+     programmer to choose the speed of a pre-planned query or the
+     flexibility of a dynamic query, just as with a plain EXECUTE
+     statement.
+    
    
   
 
   
-      Aborting and Messages
+   Aborting and Messages
 
    
     Use the RAISE statement to throw messages into the 
     Postgres elog mechanism.
 
- 
+
 RAISE level 'format' identifier ...;
+
+
     Inside the format, % is used as a placeholder for the
     subsequent comma-separated identifiers. Possible levels are
     DEBUG (silently suppressed in production running databases), NOTICE 
     (written into the database log and forwarded to the client application)
     and EXCEPTION (written into the database log and aborting the transaction).
    
+
    
- 
+
 RAISE NOTICE ''Id number '' || key || '' not found!'';
 RAISE NOTICE ''Calling cs_create_job(%)'',v_job_id;
-    
+
     In this last example, v_job_id will replace the % in the
     string.
    
+
    
- 
+
 RAISE EXCEPTION ''Inexistent ID --> %'',user_id;
-    
+
     This will abort the transaction and write to the database log.
    
      
@@ -1288,6 +1345,7 @@ RAISE EXCEPTION ''Inexistent ID --> %'',user_id;
      the whole transaction gets aborted and the system jumps back
      into the main loop to get the next query from the client application.
     
+
     
      It is possible to hook into the error mechanism to notice that this
      happens. But currently it is impossible to tell what really
@@ -1299,6 +1357,7 @@ RAISE EXCEPTION ''Inexistent ID --> %'',user_id;
      is aborted, is already sent to the client application, so resuming
      operation does not make any sense.
     
+
     
      Thus, the only thing PL/pgSQL currently does when it encounters
      an abort during execution of a function or trigger
@@ -1313,23 +1372,22 @@ RAISE EXCEPTION ''Inexistent ID --> %'',user_id;
    
 
  
-    Trigger Procedures
+  Trigger Procedures
 
-  
-   Description
-   
-    PL/pgSQL can be used to define trigger procedures. They are created
-    with the usual CREATE FUNCTION command as a function with no
-    arguments and a return type of OPAQUE.
-   
-   
-    There are some Postgres specific details
-    in functions used as trigger procedures.
-   
-   
-    First they have some special variables created automatically in the 
-    top-level blocks declaration section. They are
-   
+  
+   PL/pgSQL can be used to define trigger procedures. They are created
+   with the usual CREATE FUNCTION command as a function with no
+   arguments and a return type of OPAQUE.
+  
+
+  
+   There are some Postgres specific details
+   in functions used as trigger procedures.
+  
+
+  
+   First they have some special variables created automatically in the 
+   top-level blocks declaration section. They are
    
    
     
@@ -1438,6 +1496,7 @@ RAISE EXCEPTION ''Inexistent ID --> %'',user_id;
      
     
    
+  
 
    
     Second they must return either NULL or a record/row containing
@@ -1450,7 +1509,6 @@ RAISE EXCEPTION ''Inexistent ID --> %'',user_id;
     in NEW and return that or to build a complete new record/row to
     return.
    
-  
 
    
     A PL/pgSQL Trigger Procedure Example
@@ -1461,12 +1519,13 @@ RAISE EXCEPTION ''Inexistent ID --> %'',user_id;
      row. And it ensures that an employees name is given and that the
      salary is a positive value.
 
- 
+
 CREATE TABLE emp (
     empname text,
     salary integer,
     last_date timestamp,
-    last_user text);
+    last_user text
+);
 
 CREATE FUNCTION emp_stamp () RETURNS OPAQUE AS '
     BEGIN
@@ -1492,10 +1551,10 @@ CREATE FUNCTION emp_stamp () RETURNS OPAQUE AS '
 
 CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
     FOR EACH ROW EXECUTE PROCEDURE emp_stamp();
- 
+
     
    
-   
+  
 
   
 
@@ -1519,10 +1578,10 @@ CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
     in future versions of Postgres will be
     forward compatible.
    
+
    
     For a detailed explanation and examples of how to escape single
-    quotes in different situations, please see  in
-    Porting From Oracle PL/SQL.
+    quotes in different situations, please see .
    
 
    
@@ -1535,13 +1594,13 @@ CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
      one, returning the incremented value.
     
 
- 
+
 CREATE FUNCTION add_one (integer) RETURNS integer AS '
     BEGIN
         RETURN $1 + 1;
     END;
 ' LANGUAGE 'plpgsql';
- 
+
    
 
    
@@ -1552,29 +1611,30 @@ CREATE FUNCTION add_one (integer) RETURNS integer AS '
      returns the result of concatenating them.
     
 
- 
+
 CREATE FUNCTION concat_text (text, text) RETURNS text AS '
     BEGIN
         RETURN $1 || $2;
     END;
 ' LANGUAGE 'plpgsql';
- 
+
    
 
    
     A PL/pgSQL Function on Composite Type
 
     
-  In this example, we take EMP (a table) and an integer as
-  arguments to our function, which returns a boolean. If the
-  "salary" field of the EMP table is NULL, we return
-  "f". Otherwise we compare with that field with the integer
-  passed to the function and return the boolean result of the
-  comparison (t or f). This is the PL/pgSQL equivalent to the 
-    example from the C functions.
+     In this example, we take EMP (a table) and an
+     integer as arguments to our function, which returns
+     a boolean. If the "salary" field of the EMP table is
+     NULL, we return "f". Otherwise we compare with
+     that field with the integer passed to the function
+     and return the boolean result of the comparison (t
+     or f). This is the PL/pgSQL equivalent to the example from the C
+     functions.
     
-    
- 
+
+
 CREATE FUNCTION c_overpaid (EMP, integer) RETURNS boolean AS '
     DECLARE
         emprec ALIAS FOR $1;
@@ -1586,9 +1646,8 @@ CREATE FUNCTION c_overpaid (EMP, integer) RETURNS boolean AS '
         RETURN emprec.salary > sallim;
     END;
 ' LANGUAGE 'plpgsql';
-    
+
+   
   
 
   
@@ -1681,7 +1740,7 @@ CREATE FUNCTION c_overpaid (EMP, integer) RETURNS boolean AS '
      
       
        In PostgreSQL you need to escape single
-       quotes. See .
+       quotes. See .
       
      
     
@@ -1695,7 +1754,7 @@ CREATE FUNCTION c_overpaid (EMP, integer) RETURNS boolean AS '
      function definition. This can lead to quite amusing code at
      times, especially if you are creating a function that generates
      other function(s), as in 
-     this example.  
+     .  
      One thing to keep in mind
      when escaping lots of single quotes is that, except for the
      beginning/ending quotes, all the others will come in even
@@ -1703,7 +1762,7 @@ CREATE FUNCTION c_overpaid (EMP, integer) RETURNS boolean AS '
     
 
     
-      gives the scoop.  (You'll
+      gives the scoop.  (You'll
      love this little chart.)
     
 
@@ -1776,7 +1835,7 @@ a_output := a_output || '' AND name
          (which accounts for 8 quotes) and
          terminate that string (2 more).  You will probably only need
          that if you were using a function to generate other functions
-         (like in ).
+         (like in ).
         
         
 a_output := a_output || '' if v_'' || 
@@ -1865,7 +1924,7 @@ SHOW ERRORS;
         On PostgreSQL functions are created using single quotes as
         delimiters, so you have to escape single quotes inside your
         functions (which can be quite annoying at times; see 
-        linkend="plpgsql-quote">this example).
+        linkend="plpgsql-quote">).
        
       
 
@@ -1986,7 +2045,7 @@ end;
      The following Oracle PL/SQL procedure is used to parse a URL and
      return several elements (host, path and query). It is an
      procedure because in PL/pgSQL functions only one value can be returned
-     (see ).  In
+     (see ).  In
      PostgreSQL, one way to work around this is to split the procedure
      in three different functions: one to return the host, another for
      the path and another for the query.
@@ -2036,34 +2095,34 @@ show errors;
      Here is how this procedure could be translated for PostgreSQL:
 
 
- drop function cs_parse_url_host(varchar); 
- create function cs_parse_url_host(varchar) returns varchar as ' 
- declare 
-     v_url ALIAS FOR $1; 
-     v_host varchar; 
-     v_path varchar; 
-     a_pos1 integer; 
-     a_pos2 integer; 
-     a_pos3 integer; 
- begin 
-     v_host := NULL; 
-     a_pos1 := instr(v_url,''//''); 
-   
-     if a_pos1 = 0 then 
-         return '''';  -- Return a blank
-     end if; 
-     a_pos2 := instr(v_url,''/'',a_pos1 + 2); 
-     if a_pos2 = 0 then 
-         v_host := substr(v_url, a_pos1 + 2); 
-         v_path := ''/''; 
-         return v_host; 
-     end if; 
-     v_host := substr(v_url, a_pos1 + 2, a_pos2 - a_pos1 - 2 ); 
-     return v_host; 
- end; 
- ' language 'plpgsql';
+drop function cs_parse_url_host(varchar); 
+create function cs_parse_url_host(varchar) returns varchar as ' 
+declare 
+    v_url ALIAS FOR $1; 
+    v_host varchar; 
+    v_path varchar; 
+    a_pos1 integer; 
+    a_pos2 integer; 
+    a_pos3 integer; 
+begin 
+    v_host := NULL; 
+    a_pos1 := instr(v_url,''//''); 
+
+    if a_pos1 = 0 then 
+        return '''';  -- Return a blank
+    end if; 
+
+    a_pos2 := instr(v_url,''/'',a_pos1 + 2); 
+    if a_pos2 = 0 then 
+        v_host := substr(v_url, a_pos1 + 2); 
+        v_path := ''/''; 
+        return v_host; 
+    end if; 
+
+    v_host := substr(v_url, a_pos1 + 2, a_pos2 - a_pos1 - 2 ); 
+    return v_host; 
+end; 
+' language 'plpgsql';
 
     
    
@@ -2075,7 +2134,7 @@ show errors;
      I got tired of doing this and created my own
      instr functions that behave exactly like
      Oracle's (it makes life easier). See the 
-     linkend="plpgsql-porting-appendix"> for the code.
+     linkend="plpgsql-porting-appendix"> for the code.
     
    
   
@@ -2098,15 +2157,15 @@ show errors;
 create or replace procedure cs_create_job(v_job_id in integer)
 is
     a_running_job_count integer;
-    pragma autonomous_transaction;
+    pragma autonomous_transaction;
 begin
-    lock table cs_jobs in exclusive mode;
+    lock table cs_jobs in exclusive mode;
 
     select count(*) into a_running_job_count from cs_jobs
     where end_stamp is null;
 
     if a_running_job_count > 0 then
-        commit; -- free lock
+        commit; -- free lock
         raise_application_error(-20000, 'Unable to create a new job: a job is currently running.');
     end if;
 
@@ -2115,7 +2174,7 @@ begin
 
     begin
         insert into cs_jobs(job_id, start_stamp) values(v_job_id, sysdate);
-        exception when dup_val_on_index then null; -- don't worry if it already exists
+        exception when dup_val_on_index then null; -- don't worry if it already exists
     end;
     commit;
 end;
@@ -2190,7 +2249,7 @@ begin
         insert into cs_jobs(job_id, start_stamp) values(v_job_id, sysdate());
         return 1;
     ELSE
-        raise NOTICE ''Job already running.'';
+        raise NOTICE ''Job already running.'';
     END IF;
 
     return 0;
@@ -2301,7 +2360,7 @@ END;
      nicely, but you have to remember to use
      quote_literal(TEXT) and
      quote_string(TEXT) as described in 
-     linkend="plpgsql-statements">>.  Constructs of the type
+     linkend="plpgsql-statements-executing-dyn-queries">.  Constructs of the type
      EXECUTE ''SELECT * from $1''; will not work
      unless you use these functions.
     
@@ -2486,8 +2545,7 @@ CREATE FUNCTION instr(varchar, varchar, integer, integer) RETURNS integer AS '
         return [expr $pos + 1]
     }
 ' LANGUAGE 'pltcl';
-    
+